diff options
Diffstat (limited to '')
-rw-r--r-- | recovery.cpp | 768 |
1 files changed, 417 insertions, 351 deletions
diff --git a/recovery.cpp b/recovery.cpp index 07bd7b9d4..07ec5cfb6 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -50,9 +50,9 @@ #include <bootloader_message/bootloader_message.h> #include <cutils/android_reboot.h> #include <cutils/properties.h> /* for property_list */ -#include <healthd/BatteryMonitor.h> -#include <private/android_logger.h> /* private pmsg functions */ -#include <private/android_filesystem_config.h> /* for AID_SYSTEM */ +#include <health2/Health.h> +#include <private/android_filesystem_config.h> /* for AID_SYSTEM */ +#include <private/android_logger.h> /* private pmsg functions */ #include <selinux/android.h> #include <selinux/label.h> #include <selinux/selinux.h> @@ -61,13 +61,13 @@ #include "adb_install.h" #include "common.h" #include "device.h" -#include "error_code.h" #include "fuse_sdcard_provider.h" #include "fuse_sideload.h" #include "install.h" #include "minadbd/minadbd.h" #include "minui/minui.h" #include "otautil/DirUtil.h" +#include "otautil/error_code.h" #include "roots.h" #include "rotate_logs.h" #include "screen_ui.h" @@ -108,6 +108,7 @@ static const char *CONVERT_FBE_DIR = "/tmp/convert_fbe"; static const char *CONVERT_FBE_FILE = "/tmp/convert_fbe/convert_fbe"; static const char *CACHE_ROOT = "/cache"; static const char *DATA_ROOT = "/data"; +static const char* METADATA_ROOT = "/metadata"; static const char *SDCARD_ROOT = "/sdcard"; static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log"; static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install"; @@ -125,6 +126,10 @@ static const int BATTERY_WITH_CHARGER_OK_PERCENTAGE = 15; static constexpr const char* RECOVERY_WIPE = "/etc/recovery.wipe"; static constexpr const char* DEFAULT_LOCALE = "en-US"; +// We define RECOVERY_API_VERSION in Android.mk, which will be picked up by build system and packed +// into target_files.zip. Assert the version defined in code and in Android.mk are consistent. +static_assert(kRecoveryApiVersion == RECOVERY_API_VERSION, "Mismatching recovery API versions."); + static std::string locale; static bool has_cache = false; @@ -179,19 +184,19 @@ struct selabel_handle* sehandle; * 7b. the user reboots (pulling the battery, etc) into the main system */ -// open a given path, mounting partitions as necessary -FILE* fopen_path(const char *path, const char *mode) { - if (ensure_path_mounted(path) != 0) { - LOG(ERROR) << "Can't mount " << path; - return NULL; - } - - // When writing, try to create the containing directory, if necessary. - // Use generous permissions, the system (init.rc) will reset them. - if (strchr("wa", mode[0])) dirCreateHierarchy(path, 0777, NULL, 1, sehandle); +// Open a given path, mounting partitions as necessary. +FILE* fopen_path(const char* path, const char* mode) { + if (ensure_path_mounted(path) != 0) { + LOG(ERROR) << "Can't mount " << path; + return nullptr; + } - FILE *fp = fopen(path, mode); - return fp; + // When writing, try to create the containing directory, if necessary. Use generous permissions, + // the system (init.rc) will reset them. + if (strchr("wa", mode[0])) { + mkdir_recursively(path, 0777, true, sehandle); + } + return fopen(path, mode); } // close a file, log an error if the error indicator is set @@ -594,7 +599,7 @@ static bool erase_volume(const char* volume) { if (is_cache) { // Re-create the log dir and write back the log entries. if (ensure_path_mounted(CACHE_LOG_DIR) == 0 && - dirCreateHierarchy(CACHE_LOG_DIR, 0777, nullptr, false, sehandle) == 0) { + mkdir_recursively(CACHE_LOG_DIR, 0777, false, sehandle) == 0) { for (const auto& log : log_files) { if (!android::base::WriteStringToFile(log.data, log.name, log.sb.st_mode, log.sb.st_uid, log.sb.st_gid)) { @@ -748,11 +753,19 @@ static bool wipe_data(Device* device) { modified_flash = true; ui->Print("\n-- Wiping data...\n"); - bool success = - device->PreWipeData() && - erase_volume("/data") && - (has_cache ? erase_volume("/cache") : true) && - device->PostWipeData(); + bool success = device->PreWipeData(); + if (success) { + success &= erase_volume(DATA_ROOT); + if (has_cache) { + success &= erase_volume(CACHE_ROOT); + } + if (volume_for_mount_point(METADATA_ROOT) != nullptr) { + success &= erase_volume(METADATA_ROOT); + } + } + if (success) { + success &= device->PostWipeData(); + } ui->Print("Data wipe %s.\n", success ? "complete" : "failed"); return success; } @@ -1187,6 +1200,11 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { run_graphics_test(); break; + case Device::RUN_LOCALE_TEST: { + ScreenRecoveryUI* screen_ui = static_cast<ScreenRecoveryUI*>(ui); + screen_ui->CheckBackgroundTextImages(locale); + break; + } case Device::MOUNT_SYSTEM: // For a system image built with the root directory (i.e. system_root_image == "true"), we // mount it to /system_root, and symlink /system to /system_root/system to make adb shell @@ -1205,9 +1223,8 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { } } -static void -print_property(const char *key, const char *name, void *cookie) { - printf("%s=%s\n", key, name); +static void print_property(const char* key, const char* name, void* /* cookie */) { + printf("%s=%s\n", key, name); } static std::string load_locale_from_cache() { @@ -1241,70 +1258,91 @@ void ui_print(const char* format, ...) { static constexpr char log_characters[] = "VDIWEF"; -void UiLogger(android::base::LogId id, android::base::LogSeverity severity, - const char* tag, const char* file, unsigned int line, - const char* message) { - if (severity >= android::base::ERROR && ui != nullptr) { - ui->Print("E:%s\n", message); - } else { - fprintf(stdout, "%c:%s\n", log_characters[severity], message); - } +void UiLogger(android::base::LogId /* id */, android::base::LogSeverity severity, + const char* /* tag */, const char* /* file */, unsigned int /* line */, + const char* message) { + if (severity >= android::base::ERROR && ui != nullptr) { + ui->Print("E:%s\n", message); + } else { + fprintf(stdout, "%c:%s\n", log_characters[severity], message); + } } static bool is_battery_ok() { - struct healthd_config healthd_config = { - .batteryStatusPath = android::String8(android::String8::kEmptyString), - .batteryHealthPath = android::String8(android::String8::kEmptyString), - .batteryPresentPath = android::String8(android::String8::kEmptyString), - .batteryCapacityPath = android::String8(android::String8::kEmptyString), - .batteryVoltagePath = android::String8(android::String8::kEmptyString), - .batteryTemperaturePath = android::String8(android::String8::kEmptyString), - .batteryTechnologyPath = android::String8(android::String8::kEmptyString), - .batteryCurrentNowPath = android::String8(android::String8::kEmptyString), - .batteryCurrentAvgPath = android::String8(android::String8::kEmptyString), - .batteryChargeCounterPath = android::String8(android::String8::kEmptyString), - .batteryFullChargePath = android::String8(android::String8::kEmptyString), - .batteryCycleCountPath = android::String8(android::String8::kEmptyString), - .energyCounter = NULL, - .boot_min_cap = 0, - .screen_on = NULL - }; - healthd_board_init(&healthd_config); - - android::BatteryMonitor monitor; - monitor.init(&healthd_config); - - int wait_second = 0; - while (true) { - int charge_status = monitor.getChargeStatus(); - // Treat unknown status as charged. - bool charged = (charge_status != android::BATTERY_STATUS_DISCHARGING && - charge_status != android::BATTERY_STATUS_NOT_CHARGING); - android::BatteryProperty capacity; - android::status_t status = monitor.getProperty(android::BATTERY_PROP_CAPACITY, &capacity); - ui_print("charge_status %d, charged %d, status %d, capacity %lld\n", charge_status, - charged, status, capacity.valueInt64); - // At startup, the battery drivers in devices like N5X/N6P take some time to load - // the battery profile. Before the load finishes, it reports value 50 as a fake - // capacity. BATTERY_READ_TIMEOUT_IN_SEC is set that the battery drivers are expected - // to finish loading the battery profile earlier than 10 seconds after kernel startup. - if (status == 0 && capacity.valueInt64 == 50) { - if (wait_second < BATTERY_READ_TIMEOUT_IN_SEC) { - sleep(1); - wait_second++; - continue; - } - } - // If we can't read battery percentage, it may be a device without battery. In this - // situation, use 100 as a fake battery percentage. - if (status != 0) { - capacity.valueInt64 = 100; - } - return (charged && capacity.valueInt64 >= BATTERY_WITH_CHARGER_OK_PERCENTAGE) || - (!charged && capacity.valueInt64 >= BATTERY_OK_PERCENTAGE); + using android::hardware::health::V1_0::BatteryStatus; + using android::hardware::health::V2_0::Result; + using android::hardware::health::V2_0::toString; + using android::hardware::health::V2_0::implementation::Health; + + struct healthd_config healthd_config = { + .batteryStatusPath = android::String8(android::String8::kEmptyString), + .batteryHealthPath = android::String8(android::String8::kEmptyString), + .batteryPresentPath = android::String8(android::String8::kEmptyString), + .batteryCapacityPath = android::String8(android::String8::kEmptyString), + .batteryVoltagePath = android::String8(android::String8::kEmptyString), + .batteryTemperaturePath = android::String8(android::String8::kEmptyString), + .batteryTechnologyPath = android::String8(android::String8::kEmptyString), + .batteryCurrentNowPath = android::String8(android::String8::kEmptyString), + .batteryCurrentAvgPath = android::String8(android::String8::kEmptyString), + .batteryChargeCounterPath = android::String8(android::String8::kEmptyString), + .batteryFullChargePath = android::String8(android::String8::kEmptyString), + .batteryCycleCountPath = android::String8(android::String8::kEmptyString), + .energyCounter = NULL, + .boot_min_cap = 0, + .screen_on = NULL + }; + + auto health = + android::hardware::health::V2_0::implementation::Health::initInstance(&healthd_config); + + int wait_second = 0; + while (true) { + auto charge_status = BatteryStatus::UNKNOWN; + health + ->getChargeStatus([&charge_status](auto res, auto out_status) { + if (res == Result::SUCCESS) { + charge_status = out_status; + } + }) + .isOk(); // should not have transport error + + // Treat unknown status as charged. + bool charged = (charge_status != BatteryStatus::DISCHARGING && + charge_status != BatteryStatus::NOT_CHARGING); + + Result res = Result::UNKNOWN; + int32_t capacity = INT32_MIN; + health + ->getCapacity([&res, &capacity](auto out_res, auto out_capacity) { + res = out_res; + capacity = out_capacity; + }) + .isOk(); // should not have transport error + + ui_print("charge_status %d, charged %d, status %s, capacity %" PRId32 "\n", charge_status, + charged, toString(res).c_str(), capacity); + // At startup, the battery drivers in devices like N5X/N6P take some time to load + // the battery profile. Before the load finishes, it reports value 50 as a fake + // capacity. BATTERY_READ_TIMEOUT_IN_SEC is set that the battery drivers are expected + // to finish loading the battery profile earlier than 10 seconds after kernel startup. + if (res == Result::SUCCESS && capacity == 50) { + if (wait_second < BATTERY_READ_TIMEOUT_IN_SEC) { + sleep(1); + wait_second++; + continue; + } + } + // If we can't read battery percentage, it may be a device without battery. In this + // situation, use 100 as a fake battery percentage. + if (res != Result::SUCCESS) { + capacity = 100; + } + return (charged && capacity >= BATTERY_WITH_CHARGER_OK_PERCENTAGE) || + (!charged && capacity >= BATTERY_OK_PERCENTAGE); } } +// Set the retry count to |retry_count| in BCB. static void set_retry_bootloader_message(int retry_count, const std::vector<std::string>& args) { std::vector<std::string> options; for (const auto& arg : args) { @@ -1313,8 +1351,8 @@ static void set_retry_bootloader_message(int retry_count, const std::vector<std: } } - // Increment the retry counter by 1. - options.push_back(android::base::StringPrintf("--retry_count=%d", retry_count + 1)); + // Update the retry counter in BCB. + options.push_back(android::base::StringPrintf("--retry_count=%d", retry_count)); std::string err; if (!update_bootloader_message(options, &err)) { LOG(ERROR) << err; @@ -1349,303 +1387,331 @@ static void log_failure_code(ErrorCode code, const char *update_package) { } int main(int argc, char **argv) { - // 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 const char filter[] = "recovery/"; - // Do we need to rotate? - bool doRotate = false; - - __android_log_pmsg_file_read( - LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, - logbasename, &doRotate); - // Take action to refresh pmsg contents - __android_log_pmsg_file_read( - LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, - logrotate, &doRotate); - - // If this binary is started with the single argument "--adbd", - // instead of being the normal recovery binary, it turns into kind - // of a stripped-down version of adbd that only supports the - // 'sideload' command. Note this must be a real argument, not - // anything in the command file or bootloader control block; the - // only way recovery should be run with this argument is when it - // starts a copy of itself from the apply_from_adb() function. - if (argc == 2 && strcmp(argv[1], "--adbd") == 0) { - minadbd_main(); - return 0; - } - - time_t start = time(NULL); - - // redirect_stdio should be called only in non-sideload mode. Otherwise - // we may have two logger instances with different timestamps. - redirect_stdio(TEMPORARY_LOG_FILE); - - printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start)); - - load_volume_table(); - has_cache = volume_for_path(CACHE_ROOT) != nullptr; - - std::vector<std::string> args = get_args(argc, argv); - std::vector<char*> args_to_parse(args.size()); - std::transform(args.cbegin(), args.cend(), args_to_parse.begin(), - [](const std::string& arg) { return const_cast<char*>(arg.c_str()); }); - - const char *update_package = NULL; - bool should_wipe_data = false; - bool should_prompt_and_wipe_data = false; - bool should_wipe_cache = false; - bool should_wipe_ab = false; - size_t wipe_package_size = 0; - bool show_text = false; - bool sideload = false; - bool sideload_auto_reboot = false; - bool just_exit = false; - bool shutdown_after = false; - int retry_count = 0; - bool security_update = false; - - int arg; - int option_index; - while ((arg = getopt_long(args_to_parse.size(), args_to_parse.data(), "", OPTIONS, - &option_index)) != -1) { - switch (arg) { - case 'n': android::base::ParseInt(optarg, &retry_count, 0); break; - case 'u': update_package = optarg; break; - case 'w': should_wipe_data = true; break; - case 'c': should_wipe_cache = true; break; - case 't': show_text = true; break; - case 's': sideload = true; break; - case 'a': sideload = true; sideload_auto_reboot = true; break; - case 'x': just_exit = true; break; - case 'l': locale = optarg; break; - case 'p': shutdown_after = true; break; - case 'r': reason = optarg; break; - case 'e': security_update = true; break; - case 0: { - std::string option = OPTIONS[option_index].name; - if (option == "wipe_ab") { - should_wipe_ab = true; - } else if (option == "wipe_package_size") { - android::base::ParseUint(optarg, &wipe_package_size); - } else if (option == "prompt_and_wipe_data") { - should_prompt_and_wipe_data = true; - } - break; - } - case '?': - LOG(ERROR) << "Invalid command argument"; - continue; + // 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 const char filter[] = "recovery/"; + // Do we need to rotate? + bool doRotate = false; + + __android_log_pmsg_file_read(LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, logbasename, &doRotate); + // Take action to refresh pmsg contents + __android_log_pmsg_file_read(LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, logrotate, &doRotate); + + // If this binary is started with the single argument "--adbd", + // instead of being the normal recovery binary, it turns into kind + // of a stripped-down version of adbd that only supports the + // 'sideload' command. Note this must be a real argument, not + // anything in the command file or bootloader control block; the + // only way recovery should be run with this argument is when it + // starts a copy of itself from the apply_from_adb() function. + if (argc == 2 && strcmp(argv[1], "--adbd") == 0) { + minadbd_main(); + return 0; + } + + 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(TEMPORARY_LOG_FILE); + + printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start)); + + load_volume_table(); + has_cache = volume_for_mount_point(CACHE_ROOT) != nullptr; + + std::vector<std::string> args = get_args(argc, argv); + std::vector<char*> args_to_parse(args.size()); + std::transform(args.cbegin(), args.cend(), args_to_parse.begin(), + [](const std::string& arg) { return const_cast<char*>(arg.c_str()); }); + + const char* update_package = nullptr; + bool should_wipe_data = false; + bool should_prompt_and_wipe_data = false; + bool should_wipe_cache = false; + bool should_wipe_ab = false; + size_t wipe_package_size = 0; + bool show_text = false; + bool sideload = false; + bool sideload_auto_reboot = false; + bool just_exit = false; + bool shutdown_after = false; + int retry_count = 0; + bool security_update = false; + + int arg; + int option_index; + while ((arg = getopt_long(args_to_parse.size(), args_to_parse.data(), "", OPTIONS, + &option_index)) != -1) { + switch (arg) { + case 'n': + android::base::ParseInt(optarg, &retry_count, 0); + break; + case 'u': + update_package = optarg; + break; + case 'w': + should_wipe_data = true; + break; + case 'c': + should_wipe_cache = true; + break; + case 't': + show_text = true; + break; + case 's': + sideload = true; + break; + case 'a': + sideload = true; + sideload_auto_reboot = true; + break; + case 'x': + just_exit = true; + break; + case 'l': + locale = optarg; + break; + case 'p': + shutdown_after = true; + break; + case 'r': + reason = optarg; + break; + case 'e': + security_update = true; + break; + case 0: { + std::string option = OPTIONS[option_index].name; + if (option == "wipe_ab") { + should_wipe_ab = true; + } else if (option == "wipe_package_size") { + android::base::ParseUint(optarg, &wipe_package_size); + } else if (option == "prompt_and_wipe_data") { + should_prompt_and_wipe_data = true; } + break; + } + case '?': + LOG(ERROR) << "Invalid command argument"; + continue; } + } - if (locale.empty()) { - if (has_cache) { - locale = load_locale_from_cache(); - } + if (locale.empty()) { + if (has_cache) { + locale = load_locale_from_cache(); + } - if (locale.empty()) { - locale = DEFAULT_LOCALE; - } + if (locale.empty()) { + locale = DEFAULT_LOCALE; } + } - printf("locale is [%s]\n", locale.c_str()); - printf("stage is [%s]\n", stage.c_str()); - printf("reason is [%s]\n", reason); + printf("locale is [%s]\n", locale.c_str()); + printf("stage is [%s]\n", stage.c_str()); + printf("reason is [%s]\n", reason); - Device* device = make_device(); - if (android::base::GetBoolProperty("ro.boot.quiescent", false)) { - printf("Quiescent recovery mode.\n"); - ui = new StubRecoveryUI(); - } else { - ui = device->GetUI(); + Device* device = make_device(); + if (android::base::GetBoolProperty("ro.boot.quiescent", false)) { + printf("Quiescent recovery mode.\n"); + ui = new StubRecoveryUI(); + } else { + ui = device->GetUI(); - if (!ui->Init(locale)) { - printf("Failed to initialize UI, use stub UI instead.\n"); - ui = new StubRecoveryUI(); - } + if (!ui->Init(locale)) { + printf("Failed to initialize UI, use stub UI instead.\n"); + ui = new StubRecoveryUI(); } + } - // Set background string to "installing security update" for security update, - // otherwise set it to "installing system update". - ui->SetSystemUpdateText(security_update); + // Set background string to "installing security update" for security update, + // otherwise set it to "installing system update". + ui->SetSystemUpdateText(security_update); - int st_cur, st_max; - if (!stage.empty() && sscanf(stage.c_str(), "%d/%d", &st_cur, &st_max) == 2) { - ui->SetStage(st_cur, st_max); - } + int st_cur, st_max; + if (!stage.empty() && sscanf(stage.c_str(), "%d/%d", &st_cur, &st_max) == 2) { + ui->SetStage(st_cur, st_max); + } - ui->SetBackground(RecoveryUI::NONE); - if (show_text) ui->ShowText(true); + ui->SetBackground(RecoveryUI::NONE); + if (show_text) ui->ShowText(true); - sehandle = selinux_android_file_context_handle(); - selinux_android_set_sehandle(sehandle); - if (!sehandle) { - ui->Print("Warning: No file_contexts\n"); - } + sehandle = selinux_android_file_context_handle(); + selinux_android_set_sehandle(sehandle); + if (!sehandle) { + ui->Print("Warning: No file_contexts\n"); + } - device->StartRecovery(); + device->StartRecovery(); - printf("Command:"); - for (const auto& arg : args) { - printf(" \"%s\"", arg.c_str()); - } - printf("\n\n"); + printf("Command:"); + for (const auto& arg : args) { + printf(" \"%s\"", arg.c_str()); + } + printf("\n\n"); - property_list(print_property, NULL); - printf("\n"); + property_list(print_property, nullptr); + printf("\n"); - ui->Print("Supported API: %d\n", RECOVERY_API_VERSION); + ui->Print("Supported API: %d\n", kRecoveryApiVersion); - int status = INSTALL_SUCCESS; + int status = INSTALL_SUCCESS; - if (update_package != NULL) { - // It's not entirely true that we will modify the flash. But we want - // to log the update attempt since update_package is non-NULL. - modified_flash = true; + if (update_package != nullptr) { + // It's not entirely true that we will modify the flash. But we want + // to log the update attempt since update_package is non-NULL. + modified_flash = true; - if (!is_battery_ok()) { - ui->Print("battery capacity is not enough for installing package, needed is %d%%\n", - BATTERY_OK_PERCENTAGE); - // Log the error code to last_install when installation skips due to - // low battery. - log_failure_code(kLowBattery, update_package); - status = INSTALL_SKIPPED; - } else if (bootreason_in_blacklist()) { - // Skip update-on-reboot when bootreason is kernel_panic or similar - ui->Print("bootreason is in the blacklist; skip OTA installation\n"); - log_failure_code(kBootreasonInBlacklist, update_package); - status = INSTALL_SKIPPED; - } else { - status = install_package(update_package, &should_wipe_cache, - TEMPORARY_INSTALL_FILE, true, retry_count); - if (status == INSTALL_SUCCESS && should_wipe_cache) { - wipe_cache(false, device); - } - if (status != INSTALL_SUCCESS) { - ui->Print("Installation aborted.\n"); - // When I/O error happens, reboot and retry installation RETRY_LIMIT - // times before we abandon this OTA update. - if (status == INSTALL_RETRY && retry_count < RETRY_LIMIT) { - copy_logs(); - set_retry_bootloader_message(retry_count, args); - // Print retry count on screen. - ui->Print("Retry attempt %d\n", retry_count); - - // Reboot and retry the update - if (!reboot("reboot,recovery")) { - ui->Print("Reboot failed\n"); - } else { - while (true) { - pause(); - } - } - } - // If this is an eng or userdebug build, then automatically - // turn the text display on if the script fails so the error - // message is visible. - if (is_ro_debuggable()) { - ui->ShowText(true); - } - } - } - } else if (should_wipe_data) { - if (!wipe_data(device)) { - status = INSTALL_ERROR; - } - } else if (should_prompt_and_wipe_data) { - ui->ShowText(true); - ui->SetBackground(RecoveryUI::ERROR); - if (!prompt_and_wipe_data(device)) { - status = INSTALL_ERROR; - } - ui->ShowText(false); - } else if (should_wipe_cache) { - if (!wipe_cache(false, device)) { - status = INSTALL_ERROR; - } - } else if (should_wipe_ab) { - if (!wipe_ab_device(wipe_package_size)) { - status = INSTALL_ERROR; - } - } else if (sideload) { - // 'adb reboot sideload' acts the same as user presses key combinations - // to enter the sideload mode. When 'sideload-auto-reboot' is used, text - // display will NOT be turned on by default. And it will reboot after - // sideload finishes even if there are errors. Unless one turns on the - // text display during the installation. This is to enable automated - // testing. - if (!sideload_auto_reboot) { - ui->ShowText(true); - } - status = apply_from_adb(&should_wipe_cache, TEMPORARY_INSTALL_FILE); - if (status == INSTALL_SUCCESS && should_wipe_cache) { - if (!wipe_cache(false, device)) { - status = INSTALL_ERROR; + if (!is_battery_ok()) { + ui->Print("battery capacity is not enough for installing package, needed is %d%%\n", + BATTERY_OK_PERCENTAGE); + // Log the error code to last_install when installation skips due to + // low battery. + log_failure_code(kLowBattery, update_package); + status = INSTALL_SKIPPED; + } else if (bootreason_in_blacklist()) { + // Skip update-on-reboot when bootreason is kernel_panic or similar + ui->Print("bootreason is in the blacklist; skip OTA installation\n"); + log_failure_code(kBootreasonInBlacklist, update_package); + status = INSTALL_SKIPPED; + } else { + // It's a fresh update. Initialize the retry_count in the BCB to 1; therefore we can later + // identify the interrupted update due to unexpected reboots. + if (retry_count == 0) { + set_retry_bootloader_message(retry_count + 1, args); + } + + status = install_package(update_package, &should_wipe_cache, TEMPORARY_INSTALL_FILE, true, + retry_count); + if (status == INSTALL_SUCCESS && should_wipe_cache) { + wipe_cache(false, device); + } + if (status != INSTALL_SUCCESS) { + ui->Print("Installation aborted.\n"); + // When I/O error happens, reboot and retry installation RETRY_LIMIT + // times before we abandon this OTA update. + if (status == INSTALL_RETRY && retry_count < RETRY_LIMIT) { + copy_logs(); + retry_count += 1; + set_retry_bootloader_message(retry_count, args); + // Print retry count on screen. + ui->Print("Retry attempt %d\n", retry_count); + + // Reboot and retry the update + if (!reboot("reboot,recovery")) { + ui->Print("Reboot failed\n"); + } else { + while (true) { + pause(); } + } } - ui->Print("\nInstall from ADB complete (status: %d).\n", status); - if (sideload_auto_reboot) { - ui->Print("Rebooting automatically.\n"); + // If this is an eng or userdebug build, then automatically + // turn the text display on if the script fails so the error + // message is visible. + if (is_ro_debuggable()) { + ui->ShowText(true); } - } else if (!just_exit) { - // If this is an eng or userdebug build, automatically turn on the text display if no command - // is specified. Note that this should be called before setting the background to avoid - // flickering the background image. - if (is_ro_debuggable()) { - ui->ShowText(true); } - status = INSTALL_NONE; // No command specified - ui->SetBackground(RecoveryUI::NO_COMMAND); } + } else if (should_wipe_data) { + if (!wipe_data(device)) { + status = INSTALL_ERROR; + } + } else if (should_prompt_and_wipe_data) { + ui->ShowText(true); + ui->SetBackground(RecoveryUI::ERROR); + if (!prompt_and_wipe_data(device)) { + status = INSTALL_ERROR; + } + ui->ShowText(false); + } else if (should_wipe_cache) { + if (!wipe_cache(false, device)) { + status = INSTALL_ERROR; + } + } else if (should_wipe_ab) { + if (!wipe_ab_device(wipe_package_size)) { + status = INSTALL_ERROR; + } + } else if (sideload) { + // 'adb reboot sideload' acts the same as user presses key combinations + // to enter the sideload mode. When 'sideload-auto-reboot' is used, text + // display will NOT be turned on by default. And it will reboot after + // sideload finishes even if there are errors. Unless one turns on the + // text display during the installation. This is to enable automated + // testing. + if (!sideload_auto_reboot) { + ui->ShowText(true); + } + status = apply_from_adb(&should_wipe_cache, TEMPORARY_INSTALL_FILE); + if (status == INSTALL_SUCCESS && should_wipe_cache) { + if (!wipe_cache(false, device)) { + status = INSTALL_ERROR; + } + } + ui->Print("\nInstall from ADB complete (status: %d).\n", status); + if (sideload_auto_reboot) { + ui->Print("Rebooting automatically.\n"); + } + } else if (!just_exit) { + // If this is an eng or userdebug build, automatically turn on the text display if no command + // is specified. Note that this should be called before setting the background to avoid + // flickering the background image. + if (is_ro_debuggable()) { + ui->ShowText(true); + } + status = INSTALL_NONE; // No command specified + ui->SetBackground(RecoveryUI::NO_COMMAND); + } - if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) { - ui->SetBackground(RecoveryUI::ERROR); - if (!ui->IsTextVisible()) { - sleep(5); - } + if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) { + ui->SetBackground(RecoveryUI::ERROR); + if (!ui->IsTextVisible()) { + sleep(5); } + } - Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT; - // 1. If the recovery menu is visible, prompt and wait for commands. - // 2. If the state is INSTALL_NONE, wait for commands. (i.e. In user build, manually reboot into - // recovery to sideload a package.) - // 3. sideload_auto_reboot is an option only available in user-debug build, reboot the device - // without waiting. - // 4. In all other cases, reboot the device. Therefore, normal users will observe the device - // reboot after it shows the "error" screen for 5s. - if ((status == INSTALL_NONE && !sideload_auto_reboot) || ui->IsTextVisible()) { - Device::BuiltinAction temp = prompt_and_wait(device, status); - if (temp != Device::NO_ACTION) { - after = temp; - } + Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT; + // 1. If the recovery menu is visible, prompt and wait for commands. + // 2. If the state is INSTALL_NONE, wait for commands. (i.e. In user build, manually reboot into + // recovery to sideload a package.) + // 3. sideload_auto_reboot is an option only available in user-debug build, reboot the device + // without waiting. + // 4. In all other cases, reboot the device. Therefore, normal users will observe the device + // reboot after it shows the "error" screen for 5s. + if ((status == INSTALL_NONE && !sideload_auto_reboot) || ui->IsTextVisible()) { + Device::BuiltinAction temp = prompt_and_wait(device, status); + if (temp != Device::NO_ACTION) { + after = temp; } + } - // Save logs and clean up before rebooting or shutting down. - finish_recovery(); + // Save logs and clean up before rebooting or shutting down. + finish_recovery(); - switch (after) { - case Device::SHUTDOWN: - ui->Print("Shutting down...\n"); - android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,"); - break; + switch (after) { + case Device::SHUTDOWN: + ui->Print("Shutting down...\n"); + android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,"); + break; - case Device::REBOOT_BOOTLOADER: - ui->Print("Rebooting to bootloader...\n"); - android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader"); - break; + case Device::REBOOT_BOOTLOADER: + ui->Print("Rebooting to bootloader...\n"); + android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader"); + break; - default: - ui->Print("Rebooting...\n"); - reboot("reboot,"); - break; - } - while (true) { - pause(); - } - // Should be unreachable. - return EXIT_SUCCESS; + default: + ui->Print("Rebooting...\n"); + reboot("reboot,"); + break; + } + while (true) { + pause(); + } + // Should be unreachable. + return EXIT_SUCCESS; } |