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/Android.mk b/Android.mk
index 97b9f7a..141b144 100644
--- a/Android.mk
+++ b/Android.mk
@@ -288,7 +288,8 @@
LOCAL_LDFLAGS := $(ue_common_ldflags)
LOCAL_C_INCLUDES := \
$(ue_common_c_includes) \
- $(ue_libupdate_engine_exported_c_includes)
+ $(ue_libupdate_engine_exported_c_includes) \
+ bootable/recovery
LOCAL_STATIC_LIBRARIES := \
libpayload_consumer \
update_metadata-protos \
@@ -340,6 +341,7 @@
update_manager/state_factory.cc \
update_manager/update_manager.cc \
update_status_utils.cc \
+ utils_android.cc \
weave_service_factory.cc
ifeq ($(local_use_binder),1)
LOCAL_AIDL_INCLUDES += $(LOCAL_PATH)/binder_bindings
@@ -390,7 +392,9 @@
LOCAL_CFLAGS := $(ue_common_cflags)
LOCAL_CPPFLAGS := $(ue_common_cppflags)
LOCAL_LDFLAGS := $(ue_common_ldflags)
-LOCAL_C_INCLUDES := $(ue_common_c_includes)
+LOCAL_C_INCLUDES := \
+ $(ue_common_c_includes) \
+ bootable/recovery
#TODO(deymo): Remove external/cros/system_api/dbus once the strings are moved
# out of the DBus interface.
LOCAL_C_INCLUDES += \
@@ -411,7 +415,8 @@
hardware_android.cc \
proxy_resolver.cc \
update_attempter_android.cc \
- update_status_utils.cc
+ update_status_utils.cc \
+ utils_android.cc
include $(BUILD_STATIC_LIBRARY)
endif # local_use_omaha == 1
diff --git a/boot_control_android.cc b/boot_control_android.cc
index 78df37f..b3b7630 100644
--- a/boot_control_android.cc
+++ b/boot_control_android.cc
@@ -22,34 +22,12 @@
#include <base/strings/string_util.h>
#include <brillo/make_unique_ptr.h>
#include <brillo/message_loops/message_loop.h>
-#include <cutils/properties.h>
-#include <fs_mgr.h>
#include "update_engine/common/utils.h"
+#include "update_engine/utils_android.h"
using std::string;
-namespace {
-
-// Open the appropriate fstab file and fallback to /fstab.device if
-// that's what's being used.
-static struct fstab* OpenFSTab() {
- char propbuf[PROPERTY_VALUE_MAX];
- struct fstab* fstab;
-
- property_get("ro.hardware", propbuf, "");
- string fstab_name = string("/fstab.") + propbuf;
- fstab = fs_mgr_read_fstab(fstab_name.c_str());
- if (fstab != nullptr)
- return fstab;
-
- fstab = fs_mgr_read_fstab("/fstab.device");
- return fstab;
-}
-
-} // namespace
-
-
namespace chromeos_update_engine {
namespace boot_control {
@@ -97,9 +75,6 @@
bool BootControlAndroid::GetPartitionDevice(const string& partition_name,
Slot slot,
string* device) const {
- struct fstab* fstab;
- struct fstab_rec* record;
-
// We can't use fs_mgr to look up |partition_name| because fstab
// doesn't list every slot partition (it uses the slotselect option
// to mask the suffix).
@@ -119,20 +94,9 @@
// of misc and then finding an entry in /dev matching the sysfs
// entry.
- fstab = OpenFSTab();
- if (fstab == nullptr) {
- LOG(ERROR) << "Error opening fstab file.";
+ base::FilePath misc_device;
+ if (!utils::DeviceForMountPoint("/misc", &misc_device))
return false;
- }
- record = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
- if (record == nullptr) {
- LOG(ERROR) << "Error finding /misc entry in fstab file.";
- fs_mgr_free_fstab(fstab);
- return false;
- }
-
- base::FilePath misc_device = base::FilePath(record->blk_device);
- fs_mgr_free_fstab(fstab);
if (!utils::IsSymlink(misc_device.value().c_str())) {
LOG(ERROR) << "Device file " << misc_device.value() << " for /misc "
diff --git a/common/constants.cc b/common/constants.cc
index c0bb874..0ac22f3 100644
--- a/common/constants.cc
+++ b/common/constants.cc
@@ -18,11 +18,6 @@
namespace chromeos_update_engine {
-const char kPowerwashMarkerFile[] =
- "/mnt/stateful_partition/factory_install_reset";
-
-const char kPowerwashCommand[] = "safe fast keepimg reason=update_engine\n";
-
const char kPowerwashSafePrefsSubDirectory[] = "update_engine/prefs";
const char kPrefsSubDirectory[] = "prefs";
@@ -97,5 +92,6 @@
const char kPayloadPropertyMetadataHash[] = "METADATA_HASH";
const char kPayloadPropertyAuthorization[] = "AUTHORIZATION";
const char kPayloadPropertyUserAgent[] = "USER_AGENT";
+const char kPayloadPropertyPowerwash[] = "POWERWASH";
} // namespace chromeos_update_engine
diff --git a/common/constants.h b/common/constants.h
index 660d9a9..649034e 100644
--- a/common/constants.h
+++ b/common/constants.h
@@ -19,13 +19,6 @@
namespace chromeos_update_engine {
-// The name of the marker file used to trigger powerwash when post-install
-// completes successfully so that the device is powerwashed on next reboot.
-extern const char kPowerwashMarkerFile[];
-
-// The contents of the powerwash marker file.
-extern const char kPowerwashCommand[];
-
// Directory for AU prefs that are preserved across powerwash.
extern const char kPowerwashSafePrefsSubDirectory[];
@@ -100,6 +93,7 @@
extern const char kPayloadPropertyMetadataHash[];
extern const char kPayloadPropertyAuthorization[];
extern const char kPayloadPropertyUserAgent[];
+extern const char kPayloadPropertyPowerwash[];
// A download source is any combination of protocol and server (that's of
// interest to us when looking at UMA metrics) using which we may download
diff --git a/common/fake_hardware.h b/common/fake_hardware.h
index 4558c8c..2da12ad 100644
--- a/common/fake_hardware.h
+++ b/common/fake_hardware.h
@@ -57,6 +57,18 @@
int GetPowerwashCount() const override { return powerwash_count_; }
+ bool SchedulePowerwash() override {
+ powerwash_scheduled_ = true;
+ return true;
+ }
+
+ bool CancelPowerwash() override {
+ powerwash_scheduled_ = false;
+ return true;
+ }
+
+ bool IsPowerwashScheduled() { return powerwash_scheduled_; }
+
bool GetNonVolatileDirectory(base::FilePath* path) const override {
return false;
}
@@ -115,6 +127,7 @@
std::string firmware_version_{"Fake Firmware v1.0.1"};
std::string ec_version_{"Fake EC v1.0a"};
int powerwash_count_{kPowerwashCountNotSet};
+ bool powerwash_scheduled_{false};
DISALLOW_COPY_AND_ASSIGN(FakeHardware);
};
diff --git a/common/hardware_interface.h b/common/hardware_interface.h
index e434cc9..f5f900e 100644
--- a/common/hardware_interface.h
+++ b/common/hardware_interface.h
@@ -70,6 +70,13 @@
// recovery don't have this value set.
virtual int GetPowerwashCount() const = 0;
+ // Signals that a powerwash (stateful partition wipe) should be performed
+ // after reboot.
+ virtual bool SchedulePowerwash() = 0;
+
+ // Cancel the powerwash operation scheduled to be performed on next boot.
+ virtual bool CancelPowerwash() = 0;
+
// Store in |path| the path to a non-volatile directory (persisted across
// reboots) available for this daemon. In case of an error, such as no
// directory available, returns false.
diff --git a/common/utils.cc b/common/utils.cc
index f7d4585..a352961 100644
--- a/common/utils.cc
+++ b/common/utils.cc
@@ -879,35 +879,6 @@
return base_code;
}
-bool CreatePowerwashMarkerFile(const char* file_path) {
- const char* marker_file = file_path ? file_path : kPowerwashMarkerFile;
- bool result = utils::WriteFile(marker_file,
- kPowerwashCommand,
- strlen(kPowerwashCommand));
- if (result) {
- LOG(INFO) << "Created " << marker_file << " to powerwash on next reboot";
- } else {
- PLOG(ERROR) << "Error in creating powerwash marker file: " << marker_file;
- }
-
- return result;
-}
-
-bool DeletePowerwashMarkerFile(const char* file_path) {
- const char* marker_file = file_path ? file_path : kPowerwashMarkerFile;
- const base::FilePath kPowerwashMarkerPath(marker_file);
- bool result = base::DeleteFile(kPowerwashMarkerPath, false);
-
- if (result)
- LOG(INFO) << "Successfully deleted the powerwash marker file : "
- << marker_file;
- else
- PLOG(ERROR) << "Could not delete the powerwash marker file : "
- << marker_file;
-
- return result;
-}
-
Time TimeFromStructTimespec(struct timespec *ts) {
int64_t us = static_cast<int64_t>(ts->tv_sec) * Time::kMicrosecondsPerSecond +
static_cast<int64_t>(ts->tv_nsec) / Time::kNanosecondsPerMicrosecond;
diff --git a/common/utils.h b/common/utils.h
index e950b15..3987484 100644
--- a/common/utils.h
+++ b/common/utils.h
@@ -265,16 +265,6 @@
// it'll return the same value again.
ErrorCode GetBaseErrorCode(ErrorCode code);
-// Creates the powerwash marker file with the appropriate commands in it. Uses
-// |file_path| as the path to the marker file if non-null, otherwise uses the
-// global default. Returns true if successfully created. False otherwise.
-bool CreatePowerwashMarkerFile(const char* file_path);
-
-// Deletes the marker file used to trigger Powerwash using clobber-state. Uses
-// |file_path| as the path to the marker file if non-null, otherwise uses the
-// global default. Returns true if successfully deleted. False otherwise.
-bool DeletePowerwashMarkerFile(const char* file_path);
-
// Decodes the data in |base64_encoded| and stores it in a temporary
// file. Returns false if the given data is empty, not well-formed
// base64 or if an error occurred. If true is returned, the decoded
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)) {
diff --git a/hardware_android.h b/hardware_android.h
index 9aa729c..88e00fa 100644
--- a/hardware_android.h
+++ b/hardware_android.h
@@ -42,6 +42,8 @@
std::string GetFirmwareVersion() const override;
std::string GetECVersion() const override;
int GetPowerwashCount() const override;
+ bool SchedulePowerwash() override;
+ bool CancelPowerwash() override;
bool GetNonVolatileDirectory(base::FilePath* path) const override;
bool GetPowerwashSafeDirectory(base::FilePath* path) const override;
diff --git a/hardware_chromeos.cc b/hardware_chromeos.cc
index 57583a1..9b3a05d 100644
--- a/hardware_chromeos.cc
+++ b/hardware_chromeos.cc
@@ -53,6 +53,14 @@
// a powerwash is performed.
const char kPowerwashCountMarker[] = "powerwash_count";
+// The name of the marker file used to trigger powerwash when post-install
+// completes successfully so that the device is powerwashed on next reboot.
+const char kPowerwashMarkerFile[] =
+ "/mnt/stateful_partition/factory_install_reset";
+
+// The contents of the powerwash marker file.
+const char kPowerwashCommand[] = "safe fast keepimg reason=update_engine\n";
+
// UpdateManager config path.
const char* kConfigFilePath = "/etc/update_manager.conf";
@@ -162,6 +170,32 @@
return powerwash_count;
}
+bool HardwareChromeOS::SchedulePowerwash() {
+ bool result = utils::WriteFile(
+ kPowerwashMarkerFile, kPowerwashCommand, strlen(kPowerwashCommand));
+ if (result) {
+ LOG(INFO) << "Created " << marker_file << " to powerwash on next reboot";
+ } else {
+ PLOG(ERROR) << "Error in creating powerwash marker file: " << marker_file;
+ }
+
+ return result;
+}
+
+bool HardwareChromeOS::CancelPowerwash() {
+ bool result = base::DeleteFile(base::FilePath(kPowerwashMarkerFile), false);
+
+ if (result) {
+ LOG(INFO) << "Successfully deleted the powerwash marker file : "
+ << marker_file;
+ } else {
+ PLOG(ERROR) << "Could not delete the powerwash marker file : "
+ << marker_file;
+ }
+
+ return result;
+}
+
bool HardwareChromeOS::GetNonVolatileDirectory(base::FilePath* path) const {
*path = base::FilePath(constants::kNonVolatileDirectory);
return true;
diff --git a/hardware_chromeos.h b/hardware_chromeos.h
index d9a73f8..bc0e891 100644
--- a/hardware_chromeos.h
+++ b/hardware_chromeos.h
@@ -45,6 +45,8 @@
std::string GetFirmwareVersion() const override;
std::string GetECVersion() const override;
int GetPowerwashCount() const override;
+ bool SchedulePowerwash() override;
+ bool CancelPowerwash() override;
bool GetNonVolatileDirectory(base::FilePath* path) const override;
bool GetPowerwashSafeDirectory(base::FilePath* path) const override;
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index c24590e..47b1947 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -58,8 +58,8 @@
install_plan_ = GetInputObject();
if (install_plan_.powerwash_required) {
- if (utils::CreatePowerwashMarkerFile(powerwash_marker_file_)) {
- powerwash_marker_created_ = true;
+ if (hardware_->SchedulePowerwash()) {
+ powerwash_scheduled_ = true;
} else {
return CompletePostinstall(ErrorCode::kPostinstallPowerwashError);
}
@@ -327,9 +327,9 @@
if (error_code != ErrorCode::kSuccess) {
LOG(ERROR) << "Postinstall action failed.";
- // Undo any changes done to trigger Powerwash using clobber-state.
- if (powerwash_marker_created_)
- utils::DeletePowerwashMarkerFile(powerwash_marker_file_);
+ // Undo any changes done to trigger Powerwash.
+ if (powerwash_scheduled_)
+ hardware_->CancelPowerwash();
return;
}
diff --git a/payload_consumer/postinstall_runner_action.h b/payload_consumer/postinstall_runner_action.h
index 4cdc47e..2bde3ca 100644
--- a/payload_consumer/postinstall_runner_action.h
+++ b/payload_consumer/postinstall_runner_action.h
@@ -24,6 +24,8 @@
#include <gtest/gtest_prod.h>
#include "update_engine/common/action.h"
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/hardware_interface.h"
#include "update_engine/payload_consumer/install_plan.h"
// The Postinstall Runner Action is responsible for running the postinstall
@@ -35,8 +37,9 @@
class PostinstallRunnerAction : public InstallPlanAction {
public:
- explicit PostinstallRunnerAction(BootControlInterface* boot_control)
- : PostinstallRunnerAction(boot_control, nullptr) {}
+ PostinstallRunnerAction(BootControlInterface* boot_control,
+ HardwareInterface* hardware)
+ : boot_control_(boot_control), hardware_(hardware) {}
// InstallPlanAction overrides.
void PerformAction() override;
@@ -63,12 +66,6 @@
friend class PostinstallRunnerActionTest;
FRIEND_TEST(PostinstallRunnerActionTest, ProcessProgressLineTest);
- // Special constructor used for testing purposes.
- PostinstallRunnerAction(BootControlInterface* boot_control,
- const char* powerwash_marker_file)
- : boot_control_(boot_control),
- powerwash_marker_file_(powerwash_marker_file) {}
-
void PerformPartitionPostinstall();
// Called whenever the |progress_fd_| has data available to read.
@@ -127,13 +124,12 @@
// The BootControlInerface used to mark the new slot as ready.
BootControlInterface* boot_control_;
- // True if Powerwash Marker was created before invoking post-install script.
- // False otherwise. Used for cleaning up if post-install fails.
- bool powerwash_marker_created_{false};
+ // HardwareInterface used to signal powerwash.
+ HardwareInterface* hardware_;
- // Non-null value will cause post-install to override the default marker
- // file name; used for testing.
- const char* powerwash_marker_file_;
+ // Whether the Powerwash was scheduled before invoking post-install script.
+ // Used for cleaning up if post-install fails.
+ bool powerwash_scheduled_{false};
// Postinstall command currently running, or 0 if no program running.
pid_t current_command_{0};
diff --git a/payload_consumer/postinstall_runner_action_unittest.cc b/payload_consumer/postinstall_runner_action_unittest.cc
index 3b6b49a..5a8e950 100644
--- a/payload_consumer/postinstall_runner_action_unittest.cc
+++ b/payload_consumer/postinstall_runner_action_unittest.cc
@@ -26,7 +26,6 @@
#include <base/bind.h>
#include <base/files/file_util.h>
-#include <base/files/scoped_temp_dir.h>
#include <base/message_loop/message_loop.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
@@ -38,6 +37,7 @@
#include "update_engine/common/constants.h"
#include "update_engine/common/fake_boot_control.h"
+#include "update_engine/common/fake_hardware.h"
#include "update_engine/common/test_utils.h"
#include "update_engine/common/utils.h"
@@ -88,10 +88,6 @@
loop_.SetAsCurrent();
async_signal_handler_.Init();
subprocess_.Init(&async_signal_handler_);
- ASSERT_TRUE(working_dir_.CreateUniqueTempDir());
- // We use a test-specific powerwash marker file, to avoid race conditions.
- powerwash_marker_file_ =
- working_dir_.path().Append("factory_install_reset").value();
// These tests use the postinstall files generated by "generate_images.sh"
// stored in the "disk_ext2_unittest.img" image.
postinstall_image_ =
@@ -154,14 +150,11 @@
brillo::AsynchronousSignalHandler async_signal_handler_;
Subprocess subprocess_;
- // A temporary working directory used for the test.
- base::ScopedTempDir working_dir_;
- string powerwash_marker_file_;
-
// The path to the postinstall sample image.
string postinstall_image_;
FakeBootControl fake_boot_control_;
+ FakeHardware fake_hardware_;
PostinstActionProcessorDelegate processor_delegate_;
// The PostinstallRunnerAction delegate receiving the progress updates.
@@ -189,8 +182,7 @@
install_plan.download_url = "http://127.0.0.1:8080/update";
install_plan.powerwash_required = powerwash_required;
feeder_action.set_obj(install_plan);
- PostinstallRunnerAction runner_action(&fake_boot_control_,
- powerwash_marker_file_.c_str());
+ PostinstallRunnerAction runner_action(&fake_boot_control_, &fake_hardware_);
postinstall_action_ = &runner_action;
runner_action.set_delegate(setup_action_delegate_);
BondActions(&feeder_action, &runner_action);
@@ -216,8 +208,7 @@
}
TEST_F(PostinstallRunnerActionTest, ProcessProgressLineTest) {
- PostinstallRunnerAction action(&fake_boot_control_,
- powerwash_marker_file_.c_str());
+ PostinstallRunnerAction action(&fake_boot_control_, &fake_hardware_);
testing::StrictMock<MockPostinstallRunnerActionDelegate> mock_delegate_;
action.set_delegate(&mock_delegate_);
@@ -246,7 +237,7 @@
EXPECT_TRUE(processor_delegate_.processing_done_called_);
// Since powerwash_required was false, this should not trigger a powerwash.
- EXPECT_FALSE(utils::FileExists(powerwash_marker_file_.c_str()));
+ EXPECT_FALSE(fake_hardware_.IsPowerwashScheduled());
}
TEST_F(PostinstallRunnerActionTest, RunAsRootRunSymlinkFileTest) {
@@ -261,11 +252,8 @@
RunPosinstallAction(loop.dev(), "bin/postinst_example", true);
EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
- // Check that the powerwash marker file was set.
- string actual_cmd;
- EXPECT_TRUE(base::ReadFileToString(base::FilePath(powerwash_marker_file_),
- &actual_cmd));
- EXPECT_EQ(kPowerwashCommand, actual_cmd);
+ // Check that powerwash was scheduled.
+ EXPECT_TRUE(fake_hardware_.IsPowerwashScheduled());
}
// Runs postinstall from a partition file that doesn't mount, so it should
@@ -276,7 +264,7 @@
// In case of failure, Postinstall should not signal a powerwash even if it
// was requested.
- EXPECT_FALSE(utils::FileExists(powerwash_marker_file_.c_str()));
+ EXPECT_FALSE(fake_hardware_.IsPowerwashScheduled());
}
// Check that the failures from the postinstall script cause the action to
diff --git a/update_attempter.cc b/update_attempter.cc
index 8f19f51..f9f12bc 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -587,7 +587,8 @@
void UpdateAttempter::BuildPostInstallActions(
InstallPlanAction* previous_action) {
shared_ptr<PostinstallRunnerAction> postinstall_runner_action(
- new PostinstallRunnerAction(system_state_->boot_control()));
+ new PostinstallRunnerAction(system_state_->boot_control(),
+ system_state_->hardware()));
postinstall_runner_action->set_delegate(this);
actions_.push_back(shared_ptr<AbstractAction>(postinstall_runner_action));
BondActions(previous_action,
diff --git a/update_attempter_android.cc b/update_attempter_android.cc
index 87c7e0d..2e7fda6 100644
--- a/update_attempter_android.cc
+++ b/update_attempter_android.cc
@@ -168,7 +168,11 @@
install_plan_.source_slot = boot_control_->GetCurrentSlot();
install_plan_.target_slot = install_plan_.source_slot == 0 ? 1 : 0;
- install_plan_.powerwash_required = false;
+
+ int data_wipe = 0;
+ install_plan_.powerwash_required =
+ base::StringToInt(headers[kPayloadPropertyPowerwash], &data_wipe) &&
+ data_wipe != 0;
LOG(INFO) << "Using this install plan:";
install_plan_.Dump();
@@ -403,7 +407,7 @@
new FilesystemVerifierAction());
shared_ptr<PostinstallRunnerAction> postinstall_runner_action(
- new PostinstallRunnerAction(boot_control_));
+ new PostinstallRunnerAction(boot_control_, hardware_));
download_action->set_delegate(this);
download_action_ = download_action;
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 99cd2c8..b9a60b3 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -282,7 +282,7 @@
GetErrorCodeForAction(&filesystem_verifier_action,
ErrorCode::kError));
PostinstallRunnerAction postinstall_runner_action(
- fake_system_state.fake_boot_control());
+ fake_system_state.fake_boot_control(), fake_system_state.fake_hardware());
EXPECT_EQ(ErrorCode::kPostinstallRunnerError,
GetErrorCodeForAction(&postinstall_runner_action,
ErrorCode::kError));
diff --git a/utils_android.cc b/utils_android.cc
new file mode 100644
index 0000000..a4f1ea8
--- /dev/null
+++ b/utils_android.cc
@@ -0,0 +1,71 @@
+//
+// Copyright (C) 2016 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 "update_engine/utils_android.h"
+
+#include <cutils/properties.h>
+#include <fs_mgr.h>
+
+using std::string;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+// Open the appropriate fstab file and fallback to /fstab.device if
+// that's what's being used.
+static struct fstab* OpenFSTab() {
+ char propbuf[PROPERTY_VALUE_MAX];
+ struct fstab* fstab;
+
+ property_get("ro.hardware", propbuf, "");
+ string fstab_name = string("/fstab.") + propbuf;
+ fstab = fs_mgr_read_fstab(fstab_name.c_str());
+ if (fstab != nullptr)
+ return fstab;
+
+ fstab = fs_mgr_read_fstab("/fstab.device");
+ return fstab;
+}
+
+} // namespace
+
+namespace utils {
+
+bool DeviceForMountPoint(const string& mount_point, base::FilePath* device) {
+ struct fstab* fstab;
+ struct fstab_rec* record;
+
+ fstab = OpenFSTab();
+ if (fstab == nullptr) {
+ LOG(ERROR) << "Error opening fstab file.";
+ return false;
+ }
+ record = fs_mgr_get_entry_for_mount_point(fstab, mount_point.c_str());
+ if (record == nullptr) {
+ LOG(ERROR) << "Error finding " << mount_point << " entry in fstab file.";
+ fs_mgr_free_fstab(fstab);
+ return false;
+ }
+
+ *device = base::FilePath(record->blk_device);
+ fs_mgr_free_fstab(fstab);
+ return true;
+}
+
+} // namespace utils
+
+} // namespace chromeos_update_engine
diff --git a/utils_android.h b/utils_android.h
new file mode 100644
index 0000000..18dd8ab
--- /dev/null
+++ b/utils_android.h
@@ -0,0 +1,37 @@
+//
+// Copyright (C) 2016 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 UPDATE_ENGINE_UTILS_ANDROID_H_
+#define UPDATE_ENGINE_UTILS_ANDROID_H_
+
+#include <string>
+
+#include <base/files/file_util.h>
+
+namespace chromeos_update_engine {
+
+namespace utils {
+
+// Find the block device that should be mounted in the |mount_point| path and
+// store it in |device|. Returns whether a device was found on the fstab.
+bool DeviceForMountPoint(const std::string& mount_point,
+ base::FilePath* device);
+
+} // namespace utils
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_UTILS_ANDROID_H_