using ::aidl::android::hardware::boot::BnBootControl; using ::aidl::android::hardware::boot::IBootControl; using ::aidl::android::hardware::boot::MergeStatus; using ::ndk::ScopedAStatus;
static std::optional<size_t> GetSlotProperty(std::span<const BootSlotData> slots, const std::string& prop_name) {
std::string val = android::base::GetProperty(prop_name, ""); if (val.empty()) { return std::nullopt;
}
size_t idx; bool success = android::base::ParseUint<size_t>(val, &idx, kBootSlots - 1); if (!success) {
LOG(ERROR) << "Unable to parse value \"" << val << "\" of property " << prop_name
<< " as size_t or parsed value was not in [0.." << kBootSlots
<< " ): " << strerror(errno); return std::nullopt;
}
const BootSlotData& slot = slots[idx]; if (!slot.bootable) {
LOG(ERROR) << "Property " << prop_name << " requested boot to slot" << kSlotSuffixes[idx]
<< " (idx: " << idx << ") but slot is not marked bootable."; return std::nullopt;
}
static std::optional<BootControlConfig> Read() { auto fd = android::base::unique_fd(open(kBootConfigPath, O_RDONLY | O_SYNC, 0)); if (!fd.ok()) { if (fd.get() == ENOENT) {
LOG(INFO) << "BootControlConfig did not exist at " << kBootConfigPath;
} else {
LOG(WARNING) << "Could not open BootControlConfig for reading (" << strerror(errno)
<< ") at " << kBootConfigPath;
} return std::nullopt;
}
char buf[sizeof(BootControlConfig)]; bool success = android::base::ReadFully(fd, &buf, sizeof(buf)); if (!success) {
LOG(WARNING) << "Could not read (errno: " << strerror(errno)
<< ") BootControlConfig from " << kBootConfigPath; return std::nullopt;
} auto result = std::make_optional<BootControlConfig>();
memcpy(&*result, &buf, sizeof(BootControlConfig));
if (result->curr_slot >= kBootSlots && result->next_slot >= kBootSlots) {
LOG(ERROR)
<< "Read BootControlConfig from previous boot with incorrect BootData: {curr: "
<< result->curr_slot << " next: " << result->next_slot << " }. Must be within [0.."
<< kBootSlots << ")."; return std::nullopt;
}
if (!result->slots[result->next_slot].bootable) {
LOG(ERROR) << "BootControlConfig from previous boot said to boot into slot"
<< kSlotSuffixes[result->next_slot] << "(idx: " << result->next_slot
<< ") which is marked unbootable."; return std::nullopt;
} return result;
}
static BootControlConfig ReadOrDefault() { auto config = BootControlConfig::Read(); if (config.has_value()) { return *config;
}
bool UpdateByProperty(BootControlConfig last, bool last_boot_failed) { auto property_update =
GetSlotProperty(slots, "ro.boot.vendor.mock_boot_ctrl.update_to_slot"); if (!property_update.has_value()) { returnfalse;
}
if (last_boot_failed && *property_update == last.next_slot) {
LOG(ERROR) << "Can't update to slot" << kSlotSuffixes[*property_update]
<< " (idx: " << *property_update
<< ") because we're rolling back to it. Updating would leave us with no " "successful slot to roll back to."; returnfalse;
}
size_t rollback_slot; if (last_boot_failed) { /* Use the same rollback slot as the boot that just failed. */
rollback_slot = last.next_slot;
} elseif (*property_update != last.curr_slot) { /* Last boot succeeded; use it as the rollback slot. */
rollback_slot = last.curr_slot;
} else { /* We're updating the same slot that just succeeded; find a new rollback slot. */ bool found = false; for (size_t i = kBootSlots - 1; i > 0; --i) {
size_t idx = (*property_update + i) % kBootSlots; if (slots[idx].successful) {
rollback_slot = idx;
found = true; break;
}
} if (!found) {
LOG(ERROR)
<< "Can't update to slot" << kSlotSuffixes[*property_update]
<< " (idx: " << *property_update
<< ") because it's the last successful slot. Updating would leave us with no " "successful slot to roll back to."; returnfalse;
}
}
LOG(DEBUG) << "Updating to slot" << kSlotSuffixes[*property_update]
<< " (idx: " << *property_update
<< ") as requested by property. If update fails, roll back to slot"
<< kSlotSuffixes[rollback_slot] << " (idx: " << rollback_slot << ").";
curr_slot = *property_update;
next_slot = rollback_slot;
slots[curr_slot].successful = false; returntrue;
}
if (curr.UpdateByProperty(last, last_boot_failed)) { return curr;
}
/* Boot to the slot we set to active (i.e. "next_slot") during the last boot. */
curr.curr_slot = last.next_slot; /* *Until/unlesssetActiveBootSlotiscalled,thenext_slotshouldbethelastslotwe *bootedsuccessfully.Normally,that'sthesameslotthatthepreviousbootused, *unlessthelastfailed,inwhichcaseit'stheslotwerolledbackto.
*/
curr.next_slot = last_boot_failed ? last.next_slot : last.curr_slot; return curr;
}
};
class BootControl final : public BnBootControl { public:
BootControl(BootControlConfig cfg) : cfg_(cfg) {}
ScopedAStatus setSlotAsUnbootable(int32_t in_slot) override {
BootSlotData* slot = getSlot(in_slot); if (slot == nullptr) {
LOG(ERROR) << "setSlotAsUnbootable slot: " << in_slot << " result: no such slot"; return ScopedAStatus::fromStatus(STATUS_INVALID_OPERATION);
}
if (static_cast<size_t>(in_slot) == cfg_.curr_slot) {
LOG(ERROR) << "setSlotAsUnbootable: slot " << in_slot << " is currently running"; /* Can't write to a slot's boot partition while it's the one running. */ return ScopedAStatus::fromStatus(STATUS_INVALID_OPERATION);
}
BootSlotData* curr = getSlot(cfg_.curr_slot);
assert(curr != nullptr); if (!curr->successful) {
LOG(ERROR) << "setSlotAsUnbootable: current slot (" << cfg_.curr_slot
<< ") is not yet marked successful"; /* Don't write until we know the current slot works so we can roll back if needed. */ return ScopedAStatus::fromStatus(STATUS_INVALID_OPERATION);
}
/* We're about to write an update to this slot; can't boot to it until writing is done. */
slot->bootable = false;
slot->successful = false;
LOG(INFO) << "setSlotAsUnbootable slot: " << in_slot; return cfg_.Write();
}
ScopedAStatus setActiveBootSlot(int32_t in_slot) override {
BootSlotData* slot = getSlot(in_slot); if (slot == nullptr) {
LOG(ERROR) << "setActiveBootSlot slot: " << in_slot << " result: no such slot"; return ScopedAStatus::fromStatus(STATUS_INVALID_OPERATION);
}
if (static_cast<size_t>(in_slot) == cfg_.curr_slot) {
LOG(ERROR) << "setActiveBootSlot: slot " << in_slot << " is currently running"; /* We're already running this slot; we can't update to it. */ return ScopedAStatus::fromStatus(STATUS_INVALID_OPERATION);
}
BootSlotData* curr = getSlot(cfg_.curr_slot);
assert(curr != nullptr); if (!curr->successful) {
LOG(ERROR) << "setActiveBootSlot: current slot (" << cfg_.curr_slot
<< ") is not yet marked successful"; /* Don't update until we know the current slot works so we can roll back if needed. */ return ScopedAStatus::fromStatus(STATUS_INVALID_OPERATION);
}
/* Done writing an update to this slot; mark it bootable and try booting to it next time. */
size_t previous_active = cfg_.next_slot;
cfg_.next_slot = in_slot;
slot->successful = false;
slot->bootable = true;
LOG(INFO) << "setActiveBootSlot slot: " << in_slot
<< " (previous active: " << previous_active << " current: " << cfg_.curr_slot
<< ")";
/* The update was successful; keep booting into this slot in the future. */
slot->successful = true;
cfg_.next_slot = cfg_.curr_slot;
LOG(INFO) << "markBootSuccessful slot: " << cfg_.curr_slot;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.