summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--boot_control/Android.bp61
-rw-r--r--boot_control/include/libboot_control/libboot_control.h89
-rw-r--r--boot_control/legacy_boot_control.cpp115
-rw-r--r--boot_control/libboot_control.cpp424
-rw-r--r--bootloader_message/include/bootloader_message/bootloader_message.h102
-rw-r--r--fsck_unshare_blocks.cpp151
-rw-r--r--fsck_unshare_blocks.h22
-rw-r--r--recovery.cpp93
-rw-r--r--recovery_ui/screen_ui.cpp4
-rw-r--r--recovery_utils/roots.cpp9
-rw-r--r--tests/Android.bp20
-rw-r--r--tests/testdata/recovery.imgbin529707 -> 0 bytes
-rw-r--r--tests/testdata/recovery_bodybin0 -> 1293568 bytes
-rw-r--r--tests/testdata/recovery_headbin0 -> 45056 bytes
-rw-r--r--tests/testdata/recovery_tailbin0 -> 5191 bytes
-rw-r--r--tools/image_generator/ImageGenerator.java2
-rw-r--r--tools/recovery_l10n/res/values-ar/strings.xml8
-rw-r--r--updater/Android.bp1
-rw-r--r--updater/Android.mk1
-rw-r--r--updater_sample/AndroidManifest.xml1
21 files changed, 116 insertions, 988 deletions
diff --git a/Android.bp b/Android.bp
index c5225cc8b..8e29dc8fd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -91,7 +91,6 @@ cc_library_static {
],
srcs: [
- "fsck_unshare_blocks.cpp",
"recovery.cpp",
],
diff --git a/boot_control/Android.bp b/boot_control/Android.bp
deleted file mode 100644
index b2e68dfd4..000000000
--- a/boot_control/Android.bp
+++ /dev/null
@@ -1,61 +0,0 @@
-//
-// Copyright (C) 2018 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.
-//
-
-cc_defaults {
- name: "libboot_control_defaults",
- vendor: true,
- recovery_available: true,
- relative_install_path: "hw",
-
- cflags: [
- "-D_FILE_OFFSET_BITS=64",
- "-Werror",
- "-Wall",
- "-Wextra",
- ],
-
- shared_libs: [
- "android.hardware.boot@1.1",
- "libbase",
- "liblog",
- ],
- static_libs: [
- "libbootloader_message_vendor",
- "libfstab",
- ],
-}
-
-cc_library_static {
- name: "libboot_control",
- defaults: ["libboot_control_defaults"],
- export_include_dirs: ["include"],
-
- srcs: ["libboot_control.cpp"],
-}
-
-cc_library_shared {
- name: "bootctrl.default",
- defaults: ["libboot_control_defaults"],
-
- srcs: ["legacy_boot_control.cpp"],
-
- static_libs: [
- "libboot_control",
- ],
- shared_libs: [
- "libhardware",
- ],
-}
diff --git a/boot_control/include/libboot_control/libboot_control.h b/boot_control/include/libboot_control/libboot_control.h
deleted file mode 100644
index 546865887..000000000
--- a/boot_control/include/libboot_control/libboot_control.h
+++ /dev/null
@@ -1,89 +0,0 @@
-//
-// 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 <android/hardware/boot/1.1/IBootControl.h>
-
-namespace android {
-namespace bootable {
-
-// Helper library to implement the IBootControl HAL using the misc partition.
-class BootControl {
- using MergeStatus = ::android::hardware::boot::V1_1::MergeStatus;
-
- public:
- bool Init();
- unsigned int GetNumberSlots();
- unsigned int GetCurrentSlot();
- bool MarkBootSuccessful();
- bool SetActiveBootSlot(unsigned int slot);
- bool SetSlotAsUnbootable(unsigned int slot);
- bool SetSlotBootable(unsigned int slot);
- bool IsSlotBootable(unsigned int slot);
- const char* GetSuffix(unsigned int slot);
- bool IsSlotMarkedSuccessful(unsigned int slot);
- bool SetSnapshotMergeStatus(MergeStatus status);
- MergeStatus GetSnapshotMergeStatus();
-
- bool IsValidSlot(unsigned int slot);
-
- const std::string& misc_device() const {
- return misc_device_;
- }
-
- private:
- // Whether this object was initialized with data from the bootloader message
- // that doesn't change until next reboot.
- bool initialized_ = false;
-
- // The path to the misc_device as reported in the fstab.
- std::string misc_device_;
-
- // The number of slots present on the device.
- unsigned int num_slots_ = 0;
-
- // The slot where we are running from.
- unsigned int current_slot_ = 0;
-};
-
-// Helper functions to write the Virtual A/B merge status message. These are
-// separate because BootControl uses bootloader_control_ab in vendor space,
-// whereas the Virtual A/B merge status is in system space. A HAL might not
-// use bootloader_control_ab, but may want to use the AOSP method of maintaining
-// the merge status.
-
-// If the Virtual A/B message has not yet been initialized, then initialize it.
-// This should be called when the BootControl HAL first loads.
-//
-// If the Virtual A/B message in misc was already initialized, true is returned.
-// If initialization was attempted, but failed, false is returned, and the HAL
-// should fail to load.
-bool InitMiscVirtualAbMessageIfNeeded();
-
-// Save the current merge status as well as the current slot.
-bool SetMiscVirtualAbMergeStatus(unsigned int current_slot,
- android::hardware::boot::V1_1::MergeStatus status);
-
-// Return the current merge status. If the saved status is SNAPSHOTTED but the
-// slot hasn't changed, the status returned will be NONE.
-bool GetMiscVirtualAbMergeStatus(unsigned int current_slot,
- android::hardware::boot::V1_1::MergeStatus* status);
-
-} // namespace bootable
-} // namespace android
diff --git a/boot_control/legacy_boot_control.cpp b/boot_control/legacy_boot_control.cpp
deleted file mode 100644
index 73d3a5841..000000000
--- a/boot_control/legacy_boot_control.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2015 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 <string>
-
-#include <hardware/boot_control.h>
-#include <hardware/hardware.h>
-
-#include <libboot_control/libboot_control.h>
-
-using android::bootable::BootControl;
-
-struct boot_control_private_t {
- // The base struct needs to be first in the list.
- boot_control_module_t base;
-
- BootControl impl;
-};
-
-namespace {
-
-void BootControl_init(boot_control_module_t* module) {
- auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
- impl.Init();
-}
-
-unsigned int BootControl_getNumberSlots(boot_control_module_t* module) {
- auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
- return impl.GetNumberSlots();
-}
-
-unsigned int BootControl_getCurrentSlot(boot_control_module_t* module) {
- auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
- return impl.GetCurrentSlot();
-}
-
-int BootControl_markBootSuccessful(boot_control_module_t* module) {
- auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
- return impl.MarkBootSuccessful() ? 0 : -1;
-}
-
-int BootControl_setActiveBootSlot(boot_control_module_t* module, unsigned int slot) {
- auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
- return impl.SetActiveBootSlot(slot) ? 0 : -1;
-}
-
-int BootControl_setSlotAsUnbootable(struct boot_control_module* module, unsigned int slot) {
- auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
- return impl.SetSlotAsUnbootable(slot) ? 0 : -1;
-}
-
-int BootControl_isSlotBootable(struct boot_control_module* module, unsigned int slot) {
- auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
- return impl.IsSlotBootable(slot) ? 0 : -1;
-}
-
-int BootControl_isSlotMarkedSuccessful(struct boot_control_module* module, unsigned int slot) {
- auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
- return impl.IsSlotMarkedSuccessful(slot) ? 0 : -1;
-}
-
-const char* BootControl_getSuffix(boot_control_module_t* module, unsigned int slot) {
- auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
- return impl.GetSuffix(slot);
-}
-
-static int BootControl_open(const hw_module_t* module __unused, const char* id __unused,
- hw_device_t** device __unused) {
- /* Nothing to do currently. */
- return 0;
-}
-
-struct hw_module_methods_t BootControl_methods = {
- .open = BootControl_open,
-};
-
-} // namespace
-
-boot_control_private_t HAL_MODULE_INFO_SYM = {
- .base =
- {
- .common =
- {
- .tag = HARDWARE_MODULE_TAG,
- .module_api_version = BOOT_CONTROL_MODULE_API_VERSION_0_1,
- .hal_api_version = HARDWARE_HAL_API_VERSION,
- .id = BOOT_CONTROL_HARDWARE_MODULE_ID,
- .name = "AOSP reference bootctrl HAL",
- .author = "The Android Open Source Project",
- .methods = &BootControl_methods,
- },
- .init = BootControl_init,
- .getNumberSlots = BootControl_getNumberSlots,
- .getCurrentSlot = BootControl_getCurrentSlot,
- .markBootSuccessful = BootControl_markBootSuccessful,
- .setActiveBootSlot = BootControl_setActiveBootSlot,
- .setSlotAsUnbootable = BootControl_setSlotAsUnbootable,
- .isSlotBootable = BootControl_isSlotBootable,
- .getSuffix = BootControl_getSuffix,
- .isSlotMarkedSuccessful = BootControl_isSlotMarkedSuccessful,
- },
-};
diff --git a/boot_control/libboot_control.cpp b/boot_control/libboot_control.cpp
deleted file mode 100644
index ab9ce971b..000000000
--- a/boot_control/libboot_control.cpp
+++ /dev/null
@@ -1,424 +0,0 @@
-/*
- * Copyright (C) 2015 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 <libboot_control/libboot_control.h>
-
-#include <endian.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <string.h>
-
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-
-#include <bootloader_message/bootloader_message.h>
-
-namespace android {
-namespace bootable {
-
-using ::android::hardware::boot::V1_1::MergeStatus;
-
-// The number of boot attempts that should be made from a new slot before
-// rolling back to the previous slot.
-constexpr unsigned int kDefaultBootAttempts = 7;
-static_assert(kDefaultBootAttempts < 8, "tries_remaining field only has 3 bits");
-
-constexpr unsigned int kMaxNumSlots =
- sizeof(bootloader_control::slot_info) / sizeof(bootloader_control::slot_info[0]);
-constexpr const char* kSlotSuffixes[kMaxNumSlots] = { "_a", "_b", "_c", "_d" };
-constexpr off_t kBootloaderControlOffset = offsetof(bootloader_message_ab, slot_suffix);
-
-static uint32_t CRC32(const uint8_t* buf, size_t size) {
- static uint32_t crc_table[256];
-
- // Compute the CRC-32 table only once.
- if (!crc_table[1]) {
- for (uint32_t i = 0; i < 256; ++i) {
- uint32_t crc = i;
- for (uint32_t j = 0; j < 8; ++j) {
- uint32_t mask = -(crc & 1);
- crc = (crc >> 1) ^ (0xEDB88320 & mask);
- }
- crc_table[i] = crc;
- }
- }
-
- uint32_t ret = -1;
- for (size_t i = 0; i < size; ++i) {
- ret = (ret >> 8) ^ crc_table[(ret ^ buf[i]) & 0xFF];
- }
-
- return ~ret;
-}
-
-// Return the little-endian representation of the CRC-32 of the first fields
-// in |boot_ctrl| up to the crc32_le field.
-uint32_t BootloaderControlLECRC(const bootloader_control* boot_ctrl) {
- return htole32(
- CRC32(reinterpret_cast<const uint8_t*>(boot_ctrl), offsetof(bootloader_control, crc32_le)));
-}
-
-bool LoadBootloaderControl(const std::string& misc_device, bootloader_control* buffer) {
- android::base::unique_fd fd(open(misc_device.c_str(), O_RDONLY));
- if (fd.get() == -1) {
- PLOG(ERROR) << "failed to open " << misc_device;
- return false;
- }
- if (lseek(fd, kBootloaderControlOffset, SEEK_SET) != kBootloaderControlOffset) {
- PLOG(ERROR) << "failed to lseek " << misc_device;
- return false;
- }
- if (!android::base::ReadFully(fd.get(), buffer, sizeof(bootloader_control))) {
- PLOG(ERROR) << "failed to read " << misc_device;
- return false;
- }
- return true;
-}
-
-bool UpdateAndSaveBootloaderControl(const std::string& misc_device, bootloader_control* buffer) {
- buffer->crc32_le = BootloaderControlLECRC(buffer);
- android::base::unique_fd fd(open(misc_device.c_str(), O_WRONLY | O_SYNC));
- if (fd.get() == -1) {
- PLOG(ERROR) << "failed to open " << misc_device;
- return false;
- }
- if (lseek(fd.get(), kBootloaderControlOffset, SEEK_SET) != kBootloaderControlOffset) {
- PLOG(ERROR) << "failed to lseek " << misc_device;
- return false;
- }
- if (!android::base::WriteFully(fd.get(), buffer, sizeof(bootloader_control))) {
- PLOG(ERROR) << "failed to write " << misc_device;
- return false;
- }
- return true;
-}
-
-void InitDefaultBootloaderControl(BootControl* control, bootloader_control* boot_ctrl) {
- memset(boot_ctrl, 0, sizeof(*boot_ctrl));
-
- unsigned int current_slot = control->GetCurrentSlot();
- if (current_slot < kMaxNumSlots) {
- strlcpy(boot_ctrl->slot_suffix, kSlotSuffixes[current_slot], sizeof(boot_ctrl->slot_suffix));
- }
- boot_ctrl->magic = BOOT_CTRL_MAGIC;
- boot_ctrl->version = BOOT_CTRL_VERSION;
-
- // Figure out the number of slots by checking if the partitions exist,
- // otherwise assume the maximum supported by the header.
- boot_ctrl->nb_slot = kMaxNumSlots;
- std::string base_path = control->misc_device();
- size_t last_path_sep = base_path.rfind('/');
- if (last_path_sep != std::string::npos) {
- // We test the existence of the "boot" partition on each possible slot,
- // which is a partition required by Android Bootloader Requirements.
- base_path = base_path.substr(0, last_path_sep + 1) + "boot";
- int last_existing_slot = -1;
- int first_missing_slot = -1;
- for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
- std::string partition_path = base_path + kSlotSuffixes[slot];
- struct stat part_stat;
- int err = stat(partition_path.c_str(), &part_stat);
- if (!err) {
- last_existing_slot = slot;
- LOG(INFO) << "Found slot: " << kSlotSuffixes[slot];
- } else if (err < 0 && errno == ENOENT && first_missing_slot == -1) {
- first_missing_slot = slot;
- }
- }
- // We only declare that we found the actual number of slots if we found all
- // the boot partitions up to the number of slots, and no boot partition
- // after that. Not finding any of the boot partitions implies a problem so
- // we just leave the number of slots in the maximum value.
- if ((last_existing_slot != -1 && last_existing_slot + 1 == first_missing_slot) ||
- (first_missing_slot == -1 && last_existing_slot + 1 == kMaxNumSlots)) {
- boot_ctrl->nb_slot = last_existing_slot + 1;
- LOG(INFO) << "Found a system with " << last_existing_slot + 1 << " slots.";
- }
- }
-
- for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
- slot_metadata entry = {};
-
- if (slot < boot_ctrl->nb_slot) {
- entry.priority = 7;
- entry.tries_remaining = kDefaultBootAttempts;
- entry.successful_boot = 0;
- } else {
- entry.priority = 0; // Unbootable
- }
-
- // When the boot_control stored on disk is invalid, we assume that the
- // current slot is successful. The bootloader should repair this situation
- // before booting and write a valid boot_control slot, so if we reach this
- // stage it means that the misc partition was corrupted since boot.
- if (current_slot == slot) {
- entry.successful_boot = 1;
- }
-
- boot_ctrl->slot_info[slot] = entry;
- }
- boot_ctrl->recovery_tries_remaining = 0;
-
- boot_ctrl->crc32_le = BootloaderControlLECRC(boot_ctrl);
-}
-
-// Return the index of the slot suffix passed or -1 if not a valid slot suffix.
-int SlotSuffixToIndex(const char* suffix) {
- for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
- if (!strcmp(kSlotSuffixes[slot], suffix)) return slot;
- }
- return -1;
-}
-
-// Initialize the boot_control_private struct with the information from
-// the bootloader_message buffer stored in |boot_ctrl|. Returns whether the
-// initialization succeeded.
-bool BootControl::Init() {
- if (initialized_) return true;
-
- // Initialize the current_slot from the read-only property. If the property
- // was not set (from either the command line or the device tree), we can later
- // initialize it from the bootloader_control struct.
- std::string suffix_prop = android::base::GetProperty("ro.boot.slot_suffix", "");
- if (suffix_prop.empty()) {
- LOG(ERROR) << "Slot suffix property is not set";
- return false;
- }
- current_slot_ = SlotSuffixToIndex(suffix_prop.c_str());
-
- std::string err;
- std::string device = get_bootloader_message_blk_device(&err);
- if (device.empty()) {
- LOG(ERROR) << "Could not find bootloader message block device: " << err;
- return false;
- }
-
- bootloader_control boot_ctrl;
- if (!LoadBootloaderControl(device.c_str(), &boot_ctrl)) {
- LOG(ERROR) << "Failed to load bootloader control block";
- return false;
- }
-
- // Note that since there isn't a module unload function this memory is leaked.
- // We use `device` below sometimes, so it's not moved out of here.
- misc_device_ = device;
- initialized_ = true;
-
- // Validate the loaded data, otherwise we will destroy it and re-initialize it
- // with the current information.
- uint32_t computed_crc32 = BootloaderControlLECRC(&boot_ctrl);
- if (boot_ctrl.crc32_le != computed_crc32) {
- LOG(WARNING) << "Invalid boot control found, expected CRC-32 0x" << std::hex << computed_crc32
- << " but found 0x" << std::hex << boot_ctrl.crc32_le << ". Re-initializing.";
- InitDefaultBootloaderControl(this, &boot_ctrl);
- UpdateAndSaveBootloaderControl(device.c_str(), &boot_ctrl);
- }
-
- if (!InitMiscVirtualAbMessageIfNeeded()) {
- return false;
- }
-
- num_slots_ = boot_ctrl.nb_slot;
- return true;
-}
-
-unsigned int BootControl::GetNumberSlots() {
- return num_slots_;
-}
-
-unsigned int BootControl::GetCurrentSlot() {
- return current_slot_;
-}
-
-bool BootControl::MarkBootSuccessful() {
- bootloader_control bootctrl;
- if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false;
-
- bootctrl.slot_info[current_slot_].successful_boot = 1;
- // tries_remaining == 0 means that the slot is not bootable anymore, make
- // sure we mark the current slot as bootable if it succeeds in the last
- // attempt.
- bootctrl.slot_info[current_slot_].tries_remaining = 1;
- return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl);
-}
-
-bool BootControl::SetActiveBootSlot(unsigned int slot) {
- if (slot >= kMaxNumSlots || slot >= num_slots_) {
- // Invalid slot number.
- return false;
- }
-
- bootloader_control bootctrl;
- if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false;
-
- // Set every other slot with a lower priority than the new "active" slot.
- const unsigned int kActivePriority = 15;
- const unsigned int kActiveTries = 6;
- for (unsigned int i = 0; i < num_slots_; ++i) {
- if (i != slot) {
- if (bootctrl.slot_info[i].priority >= kActivePriority)
- bootctrl.slot_info[i].priority = kActivePriority - 1;
- }
- }
-
- // Note that setting a slot as active doesn't change the successful bit.
- // The successful bit will only be changed by setSlotAsUnbootable().
- bootctrl.slot_info[slot].priority = kActivePriority;
- bootctrl.slot_info[slot].tries_remaining = kActiveTries;
-
- // Setting the current slot as active is a way to revert the operation that
- // set *another* slot as active at the end of an updater. This is commonly
- // used to cancel the pending update. We should only reset the verity_corrpted
- // bit when attempting a new slot, otherwise the verity bit on the current
- // slot would be flip.
- if (slot != current_slot_) bootctrl.slot_info[slot].verity_corrupted = 0;
-
- return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl);
-}
-
-bool BootControl::SetSlotAsUnbootable(unsigned int slot) {
- if (slot >= kMaxNumSlots || slot >= num_slots_) {
- // Invalid slot number.
- return false;
- }
-
- bootloader_control bootctrl;
- if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false;
-
- // The only way to mark a slot as unbootable, regardless of the priority is to
- // set the tries_remaining to 0.
- bootctrl.slot_info[slot].successful_boot = 0;
- bootctrl.slot_info[slot].tries_remaining = 0;
- return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl);
-}
-
-bool BootControl::IsSlotBootable(unsigned int slot) {
- if (slot >= kMaxNumSlots || slot >= num_slots_) {
- // Invalid slot number.
- return false;
- }
-
- bootloader_control bootctrl;
- if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false;
-
- return bootctrl.slot_info[slot].tries_remaining != 0;
-}
-
-bool BootControl::IsSlotMarkedSuccessful(unsigned int slot) {
- if (slot >= kMaxNumSlots || slot >= num_slots_) {
- // Invalid slot number.
- return false;
- }
-
- bootloader_control bootctrl;
- if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false;
-
- return bootctrl.slot_info[slot].successful_boot && bootctrl.slot_info[slot].tries_remaining;
-}
-
-bool BootControl::IsValidSlot(unsigned int slot) {
- return slot < kMaxNumSlots && slot < num_slots_;
-}
-
-bool BootControl::SetSnapshotMergeStatus(MergeStatus status) {
- return SetMiscVirtualAbMergeStatus(current_slot_, status);
-}
-
-MergeStatus BootControl::GetSnapshotMergeStatus() {
- MergeStatus status;
- if (!GetMiscVirtualAbMergeStatus(current_slot_, &status)) {
- return MergeStatus::UNKNOWN;
- }
- return status;
-}
-
-const char* BootControl::GetSuffix(unsigned int slot) {
- if (slot >= kMaxNumSlots || slot >= num_slots_) {
- return nullptr;
- }
- return kSlotSuffixes[slot];
-}
-
-bool InitMiscVirtualAbMessageIfNeeded() {
- std::string err;
- misc_virtual_ab_message message;
- if (!ReadMiscVirtualAbMessage(&message, &err)) {
- LOG(ERROR) << "Could not read merge status: " << err;
- return false;
- }
-
- if (message.version == MISC_VIRTUAL_AB_MESSAGE_VERSION &&
- message.magic == MISC_VIRTUAL_AB_MAGIC_HEADER) {
- // Already initialized.
- return true;
- }
-
- message = {};
- message.version = MISC_VIRTUAL_AB_MESSAGE_VERSION;
- message.magic = MISC_VIRTUAL_AB_MAGIC_HEADER;
- if (!WriteMiscVirtualAbMessage(message, &err)) {
- LOG(ERROR) << "Could not write merge status: " << err;
- return false;
- }
- return true;
-}
-
-bool SetMiscVirtualAbMergeStatus(unsigned int current_slot,
- android::hardware::boot::V1_1::MergeStatus status) {
- std::string err;
- misc_virtual_ab_message message;
-
- if (!ReadMiscVirtualAbMessage(&message, &err)) {
- LOG(ERROR) << "Could not read merge status: " << err;
- return false;
- }
-
- message.merge_status = static_cast<uint8_t>(status);
- message.source_slot = current_slot;
- if (!WriteMiscVirtualAbMessage(message, &err)) {
- LOG(ERROR) << "Could not write merge status: " << err;
- return false;
- }
- return true;
-}
-
-bool GetMiscVirtualAbMergeStatus(unsigned int current_slot,
- android::hardware::boot::V1_1::MergeStatus* status) {
- std::string err;
- misc_virtual_ab_message message;
-
- if (!ReadMiscVirtualAbMessage(&message, &err)) {
- LOG(ERROR) << "Could not read merge status: " << err;
- return false;
- }
-
- // If the slot reverted after having created a snapshot, then the snapshot will
- // be thrown away at boot. Thus we don't count this as being in a snapshotted
- // state.
- *status = static_cast<MergeStatus>(message.merge_status);
- if (*status == MergeStatus::SNAPSHOTTED && current_slot == message.source_slot) {
- *status = MergeStatus::NONE;
- }
- return true;
-}
-
-} // namespace bootable
-} // namespace android
diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h
index 9a482d423..e4cf09b22 100644
--- a/bootloader_message/include/bootloader_message/bootloader_message.h
+++ b/bootloader_message/include/bootloader_message/bootloader_message.h
@@ -83,108 +83,6 @@ struct bootloader_message {
char reserved[1184];
};
-/**
- * We must be cautious when changing the bootloader_message struct size,
- * because A/B-specific fields may end up with different offsets.
- */
-#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
-static_assert(sizeof(struct bootloader_message) == 2048,
- "struct bootloader_message size changes, which may break A/B devices");
-#endif
-
-/**
- * The A/B-specific bootloader message structure (4-KiB).
- *
- * We separate A/B boot control metadata from the regular bootloader
- * message struct and keep it here. Everything that's A/B-specific
- * stays after struct bootloader_message, which should be managed by
- * the A/B-bootloader or boot control HAL.
- *
- * The slot_suffix field is used for A/B implementations where the
- * bootloader does not set the androidboot.ro.boot.slot_suffix kernel
- * commandline parameter. This is used by fs_mgr to mount /system and
- * other partitions with the slotselect flag set in fstab. A/B
- * implementations are free to use all 32 bytes and may store private
- * data past the first NUL-byte in this field. It is encouraged, but
- * not mandatory, to use 'struct bootloader_control' described below.
- *
- * The update_channel field is used to store the Omaha update channel
- * if update_engine is compiled with Omaha support.
- */
-struct bootloader_message_ab {
- struct bootloader_message message;
- char slot_suffix[32];
- char update_channel[128];
-
- // Round up the entire struct to 4096-byte.
- char reserved[1888];
-};
-
-/**
- * Be cautious about the struct size change, in case we put anything post
- * bootloader_message_ab struct (b/29159185).
- */
-#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
-static_assert(sizeof(struct bootloader_message_ab) == 4096,
- "struct bootloader_message_ab size changes");
-#endif
-
-#define BOOT_CTRL_MAGIC 0x42414342 /* Bootloader Control AB */
-#define BOOT_CTRL_VERSION 1
-
-struct slot_metadata {
- // Slot priority with 15 meaning highest priority, 1 lowest
- // priority and 0 the slot is unbootable.
- uint8_t priority : 4;
- // Number of times left attempting to boot this slot.
- uint8_t tries_remaining : 3;
- // 1 if this slot has booted successfully, 0 otherwise.
- uint8_t successful_boot : 1;
- // 1 if this slot is corrupted from a dm-verity corruption, 0
- // otherwise.
- uint8_t verity_corrupted : 1;
- // Reserved for further use.
- uint8_t reserved : 7;
-} __attribute__((packed));
-
-/* Bootloader Control AB
- *
- * This struct can be used to manage A/B metadata. It is designed to
- * be put in the 'slot_suffix' field of the 'bootloader_message'
- * structure described above. It is encouraged to use the
- * 'bootloader_control' structure to store the A/B metadata, but not
- * mandatory.
- */
-struct bootloader_control {
- // NUL terminated active slot suffix.
- char slot_suffix[4];
- // Bootloader Control AB magic number (see BOOT_CTRL_MAGIC).
- uint32_t magic;
- // Version of struct being used (see BOOT_CTRL_VERSION).
- uint8_t version;
- // Number of slots being managed.
- uint8_t nb_slot : 3;
- // Number of times left attempting to boot recovery.
- uint8_t recovery_tries_remaining : 3;
- // Status of any pending snapshot merge of dynamic partitions.
- uint8_t merge_status : 3;
- // Ensure 4-bytes alignment for slot_info field.
- uint8_t reserved0[1];
- // Per-slot information. Up to 4 slots.
- struct slot_metadata slot_info[4];
- // Reserved for further use.
- uint8_t reserved1[8];
- // CRC32 of all 28 bytes preceding this field (little endian
- // format).
- uint32_t crc32_le;
-} __attribute__((packed));
-
-#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
-static_assert(sizeof(struct bootloader_control) ==
- sizeof(((struct bootloader_message_ab *)0)->slot_suffix),
- "struct bootloader_control has wrong size");
-#endif
-
// Holds Virtual A/B merge status information. Current version is 1. New fields
// must be added to the end.
struct misc_virtual_ab_message {
diff --git a/fsck_unshare_blocks.cpp b/fsck_unshare_blocks.cpp
deleted file mode 100644
index e0b2d966b..000000000
--- a/fsck_unshare_blocks.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2018 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 "fsck_unshare_blocks.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <spawn.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <algorithm>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/unique_fd.h>
-#include <fs_mgr/roots.h>
-
-#include "recovery_utils/roots.h"
-
-static constexpr const char* SYSTEM_E2FSCK_BIN = "/system/bin/e2fsck_static";
-static constexpr const char* TMP_E2FSCK_BIN = "/tmp/e2fsck.bin";
-
-static bool copy_file(const char* source, const char* dest) {
- android::base::unique_fd source_fd(open(source, O_RDONLY));
- if (source_fd < 0) {
- PLOG(ERROR) << "open %s failed" << source;
- return false;
- }
-
- android::base::unique_fd dest_fd(open(dest, O_CREAT | O_WRONLY, S_IRWXU));
- if (dest_fd < 0) {
- PLOG(ERROR) << "open %s failed" << dest;
- return false;
- }
-
- for (;;) {
- char buf[4096];
- ssize_t rv = read(source_fd, buf, sizeof(buf));
- if (rv < 0) {
- PLOG(ERROR) << "read failed";
- return false;
- }
- if (rv == 0) {
- break;
- }
- if (write(dest_fd, buf, rv) != rv) {
- PLOG(ERROR) << "write failed";
- return false;
- }
- }
- return true;
-}
-
-static bool run_e2fsck(const std::string& partition) {
- Volume* volume = volume_for_mount_point(partition);
- if (!volume) {
- LOG(INFO) << "No fstab entry for " << partition << ", skipping.";
- return true;
- }
-
- LOG(INFO) << "Running e2fsck on device " << volume->blk_device;
-
- std::vector<std::string> args = { TMP_E2FSCK_BIN, "-p", "-E", "unshare_blocks",
- volume->blk_device };
- std::vector<char*> argv(args.size());
- std::transform(args.cbegin(), args.cend(), argv.begin(),
- [](const std::string& arg) { return const_cast<char*>(arg.c_str()); });
- argv.push_back(nullptr);
-
- pid_t child;
- char* env[] = { nullptr };
- if (posix_spawn(&child, argv[0], nullptr, nullptr, argv.data(), env)) {
- PLOG(ERROR) << "posix_spawn failed";
- return false;
- }
-
- int status = 0;
- int ret = TEMP_FAILURE_RETRY(waitpid(child, &status, 0));
- if (ret < 0) {
- PLOG(ERROR) << "waitpid failed";
- return false;
- }
- if (!WIFEXITED(status)) {
- LOG(ERROR) << "e2fsck exited abnormally: " << status;
- return false;
- }
- int return_code = WEXITSTATUS(status);
- if (return_code >= 8) {
- LOG(ERROR) << "e2fsck could not unshare blocks: " << return_code;
- return false;
- }
-
- LOG(INFO) << "Successfully unshared blocks on " << partition;
- return true;
-}
-
-bool do_fsck_unshare_blocks() {
- // List of partitions we will try to e2fsck -E unshare_blocks.
- std::vector<std::string> partitions = { "/odm", "/oem", "/product", "/vendor" };
-
- // Temporarily mount system so we can copy e2fsck_static.
- auto system_root = android::fs_mgr::GetSystemRoot();
- bool mounted = ensure_path_mounted_at(system_root, "/mnt/system") != -1;
- partitions.push_back(system_root);
-
- if (!mounted) {
- LOG(ERROR) << "Failed to mount system image.";
- return false;
- }
- if (!copy_file(SYSTEM_E2FSCK_BIN, TMP_E2FSCK_BIN)) {
- LOG(ERROR) << "Could not copy e2fsck to /tmp.";
- return false;
- }
- if (umount("/mnt/system") < 0) {
- PLOG(ERROR) << "umount failed";
- return false;
- }
-
- bool ok = true;
- for (const auto& partition : partitions) {
- ok &= run_e2fsck(partition);
- }
-
- if (ok) {
- LOG(INFO) << "Finished running e2fsck.";
- } else {
- LOG(ERROR) << "Finished running e2fsck, but not all partitions succceeded.";
- }
- return ok;
-}
diff --git a/fsck_unshare_blocks.h b/fsck_unshare_blocks.h
deleted file mode 100644
index 9de8ef9a3..000000000
--- a/fsck_unshare_blocks.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#ifndef _FILESYSTEM_CMDS_H
-#define _FILESYSTEM_CMDS_H
-
-bool do_fsck_unshare_blocks();
-
-#endif // _FILESYSTEM_CMDS_H
diff --git a/recovery.cpp b/recovery.cpp
index 582ca48a5..0382697ab 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -45,7 +45,6 @@
#include <ziparchive/zip_archive.h>
#include "bootloader_message/bootloader_message.h"
-#include "fsck_unshare_blocks.h"
#include "install/adb_install.h"
#include "install/fuse_install.h"
#include "install/install.h"
@@ -311,11 +310,56 @@ static void run_graphics_test(RecoveryUI* ui) {
ui->ShowText(true);
}
+static void WriteUpdateInProgress() {
+ std::string err;
+ if (!update_bootloader_message({ "--reason=update_in_progress" }, &err)) {
+ LOG(ERROR) << "Failed to WriteUpdateInProgress: " << err;
+ }
+}
+
+static bool AskToReboot(Device* device, Device::BuiltinAction chosen_action) {
+ bool is_non_ab = android::base::GetProperty("ro.boot.slot_suffix", "").empty();
+ bool is_virtual_ab = android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
+ if (!is_non_ab && !is_virtual_ab) {
+ // Only prompt for non-A/B or Virtual A/B devices.
+ return true;
+ }
+
+ std::string header_text;
+ std::string item_text;
+ switch (chosen_action) {
+ case Device::REBOOT:
+ header_text = "reboot";
+ item_text = " Reboot system now";
+ break;
+ case Device::SHUTDOWN:
+ header_text = "power off";
+ item_text = " Power off";
+ break;
+ default:
+ LOG(FATAL) << "Invalid chosen action " << chosen_action;
+ break;
+ }
+
+ std::vector<std::string> headers{ "WARNING: Previous installation has failed.",
+ " Your device may fail to boot if you " + header_text +
+ " now.",
+ " Confirm reboot?" };
+ std::vector<std::string> items{ " Cancel", item_text };
+
+ size_t chosen_item = device->GetUI()->ShowMenu(
+ headers, items, 0, true /* menu_only */,
+ std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
+
+ return (chosen_item == 1);
+}
+
// Shows the recovery UI and waits for user input. Returns one of the device builtin actions, such
// as REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION means to take the default, which
// is to reboot or shutdown depending on if the --shutdown_after flag was passed to recovery.
static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status) {
auto ui = device->GetUI();
+ bool update_in_progress = (device->GetReason().value_or("") == "update_in_progress");
for (;;) {
FinishRecovery(ui);
switch (status) {
@@ -340,8 +384,14 @@ static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status)
}
ui->SetProgressType(RecoveryUI::EMPTY);
+ std::vector<std::string> headers;
+ if (update_in_progress) {
+ headers = { "WARNING: Previous installation has failed.",
+ " Your device may fail to boot if you reboot or power off now." };
+ }
+
size_t chosen_item = ui->ShowMenu(
- {}, device->GetMenuItems(), 0, false,
+ headers, device->GetMenuItems(), 0, false,
std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
// Handle Interrupt key
if (chosen_item == static_cast<size_t>(RecoveryUI::KeyError::INTERRUPTED)) {
@@ -362,14 +412,27 @@ static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status)
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::REBOOT:
+ case Device::SHUTDOWN:
+ if (!ui->IsTextVisible()) {
+ return Device::REBOOT;
+ }
+ // okay to reboot; no need to ask.
+ if (!update_in_progress) {
+ return Device::REBOOT;
+ }
+ // An update might have been failed. Ask if user really wants to reboot.
+ if (AskToReboot(device, chosen_action)) {
+ return Device::REBOOT;
+ }
+ break;
+
case Device::WIPE_DATA:
save_current_log = true;
if (ui->IsTextVisible()) {
@@ -397,6 +460,9 @@ static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status)
case Device::ENTER_RESCUE: {
save_current_log = true;
+ update_in_progress = true;
+ WriteUpdateInProgress();
+
bool adb = true;
Device::BuiltinAction reboot_action;
if (chosen_action == Device::ENTER_RESCUE) {
@@ -415,12 +481,15 @@ static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status)
return reboot_action;
}
- if (status != INSTALL_SUCCESS) {
+ if (status == INSTALL_SUCCESS) {
+ update_in_progress = false;
+ if (!ui->IsTextVisible()) {
+ return Device::NO_ACTION; // reboot if logs aren't visible
+ }
+ } else {
ui->SetBackground(RecoveryUI::ERROR);
ui->Print("Installation aborted.\n");
copy_logs(save_current_log);
- } else if (!ui->IsTextVisible()) {
- return Device::NO_ACTION; // reboot if logs aren't visible
}
break;
}
@@ -524,7 +593,6 @@ static void log_failure_code(ErrorCode code, const std::string& update_package)
Device::BuiltinAction start_recovery(Device* device, const std::vector<std::string>& args) {
static constexpr struct option OPTIONS[] = {
{ "fastboot", no_argument, nullptr, 0 },
- { "fsck_unshare_blocks", no_argument, nullptr, 0 },
{ "install_with_fuse", no_argument, nullptr, 0 },
{ "just_exit", no_argument, nullptr, 'x' },
{ "locale", required_argument, nullptr, 0 },
@@ -557,7 +625,6 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
bool rescue = false;
bool just_exit = false;
bool shutdown_after = false;
- bool fsck_unshare_blocks = false;
int retry_count = 0;
bool security_update = false;
std::string locale;
@@ -580,9 +647,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
break;
case 0: {
std::string option = OPTIONS[option_index].name;
- if (option == "fsck_unshare_blocks") {
- fsck_unshare_blocks = true;
- } else if (option == "install_with_fuse") {
+ if (option == "install_with_fuse") {
install_with_fuse = true;
} else if (option == "locale" || option == "fastboot" || option == "reason") {
// Handled in recovery_main.cpp
@@ -777,10 +842,6 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
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;
- }
} 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
diff --git a/recovery_ui/screen_ui.cpp b/recovery_ui/screen_ui.cpp
index 087fc0e84..6dcb161fa 100644
--- a/recovery_ui/screen_ui.cpp
+++ b/recovery_ui/screen_ui.cpp
@@ -448,7 +448,9 @@ void ScreenRecoveryUI::draw_foreground_locked() {
int frame_height = gr_get_height(frame);
int frame_x = (ScreenWidth() - frame_width) / 2;
int frame_y = GetAnimationBaseline();
- DrawSurface(frame, 0, 0, frame_width, frame_height, frame_x, frame_y);
+ if (frame_x >= 0 && frame_y >= 0 && (frame_x + frame_width) < ScreenWidth() &&
+ (frame_y + frame_height) < ScreenHeight())
+ DrawSurface(frame, 0, 0, frame_width, frame_height, frame_x, frame_y);
}
if (progressBarType != EMPTY) {
diff --git a/recovery_utils/roots.cpp b/recovery_utils/roots.cpp
index 127039872..58a313973 100644
--- a/recovery_utils/roots.cpp
+++ b/recovery_utils/roots.cpp
@@ -202,6 +202,15 @@ int format_volume(const std::string& volume, const std::string& directory) {
mke2fs_args.push_back("512");
}
+ if (v->fs_mgr_flags.ext_meta_csum) {
+ mke2fs_args.push_back("-O");
+ mke2fs_args.push_back("metadata_csum");
+ mke2fs_args.push_back("-O");
+ mke2fs_args.push_back("64bit");
+ mke2fs_args.push_back("-O");
+ mke2fs_args.push_back("extent");
+ }
+
int raid_stride = v->logical_blk_size / kBlockSize;
int raid_stripe_width = v->erase_blk_size / kBlockSize;
// stride should be the max of 8KB and logical block size
diff --git a/tests/Android.bp b/tests/Android.bp
index 640bb042e..a9a088a32 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -90,12 +90,29 @@ librecovery_static_libs = [
"libfs_mgr",
"libhidl-gen-utils",
"libhidlbase",
- "libbinderthreadstate",
"liblp",
"libtinyxml2",
"libc++fs",
]
+// recovery image for unittests.
+// ========================================================
+genrule {
+ name: "recovery_image",
+ cmd: "cat $(location testdata/recovery_head) <(cat $(location testdata/recovery_body) | $(location minigzip)) $(location testdata/recovery_tail) > $(out)",
+ srcs: [
+ "testdata/recovery_head",
+ "testdata/recovery_body",
+ "testdata/recovery_tail",
+ ],
+ tools: [
+ "minigzip",
+ ],
+ out: [
+ "testdata/recovery.img",
+ ],
+}
+
cc_test {
name: "recovery_unit_test",
isolated: true,
@@ -129,6 +146,7 @@ cc_test {
data: [
"testdata/*",
+ ":recovery_image",
":res-testdata",
],
}
diff --git a/tests/testdata/recovery.img b/tests/testdata/recovery.img
deleted file mode 100644
index b862e6f0c..000000000
--- a/tests/testdata/recovery.img
+++ /dev/null
Binary files differ
diff --git a/tests/testdata/recovery_body b/tests/testdata/recovery_body
new file mode 100644
index 000000000..48d7c10a5
--- /dev/null
+++ b/tests/testdata/recovery_body
Binary files differ
diff --git a/tests/testdata/recovery_head b/tests/testdata/recovery_head
new file mode 100644
index 000000000..7f494d098
--- /dev/null
+++ b/tests/testdata/recovery_head
Binary files differ
diff --git a/tests/testdata/recovery_tail b/tests/testdata/recovery_tail
new file mode 100644
index 000000000..7fe2c6ce8
--- /dev/null
+++ b/tests/testdata/recovery_tail
Binary files differ
diff --git a/tools/image_generator/ImageGenerator.java b/tools/image_generator/ImageGenerator.java
index fd8e54295..1da43e5c6 100644
--- a/tools/image_generator/ImageGenerator.java
+++ b/tools/image_generator/ImageGenerator.java
@@ -123,7 +123,7 @@ public class ImageGenerator {
put("hy", "NotoSansArmenian-Regular");
put("iw", "NotoSansHebrew-Regular");
put("ja", "NotoSansCJK-Regular");
- put("ka", "NotoSansGeorgian-Regular");
+ put("ka", "NotoSansGeorgian-VF");
put("ko", "NotoSansCJK-Regular");
put("km", "NotoSansKhmerUI-Regular");
put("kn", "NotoSansKannadaUI-Regular");
diff --git a/tools/recovery_l10n/res/values-ar/strings.xml b/tools/recovery_l10n/res/values-ar/strings.xml
index 2af36d64a..69191287d 100644
--- a/tools/recovery_l10n/res/values-ar/strings.xml
+++ b/tools/recovery_l10n/res/values-ar/strings.xml
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="recovery_installing" msgid="2013591905463558223">"جارٍ تثبيت تحديث النظام"</string>
+ <string name="recovery_installing" msgid="2013591905463558223">"جارٍ تثبيت إعادة تحميل النظام"</string>
<string name="recovery_erasing" msgid="7334826894904037088">"جارٍ محو البيانات"</string>
<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_installing_security" msgid="9184031299717114342">"جارٍ تثبيت إعادة تحميل الأمان"</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_factory_data_reset" msgid="7321351565602894783">"إعادة الضبط على الإعدادات الأصلية"</string>
<string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"هل تريد حجب كل بيانات المستخدم؟\n\n لا يمكن التراجع عن هذا الإجراء."</string>
<string name="recovery_cancel_wipe_data" msgid="66987687653647384">"إلغاء"</string>
</resources>
diff --git a/updater/Android.bp b/updater/Android.bp
index cbef43099..f00a192b9 100644
--- a/updater/Android.bp
+++ b/updater/Android.bp
@@ -25,6 +25,7 @@ cc_defaults {
"libdm",
"libfec",
"libfec_rs",
+ "libavb",
"libverity_tree",
"libgtest_prod",
"liblog",
diff --git a/updater/Android.mk b/updater/Android.mk
index 8a4cd86d9..46300d974 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -32,6 +32,7 @@ updater_common_static_libraries := \
libdm \
libfec \
libfec_rs \
+ libavb \
libverity_tree \
libgtest_prod \
liblog \
diff --git a/updater_sample/AndroidManifest.xml b/updater_sample/AndroidManifest.xml
index 0a2511617..981cd8eba 100644
--- a/updater_sample/AndroidManifest.xml
+++ b/updater_sample/AndroidManifest.xml
@@ -20,6 +20,7 @@
<uses-sdk android:minSdkVersion="27" android:targetSdkVersion="27" />
<uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
+ <uses-permission android:name="android.permission.INTERNET"/>
<application
android:icon="@mipmap/ic_launcher"