fastboot: Implement helper commands for Virtual A/B.

This introduces two new commands to the fastboot protocol:

  - getvar snapshot-update-status - Return "none", "snapshotted", or
    "merging" depending on the current status set by the boot control
    HAL.
  - snapshot-update [cancel] - Cancel any pending snapshot-based updates
    via the boot control HAL. After this, the HAL should return
    MergeStatus::CANCELLED and "update-merge-status" should be "none".
    If no argument is specified, the snapshot-update-status is returned
    via an INFO response.

Bootloaders are expected to implement this in a manner consistent with
the boot control HAL.

Fastboot-based tooling should expect wipes of userdata to fail when
update-merge-status returns "merging". Thus, the force flag now cancel
any pending snapshots.

Bug: 139154945
Test: fastboot getvar snapshot-update-status
      fastboot snapshot-update cancel
      fastboot snapshot-update

Change-Id: Idc423fe7656b212e929e64eb0e6b85b453e0e8dc
This commit is contained in:
David Anderson 2019-10-21 16:45:59 -07:00
parent 32e7325c44
commit ab8f466107
10 changed files with 116 additions and 1 deletions

View file

@ -122,6 +122,7 @@ cc_binary {
shared_libs: [
"android.hardware.boot@1.0",
"android.hardware.boot@1.1",
"android.hardware.fastboot@1.0",
"android.hardware.health@2.0",
"libadbd",

View file

@ -34,6 +34,7 @@
#define FB_CMD_UPDATE_SUPER "update-super"
#define FB_CMD_OEM "oem"
#define FB_CMD_GSI "gsi"
#define FB_CMD_SNAPSHOT_UPDATE "snapshot-update"
#define RESPONSE_OKAY "OKAY"
#define RESPONSE_FAIL "FAIL"
@ -66,3 +67,4 @@
#define FB_VAR_BATTERY_VOLTAGE "battery-voltage"
#define FB_VAR_BATTERY_SOC_OK "battery-soc-ok"
#define FB_VAR_SUPER_PARTITION_NAME "super-partition-name"
#define FB_VAR_SNAPSHOT_UPDATE_STATUS "snapshot-update-status"

View file

@ -25,6 +25,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <android/hardware/boot/1.1/IBootControl.h>
#include <cutils/android_reboot.h>
#include <ext4_utils/wipe.h>
#include <fs_mgr.h>
@ -44,8 +45,10 @@ using ::android::hardware::hidl_string;
using ::android::hardware::boot::V1_0::BoolResult;
using ::android::hardware::boot::V1_0::CommandResult;
using ::android::hardware::boot::V1_0::Slot;
using ::android::hardware::boot::V1_1::MergeStatus;
using ::android::hardware::fastboot::V1_0::Result;
using ::android::hardware::fastboot::V1_0::Status;
using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl;
struct VariableHandlers {
// Callback to retrieve the value of a single variable.
@ -101,7 +104,8 @@ bool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args)
{FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
{FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
{FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
{FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}}};
{FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}},
{FB_VAR_SNAPSHOT_UPDATE_STATUS, {GetSnapshotUpdateStatus, nullptr}}};
if (args.size() < 2) {
return device->WriteFail("Missing argument");
@ -547,3 +551,40 @@ bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args) {
}
return device->WriteStatus(FastbootResult::OKAY, "Success");
}
bool SnapshotUpdateHandler(FastbootDevice* device, const std::vector<std::string>& args) {
// Note that we use the HAL rather than mounting /metadata, since we want
// our results to match the bootloader.
auto hal = device->boot_control_hal();
if (!hal) return device->WriteFail("Not supported");
android::sp<IBootControl1_1> hal11 = IBootControl1_1::castFrom(hal);
if (!hal11) return device->WriteFail("Not supported");
// If no arguments, return the same thing as a getvar. Note that we get the
// HAL first so we can return "not supported" before we return the less
// specific error message below.
if (args.size() < 2 || args[1].empty()) {
std::string message;
if (!GetSnapshotUpdateStatus(device, {}, &message)) {
return device->WriteFail("Could not determine update status");
}
device->WriteInfo(message);
return device->WriteOkay("");
}
if (args.size() != 2 || args[1] != "cancel") {
return device->WriteFail("Invalid arguments");
}
MergeStatus status = hal11->getSnapshotMergeStatus();
switch (status) {
case MergeStatus::SNAPSHOTTED:
case MergeStatus::MERGING:
hal11->setSnapshotMergeStatus(MergeStatus::CANCELLED);
break;
default:
break;
}
return device->WriteStatus(FastbootResult::OKAY, "Success");
}

View file

@ -49,3 +49,4 @@ bool ResizePartitionHandler(FastbootDevice* device, const std::vector<std::strin
bool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args);
bool OemCmdHandler(FastbootDevice* device, const std::vector<std::string>& args);
bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args);
bool SnapshotUpdateHandler(FastbootDevice* device, const std::vector<std::string>& args);

View file

@ -54,6 +54,7 @@ FastbootDevice::FastbootDevice()
{FB_CMD_UPDATE_SUPER, UpdateSuperHandler},
{FB_CMD_OEM, OemCmdHandler},
{FB_CMD_GSI, GsiHandler},
{FB_CMD_SNAPSHOT_UPDATE, SnapshotUpdateHandler},
}),
transport_(std::make_unique<ClientUsbTransport>()),
boot_control_hal_(IBootControl::getService()),

