Implement powerwash on Android.
Powerwash, the name for the equivalent of a factory reset or /data wipe,
can be triggered in Android by writing the desired command to the
recovery command file and rebooting into recovery.
This patch moves the powerwash scheduling/canceling logic to the
HardwareInterface and implements it on Android.
Bug: 28700985
TEST=Called update_engine_client passing POWERWASH=1, BCB is stored up
to offset 832.
Change-Id: If737fd4b9b3e2ed9bce709b3b59f22e9f0a3dc9a
diff --git a/hardware_android.cc b/hardware_android.cc
index 60e26f2..8de02c7 100644
--- a/hardware_android.cc
+++ b/hardware_android.cc
@@ -16,17 +16,71 @@
#include "update_engine/hardware_android.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <algorithm>
+
+#include <bootloader.h>
+
#include <base/files/file_util.h>
#include <brillo/make_unique_ptr.h>
#include <cutils/properties.h>
#include "update_engine/common/hardware.h"
#include "update_engine/common/platform_constants.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/utils_android.h"
using std::string;
namespace chromeos_update_engine {
+namespace {
+
+// The powerwash arguments passed to recovery. Arguments are separated by \n.
+const char kAndroidRecoveryPowerwashCommand[] =
+ "recovery\n"
+ "--wipe_data\n"
+ "--reason=wipe_data_from_ota\n";
+
+// Write a recovery command line |message| to the BCB. The arguments to recovery
+// must be separated by '\n'. An empty string will erase the BCB.
+bool WriteBootloaderRecoveryMessage(const string& message) {
+ base::FilePath misc_device;
+ if (!utils::DeviceForMountPoint("/misc", &misc_device))
+ return false;
+
+ // Setup a bootloader_message with just the command and recovery fields set.
+ bootloader_message boot = {};
+ if (!message.empty()) {
+ strncpy(boot.command, "boot-recovery", sizeof(boot.command) - 1);
+ memcpy(boot.recovery,
+ message.data(),
+ std::min(message.size(), sizeof(boot.recovery) - 1));
+ }
+
+ int fd =
+ HANDLE_EINTR(open(misc_device.value().c_str(), O_WRONLY | O_SYNC, 0600));
+ if (fd < 0) {
+ PLOG(ERROR) << "Opening misc";
+ return false;
+ }
+ ScopedFdCloser fd_closer(&fd);
+ // We only re-write the first part of the bootloader_message, up to and
+ // including the recovery message.
+ size_t boot_size =
+ offsetof(bootloader_message, recovery) + sizeof(boot.recovery);
+ if (!utils::WriteAll(fd, &boot, boot_size)) {
+ PLOG(ERROR) << "Writing recovery command to misc";
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
namespace hardware {
// Factory defined in hardware.h.
@@ -100,6 +154,15 @@
return 0;
}
+bool HardwareAndroid::SchedulePowerwash() {
+ LOG(INFO) << "Scheduling a powerwash to BCB.";
+ return WriteBootloaderRecoveryMessage(kAndroidRecoveryPowerwashCommand);
+}
+
+bool HardwareAndroid::CancelPowerwash() {
+ return WriteBootloaderRecoveryMessage("");
+}
+
bool HardwareAndroid::GetNonVolatileDirectory(base::FilePath* path) const {
base::FilePath local_path(constants::kNonVolatileDirectory);
if (!base::PathExists(local_path)) {