diff options
-rw-r--r-- | Android.bp | 3 | ||||
-rw-r--r-- | TEST_MAPPING | 17 | ||||
-rw-r--r-- | fsck_unshare_blocks.cpp | 1 | ||||
-rw-r--r-- | install/Android.bp | 2 | ||||
-rw-r--r-- | install/adb_install.cpp | 125 | ||||
-rw-r--r-- | install/include/install/adb_install.h | 7 | ||||
-rw-r--r-- | install/include/install/install.h | 6 | ||||
-rw-r--r-- | install/include/install/wipe_device.h | 29 | ||||
-rw-r--r-- | install/wipe_device.cpp | 198 | ||||
-rw-r--r-- | minadbd/minadbd_services.cpp | 89 | ||||
-rw-r--r-- | minadbd/minadbd_services_test.cpp | 8 | ||||
-rw-r--r-- | minadbd/minadbd_types.h | 17 | ||||
-rw-r--r-- | minui/resources.cpp | 18 | ||||
-rw-r--r-- | otautil/Android.bp | 4 | ||||
-rw-r--r-- | recovery.cpp | 243 | ||||
-rw-r--r-- | recovery_main.cpp | 32 | ||||
-rw-r--r-- | recovery_ui/include/recovery_ui/device.h | 7 | ||||
-rw-r--r-- | recovery_ui/include/recovery_ui/ui.h | 2 | ||||
-rw-r--r-- | recovery_ui/screen_ui.cpp | 20 | ||||
-rw-r--r-- | tests/Android.bp | 2 | ||||
-rw-r--r-- | tests/AndroidTest.xml | 33 | ||||
-rw-r--r-- | tests/component/install_test.cpp | 1 | ||||
-rw-r--r-- | tests/unit/locale_test.cpp | 4 | ||||
-rw-r--r-- | tools/recovery_l10n/res/values-gl/strings.xml | 4 | ||||
-rw-r--r-- | tools/recovery_l10n/res/values-ja/strings.xml | 2 |
25 files changed, 573 insertions, 301 deletions
diff --git a/Android.bp b/Android.bp index f92078256..0eb5fd9e5 100644 --- a/Android.bp +++ b/Android.bp @@ -76,7 +76,6 @@ cc_defaults { // external dependencies "libhealthhalutils", - "libfstab", ], } @@ -150,7 +149,6 @@ cc_binary { static_libs: [ "libotautil", - "libfstab", ], init_rc: [ @@ -177,7 +175,6 @@ cc_binary { static_libs: [ "libotautil", - "libfstab", ], init_rc: [ diff --git a/TEST_MAPPING b/TEST_MAPPING new file mode 100644 index 000000000..c87ece24e --- /dev/null +++ b/TEST_MAPPING @@ -0,0 +1,17 @@ +{ + "presubmit": [ + { + "name": "minadbd_test" + }, + { + "name": "recovery_unit_test" + }, + { + "name": "recovery_component_test" + }, + { + "name": "recovery_host_test", + "host": true + } + ] +} diff --git a/fsck_unshare_blocks.cpp b/fsck_unshare_blocks.cpp index e74f8ba6f..0f8ffface 100644 --- a/fsck_unshare_blocks.cpp +++ b/fsck_unshare_blocks.cpp @@ -34,7 +34,6 @@ #include <android-base/logging.h> #include <android-base/properties.h> #include <android-base/unique_fd.h> -#include <fstab/fstab.h> #include "otautil/roots.h" diff --git a/install/Android.bp b/install/Android.bp index ea893a075..4696e501e 100644 --- a/install/Android.bp +++ b/install/Android.bp @@ -47,7 +47,6 @@ cc_defaults { // external dependencies "libvintf_recovery", "libvintf", - "libfstab", ], } @@ -67,6 +66,7 @@ cc_library_static { "package.cpp", "verifier.cpp", "wipe_data.cpp", + "wipe_device.cpp", ], shared_libs: [ diff --git a/install/adb_install.cpp b/install/adb_install.cpp index f430920a4..4dd1f1b09 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -31,6 +31,7 @@ #include <atomic> #include <functional> #include <map> +#include <utility> #include <vector> #include <android-base/file.h> @@ -42,32 +43,37 @@ #include "fuse_sideload.h" #include "install/install.h" +#include "install/wipe_data.h" #include "minadbd_types.h" #include "otautil/sysutil.h" +#include "recovery_ui/device.h" #include "recovery_ui/ui.h" -using CommandFunction = std::function<bool()>; +// A CommandFunction returns a pair of (result, should_continue), which indicates the command +// execution result and whether it should proceed to the next iteration. The execution result will +// always be sent to the minadbd side. +using CommandFunction = std::function<std::pair<bool, bool>()>; static bool SetUsbConfig(const std::string& state) { android::base::SetProperty("sys.usb.config", state); return android::base::WaitForProperty("sys.usb.state", state); } -// Parses the minadbd command in |message|; returns MinadbdCommands::kError upon errors. -static MinadbdCommands ParseMinadbdCommands(const std::string& message) { +// Parses the minadbd command in |message|; returns MinadbdCommand::kError upon errors. +static MinadbdCommand ParseMinadbdCommand(const std::string& message) { if (!android::base::StartsWith(message, kMinadbdCommandPrefix)) { LOG(ERROR) << "Failed to parse command in message " << message; - return MinadbdCommands::kError; + return MinadbdCommand::kError; } auto cmd_code_string = message.substr(strlen(kMinadbdCommandPrefix)); auto cmd_code = android::base::get_unaligned<uint32_t>(cmd_code_string.c_str()); - if (cmd_code >= static_cast<uint32_t>(MinadbdCommands::kError)) { + if (cmd_code >= static_cast<uint32_t>(MinadbdCommand::kError)) { LOG(ERROR) << "Unsupported command code: " << cmd_code; - return MinadbdCommands::kError; + return MinadbdCommand::kError; } - return static_cast<MinadbdCommands>(cmd_code); + return static_cast<MinadbdCommand>(cmd_code); } static bool WriteStatusToFd(MinadbdCommandStatus status, int fd) { @@ -82,13 +88,15 @@ static bool WriteStatusToFd(MinadbdCommandStatus status, int fd) { return true; } -// Installs the package from FUSE. Returns true if the installation succeeds, and false otherwise. -static bool AdbInstallPackageHandler(RecoveryUI* ui, int* result) { +// Installs the package from FUSE. Returns the installation result and whether it should continue +// waiting for new commands. +static auto AdbInstallPackageHandler(RecoveryUI* ui, int* result) { // How long (in seconds) we wait for the package path to be ready. It doesn't need to be too long // because the minadbd service has already issued an install command. FUSE_SIDELOAD_HOST_PATHNAME // will start to exist once the host connects and starts serving a package. Poll for its // appearance. (Note that inotify doesn't work with FUSE.) constexpr int ADB_INSTALL_TIMEOUT = 15; + bool should_continue = true; *result = INSTALL_ERROR; for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) { struct stat st; @@ -97,6 +105,7 @@ static bool AdbInstallPackageHandler(RecoveryUI* ui, int* result) { sleep(1); continue; } else { + should_continue = false; ui->Print("\nTimed out waiting for fuse to be ready.\n\n"); break; } @@ -108,13 +117,40 @@ static bool AdbInstallPackageHandler(RecoveryUI* ui, int* result) { // Calling stat() on this magic filename signals the FUSE to exit. struct stat st; stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st); - return *result == INSTALL_SUCCESS; + return std::make_pair(*result == INSTALL_SUCCESS, should_continue); } -// Parses and executes the command from minadbd. Returns false if we enter an invalid state so that -// the caller can kill the minadbd service properly. -static bool HandleMessageFromMinadbd( - int socket_fd, const std::map<MinadbdCommands, CommandFunction>& command_map) { +static auto AdbRebootHandler(MinadbdCommand command, int* result, + Device::BuiltinAction* reboot_action) { + // Use Device::REBOOT_{FASTBOOT,RECOVERY,RESCUE}, instead of the ones with ENTER_. This allows + // rebooting back into fastboot/recovery/rescue mode through bootloader, which may use a newly + // installed bootloader/recovery image. + switch (command) { + case MinadbdCommand::kRebootBootloader: + *reboot_action = Device::REBOOT_BOOTLOADER; + break; + case MinadbdCommand::kRebootFastboot: + *reboot_action = Device::REBOOT_FASTBOOT; + break; + case MinadbdCommand::kRebootRecovery: + *reboot_action = Device::REBOOT_RECOVERY; + break; + case MinadbdCommand::kRebootRescue: + *reboot_action = Device::REBOOT_RESCUE; + break; + case MinadbdCommand::kRebootAndroid: + default: + *reboot_action = Device::REBOOT; + break; + } + *result = INSTALL_REBOOT; + return std::make_pair(true, false); +} + +// Parses and executes the command from minadbd. Returns whether the caller should keep waiting for +// next command. +static bool HandleMessageFromMinadbd(int socket_fd, + const std::map<MinadbdCommand, CommandFunction>& command_map) { char buffer[kMinadbdMessageSize]; if (!android::base::ReadFully(socket_fd, buffer, kMinadbdMessageSize)) { PLOG(ERROR) << "Failed to read message from minadbd"; @@ -122,8 +158,8 @@ static bool HandleMessageFromMinadbd( } std::string message(buffer, buffer + kMinadbdMessageSize); - auto command_type = ParseMinadbdCommands(message); - if (command_type == MinadbdCommands::kError) { + auto command_type = ParseMinadbdCommand(message); + if (command_type == MinadbdCommand::kError) { return false; } if (command_map.find(command_type) == command_map.end()) { @@ -135,17 +171,19 @@ static bool HandleMessageFromMinadbd( // We have received a valid command, execute the corresponding function. const auto& command_func = command_map.at(command_type); - if (!command_func()) { - LOG(ERROR) << "Failed to execute command " << static_cast<unsigned int>(command_type); - return WriteStatusToFd(MinadbdCommandStatus::kFailure, socket_fd); + const auto [result, should_continue] = command_func(); + LOG(INFO) << "Command " << static_cast<uint32_t>(command_type) << " finished with " << result; + if (!WriteStatusToFd(result ? MinadbdCommandStatus::kSuccess : MinadbdCommandStatus::kFailure, + socket_fd)) { + return false; } - return WriteStatusToFd(MinadbdCommandStatus::kSuccess, socket_fd); + return should_continue; } // TODO(xunchang) add a wrapper function and kill the minadbd service there. static void ListenAndExecuteMinadbdCommands( - pid_t minadbd_pid, android::base::unique_fd&& socket_fd, - const std::map<MinadbdCommands, CommandFunction>& command_map) { + RecoveryUI* ui, pid_t minadbd_pid, android::base::unique_fd&& socket_fd, + const std::map<MinadbdCommand, CommandFunction>& command_map) { android::base::unique_fd epoll_fd(epoll_create1(O_CLOEXEC)); if (epoll_fd == -1) { PLOG(ERROR) << "Failed to create epoll"; @@ -167,6 +205,10 @@ static void ListenAndExecuteMinadbdCommands( // Set the timeout to be 300s when waiting for minadbd commands. constexpr int TIMEOUT_MILLIS = 300 * 1000; while (true) { + // Reset the progress bar and the background image before each command. + ui->SetProgressType(RecoveryUI::EMPTY); + ui->SetBackground(RecoveryUI::NO_COMMAND); + // Poll for the status change of the socket_fd, and handle the message if the fd is ready to // read. int event_count = @@ -230,7 +272,8 @@ static void ListenAndExecuteMinadbdCommands( // b11. exit the listening loop // static void CreateMinadbdServiceAndExecuteCommands( - const std::map<MinadbdCommands, CommandFunction>& command_map, bool rescue_mode) { + RecoveryUI* ui, const std::map<MinadbdCommand, CommandFunction>& command_map, + bool rescue_mode) { signal(SIGPIPE, SIG_IGN); android::base::unique_fd recovery_socket; @@ -269,9 +312,8 @@ static void CreateMinadbdServiceAndExecuteCommands( return; } - std::thread listener_thread(ListenAndExecuteMinadbdCommands, child, std::move(recovery_socket), - std::ref(command_map)); - + std::thread listener_thread(ListenAndExecuteMinadbdCommands, ui, child, + std::move(recovery_socket), std::ref(command_map)); if (listener_thread.joinable()) { listener_thread.join(); } @@ -289,7 +331,7 @@ static void CreateMinadbdServiceAndExecuteCommands( signal(SIGPIPE, SIG_DFL); } -int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode) { +int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action) { // Save the usb state to restore after the sideload operation. std::string usb_state = android::base::GetProperty("sys.usb.state", "none"); // Clean up state and stop adbd. @@ -298,20 +340,37 @@ int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode) { return INSTALL_ERROR; } + RecoveryUI* ui = device->GetUI(); + + int install_result = INSTALL_ERROR; + std::map<MinadbdCommand, CommandFunction> command_map{ + { MinadbdCommand::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) }, + { MinadbdCommand::kRebootAndroid, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootAndroid, + &install_result, reboot_action) }, + { MinadbdCommand::kRebootBootloader, + std::bind(&AdbRebootHandler, MinadbdCommand::kRebootBootloader, &install_result, + reboot_action) }, + { MinadbdCommand::kRebootFastboot, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootFastboot, + &install_result, reboot_action) }, + { MinadbdCommand::kRebootRecovery, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootRecovery, + &install_result, reboot_action) }, + { MinadbdCommand::kRebootRescue, + std::bind(&AdbRebootHandler, MinadbdCommand::kRebootRescue, &install_result, reboot_action) }, + }; + if (!rescue_mode) { ui->Print( "\n\nNow send the package you want to apply\n" "to the device with \"adb sideload <filename>\"...\n"); } else { ui->Print("\n\nWaiting for rescue commands...\n"); + command_map.emplace(MinadbdCommand::kWipeData, [&device]() { + bool result = WipeData(device, false); + return std::make_pair(result, true); + }); } - int install_result = INSTALL_ERROR; - std::map<MinadbdCommands, CommandFunction> command_map{ - { MinadbdCommands::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) }, - }; - - CreateMinadbdServiceAndExecuteCommands(command_map, rescue_mode); + CreateMinadbdServiceAndExecuteCommands(ui, command_map, rescue_mode); // Clean up before switching to the older state, for example setting the state // to none sets sys/class/android_usb/android0/enable to 0. diff --git a/install/include/install/adb_install.h b/install/include/install/adb_install.h index 208d0c780..3a0a81747 100644 --- a/install/include/install/adb_install.h +++ b/install/include/install/adb_install.h @@ -16,6 +16,9 @@ #pragma once -#include <recovery_ui/ui.h> +#include <recovery_ui/device.h> -int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode); +// Applies a package via `adb sideload` or `adb rescue`. Returns the install result (in `enum +// InstallResult`). When a reboot has been requested, INSTALL_REBOOT will be the return value, with +// the reboot target set in reboot_action. +int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action); diff --git a/install/include/install/install.h b/install/include/install/install.h index 1e41b4843..d90c20f3a 100644 --- a/install/include/install/install.h +++ b/install/include/install/install.h @@ -34,7 +34,8 @@ enum InstallResult { INSTALL_NONE, INSTALL_SKIPPED, INSTALL_RETRY, - INSTALL_KEY_INTERRUPTED + INSTALL_KEY_INTERRUPTED, + INSTALL_REBOOT, }; enum class OtaType { @@ -57,9 +58,6 @@ bool verify_package(Package* package, RecoveryUI* ui); // result to |metadata|. Return true if succeed, otherwise return false. bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map<std::string, std::string>* metadata); -// Reads the "recovery.wipe" entry in the zip archive returns a list of partitions to wipe. -std::vector<std::string> GetWipePartitionList(Package* wipe_package); - // Verifies the compatibility info in a Treble-compatible package. Returns true directly if the // entry doesn't exist. bool verify_package_compatibility(ZipArchiveHandle package_zip); diff --git a/install/include/install/wipe_device.h b/install/include/install/wipe_device.h new file mode 100644 index 000000000..c60b99997 --- /dev/null +++ b/install/include/install/wipe_device.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <string> +#include <vector> + +#include "install/package.h" +#include "recovery_ui/device.h" + +// Wipes the current A/B device, with a secure wipe of all the partitions in RECOVERY_WIPE. +bool WipeAbDevice(Device* device, size_t wipe_package_size); + +// Reads the "recovery.wipe" entry in the zip archive returns a list of partitions to wipe. +std::vector<std::string> GetWipePartitionList(Package* wipe_package); diff --git a/install/wipe_device.cpp b/install/wipe_device.cpp new file mode 100644 index 000000000..72b96f7b4 --- /dev/null +++ b/install/wipe_device.cpp @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "install/wipe_device.h" + +#include <errno.h> +#include <fcntl.h> +#include <linux/fs.h> +#include <stdint.h> +#include <sys/ioctl.h> + +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/strings.h> +#include <android-base/unique_fd.h> +#include <ziparchive/zip_archive.h> + +#include "bootloader_message/bootloader_message.h" +#include "install/install.h" +#include "install/package.h" +#include "recovery_ui/device.h" +#include "recovery_ui/ui.h" + +std::vector<std::string> GetWipePartitionList(Package* wipe_package) { + ZipArchiveHandle zip = wipe_package->GetZipArchiveHandle(); + if (!zip) { + LOG(ERROR) << "Failed to get ZipArchiveHandle"; + return {}; + } + + constexpr char RECOVERY_WIPE_ENTRY_NAME[] = "recovery.wipe"; + + std::string partition_list_content; + ZipString path(RECOVERY_WIPE_ENTRY_NAME); + ZipEntry entry; + if (FindEntry(zip, path, &entry) == 0) { + uint32_t length = entry.uncompressed_length; + partition_list_content = std::string(length, '\0'); + if (auto err = ExtractToMemory( + zip, &entry, reinterpret_cast<uint8_t*>(partition_list_content.data()), length); + err != 0) { + LOG(ERROR) << "Failed to extract " << RECOVERY_WIPE_ENTRY_NAME << ": " + << ErrorCodeString(err); + return {}; + } + } else { + LOG(INFO) << "Failed to find " << RECOVERY_WIPE_ENTRY_NAME + << ", falling back to use the partition list on device."; + + constexpr char RECOVERY_WIPE_ON_DEVICE[] = "/etc/recovery.wipe"; + if (!android::base::ReadFileToString(RECOVERY_WIPE_ON_DEVICE, &partition_list_content)) { + PLOG(ERROR) << "failed to read \"" << RECOVERY_WIPE_ON_DEVICE << "\""; + return {}; + } + } + + std::vector<std::string> result; + auto lines = android::base::Split(partition_list_content, "\n"); + for (const auto& line : lines) { + auto partition = android::base::Trim(line); + // Ignore '#' comment or empty lines. + if (android::base::StartsWith(partition, "#") || partition.empty()) { + continue; + } + result.push_back(line); + } + + return result; +} + +// Secure-wipes a given partition. It uses BLKSECDISCARD, if supported. Otherwise, it goes with +// BLKDISCARD (if device supports BLKDISCARDZEROES) or BLKZEROOUT. +static bool SecureWipePartition(const std::string& partition) { + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(partition.c_str(), O_WRONLY))); + if (fd == -1) { + PLOG(ERROR) << "Failed to open \"" << partition << "\""; + return false; + } + + uint64_t range[2] = { 0, 0 }; + if (ioctl(fd, BLKGETSIZE64, &range[1]) == -1 || range[1] == 0) { + PLOG(ERROR) << "Failed to get partition size"; + return false; + } + LOG(INFO) << "Secure-wiping \"" << partition << "\" from " << range[0] << " to " << range[1]; + + LOG(INFO) << " Trying BLKSECDISCARD..."; + if (ioctl(fd, BLKSECDISCARD, &range) == -1) { + PLOG(WARNING) << " Failed"; + + // Use BLKDISCARD if it zeroes out blocks, otherwise use BLKZEROOUT. + unsigned int zeroes; + if (ioctl(fd, BLKDISCARDZEROES, &zeroes) == 0 && zeroes != 0) { + LOG(INFO) << " Trying BLKDISCARD..."; + if (ioctl(fd, BLKDISCARD, &range) == -1) { + PLOG(ERROR) << " Failed"; + return false; + } + } else { + LOG(INFO) << " Trying BLKZEROOUT..."; + if (ioctl(fd, BLKZEROOUT, &range) == -1) { + PLOG(ERROR) << " Failed"; + return false; + } + } + } + + LOG(INFO) << " Done"; + return true; +} + +static std::unique_ptr<Package> ReadWipePackage(size_t wipe_package_size) { + if (wipe_package_size == 0) { + LOG(ERROR) << "wipe_package_size is zero"; + return nullptr; + } + + std::string wipe_package; + if (std::string err_str; !read_wipe_package(&wipe_package, wipe_package_size, &err_str)) { + PLOG(ERROR) << "Failed to read wipe package" << err_str; + return nullptr; + } + + return Package::CreateMemoryPackage( + std::vector<uint8_t>(wipe_package.begin(), wipe_package.end()), nullptr); +} + +// Checks if the wipe package matches expectation. If the check passes, reads the list of +// partitions to wipe from the package. Checks include +// 1. verify the package. +// 2. check metadata (ota-type, pre-device and serial number if having one). +static bool CheckWipePackage(Package* wipe_package, RecoveryUI* ui) { + if (!verify_package(wipe_package, ui)) { + LOG(ERROR) << "Failed to verify package"; + return false; + } + + ZipArchiveHandle zip = wipe_package->GetZipArchiveHandle(); + if (!zip) { + LOG(ERROR) << "Failed to get ZipArchiveHandle"; + return false; + } + + std::map<std::string, std::string> metadata; + if (!ReadMetadataFromPackage(zip, &metadata)) { + LOG(ERROR) << "Failed to parse metadata in the zip file"; + return false; + } + + return CheckPackageMetadata(metadata, OtaType::BRICK) == 0; +} + +bool WipeAbDevice(Device* device, size_t wipe_package_size) { + auto ui = device->GetUI(); + ui->SetBackground(RecoveryUI::ERASING); + ui->SetProgressType(RecoveryUI::INDETERMINATE); + + auto wipe_package = ReadWipePackage(wipe_package_size); + if (!wipe_package) { + LOG(ERROR) << "Failed to open wipe package"; + return false; + } + + if (!CheckWipePackage(wipe_package.get(), ui)) { + LOG(ERROR) << "Failed to verify wipe package"; + return false; + } + + auto partition_list = GetWipePartitionList(wipe_package.get()); + if (partition_list.empty()) { + LOG(ERROR) << "Empty wipe ab partition list"; + return false; + } + + for (const auto& partition : partition_list) { + // Proceed anyway even if it fails to wipe some partition. + SecureWipePartition(partition); + } + return true; +} diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index f6aff71f8..1c4c0f494 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -64,7 +64,7 @@ void SetSideloadMountPoint(const std::string& path) { sideload_mount_point = path; } -static bool WriteCommandToFd(MinadbdCommands cmd, int fd) { +static bool WriteCommandToFd(MinadbdCommand cmd, int fd) { char message[kMinadbdMessageSize]; memcpy(message, kMinadbdCommandPrefix, strlen(kMinadbdStatusPrefix)); android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), cmd); @@ -104,12 +104,12 @@ static MinadbdErrorCode RunAdbFuseSideload(int sfd, const std::string& args, if (pieces.size() != 2 || !android::base::ParseInt(pieces[0], &file_size) || file_size <= 0 || !android::base::ParseInt(pieces[1], &block_size) || block_size <= 0) { LOG(ERROR) << "bad sideload-host arguments: " << args; - return kMinadbdPackageSizeError; + return kMinadbdHostCommandArgumentError; } LOG(INFO) << "sideload-host file size " << file_size << ", block size " << block_size; - if (!WriteCommandToFd(MinadbdCommands::kInstall, minadbd_socket)) { + if (!WriteCommandToFd(MinadbdCommand::kInstall, minadbd_socket)) { return kMinadbdSocketIOError; } @@ -124,17 +124,17 @@ static MinadbdErrorCode RunAdbFuseSideload(int sfd, const std::string& args, return kMinadbdMessageFormatError; } - // Signal host-side adb to stop. For sideload mode, we always send kSideloadServiceExitSuccess + // Signal host-side adb to stop. For sideload mode, we always send kMinadbdServicesExitSuccess // (i.e. "DONEDONE") regardless of the install result. For rescue mode, we send failure message on // install error. if (!rescue_mode || *status == MinadbdCommandStatus::kSuccess) { - if (!android::base::WriteFully(sfd, kSideloadServiceExitSuccess, - strlen(kSideloadServiceExitSuccess))) { + if (!android::base::WriteFully(sfd, kMinadbdServicesExitSuccess, + strlen(kMinadbdServicesExitSuccess))) { return kMinadbdHostSocketIOError; } } else { - if (!android::base::WriteFully(sfd, kSideloadServiceExitFailure, - strlen(kSideloadServiceExitFailure))) { + if (!android::base::WriteFully(sfd, kMinadbdServicesExitFailure, + strlen(kMinadbdServicesExitFailure))) { return kMinadbdHostSocketIOError; } } @@ -175,7 +175,73 @@ static void RescueGetpropHostService(unique_fd sfd, const std::string& prop) { } } +// Reboots into the given target. We don't reboot directly from minadbd, but going through recovery +// instead. This allows recovery to finish all the pending works (clear BCB, save logs etc) before +// the reboot. +static void RebootHostService(unique_fd /* sfd */, const std::string& target) { + MinadbdCommand command; + if (target == "bootloader") { + command = MinadbdCommand::kRebootBootloader; + } else if (target == "rescue") { + command = MinadbdCommand::kRebootRescue; + } else if (target == "recovery") { + command = MinadbdCommand::kRebootRecovery; + } else if (target == "fastboot") { + command = MinadbdCommand::kRebootFastboot; + } else { + command = MinadbdCommand::kRebootAndroid; + } + if (!WriteCommandToFd(command, minadbd_socket)) { + exit(kMinadbdSocketIOError); + } + MinadbdCommandStatus status; + if (!WaitForCommandStatus(minadbd_socket, &status)) { + exit(kMinadbdMessageFormatError); + } +} + +static void WipeDeviceService(unique_fd fd, const std::string& args) { + auto pieces = android::base::Split(args, ":"); + if (pieces.size() != 2 || pieces[0] != "userdata") { + LOG(ERROR) << "Failed to parse wipe device command arguments " << args; + exit(kMinadbdHostCommandArgumentError); + } + + size_t message_size; + if (!android::base::ParseUint(pieces[1], &message_size) || + message_size < strlen(kMinadbdServicesExitSuccess)) { + LOG(ERROR) << "Failed to parse wipe device message size in " << args; + exit(kMinadbdHostCommandArgumentError); + } + + WriteCommandToFd(MinadbdCommand::kWipeData, minadbd_socket); + MinadbdCommandStatus status; + if (!WaitForCommandStatus(minadbd_socket, &status)) { + exit(kMinadbdMessageFormatError); + } + + std::string response = (status == MinadbdCommandStatus::kSuccess) ? kMinadbdServicesExitSuccess + : kMinadbdServicesExitFailure; + response += std::string(message_size - response.size(), '\0'); + if (!android::base::WriteFully(fd, response.c_str(), response.size())) { + exit(kMinadbdHostSocketIOError); + } +} + unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport */) { + // Common services that are supported both in sideload and rescue modes. + if (ConsumePrefix(&name, "reboot:")) { + // "reboot:<target>", where target must be one of the following. + std::string args(name); + if (args.empty() || args == "bootloader" || args == "rescue" || args == "recovery" || + args == "fastboot") { + return create_service_thread("reboot", + std::bind(RebootHostService, std::placeholders::_1, args)); + } + return unique_fd{}; + } + + // Rescue-specific services. if (rescue_mode) { if (ConsumePrefix(&name, "rescue-install:")) { // rescue-install:<file-size>:<block-size> @@ -187,10 +253,17 @@ unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport * std::string args(name); return create_service_thread( "rescue-getprop", std::bind(RescueGetpropHostService, std::placeholders::_1, args)); + } else if (ConsumePrefix(&name, "rescue-wipe:")) { + // rescue-wipe:target:<message-size> + std::string args(name); + return create_service_thread("rescue-wipe", + std::bind(WipeDeviceService, std::placeholders::_1, args)); } + return unique_fd{}; } + // Sideload-specific services. if (name.starts_with("sideload:")) { // This exit status causes recovery to print a special error message saying to use a newer adb // (that supports sideload-host). diff --git a/minadbd/minadbd_services_test.cpp b/minadbd/minadbd_services_test.cpp index 413ba0df6..f87873792 100644 --- a/minadbd/minadbd_services_test.cpp +++ b/minadbd/minadbd_services_test.cpp @@ -62,7 +62,7 @@ class MinadbdServicesTest : public ::testing::Test { signal(SIGPIPE, SIG_DFL); } - void ReadAndCheckCommandMessage(int fd, MinadbdCommands expected_command) { + void ReadAndCheckCommandMessage(int fd, MinadbdCommand expected_command) { std::vector<uint8_t> received(kMinadbdMessageSize, '\0'); ASSERT_TRUE(android::base::ReadFully(fd, received.data(), kMinadbdMessageSize)); @@ -122,7 +122,7 @@ class MinadbdServicesTest : public ::testing::Test { TEST_F(MinadbdServicesTest, SideloadHostService_wrong_size_argument) { ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:abc:4096"), - ::testing::ExitedWithCode(kMinadbdPackageSizeError), ""); + ::testing::ExitedWithCode(kMinadbdHostCommandArgumentError), ""); } TEST_F(MinadbdServicesTest, SideloadHostService_wrong_block_size) { @@ -147,7 +147,7 @@ TEST_F(MinadbdServicesTest, SideloadHostService_wrong_command_format) { unique_fd fd = daemon_service_to_fd(command, nullptr); ASSERT_NE(-1, fd); WaitForFusePath(); - ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommands::kInstall); + ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommand::kInstall); struct stat sb; ASSERT_EQ(0, stat(exit_flag_.c_str(), &sb)); @@ -188,7 +188,7 @@ TEST_F(MinadbdServicesTest, SideloadHostService_read_data_from_fuse) { unique_fd fd = daemon_service_to_fd("sideload-host:4096:4096", nullptr); ASSERT_NE(-1, fd); - ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommands::kInstall); + ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommand::kInstall); // Mimic the response from adb host. std::string adb_message(8, '\0'); diff --git a/minadbd/minadbd_types.h b/minadbd/minadbd_types.h index 5fb7803e7..99fd45e83 100644 --- a/minadbd/minadbd_types.h +++ b/minadbd/minadbd_types.h @@ -30,7 +30,7 @@ enum MinadbdErrorCode : int { kMinadbdSocketIOError = 2, kMinadbdMessageFormatError = 3, kMinadbdAdbVersionError = 4, - kMinadbdPackageSizeError = 5, + kMinadbdHostCommandArgumentError = 5, kMinadbdFuseStartError = 6, kMinadbdUnsupportedCommandError = 7, kMinadbdCommandExecutionError = 8, @@ -43,12 +43,21 @@ enum class MinadbdCommandStatus : uint32_t { kFailure = 1, }; -enum class MinadbdCommands : uint32_t { +enum class MinadbdCommand : uint32_t { kInstall = 0, kUiPrint = 1, - kError = 2, + kRebootAndroid = 2, + kRebootBootloader = 3, + kRebootFastboot = 4, + kRebootRecovery = 5, + kRebootRescue = 6, + kWipeCache = 7, + kWipeData = 8, + + // Last but invalid command. + kError, }; -static_assert(kMinadbdMessageSize == sizeof(kMinadbdCommandPrefix) - 1 + sizeof(MinadbdCommands)); +static_assert(kMinadbdMessageSize == sizeof(kMinadbdCommandPrefix) - 1 + sizeof(MinadbdCommand)); static_assert(kMinadbdMessageSize == sizeof(kMinadbdStatusPrefix) - 1 + sizeof(MinadbdCommandStatus)); diff --git a/minui/resources.cpp b/minui/resources.cpp index 069a49529..00d36d5fb 100644 --- a/minui/resources.cpp +++ b/minui/resources.cpp @@ -347,6 +347,10 @@ bool matches_locale(const std::string& prefix, const std::string& locale) { // match the locale string without the {script} section. // For instance, prefix == "en" matches locale == "en-US", prefix == "sr-Latn" matches locale // == "sr-Latn-BA", and prefix == "zh-CN" matches locale == "zh-Hans-CN". + if (prefix.empty()) { + return false; + } + if (android::base::StartsWith(locale, prefix)) { return true; } @@ -414,12 +418,18 @@ int res_create_localized_alpha_surface(const char* name, __unused int len = row[4]; char* loc = reinterpret_cast<char*>(&row[5]); - if (y + 1 + h >= height || matches_locale(loc, locale)) { + // We need to include one additional line for the metadata of the localized image. + if (y + 1 + h > height) { + printf("Read exceeds the image boundary, y %u, h %d, height %u\n", y, h, height); + return -8; + } + + if (matches_locale(loc, locale)) { printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y); auto surface = GRSurface::Create(w, h, w, 1); if (!surface) { - return -8; + return -9; } for (int i = 0; i < h; ++i, ++y) { @@ -428,7 +438,7 @@ int res_create_localized_alpha_surface(const char* name, } *pSurface = surface.release(); - break; + return 0; } for (int i = 0; i < h; ++i, ++y) { @@ -436,7 +446,7 @@ int res_create_localized_alpha_surface(const char* name, } } - return 0; + return -10; } void res_free_surface(GRSurface* surface) { diff --git a/otautil/Android.bp b/otautil/Android.bp index 0a21731e8..73398c3aa 100644 --- a/otautil/Android.bp +++ b/otautil/Android.bp @@ -62,6 +62,10 @@ cc_library_static { "libfs_mgr", "libselinux", ], + + export_static_lib_headers: [ + "libfstab", + ], }, }, } diff --git a/recovery.cpp b/recovery.cpp index ce29cb27b..3cb6d6de5 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -18,11 +18,9 @@ #include <ctype.h> #include <errno.h> -#include <fcntl.h> #include <getopt.h> #include <inttypes.h> #include <limits.h> -#include <linux/fs.h> #include <linux/input.h> #include <stdio.h> #include <stdlib.h> @@ -30,8 +28,8 @@ #include <sys/types.h> #include <unistd.h> -#include <algorithm> #include <functional> +#include <iterator> #include <memory> #include <string> #include <vector> @@ -42,12 +40,11 @@ #include <android-base/properties.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> -#include <android-base/unique_fd.h> -#include <bootloader_message/bootloader_message.h> #include <cutils/properties.h> /* for property_list */ #include <healthhalutils/HealthHalUtils.h> #include <ziparchive/zip_archive.h> +#include "bootloader_message/bootloader_message.h" #include "common.h" #include "fsck_unshare_blocks.h" #include "install/adb_install.h" @@ -55,6 +52,7 @@ #include "install/install.h" #include "install/package.h" #include "install/wipe_data.h" +#include "install/wipe_device.h" #include "otautil/error_code.h" #include "otautil/logging.h" #include "otautil/paths.h" @@ -221,165 +219,6 @@ static InstallResult prompt_and_wipe_data(Device* device) { } } -// Secure-wipe a given partition. It uses BLKSECDISCARD, if supported. Otherwise, it goes with -// BLKDISCARD (if device supports BLKDISCARDZEROES) or BLKZEROOUT. -static bool secure_wipe_partition(const std::string& partition) { - android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(partition.c_str(), O_WRONLY))); - if (fd == -1) { - PLOG(ERROR) << "Failed to open \"" << partition << "\""; - return false; - } - - uint64_t range[2] = { 0, 0 }; - if (ioctl(fd, BLKGETSIZE64, &range[1]) == -1 || range[1] == 0) { - PLOG(ERROR) << "Failed to get partition size"; - return false; - } - LOG(INFO) << "Secure-wiping \"" << partition << "\" from " << range[0] << " to " << range[1]; - - LOG(INFO) << " Trying BLKSECDISCARD..."; - if (ioctl(fd, BLKSECDISCARD, &range) == -1) { - PLOG(WARNING) << " Failed"; - - // Use BLKDISCARD if it zeroes out blocks, otherwise use BLKZEROOUT. - unsigned int zeroes; - if (ioctl(fd, BLKDISCARDZEROES, &zeroes) == 0 && zeroes != 0) { - LOG(INFO) << " Trying BLKDISCARD..."; - if (ioctl(fd, BLKDISCARD, &range) == -1) { - PLOG(ERROR) << " Failed"; - return false; - } - } else { - LOG(INFO) << " Trying BLKZEROOUT..."; - if (ioctl(fd, BLKZEROOUT, &range) == -1) { - PLOG(ERROR) << " Failed"; - return false; - } - } - } - - LOG(INFO) << " Done"; - return true; -} - -static std::unique_ptr<Package> ReadWipePackage(size_t wipe_package_size) { - if (wipe_package_size == 0) { - LOG(ERROR) << "wipe_package_size is zero"; - return nullptr; - } - - std::string wipe_package; - std::string err_str; - if (!read_wipe_package(&wipe_package, wipe_package_size, &err_str)) { - PLOG(ERROR) << "Failed to read wipe package" << err_str; - return nullptr; - } - - return Package::CreateMemoryPackage( - std::vector<uint8_t>(wipe_package.begin(), wipe_package.end()), nullptr); -} - -// Checks if the wipe package matches expectation. If the check passes, reads the list of -// partitions to wipe from the package. Checks include -// 1. verify the package. -// 2. check metadata (ota-type, pre-device and serial number if having one). -static bool CheckWipePackage(Package* wipe_package) { - if (!verify_package(wipe_package, ui)) { - LOG(ERROR) << "Failed to verify package"; - return false; - } - - ZipArchiveHandle zip = wipe_package->GetZipArchiveHandle(); - if (!zip) { - LOG(ERROR) << "Failed to get ZipArchiveHandle"; - return false; - } - - std::map<std::string, std::string> metadata; - if (!ReadMetadataFromPackage(zip, &metadata)) { - LOG(ERROR) << "Failed to parse metadata in the zip file"; - return false; - } - - return CheckPackageMetadata(metadata, OtaType::BRICK) == 0; -} - -std::vector<std::string> GetWipePartitionList(Package* wipe_package) { - ZipArchiveHandle zip = wipe_package->GetZipArchiveHandle(); - if (!zip) { - LOG(ERROR) << "Failed to get ZipArchiveHandle"; - return {}; - } - - static constexpr const char* RECOVERY_WIPE_ENTRY_NAME = "recovery.wipe"; - - std::string partition_list_content; - ZipString path(RECOVERY_WIPE_ENTRY_NAME); - ZipEntry entry; - if (FindEntry(zip, path, &entry) == 0) { - uint32_t length = entry.uncompressed_length; - partition_list_content = std::string(length, '\0'); - if (auto err = ExtractToMemory( - zip, &entry, reinterpret_cast<uint8_t*>(partition_list_content.data()), length); - err != 0) { - LOG(ERROR) << "Failed to extract " << RECOVERY_WIPE_ENTRY_NAME << ": " - << ErrorCodeString(err); - return {}; - } - } else { - LOG(INFO) << "Failed to find " << RECOVERY_WIPE_ENTRY_NAME - << ", falling back to use the partition list on device."; - - static constexpr const char* RECOVERY_WIPE_ON_DEVICE = "/etc/recovery.wipe"; - if (!android::base::ReadFileToString(RECOVERY_WIPE_ON_DEVICE, &partition_list_content)) { - PLOG(ERROR) << "failed to read \"" << RECOVERY_WIPE_ON_DEVICE << "\""; - return {}; - } - } - - std::vector<std::string> result; - std::vector<std::string> lines = android::base::Split(partition_list_content, "\n"); - for (const std::string& line : lines) { - std::string partition = android::base::Trim(line); - // Ignore '#' comment or empty lines. - if (android::base::StartsWith(partition, "#") || partition.empty()) { - continue; - } - result.push_back(line); - } - - return result; -} - -// Wipes the current A/B device, with a secure wipe of all the partitions in RECOVERY_WIPE. -static bool wipe_ab_device(size_t wipe_package_size) { - ui->SetBackground(RecoveryUI::ERASING); - ui->SetProgressType(RecoveryUI::INDETERMINATE); - - auto wipe_package = ReadWipePackage(wipe_package_size); - if (!wipe_package) { - LOG(ERROR) << "Failed to open wipe package"; - return false; - } - - if (!CheckWipePackage(wipe_package.get())) { - LOG(ERROR) << "Failed to verify wipe package"; - return false; - } - - auto partition_list = GetWipePartitionList(wipe_package.get()); - if (partition_list.empty()) { - LOG(ERROR) << "Empty wipe ab partition list"; - return false; - } - - for (const auto& partition : partition_list) { - // Proceed anyway even if it fails to wipe some partition. - secure_wipe_partition(partition); - } - return true; -} - static void choose_recovery_file(Device* device) { std::vector<std::string> entries; if (has_cache) { @@ -509,11 +348,14 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { case Device::NO_ACTION: break; - case Device::REBOOT: - case Device::SHUTDOWN: - case Device::REBOOT_BOOTLOADER: case Device::ENTER_FASTBOOT: case Device::ENTER_RECOVERY: + case Device::REBOOT: + case Device::REBOOT_BOOTLOADER: + case Device::REBOOT_FASTBOOT: + case Device::REBOOT_RECOVERY: + case Device::REBOOT_RESCUE: + case Device::SHUTDOWN: return chosen_action; case Device::WIPE_DATA: @@ -537,32 +379,36 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { if (!ui->IsTextVisible()) return Device::NO_ACTION; break; } + case Device::APPLY_ADB_SIDELOAD: case Device::APPLY_SDCARD: case Device::ENTER_RESCUE: { save_current_log = true; bool adb = true; + Device::BuiltinAction reboot_action; if (chosen_action == Device::ENTER_RESCUE) { // Switch to graphics screen. ui->ShowText(false); - status = ApplyFromAdb(ui, true /* rescue_mode */); - ui->ShowText(true); + status = ApplyFromAdb(device, true /* rescue_mode */, &reboot_action); } else if (chosen_action == Device::APPLY_ADB_SIDELOAD) { - status = ApplyFromAdb(ui, false /* rescue_mode */); + status = ApplyFromAdb(device, false /* rescue_mode */, &reboot_action); } else { adb = false; status = ApplyFromSdcard(device, ui); } + ui->Print("\nInstall from %s completed with status %d.\n", adb ? "ADB" : "SD card", status); + if (status == INSTALL_REBOOT) { + return reboot_action; + } + if (status != INSTALL_SUCCESS) { ui->SetBackground(RecoveryUI::ERROR); ui->Print("Installation aborted.\n"); copy_logs(save_current_log, has_cache, sehandle); } else if (!ui->IsTextVisible()) { return Device::NO_ACTION; // reboot if logs aren't visible - } else { - ui->Print("\nInstall from %s complete.\n", adb ? "ADB" : "SD card"); } break; } @@ -723,6 +569,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri { "locale", required_argument, nullptr, 0 }, { "prompt_and_wipe_data", no_argument, nullptr, 0 }, { "reason", required_argument, nullptr, 0 }, + { "rescue", no_argument, nullptr, 0 }, { "retry_count", required_argument, nullptr, 0 }, { "security", no_argument, nullptr, 0 }, { "show_text", no_argument, nullptr, 't' }, @@ -745,6 +592,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri size_t wipe_package_size = 0; bool sideload = false; bool sideload_auto_reboot = false; + bool rescue = false; bool just_exit = false; bool shutdown_after = false; bool fsck_unshare_blocks = false; @@ -778,6 +626,8 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri should_prompt_and_wipe_data = true; } else if (option == "reason") { reason = optarg; + } else if (option == "rescue") { + rescue = true; } else if (option == "retry_count") { android::base::ParseInt(optarg, &retry_count, 0); } else if (option == "security") { @@ -841,6 +691,9 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri ui->Print("Supported API: %d\n", kRecoveryApiVersion); int status = INSTALL_SUCCESS; + // next_action indicates the next target to reboot into upon finishing the install. It could be + // overridden to a different reboot target per user request. + Device::BuiltinAction next_action = shutdown_after ? Device::SHUTDOWN : Device::REBOOT; if (update_package != nullptr) { // It's not entirely true that we will modify the flash. But we want @@ -920,25 +773,28 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri status = INSTALL_ERROR; } } else if (should_wipe_ab) { - if (!wipe_ab_device(wipe_package_size)) { + if (!WipeAbDevice(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 + // '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. This is to enable automated // testing. save_current_log = true; if (!sideload_auto_reboot) { ui->ShowText(true); } - status = ApplyFromAdb(ui, false /* rescue_mode */); + status = ApplyFromAdb(device, false /* rescue_mode */, &next_action); ui->Print("\nInstall from ADB complete (status: %d).\n", status); if (sideload_auto_reboot) { + status = INSTALL_REBOOT; ui->Print("Rebooting automatically.\n"); } + } else if (rescue) { + save_current_log = true; + status = ApplyFromAdb(device, true /* rescue_mode */, &next_action); + ui->Print("\nInstall from ADB complete (status: %d).\n", status); } else if (fsck_unshare_blocks) { if (!do_fsck_unshare_blocks()) { status = INSTALL_ERROR; @@ -961,23 +817,26 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri } } - 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; + // Determine the next action. + // - If the state is INSTALL_REBOOT, device will reboot into the target as specified in + // `next_action`. + // - If the recovery menu is visible, prompt and wait for commands. + // - If the state is INSTALL_NONE, wait for commands (e.g. in user build, one manually boots + // into recovery to sideload a package or to wipe the device). + // - In all other cases, reboot the device. Therefore, normal users will observe the device + // rebooting a) immediately upon successful finish (INSTALL_SUCCESS); or b) an "error" screen + // for 5s followed by an automatic reboot. + if (status != INSTALL_REBOOT) { + if (status == INSTALL_NONE || ui->IsTextVisible()) { + Device::BuiltinAction temp = prompt_and_wait(device, status); + if (temp != Device::NO_ACTION) { + next_action = temp; + } } } // Save logs and clean up before rebooting or shutting down. finish_recovery(); - return after; + return next_action; } diff --git a/recovery_main.cpp b/recovery_main.cpp index 38e1db73b..6e69b7009 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -155,9 +155,11 @@ static std::vector<std::string> get_args(const int argc, char** const argv) { } // Finally, if no arguments were specified, check whether we should boot - // into fastboot. + // into fastboot or rescue mode. if (args.size() == 1 && boot_command == "boot-fastboot") { args.emplace_back("--fastboot"); + } else if (args.size() == 1 && boot_command == "boot-rescue") { + args.emplace_back("--rescue"); } return args; @@ -373,7 +375,6 @@ int main(int argc, char** argv) { } if (locale.empty()) { - static constexpr const char* DEFAULT_LOCALE = "en-US"; locale = DEFAULT_LOCALE; } } @@ -470,6 +471,7 @@ int main(int argc, char** argv) { switch (ret) { case Device::SHUTDOWN: ui->Print("Shutting down...\n"); + // TODO: Move all the reboots to reboot(), which should conditionally set quiescent flag. android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,"); break; @@ -478,6 +480,32 @@ int main(int argc, char** argv) { android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader"); break; + case Device::REBOOT_FASTBOOT: + ui->Print("Rebooting to recovery/fastboot...\n"); + android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,fastboot"); + break; + + case Device::REBOOT_RECOVERY: + ui->Print("Rebooting to recovery...\n"); + reboot("reboot,recovery"); + break; + + case Device::REBOOT_RESCUE: { + // Not using `reboot("reboot,rescue")`, as it requires matching support in kernel and/or + // bootloader. + bootloader_message boot = {}; + strlcpy(boot.command, "boot-rescue", sizeof(boot.command)); + std::string err; + if (!write_bootloader_message(boot, &err)) { + LOG(ERROR) << "Failed to write bootloader message: " << err; + // Stay under recovery on failure. + continue; + } + ui->Print("Rebooting to recovery/rescue...\n"); + reboot("reboot,recovery"); + break; + } + case Device::ENTER_FASTBOOT: if (logical_partitions_mapped()) { ui->Print("Partitions may be mounted - rebooting to enter fastboot."); diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h index 8f17639d6..7c76cdb0a 100644 --- a/recovery_ui/include/recovery_ui/device.h +++ b/recovery_ui/include/recovery_ui/device.h @@ -33,6 +33,10 @@ class Device { static constexpr const int kHighlightDown = -3; static constexpr const int kInvokeItem = -4; + // ENTER vs REBOOT: The latter will trigger a reboot that goes through bootloader, which allows + // using a new bootloader / recovery image if applicable. For example, REBOOT_RESCUE goes from + // rescue -> bootloader -> rescue, whereas ENTER_RESCUE switches from recovery -> rescue + // directly. enum BuiltinAction { NO_ACTION = 0, REBOOT = 1, @@ -51,6 +55,9 @@ class Device { ENTER_FASTBOOT = 14, ENTER_RECOVERY = 15, ENTER_RESCUE = 16, + REBOOT_FASTBOOT = 17, + REBOOT_RECOVERY = 18, + REBOOT_RESCUE = 19, }; explicit Device(RecoveryUI* ui); diff --git a/recovery_ui/include/recovery_ui/ui.h b/recovery_ui/include/recovery_ui/ui.h index d55322cf0..797e2f0d5 100644 --- a/recovery_ui/include/recovery_ui/ui.h +++ b/recovery_ui/include/recovery_ui/ui.h @@ -27,6 +27,8 @@ #include <thread> #include <vector> +static constexpr const char* DEFAULT_LOCALE = "en-US"; + // Abstract class for controlling the user interface during recovery. class RecoveryUI { public: diff --git a/recovery_ui/screen_ui.cpp b/recovery_ui/screen_ui.cpp index 870db621c..823004521 100644 --- a/recovery_ui/screen_ui.cpp +++ b/recovery_ui/screen_ui.cpp @@ -817,12 +817,22 @@ std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadBitmap(const std::string& filen std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadLocalizedBitmap(const std::string& filename) { GRSurface* surface; - if (auto result = res_create_localized_alpha_surface(filename.c_str(), locale_.c_str(), &surface); - result < 0) { - LOG(ERROR) << "Failed to load bitmap " << filename << " (error " << result << ")"; - return nullptr; + auto result = res_create_localized_alpha_surface(filename.c_str(), locale_.c_str(), &surface); + if (result == 0) { + return std::unique_ptr<GRSurface>(surface); } - return std::unique_ptr<GRSurface>(surface); + // TODO(xunchang) create a error code enum to refine the retry condition. + LOG(WARNING) << "Failed to load bitmap " << filename << " for locale " << locale_ << " (error " + << result << "). Falling back to use default locale."; + + result = res_create_localized_alpha_surface(filename.c_str(), DEFAULT_LOCALE, &surface); + if (result == 0) { + return std::unique_ptr<GRSurface>(surface); + } + + LOG(ERROR) << "Failed to load bitmap " << filename << " for locale " << DEFAULT_LOCALE + << " (error " << result << ")"; + return nullptr; } static char** Alloc2d(size_t rows, size_t cols) { diff --git a/tests/Android.bp b/tests/Android.bp index 09ef716d6..2e5334d9e 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -197,6 +197,8 @@ cc_test_host { "libz", ], + test_suites: ["general-tests"], + data: ["testdata/*"], target: { diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml deleted file mode 100644 index 6b86085aa..000000000 --- a/tests/AndroidTest.xml +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<configuration description="Config for recovery_component_test and recovery_unit_test"> - <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> - <option name="cleanup" value="true" /> - <option name="push" value="recovery_component_test->/data/local/tmp/recovery_component_test/recovery_component_test" /> - <option name="push" value="testdata->/data/local/tmp/recovery_component_test/testdata" /> - <option name="push" value="recovery_unit_test->/data/local/tmp/recovery_unit_test/recovery_unit_test" /> - <option name="push" value="testdata->/data/local/tmp/recovery_unit_test/testdata" /> - </target_preparer> - <option name="test-suite-tag" value="apct" /> - <test class="com.android.tradefed.testtype.GTest" > - <option name="native-test-device-path" value="/data/local/tmp/recovery_component_test" /> - <option name="module-name" value="recovery_component_test" /> - </test> - <test class="com.android.tradefed.testtype.GTest" > - <option name="native-test-device-path" value="/data/local/tmp/recovery_unit_test" /> - <option name="module-name" value="recovery_unit_test" /> - </test> -</configuration> diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp index 385132939..36820f837 100644 --- a/tests/component/install_test.cpp +++ b/tests/component/install_test.cpp @@ -33,6 +33,7 @@ #include <ziparchive/zip_writer.h> #include "install/install.h" +#include "install/wipe_device.h" #include "otautil/paths.h" #include "private/setup_commands.h" diff --git a/tests/unit/locale_test.cpp b/tests/unit/locale_test.cpp index cdaba0e8b..c69434c12 100644 --- a/tests/unit/locale_test.cpp +++ b/tests/unit/locale_test.cpp @@ -27,7 +27,7 @@ TEST(LocaleTest, Misc) { EXPECT_FALSE(matches_locale("en-GB", "en")); EXPECT_FALSE(matches_locale("en-GB", "en-US")); EXPECT_FALSE(matches_locale("en-US", "")); - // Empty locale prefix in the PNG file will match the input locale. - EXPECT_TRUE(matches_locale("", "en-US")); + // Empty locale prefix in the PNG file should not match the input locale. + EXPECT_FALSE(matches_locale("", "en-US")); EXPECT_TRUE(matches_locale("sr-Latn", "sr-Latn-BA")); } diff --git a/tools/recovery_l10n/res/values-gl/strings.xml b/tools/recovery_l10n/res/values-gl/strings.xml index e6f2ffd84..e51b36dfb 100644 --- a/tools/recovery_l10n/res/values-gl/strings.xml +++ b/tools/recovery_l10n/res/values-gl/strings.xml @@ -6,9 +6,9 @@ <string name="recovery_no_command" msgid="4465476568623024327">"Non hai ningún comando"</string> <string name="recovery_error" msgid="5748178989622716736">"Erro"</string> <string name="recovery_installing_security" msgid="9184031299717114342">"Instalando actualización de seguranza"</string> - <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Non se puido cargar o sistema Android. Os teus datos poden estar danados. Se segue aparecendo esta mensaxe, pode ser necesario restablecer os datos de fábrica e borrar todos os datos do usuario almacenados neste dispositivo."</string> + <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Non se puido cargar o sistema Android. Os teus datos poden estar danados. Se segue aparecendo esta mensaxe, pode ser necesario restablecer os datos de fábrica e borrar todos os datos de usuario almacenados neste dispositivo."</string> <string name="recovery_try_again" msgid="7168248750158873496">"Tentar de novo"</string> <string name="recovery_factory_data_reset" msgid="7321351565602894783">"Restablecemento dos datos de fábrica"</string> - <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Queres borrar todos os datos do usuario?\n\n ESTA ACCIÓN NON SE PODE DESFACER."</string> + <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Queres borrar todos os datos de usuario?\n\n ESTA ACCIÓN NON SE PODE DESFACER."</string> <string name="recovery_cancel_wipe_data" msgid="66987687653647384">"Cancelar"</string> </resources> diff --git a/tools/recovery_l10n/res/values-ja/strings.xml b/tools/recovery_l10n/res/values-ja/strings.xml index 2d6c0abc4..3d6637278 100644 --- a/tools/recovery_l10n/res/values-ja/strings.xml +++ b/tools/recovery_l10n/res/values-ja/strings.xml @@ -6,7 +6,7 @@ <string name="recovery_no_command" msgid="4465476568623024327">"コマンドが指定されていません"</string> <string name="recovery_error" msgid="5748178989622716736">"エラーが発生しました。"</string> <string name="recovery_installing_security" msgid="9184031299717114342">"セキュリティ アップデートをインストールしています"</string> - <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Android システムを読み込めません。データが破損している可能性があります。このメッセージが引き続き表示される場合は、データの初期化を行い、このデバイスに保存されているすべてのユーザー データを消去することが必要な場合があります。"</string> + <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Android システムを読み込めません。データが破損している可能性があります。このメッセージが引き続き表示される場合は、データの初期化を行い、この端末に保存されているすべてのユーザー データを消去することが必要な場合があります。"</string> <string name="recovery_try_again" msgid="7168248750158873496">"再試行"</string> <string name="recovery_factory_data_reset" msgid="7321351565602894783">"データの初期化"</string> <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"すべてのユーザー データをワイプしますか?\n\nこの操作は元に戻せません。"</string> |