View file

@ -23,6 +23,7 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android/hardware/boot/1.1/IBootControl.h>
#include <ext4_utils/ext4_utils.h>
#include <fs_mgr.h>
#include <healthhalutils/HealthHalUtils.h>
@ -34,9 +35,11 @@
using ::android::hardware::boot::V1_0::BoolResult;
using ::android::hardware::boot::V1_0::Slot;
using ::android::hardware::boot::V1_1::MergeStatus;
using ::android::hardware::fastboot::V1_0::FileSystemType;
using ::android::hardware::fastboot::V1_0::Result;
using ::android::hardware::fastboot::V1_0::Status;
using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl;
using namespace android::fs_mgr;
constexpr char kFastbootProtocolVersion[] = "0.4";
@ -424,3 +427,34 @@ bool GetSuperPartitionName(FastbootDevice* device, const std::vector<std::string
*message = fs_mgr_get_super_partition_name(slot_number);
return true;
}
bool GetSnapshotUpdateStatus(FastbootDevice* device, const std::vector<std::string>& /* args */,
std::string* message) {
// Note that we use the HAL rather than mounting /metadata, since we want
// our results to match the bootloader.
auto hal = device->boot_control_hal();
if (!hal) {
*message = "not supported";
return false;
}
android::sp<IBootControl1_1> hal11 = IBootControl1_1::castFrom(hal);
if (!hal11) {
*message = "not supported";
return false;
}
MergeStatus status = hal11->getSnapshotMergeStatus();
switch (status) {
case MergeStatus::SNAPSHOTTED:
*message = "snapshotted";
break;
case MergeStatus::MERGING:
*message = "merging";
break;
default:
*message = "none";
break;
}
return true;
}

View file

@ -61,6 +61,8 @@ bool GetBatterySoCOk(FastbootDevice* device, const std::vector<std::string>& arg
std::string* message);
bool GetSuperPartitionName(FastbootDevice* device, const std::vector<std::string>& args,
std::string* message);
bool GetSnapshotUpdateStatus(FastbootDevice* device, const std::vector<std::string>& args,
std::string* message);
// Helpers for getvar all.
std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);

View file

@ -396,6 +396,9 @@ static int show_help() {
" gsi wipe|disable Wipe or disable a GSI installation (fastbootd only).\n"
" wipe-super [SUPER_EMPTY] Wipe the super partition. This will reset it to\n"
" contain an empty set of default dynamic partitions.\n"
" snapshot-update cancel On devices that support snapshot-based updates, cancel\n"
" an in-progress update. This may make the device\n"
" unbootable until it is reflashed.\n"
"\n"
"boot image:\n"
" boot KERNEL [RAMDISK [SECOND]]\n"
@ -1216,6 +1219,14 @@ static void reboot_to_userspace_fastboot() {
target_sparse_limit = -1;
}
static void CancelSnapshotIfNeeded() {
std::string merge_status = "none";
if (fb->GetVar(FB_VAR_SNAPSHOT_UPDATE_STATUS, &merge_status) == fastboot::SUCCESS &&
merge_status != "none") {
fb->SnapshotUpdateCommand("Cancel");
}
}
class ImageSource {
public:
virtual bool ReadFile(const std::string& name, std::vector<char>* out) const = 0;
@ -1268,6 +1279,8 @@ void FlashAllTool::Flash() {
DetermineSecondarySlot();
CollectImages();
CancelSnapshotIfNeeded();
// First flash boot partitions. We allow this to happen either in userspace
// or in bootloader fastboot.
FlashImages(boot_images_);
@ -2071,12 +2084,24 @@ int FastBootTool::Main(int argc, char* argv[]) {
image = next_arg(&args);
}
do_wipe_super(image, slot_override);
} else if (command == "snapshot-update") {
std::string arg;
if (!args.empty()) {
arg = next_arg(&args);
}
if (!arg.empty() && arg != "cancel") {
syntax_error("expected: snapshot-update [cancel]");
}
fb->SnapshotUpdateCommand(arg);
} else {
syntax_error("unknown command %s", command.c_str());
}
}
if (wants_wipe) {
if (force_flash) {
CancelSnapshotIfNeeded();
}
std::vector<std::string> partitions = { "userdata", "cache", "metadata" };
for (const auto& partition : partitions) {
std::string partition_type;

View file

@ -122,6 +122,12 @@ RetCode FastBootDriver::SetActive(const std::string& slot, std::string* response
response, info);
}
RetCode FastBootDriver::SnapshotUpdateCommand(const std::string& command, std::string* response,
std::vector<std::string>* info) {
std::string raw = FB_CMD_SNAPSHOT_UPDATE ":" + command;
return RawCommand(raw, response, info);
}
RetCode FastBootDriver::FlashPartition(const std::string& partition,
const std::vector<char>& data) {
RetCode ret;

View file

@ -104,6 +104,8 @@ class FastBootDriver {
std::vector<std::string>* info = nullptr);
RetCode Upload(const std::string& outfile, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
RetCode SnapshotUpdateCommand(const std::string& command, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
/* HIGHER LEVEL COMMANDS -- Composed of the commands above */
RetCode FlashPartition(const std::string& partition, const std::vector<char>& data);