From ec57903a7ec0bfe3c2f39dd6ee9cfc3de4ed20e6 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 21 Jul 2017 12:13:15 -0700 Subject: Avoid crashing recovery with unwritable /cache. When /cache is unwritable, recovery hits a crash loop. Because it passes nullptr to fileno(3) when writing back the locale file. This prevents user from recovering a device - it cannot boot far enough to recovery menu which allows wiping /cache. Bug: 63927337 Test: Corrupt /cache and boot into recovery on bullhead: 1. m -j recoveryimage 2. fastboot erase cache 3. fastboot boot $OUT/recovery.img 4. recovery menu shows up. Change-Id: I1407743f802049eb48add56a36298b665cb86139 --- recovery.cpp | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) (limited to 'recovery.cpp') diff --git a/recovery.cpp b/recovery.cpp index e2d993e23..55b12d5dd 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -477,40 +477,38 @@ static void copy_logs() { sync(); } -// clear the recovery command and prepare to boot a (hopefully working) system, +// Clear the recovery command and prepare to boot a (hopefully working) system, // copy our log file to cache as well (for the system to read). This function is // idempotent: call it as many times as you like. static void finish_recovery() { - // Save the locale to cache, so if recovery is next started up - // without a --locale argument (eg, directly from the bootloader) - // it will use the last-known locale. - if (!locale.empty() && has_cache) { - LOG(INFO) << "Saving locale \"" << locale << "\""; - - FILE* fp = fopen_path(LOCALE_FILE, "we"); - if (!android::base::WriteStringToFd(locale, fileno(fp))) { - PLOG(ERROR) << "Failed to save locale to " << LOCALE_FILE; - } - check_and_fclose(fp, LOCALE_FILE); + // 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(); + copy_logs(); - // 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; - } + // 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); + // 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. + sync(); // For good measure. } struct saved_log_file { -- cgit v1.2.3 From 7022f33ec8531c742f8f4701552d687233901495 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 25 Jul 2017 09:52:36 -0700 Subject: recovery: Fix the flickering when turning on text mode. When there's no command specified when booting into debuggable builds (such as using `adb reboot recovery`), we turn on the text mode (i.e. recovery menu) directly. This CL fixes the issue to avoid showing the background image in a flash while turning on the text mode. Bug: 63985334 Test: `fastboot boot $OUT/recovery.img` and it shows the recovery menu directly without the no command image in a flash. Change-Id: Id86bbe346ab76c8defc95e2b423e695a86774b09 --- recovery.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'recovery.cpp') diff --git a/recovery.cpp b/recovery.cpp index 55b12d5dd..8f3e9bdea 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -1594,15 +1594,14 @@ int main(int argc, char **argv) { ui->Print("Rebooting automatically.\n"); } } else if (!just_exit) { - status = INSTALL_NONE; // No command specified - ui->SetBackground(RecoveryUI::NO_COMMAND); - - // http://b/17489952 - // If this is an eng or userdebug build, automatically turn on the - // text display if no command is specified. - if (is_ro_debuggable()) { - ui->ShowText(true); - } + // 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) { -- cgit v1.2.3 From ac3d1edca0ea91a192c3a9d52efbc439702e9cae Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Sun, 23 Jul 2017 00:01:02 -0700 Subject: otautil: Clean up dirCreateHierarchy(). - Changed to std::string based implementation (mostly moved from the former make_parents() in updater/install.cpp); - Removed the timestamp parameter, which is only neeed by file-based OTA; - Changed the type of mode from int to mode_t; - Renamed dirCreateHierarchy() to mkdir_recursively(). Test: recovery_unit_test passes. Test: No external user of dirCreateHierarchy() in code search. Change-Id: I71f8c4b29bab625513bbc3af6d0d1ecdc3a2719a --- recovery.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'recovery.cpp') diff --git a/recovery.cpp b/recovery.cpp index 8f3e9bdea..233e56246 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -178,19 +178,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 @@ -593,7 +593,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)) { -- cgit v1.2.3 From 2bbc6d642d1fbfb007905d95b629fe5f833b2a1b Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Sun, 13 Aug 2017 23:48:55 -0700 Subject: screen_ui: Word-wrap menu headers. This CL adds ScreenRecoveryUI::DrawWrappedTextLines() to better handle long menu header texts. It does a word wrap at spaces, if available. This avoids fixed-length menu headers being truncated on small screens. Bug: 64293520 Test: On bullhead, boot into recovery with --prompt_and_wipe_data, and check the prompt texts. Change-Id: Ia22746583516dd230567a267584aca558429395e --- recovery.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'recovery.cpp') diff --git a/recovery.cpp b/recovery.cpp index 233e56246..c5ead451a 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -757,12 +757,13 @@ static bool wipe_data(Device* device) { } static bool prompt_and_wipe_data(Device* device) { + // Use a single string and let ScreenRecoveryUI handles the wrapping. const char* const headers[] = { - "Can't load Android system. Your data may be corrupt.", - "If you continue to get this message, you may need to", - "perform a factory data reset and erase all user data", + "Can't load Android system. Your data may be corrupt. " + "If you continue to get this message, you may need to " + "perform a factory data reset and erase all user data " "stored on this device.", - NULL + nullptr }; const char* const items[] = { "Try again", -- cgit v1.2.3 From bd0ddcd5e8794511395bf668833acc5c2da69fb0 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 4 May 2017 13:03:18 -0700 Subject: Remove EXPAND/STRINGIFY macros. This reverts commit 8be0f39fec7f26164fd0791ff6d15bde65fc849c to reland the change that removes EXPAND/STRINGIFY macros. It's error-prone by putting anything into a string (e.g. EXPAND(RECOVERY_API_VERSION) would become "RECOVER_API_VERSION" if we forgot to pass -DRECOVERY_API_VERSION=3). The initial attempt put RECOVERY_API_VERSION into common.h, which might be included by device-specific codes but without defining that when compiling the module. This CL avoids the issue by using a constant in the header, with a static_assert in recovery.cpp that guards the consistency. Test: recovery_component_test Test: Sideload OTAs on bullhead and sailfish respectively. Change-Id: I12af3f73392a85554ba703f04970ec9d984ccbaa --- recovery.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'recovery.cpp') diff --git a/recovery.cpp b/recovery.cpp index 6f62ff17c..d037b7971 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -125,6 +125,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; @@ -1498,7 +1502,7 @@ int main(int argc, char **argv) { property_list(print_property, NULL); printf("\n"); - ui->Print("Supported API: %d\n", RECOVERY_API_VERSION); + ui->Print("Supported API: %d\n", kRecoveryApiVersion); int status = INSTALL_SUCCESS; -- cgit v1.2.3 From 29d5575fa877770f6387420294d9dc184a84a115 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 20 Sep 2017 17:53:46 -0700 Subject: Add a new option in recovery menu to test the background texts Add a new option "Run locale test" to check the background text images (i.e. texts for "erasing", "error", "no_command" and "installing" with different locales.) Use volume up/down button to cycle through all the locales embedded in the png file, and power button to go back to recovery main menu. Test: Run locale test with bullhead. Change-Id: Ib16e119f372110cdb5e611ef497b0f9b9b418f51 --- recovery.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'recovery.cpp') diff --git a/recovery.cpp b/recovery.cpp index d037b7971..076b4492e 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -1191,6 +1191,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(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 -- cgit v1.2.3 From 3e18f2bf4004e9df2f7fcee0d2035552404ff715 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 29 Sep 2017 17:11:13 -0700 Subject: roots: Fix an issue with volume_for_path(). The earlier commit in 2dfc1a38982c4052bb32bc7fc06edeadf3908fb9 unintentionally changed the behavior. It gives a different result when looking up non-existent mount points (e.g. /cache on marlin). The logic behind volume_for_path("/xyz") is unclear: - It's fine to return non-null value if it's called by ensure_path_mounted() before accessing that file "/xyz". (Just based on the function name, we're not actually having this case.) - It should return nullptr if the caller is interested in the existence of that particular mount point "/xyz". This CL renames the function to volume_for_mount_point(), which does an exact match by querying the given mount point from libfs_mgr. The former volume_for_path() has been moved down to function scope for serving ensure_path_mounted() only. Test: Build and boot into recovery on bullhead and marlin respectively. 'View recovery logs'. Test: 'Mount /system' Test: 'Apply update from ADB' Change-Id: I1a16390f57540cae08a2b8f3d439d17886975217 --- recovery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'recovery.cpp') diff --git a/recovery.cpp b/recovery.cpp index 076b4492e..4dc5b540e 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -1396,7 +1396,7 @@ int main(int argc, char **argv) { printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start)); load_volume_table(); - has_cache = volume_for_path(CACHE_ROOT) != nullptr; + has_cache = volume_for_mount_point(CACHE_ROOT) != nullptr; std::vector args = get_args(argc, argv); std::vector args_to_parse(args.size()); -- cgit v1.2.3 From 623fe7e701d5d0fb17082d1ced14498af1b44e5b Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 2 Oct 2017 16:28:06 -0700 Subject: Move error_code.h into otautil. This way it stops requiring relative path ".." in LOCAL_C_INCLUDES (uncrypt and edify). Soong doesn't accept non-local ".." in "local_include_dirs". Test: mmma bootable/recovery Change-Id: Ia4649789cef2aaeb2785483660e9ea5a8b389c62 --- recovery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'recovery.cpp') diff --git a/recovery.cpp b/recovery.cpp index 4dc5b540e..243ee4af3 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -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" -- cgit v1.2.3 From 26436d6d6010d5323349af7e119ff8f34f85c40c Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 5 Oct 2017 17:16:31 +0000 Subject: Revert "Move error_code.h into otautil." This reverts commit 623fe7e701d5d0fb17082d1ced14498af1b44e5b. Reason for revert: Need to address device-specific modules. Change-Id: Ib7a4191e7f193dfff49b02d3de76dda856800251 --- recovery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'recovery.cpp') diff --git a/recovery.cpp b/recovery.cpp index 243ee4af3..4dc5b540e 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -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" -- cgit v1.2.3 From 1fc5bf353a8719d16fd9ba29a661d211bad4038f Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 6 Oct 2017 07:43:41 -0700 Subject: Revert "Revert "Move error_code.h into otautil."" This reverts commit 26436d6d6010d5323349af7e119ff8f34f85c40c to re-land "Move error_code.h into otautil.". This way it stops requiring relative path ".." in LOCAL_C_INCLUDES (uncrypt and edify). Soong doesn't accept non-local ".." in "local_include_dirs". This CL needs to land with device-specific module changes (e.g. adding the dependency on libotautil). Test: lunch aosp_{angler,bullhead,dragon,fugu,sailfish}-userdebug; mmma bootable/recovery Change-Id: If193241801af2dae73eccd31ce57cd2b81c9fd96 --- recovery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'recovery.cpp') diff --git a/recovery.cpp b/recovery.cpp index 4dc5b540e..243ee4af3 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -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" -- cgit v1.2.3 From 99f0d9e52bdbe314d77300f883d66e4470d4a5ee Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 13 Oct 2016 12:46:38 -0700 Subject: Drop -Wno-unused-parameter. The only one left is libedify. Will handle that in a separate CL. Test: mmma bootable/recovery Change-Id: I732a5f85229da90fd767bee2e46c5c95f529c396 --- recovery.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'recovery.cpp') diff --git a/recovery.cpp b/recovery.cpp index 243ee4af3..a89916337 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -1214,9 +1214,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() { @@ -1250,14 +1249,14 @@ 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() { -- cgit v1.2.3 From e8e4c40246a8c15208b70b4467a0d1790c7ce8fb Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Wed, 8 Nov 2017 14:56:03 -0800 Subject: is_battery_ok use health 2.0 HAL Test: call is_battery_ok in graphics test, and test manually Bug: 69268160 Bug: 63702641 Change-Id: Ifcf4d2e2cb459689c11fc5d1b880bb053aaae8ae --- recovery.cpp | 122 ++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 71 insertions(+), 51 deletions(-) (limited to 'recovery.cpp') diff --git a/recovery.cpp b/recovery.cpp index a89916337..7574065a4 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -50,9 +50,9 @@ #include #include #include /* for property_list */ -#include -#include /* private pmsg functions */ -#include /* for AID_SYSTEM */ +#include +#include /* for AID_SYSTEM */ +#include /* private pmsg functions */ #include #include #include @@ -1260,56 +1260,76 @@ void UiLogger(android::base::LogId /* id */, android::base::LogSeverity severity } 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); + 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 + }; - android::BatteryMonitor monitor; - monitor.init(&healthd_config); + auto health = + android::hardware::health::V2_0::implementation::Health::initInstance(&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); + 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); } } -- cgit v1.2.3 From 99b73be3a8f3a97ff11f3e50a4169292a2f36fb1 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 28 Nov 2017 17:23:06 -0800 Subject: Detect interrupted update due to power off An interrupted update may stash extra blocks in /cache, leading to a failure when checking the cache size. We can save the incremented retry_count in the BCB before installing the update; and distinguish a fresh update from an interrupted one this way. Bug: 68679601 Test: An interrupted update reapplies successfully. Change-Id: Ic1403e1fd25a937c91ef34c14b92a0f6c8f1c0f4 --- recovery.cpp | 565 +++++++++++++++++++++++++++++++---------------------------- 1 file changed, 297 insertions(+), 268 deletions(-) (limited to 'recovery.cpp') diff --git a/recovery.cpp b/recovery.cpp index a89916337..6bd291463 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -1313,6 +1313,7 @@ static bool is_battery_ok() { } } +// Set the retry count to |retry_count| in BCB. static void set_retry_bootloader_message(int retry_count, const std::vector& args) { std::vector options; for (const auto& arg : args) { @@ -1321,8 +1322,8 @@ static void set_retry_bootloader_message(int retry_count, const std::vector args = get_args(argc, argv); - std::vector args_to_parse(args.size()); - std::transform(args.cbegin(), args.cend(), args_to_parse.begin(), - [](const std::string& arg) { return const_cast(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 args = get_args(argc, argv); + std::vector args_to_parse(args.size()); + std::transform(args.cbegin(), args.cend(), args_to_parse.begin(), + [](const std::string& arg) { return const_cast(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", kRecoveryApiVersion); + 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; } -- cgit v1.2.3 From 723056a83f8c8b15af02d9c302862dbb2304ea8c Mon Sep 17 00:00:00 2001 From: Paul Crowley Date: Wed, 8 Jun 2016 13:51:41 -0700 Subject: Wipe the metadata partition when we wipe data. Bug: 78469201 Test: Wipe from recovery menu, check that wipe is logged correctly and boot works as expected. Merged-In: I5bc8ef1b83d78de8b5edba6cc17882edcc744356 Change-Id: I5bc8ef1b83d78de8b5edba6cc17882edcc744356 --- recovery.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'recovery.cpp') diff --git a/recovery.cpp b/recovery.cpp index d887d07fb..07ec5cfb6 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -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"; @@ -752,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; } -- cgit v1.2.3