Add maximum timestamp to the payload.
am: f8527ada7b  -s ours

Change-Id: I527f6405642f1e195f388d3c994ed249315fded2
diff --git a/Android.mk b/Android.mk
index 29591fa..3f44ecc 100644
--- a/Android.mk
+++ b/Android.mk
@@ -62,9 +62,8 @@
     external/gtest/include \
     system
 ue_common_shared_libraries := \
-    libbrillo \
-    libbrillo-http \
     libbrillo-stream \
+    libbrillo \
     libchrome
 
 ifeq ($(local_use_dbus),1)
@@ -146,14 +145,11 @@
     $(ue_update_metadata_protos_exported_static_libraries)
 ue_libpayload_consumer_exported_shared_libraries := \
     libcrypto-host \
-    libcurl-host \
-    libssl-host \
     $(ue_update_metadata_protos_exported_shared_libraries)
 
 ue_libpayload_consumer_src_files := \
     common/action_processor.cc \
     common/boot_control_stub.cc \
-    common/certificate_checker.cc \
     common/clock.cc \
     common/constants.cc \
     common/cpu_limiter.cc \
@@ -161,8 +157,8 @@
     common/hash_calculator.cc \
     common/http_common.cc \
     common/http_fetcher.cc \
+    common/file_fetcher.cc \
     common/hwid_override.cc \
-    common/libcurl_http_fetcher.cc \
     common/multi_range_http_fetcher.cc \
     common/platform_constants_android.cc \
     common/prefs.cc \
@@ -258,7 +254,9 @@
     libexpat \
     libbrillo-policy \
     libhardware \
+    libcurl \
     libcutils \
+    libssl \
     $(ue_libpayload_consumer_exported_shared_libraries) \
     $(ue_update_metadata_protos_exported_shared_libraries)
 ifeq ($(local_use_binder),1)
@@ -286,7 +284,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 \
@@ -303,6 +302,7 @@
     $(ue_update_metadata_protos_exported_shared_libraries)
 LOCAL_SRC_FILES := \
     boot_control_android.cc \
+    certificate_checker.cc \
     common_service.cc \
     connection_manager.cc \
     daemon.cc \
@@ -310,6 +310,7 @@
     hardware_android.cc \
     image_properties_android.cc \
     libcros_proxy.cc \
+    libcurl_http_fetcher.cc \
     metrics.cc \
     metrics_utils.cc \
     omaha_request_action.cc \
@@ -336,6 +337,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
@@ -371,11 +373,14 @@
     $(ue_libpayload_consumer_exported_static_libraries)
 ue_libupdate_engine_android_exported_shared_libraries := \
     $(ue_libpayload_consumer_exported_shared_libraries) \
+    libandroid \
     libbinder \
     libbinderwrapper \
     libbrillo-binder \
     libcutils \
+    libcurl \
     libhardware \
+    libssl \
     libutils
 
 include $(CLEAR_VARS)
@@ -386,7 +391,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 += \
@@ -402,12 +409,16 @@
     binder_bindings/android/os/IUpdateEngineCallback.aidl \
     binder_service_android.cc \
     boot_control_android.cc \
+    certificate_checker.cc \
     daemon.cc \
     daemon_state_android.cc \
     hardware_android.cc \
+    libcurl_http_fetcher.cc \
+    network_selector_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  # !defined(BRILLO)
@@ -455,6 +466,74 @@
 LOCAL_INIT_RC := update_engine.rc
 include $(BUILD_EXECUTABLE)
 
+# update_engine_sideload (type: executable)
+# ========================================================
+# A static binary equivalent to update_engine daemon that installs an update
+# from a local file directly instead of running in the background.
+include $(CLEAR_VARS)
+LOCAL_MODULE := update_engine_sideload
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_REQUIRED_MODULES := \
+    bspatch_recovery
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CLANG := true
+LOCAL_CFLAGS := \
+    $(ue_common_cflags) \
+    -D_UE_SIDELOAD
+LOCAL_CPPFLAGS := $(ue_common_cppflags)
+LOCAL_LDFLAGS := $(ue_common_ldflags)
+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 += \
+    external/cros/system_api/dbus
+LOCAL_SRC_FILES := \
+    boot_control_android.cc \
+    hardware_android.cc \
+    network_selector_stub.cc \
+    proxy_resolver.cc \
+    sideload_main.cc \
+    update_attempter_android.cc \
+    update_status_utils.cc \
+    utils_android.cc
+LOCAL_STATIC_LIBRARIES := \
+    libfs_mgr \
+    libpayload_consumer \
+    update_metadata-protos \
+    $(ue_libpayload_consumer_exported_static_libraries:-host=) \
+    $(ue_update_metadata_protos_exported_static_libraries)
+# We add the static versions of the shared libraries since we are forcing this
+# binary to be a static binary, so we also need to include all the static
+# library dependencies of these static libraries.
+LOCAL_STATIC_LIBRARIES += \
+    $(ue_common_shared_libraries) \
+    libcutils \
+    libcrypto_static \
+    $(ue_update_metadata_protos_exported_shared_libraries) \
+    libevent \
+    libmodpb64 \
+    liblog
+
+ifeq ($(strip $(PRODUCT_STATIC_BOOT_CONTROL_HAL)),)
+# No static boot_control HAL defined, so no sideload support. We use a fake
+# boot_control HAL to allow compiling update_engine_sideload for test purposes.
+ifeq ($(strip $(AB_OTA_UPDATER)),true)
+$(warning No PRODUCT_STATIC_BOOT_CONTROL_HAL configured but AB_OTA_UPDATER is \
+true, no update sideload support.)
+endif  # AB_OTA_UPDATER == true
+LOCAL_SRC_FILES += \
+    boot_control_recovery_stub.cc
+else  # PRODUCT_STATIC_BOOT_CONTROL_HAL != ""
+LOCAL_STATIC_LIBRARIES += \
+    $(PRODUCT_STATIC_BOOT_CONTROL_HAL)
+endif  # PRODUCT_STATIC_BOOT_CONTROL_HAL != ""
+
+include $(BUILD_EXECUTABLE)
+
 # libupdate_engine_client (type: shared_library)
 # ========================================================
 include $(CLEAR_VARS)
@@ -819,12 +898,13 @@
     $(ue_libupdate_engine_exported_shared_libraries:-host=) \
     $(ue_libpayload_generator_exported_shared_libraries:-host=)
 LOCAL_SRC_FILES := \
+    certificate_checker_unittest.cc \
     common/action_pipe_unittest.cc \
     common/action_processor_unittest.cc \
     common/action_unittest.cc \
-    common/certificate_checker_unittest.cc \
     common/cpu_limiter_unittest.cc \
     common/fake_prefs.cc \
+    common/file_fetcher_unittest.cc \
     common/hash_calculator_unittest.cc \
     common/http_fetcher_unittest.cc \
     common/hwid_override_unittest.cc \
diff --git a/boot_control_android.cc b/boot_control_android.cc
index e4d3c16..d096a1b 100644
--- a/boot_control_android.cc
+++ b/boot_control_android.cc
@@ -22,33 +22,19 @@
 #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
-
+#ifdef _UE_SIDELOAD
+// When called from update_engine_sideload, we don't attempt to dynamically load
+// the right boot_control HAL, instead we use the only HAL statically linked in
+// via the PRODUCT_STATIC_BOOT_CONTROL_HAL make variable and access the module
+// struct directly.
+extern const hw_module_t HAL_MODULE_INFO_SYM;
+#endif  // _UE_SIDELOAD
 
 namespace chromeos_update_engine {
 
@@ -69,7 +55,18 @@
   const hw_module_t* hw_module;
   int ret;
 
+#ifdef _UE_SIDELOAD
+  // For update_engine_sideload, we simulate the hw_get_module() by accessing it
+  // from the current process directly.
+  hw_module = &HAL_MODULE_INFO_SYM;
+  ret = 0;
+  if (!hw_module ||
+      strcmp(BOOT_CONTROL_HARDWARE_MODULE_ID, hw_module->id) != 0) {
+    ret = -EINVAL;
+  }
+#else  // !_UE_SIDELOAD
   ret = hw_get_module(BOOT_CONTROL_HARDWARE_MODULE_ID, &hw_module);
+#endif  // _UE_SIDELOAD
   if (ret != 0) {
     LOG(ERROR) << "Error loading boot_control HAL implementation.";
     return false;
@@ -97,9 +94,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 +113,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/boot_control_recovery_stub.cc b/boot_control_recovery_stub.cc
new file mode 100644
index 0000000..129c5d0
--- /dev/null
+++ b/boot_control_recovery_stub.cc
@@ -0,0 +1,21 @@
+//
+// 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 <hardware/hardware.h>
+
+hw_module_t HAL_MODULE_INFO_SYM = {
+  .id = "stub",
+};
diff --git a/common/certificate_checker.cc b/certificate_checker.cc
similarity index 98%
rename from common/certificate_checker.cc
rename to certificate_checker.cc
index 86df950..6e886e7 100644
--- a/common/certificate_checker.cc
+++ b/certificate_checker.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/common/certificate_checker.h"
+#include "update_engine/certificate_checker.h"
 
 #include <string>
 
diff --git a/common/certificate_checker.h b/certificate_checker.h
similarity index 97%
rename from common/certificate_checker.h
rename to certificate_checker.h
index c785192..5d0b5ba 100644
--- a/common/certificate_checker.h
+++ b/certificate_checker.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_COMMON_CERTIFICATE_CHECKER_H_
-#define UPDATE_ENGINE_COMMON_CERTIFICATE_CHECKER_H_
+#ifndef UPDATE_ENGINE_CERTIFICATE_CHECKER_H_
+#define UPDATE_ENGINE_CERTIFICATE_CHECKER_H_
 
 #include <curl/curl.h>
 #include <openssl/ssl.h>
@@ -172,4 +172,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_COMMON_CERTIFICATE_CHECKER_H_
+#endif  // UPDATE_ENGINE_CERTIFICATE_CHECKER_H_
diff --git a/common/certificate_checker_unittest.cc b/certificate_checker_unittest.cc
similarity index 97%
rename from common/certificate_checker_unittest.cc
rename to certificate_checker_unittest.cc
index c30acc5..20efce9 100644
--- a/common/certificate_checker_unittest.cc
+++ b/certificate_checker_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/common/certificate_checker.h"
+#include "update_engine/certificate_checker.h"
 
 #include <string>
 
@@ -24,8 +24,8 @@
 #include <gtest/gtest.h>
 
 #include "update_engine/common/constants.h"
-#include "update_engine/common/mock_certificate_checker.h"
 #include "update_engine/common/mock_prefs.h"
+#include "update_engine/mock_certificate_checker.h"
 
 using ::testing::DoAll;
 using ::testing::Return;
diff --git a/common/constants.cc b/common/constants.cc
index f138ce3..c3556f8 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";
@@ -96,5 +91,7 @@
 const char kPayloadPropertyMetadataHash[] = "METADATA_HASH";
 const char kPayloadPropertyAuthorization[] = "AUTHORIZATION";
 const char kPayloadPropertyUserAgent[] = "USER_AGENT";
+const char kPayloadPropertyPowerwash[] = "POWERWASH";
+const char kPayloadPropertyNetworkId[] = "NETWORK_ID";
 
 }  // namespace chromeos_update_engine
diff --git a/common/constants.h b/common/constants.h
index f0d589d..ba2594a 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[];
 
@@ -99,6 +92,8 @@
 extern const char kPayloadPropertyMetadataHash[];
 extern const char kPayloadPropertyAuthorization[];
 extern const char kPayloadPropertyUserAgent[];
+extern const char kPayloadPropertyPowerwash[];
+extern const char kPayloadPropertyNetworkId[];
 
 // 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 221a451..25324ae 100644
--- a/common/fake_hardware.h
+++ b/common/fake_hardware.h
@@ -62,6 +62,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;
   }
@@ -121,6 +133,7 @@
   std::string firmware_version_;
   std::string ec_version_;
   int powerwash_count_;
+  bool powerwash_scheduled_{false};
   int64_t build_timestamp_{0};
 
   DISALLOW_COPY_AND_ASSIGN(FakeHardware);
diff --git a/common/file_fetcher.cc b/common/file_fetcher.cc
new file mode 100644
index 0000000..d0a109b
--- /dev/null
+++ b/common/file_fetcher.cc
@@ -0,0 +1,186 @@
+//
+// 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/common/file_fetcher.h"
+
+#include <algorithm>
+#include <string>
+
+#include <base/bind.h>
+#include <base/format_macros.h>
+#include <base/location.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/streams/file_stream.h>
+
+#include "update_engine/common/hardware_interface.h"
+#include "update_engine/common/platform_constants.h"
+
+using std::string;
+
+namespace {
+
+size_t kReadBufferSize = 16 * 1024;
+
+}  // namespace
+
+namespace chromeos_update_engine {
+
+// static
+bool FileFetcher::SupportedUrl(const string& url) {
+  // Note that we require the file path to start with a "/".
+  return base::StartsWith(
+      url, "file:///", base::CompareCase::INSENSITIVE_ASCII);
+}
+
+FileFetcher::~FileFetcher() {
+  LOG_IF(ERROR, transfer_in_progress_)
+      << "Destroying the fetcher while a transfer is in progress.";
+  CleanUp();
+}
+
+// Begins the transfer, which must not have already been started.
+void FileFetcher::BeginTransfer(const string& url) {
+  CHECK(!transfer_in_progress_);
+
+  if (!SupportedUrl(url)) {
+    LOG(ERROR) << "Unsupported file URL: " << url;
+    // No HTTP error code when the URL is not supported.
+    http_response_code_ = 0;
+    CleanUp();
+    if (delegate_)
+      delegate_->TransferComplete(this, false);
+    return;
+  }
+
+  string file_path = url.substr(strlen("file://"));
+  stream_ =
+      brillo::FileStream::Open(base::FilePath(file_path),
+                               brillo::Stream::AccessMode::READ,
+                               brillo::FileStream::Disposition::OPEN_EXISTING,
+                               nullptr);
+
+  if (!stream_) {
+    LOG(ERROR) << "Couldn't open " << file_path;
+    http_response_code_ = kHttpResponseNotFound;
+    CleanUp();
+    if (delegate_)
+      delegate_->TransferComplete(this, false);
+    return;
+  }
+  http_response_code_ = kHttpResponseOk;
+
+  if (offset_)
+    stream_->SetPosition(offset_, nullptr);
+  bytes_copied_ = 0;
+  transfer_in_progress_ = true;
+  ScheduleRead();
+}
+
+void FileFetcher::TerminateTransfer() {
+  CleanUp();
+  if (delegate_) {
+    // Note that after the callback returns this object may be destroyed.
+    delegate_->TransferTerminated(this);
+  }
+}
+
+void FileFetcher::ScheduleRead() {
+  if (transfer_paused_ || ongoing_read_ || !transfer_in_progress_)
+    return;
+
+  buffer_.resize(kReadBufferSize);
+  size_t bytes_to_read = buffer_.size();
+  if (data_length_ >= 0) {
+    bytes_to_read = std::min(static_cast<uint64_t>(bytes_to_read),
+                             data_length_ - bytes_copied_);
+  }
+
+  if (!bytes_to_read) {
+    OnReadDoneCallback(0);
+    return;
+  }
+
+  ongoing_read_ = stream_->ReadAsync(
+      buffer_.data(),
+      bytes_to_read,
+      base::Bind(&FileFetcher::OnReadDoneCallback, base::Unretained(this)),
+      base::Bind(&FileFetcher::OnReadErrorCallback, base::Unretained(this)),
+      nullptr);
+
+  if (!ongoing_read_) {
+    LOG(ERROR) << "Unable to schedule an asynchronous read from the stream.";
+    CleanUp();
+    if (delegate_)
+      delegate_->TransferComplete(this, false);
+  }
+}
+
+void FileFetcher::OnReadDoneCallback(size_t bytes_read) {
+  ongoing_read_ = false;
+  if (bytes_read == 0) {
+    CleanUp();
+    if (delegate_)
+      delegate_->TransferComplete(this, true);
+  } else {
+    bytes_copied_ += bytes_read;
+    if (delegate_)
+      delegate_->ReceivedBytes(this, buffer_.data(), bytes_read);
+    ScheduleRead();
+  }
+}
+
+void FileFetcher::OnReadErrorCallback(const brillo::Error* error) {
+  LOG(ERROR) << "Asynchronous read failed: " << error->GetMessage();
+  CleanUp();
+  if (delegate_)
+    delegate_->TransferComplete(this, false);
+}
+
+void FileFetcher::Pause() {
+  if (transfer_paused_) {
+    LOG(ERROR) << "Fetcher already paused.";
+    return;
+  }
+  transfer_paused_ = true;
+}
+
+void FileFetcher::Unpause() {
+  if (!transfer_paused_) {
+    LOG(ERROR) << "Resume attempted when fetcher not paused.";
+    return;
+  }
+  transfer_paused_ = false;
+  ScheduleRead();
+}
+
+void FileFetcher::CleanUp() {
+  if (stream_) {
+    stream_->CancelPendingAsyncOperations();
+    stream_->CloseBlocking(nullptr);
+    stream_.reset();
+  }
+  // Destroying the |stream_| releases the callback, so we don't have any
+  // ongoing read at this point.
+  ongoing_read_ = false;
+  buffer_ = brillo::Blob();
+
+  transfer_in_progress_ = false;
+  transfer_paused_ = false;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/file_fetcher.h b/common/file_fetcher.h
new file mode 100644
index 0000000..2368b1d
--- /dev/null
+++ b/common/file_fetcher.h
@@ -0,0 +1,119 @@
+//
+// 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_COMMON_FILE_FETCHER_H_
+#define UPDATE_ENGINE_COMMON_FILE_FETCHER_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <base/logging.h>
+#include <base/macros.h>
+#include <brillo/message_loops/message_loop.h>
+#include <brillo/streams/stream.h>
+
+#include "update_engine/common/http_fetcher.h"
+
+// This is a concrete implementation of HttpFetcher that reads files
+// asynchronously.
+
+namespace chromeos_update_engine {
+
+class FileFetcher : public HttpFetcher {
+ public:
+  // Returns whether the passed url is supported.
+  static bool SupportedUrl(const std::string& url);
+
+  FileFetcher() : HttpFetcher(nullptr) {}
+
+  // Cleans up all internal state. Does not notify delegate.
+  ~FileFetcher() override;
+
+  // HttpFetcher overrides.
+  void SetOffset(off_t offset) override { offset_ = offset; }
+  void SetLength(size_t length) override { data_length_ = length; }
+  void UnsetLength() override { SetLength(0); }
+
+  // Begins the transfer if it hasn't already begun.
+  void BeginTransfer(const std::string& url) override;
+
+  // If the transfer is in progress, aborts the transfer early. The transfer
+  // cannot be resumed.
+  void TerminateTransfer() override;
+
+  // Ignore all extra headers for files.
+  void SetHeader(const std::string& header_name,
+                 const std::string& header_value) override {};
+
+  // Suspend the asynchronous file read.
+  void Pause() override;
+
+  // Resume the suspended file read.
+  void Unpause() override;
+
+  size_t GetBytesDownloaded() override {
+    return static_cast<size_t>(bytes_copied_);
+  }
+
+  // Ignore all the time limits for files.
+  void set_low_speed_limit(int low_speed_bps, int low_speed_sec) override {}
+  void set_connect_timeout(int connect_timeout_seconds) override {}
+  void set_max_retry_count(int max_retry_count) override {}
+
+ private:
+  // Cleans up the fetcher, resetting its status to a newly constructed one.
+  void CleanUp();
+
+  // Schedule a new asynchronous read if the stream is not paused and no other
+  // read is in process. This method can be called at any point.
+  void ScheduleRead();
+
+  // Called from the main loop when a single read from |stream_| succeeds or
+  // fails, calling OnReadDoneCallback() and OnReadErrorCallback() respectively.
+  void OnReadDoneCallback(size_t bytes_read);
+  void OnReadErrorCallback(const brillo::Error* error);
+
+  // Whether the transfer was started and didn't finish yet.
+  bool transfer_in_progress_{false};
+
+  // Whether the transfer is paused.
+  bool transfer_paused_{false};
+
+  // Whether there's an ongoing asynchronous read. When this value is true, the
+  // the |buffer_| is being used by the |stream_|.
+  bool ongoing_read_{false};
+
+  // Total number of bytes copied.
+  uint64_t bytes_copied_{0};
+
+  // The offset inside the file where the read should start.
+  uint64_t offset_{0};
+
+  // The length of the data or -1 if unknown (will read until EOF).
+  int64_t data_length_{-1};
+
+  brillo::StreamPtr stream_;
+
+  // The buffer used for reading from the stream.
+  brillo::Blob buffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(FileFetcher);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_FILE_FETCHER_H_
diff --git a/common/file_fetcher_unittest.cc b/common/file_fetcher_unittest.cc
new file mode 100644
index 0000000..9c6b0ec
--- /dev/null
+++ b/common/file_fetcher_unittest.cc
@@ -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.
+//
+
+#include "update_engine/common/file_fetcher.h"
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include "update_engine/common/test_utils.h"
+
+namespace chromeos_update_engine {
+
+class FileFetcherUnitTest : public ::testing::Test {};
+
+TEST_F(FileFetcherUnitTest, SupporterUrlsTest) {
+  EXPECT_TRUE(FileFetcher::SupportedUrl("file:///path/to/somewhere.bin"));
+  EXPECT_TRUE(FileFetcher::SupportedUrl("FILE:///I/LIKE/TO/SHOUT"));
+
+  EXPECT_FALSE(FileFetcher::SupportedUrl("file://relative"));
+  EXPECT_FALSE(FileFetcher::SupportedUrl("http:///no_http_here"));
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/hardware_interface.h b/common/hardware_interface.h
index c3688b1..d5f73f7 100644
--- a/common/hardware_interface.h
+++ b/common/hardware_interface.h
@@ -67,6 +67,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/hash_calculator_unittest.cc b/common/hash_calculator_unittest.cc
index 27dbc56..436e6a7 100644
--- a/common/hash_calculator_unittest.cc
+++ b/common/hash_calculator_unittest.cc
@@ -25,7 +25,6 @@
 #include <brillo/secure_blob.h>
 #include <gtest/gtest.h>
 
-#include "update_engine/common/libcurl_http_fetcher.h"
 #include "update_engine/common/utils.h"
 
 using std::string;
diff --git a/common/http_fetcher_unittest.cc b/common/http_fetcher_unittest.cc
index 0d4b5da..f99007f 100644
--- a/common/http_fetcher_unittest.cc
+++ b/common/http_fetcher_unittest.cc
@@ -42,12 +42,13 @@
 #include <gtest/gtest.h>
 
 #include "update_engine/common/fake_hardware.h"
+#include "update_engine/common/file_fetcher.h"
 #include "update_engine/common/http_common.h"
-#include "update_engine/common/libcurl_http_fetcher.h"
 #include "update_engine/common/mock_http_fetcher.h"
 #include "update_engine/common/multi_range_http_fetcher.h"
 #include "update_engine/common/test_utils.h"
 #include "update_engine/common/utils.h"
+#include "update_engine/libcurl_http_fetcher.h"
 #include "update_engine/mock_proxy_resolver.h"
 #include "update_engine/proxy_resolver.h"
 
@@ -219,6 +220,7 @@
 
   virtual bool IsMock() const = 0;
   virtual bool IsMulti() const = 0;
+  virtual bool IsHttpSupported() const = 0;
 
   virtual void IgnoreServerAborting(HttpServer* server) const {}
 
@@ -251,6 +253,7 @@
 
   bool IsMock() const override { return true; }
   bool IsMulti() const override { return false; }
+  bool IsHttpSupported() const override { return true; }
 
   HttpServer* CreateServer() override {
     return new NullHttpServer;
@@ -291,6 +294,7 @@
 
   bool IsMock() const override { return false; }
   bool IsMulti() const override { return false; }
+  bool IsHttpSupported() const override { return true; }
 
   void IgnoreServerAborting(HttpServer* server) const override {
     // Nothing to do.
@@ -326,6 +330,42 @@
   bool IsMulti() const override { return true; }
 };
 
+class FileFetcherTest : public AnyHttpFetcherTest {
+ public:
+  // Necessary to unhide the definition in the base class.
+  using AnyHttpFetcherTest::NewLargeFetcher;
+  HttpFetcher* NewLargeFetcher(ProxyResolver* /* proxy_resolver */) override {
+    return new FileFetcher();
+  }
+
+  // Necessary to unhide the definition in the base class.
+  using AnyHttpFetcherTest::NewSmallFetcher;
+  HttpFetcher* NewSmallFetcher(ProxyResolver* proxy_resolver) override {
+    return NewLargeFetcher(proxy_resolver);
+  }
+
+  string BigUrl(in_port_t port) const override {
+    return "file://" + temp_file_.path();
+  }
+  string SmallUrl(in_port_t port) const override {
+    test_utils::WriteFileString(temp_file_.path(), "small contents");
+    return "file://" + temp_file_.path();
+  }
+  string ErrorUrl(in_port_t port) const override {
+    return "file:///path/to/non-existing-file";
+  }
+
+  bool IsMock() const override { return false; }
+  bool IsMulti() const override { return false; }
+  bool IsHttpSupported() const override { return false; }
+
+  void IgnoreServerAborting(HttpServer* server) const override {}
+
+  HttpServer* CreateServer() override { return new NullHttpServer; }
+
+ private:
+  test_utils::ScopedTempFile temp_file_{"ue_file_fetcher.XXXXXX"};
+};
 
 //
 // Infrastructure for type tests of HTTP fetcher.
@@ -363,7 +403,9 @@
 // Test case types list.
 typedef ::testing::Types<LibcurlHttpFetcherTest,
                          MockHttpFetcherTest,
-                         MultiRangeHttpFetcherTest> HttpFetcherTestTypes;
+                         MultiRangeHttpFetcherTest,
+                         FileFetcherTest>
+    HttpFetcherTestTypes;
 TYPED_TEST_CASE(HttpFetcherTest, HttpFetcherTestTypes);
 
 
@@ -394,6 +436,7 @@
   void TransferTerminated(HttpFetcher* fetcher) override {
     ADD_FAILURE();
     times_transfer_terminated_called_++;
+    MessageLoop::current()->BreakLoop();
   }
 
   // Are we expecting an error response? (default: no)
@@ -477,7 +520,7 @@
 }
 
 TYPED_TEST(HttpFetcherTest, ExtraHeadersInRequestTest) {
-  if (this->test_.IsMock())
+  if (this->test_.IsMock() || !this->test_.IsHttpSupported())
     return;
 
   HttpFetcherTestDelegate delegate;
@@ -498,7 +541,11 @@
   int port = server.GetPort();
   ASSERT_TRUE(server.started_);
 
-  StartTransfer(fetcher.get(), LocalServerUrlForPath(port, "/echo-headers"));
+  this->loop_.PostTask(
+      FROM_HERE,
+      base::Bind(StartTransfer,
+                 fetcher.get(),
+                 LocalServerUrlForPath(port, "/echo-headers")));
   this->loop_.Run();
 
   EXPECT_NE(string::npos,
@@ -571,7 +618,7 @@
 // This test will pause the fetcher while the download is not yet started
 // because it is waiting for the proxy to be resolved.
 TYPED_TEST(HttpFetcherTest, PauseWhileResolvingProxyTest) {
-  if (this->test_.IsMock())
+  if (this->test_.IsMock() || !this->test_.IsHttpSupported())
     return;
   MockProxyResolver mock_resolver;
   unique_ptr<HttpFetcher> fetcher(this->test_.NewLargeFetcher(&mock_resolver));
@@ -684,7 +731,7 @@
 }  // namespace
 
 TYPED_TEST(HttpFetcherTest, FlakyTest) {
-  if (this->test_.IsMock())
+  if (this->test_.IsMock() || !this->test_.IsHttpSupported())
     return;
   {
     FlakyHttpFetcherTestDelegate delegate;
@@ -897,7 +944,7 @@
 }  // namespace
 
 TYPED_TEST(HttpFetcherTest, SimpleRedirectTest) {
-  if (this->test_.IsMock())
+  if (this->test_.IsMock() || !this->test_.IsHttpSupported())
     return;
 
   unique_ptr<HttpServer> server(this->test_.CreateServer());
@@ -912,7 +959,7 @@
 }
 
 TYPED_TEST(HttpFetcherTest, MaxRedirectTest) {
-  if (this->test_.IsMock())
+  if (this->test_.IsMock() || !this->test_.IsHttpSupported())
     return;
 
   unique_ptr<HttpServer> server(this->test_.CreateServer());
@@ -928,7 +975,7 @@
 }
 
 TYPED_TEST(HttpFetcherTest, BeyondMaxRedirectTest) {
-  if (this->test_.IsMock())
+  if (this->test_.IsMock() || !this->test_.IsHttpSupported())
     return;
 
   unique_ptr<HttpServer> server(this->test_.CreateServer());
diff --git a/common/prefs.cc b/common/prefs.cc
index a4b97d0..12d06c0 100644
--- a/common/prefs.cc
+++ b/common/prefs.cc
@@ -29,31 +29,12 @@
 
 namespace chromeos_update_engine {
 
-bool Prefs::Init(const base::FilePath& prefs_dir) {
-  prefs_dir_ = prefs_dir;
-  return true;
+bool PrefsBase::GetString(const string& key, string* value) const {
+  return storage_->GetKey(key, value);
 }
 
-bool Prefs::GetString(const string& key, string* value) const {
-  base::FilePath filename;
-  TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
-  if (!base::ReadFileToString(filename, value)) {
-    LOG(INFO) << key << " not present in " << prefs_dir_.value();
-    return false;
-  }
-  return true;
-}
-
-bool Prefs::SetString(const string& key, const string& value) {
-  base::FilePath filename;
-  TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
-  if (!base::DirectoryExists(filename.DirName())) {
-    // Only attempt to create the directory if it doesn't exist to avoid calls
-    // to parent directories where we might not have permission to write to.
-    TEST_AND_RETURN_FALSE(base::CreateDirectory(filename.DirName()));
-  }
-  TEST_AND_RETURN_FALSE(base::WriteFile(filename, value.data(), value.size()) ==
-                        static_cast<int>(value.size()));
+bool PrefsBase::SetString(const string& key, const string& value) {
+  TEST_AND_RETURN_FALSE(storage_->SetKey(key, value));
   const auto observers_for_key = observers_.find(key);
   if (observers_for_key != observers_.end()) {
     std::vector<ObserverInterface*> copy_observers(observers_for_key->second);
@@ -63,7 +44,7 @@
   return true;
 }
 
-bool Prefs::GetInt64(const string& key, int64_t* value) const {
+bool PrefsBase::GetInt64(const string& key, int64_t* value) const {
   string str_value;
   if (!GetString(key, &str_value))
     return false;
@@ -72,11 +53,11 @@
   return true;
 }
 
-bool Prefs::SetInt64(const string& key, const int64_t value) {
+bool PrefsBase::SetInt64(const string& key, const int64_t value) {
   return SetString(key, base::Int64ToString(value));
 }
 
-bool Prefs::GetBoolean(const string& key, bool* value) const {
+bool PrefsBase::GetBoolean(const string& key, bool* value) const {
   string str_value;
   if (!GetString(key, &str_value))
     return false;
@@ -92,20 +73,16 @@
   return false;
 }
 
-bool Prefs::SetBoolean(const string& key, const bool value) {
+bool PrefsBase::SetBoolean(const string& key, const bool value) {
   return SetString(key, value ? "true" : "false");
 }
 
-bool Prefs::Exists(const string& key) const {
-  base::FilePath filename;
-  TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
-  return base::PathExists(filename);
+bool PrefsBase::Exists(const string& key) const {
+  return storage_->KeyExists(key);
 }
 
-bool Prefs::Delete(const string& key) {
-  base::FilePath filename;
-  TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
-  TEST_AND_RETURN_FALSE(base::DeleteFile(filename, false));
+bool PrefsBase::Delete(const string& key) {
+  TEST_AND_RETURN_FALSE(storage_->DeleteKey(key));
   const auto observers_for_key = observers_.find(key);
   if (observers_for_key != observers_.end()) {
     std::vector<ObserverInterface*> copy_observers(observers_for_key->second);
@@ -115,11 +92,11 @@
   return true;
 }
 
-void Prefs::AddObserver(const string& key, ObserverInterface* observer) {
+void PrefsBase::AddObserver(const string& key, ObserverInterface* observer) {
   observers_[key].push_back(observer);
 }
 
-void Prefs::RemoveObserver(const string& key, ObserverInterface* observer) {
+void PrefsBase::RemoveObserver(const string& key, ObserverInterface* observer) {
   std::vector<ObserverInterface*>& observers_for_key = observers_[key];
   auto observer_it =
       std::find(observers_for_key.begin(), observers_for_key.end(), observer);
@@ -127,8 +104,55 @@
     observers_for_key.erase(observer_it);
 }
 
-bool Prefs::GetFileNameForKey(const string& key,
-                              base::FilePath* filename) const {
+// Prefs
+
+bool Prefs::Init(const base::FilePath& prefs_dir) {
+  return file_storage_.Init(prefs_dir);
+}
+
+bool Prefs::FileStorage::Init(const base::FilePath& prefs_dir) {
+  prefs_dir_ = prefs_dir;
+  return true;
+}
+
+bool Prefs::FileStorage::GetKey(const string& key, string* value) const {
+  base::FilePath filename;
+  TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
+  if (!base::ReadFileToString(filename, value)) {
+    LOG(INFO) << key << " not present in " << prefs_dir_.value();
+    return false;
+  }
+  return true;
+}
+
+bool Prefs::FileStorage::SetKey(const string& key, const string& value) {
+  base::FilePath filename;
+  TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
+  if (!base::DirectoryExists(filename.DirName())) {
+    // Only attempt to create the directory if it doesn't exist to avoid calls
+    // to parent directories where we might not have permission to write to.
+    TEST_AND_RETURN_FALSE(base::CreateDirectory(filename.DirName()));
+  }
+  TEST_AND_RETURN_FALSE(base::WriteFile(filename, value.data(), value.size()) ==
+                        static_cast<int>(value.size()));
+  return true;
+}
+
+bool Prefs::FileStorage::KeyExists(const string& key) const {
+  base::FilePath filename;
+  TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
+  return base::PathExists(filename);
+}
+
+bool Prefs::FileStorage::DeleteKey(const string& key) {
+  base::FilePath filename;
+  TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
+  TEST_AND_RETURN_FALSE(base::DeleteFile(filename, false));
+  return true;
+}
+
+bool Prefs::FileStorage::GetFileNameForKey(const string& key,
+                                           base::FilePath* filename) const {
   // Allows only non-empty keys containing [A-Za-z0-9_-].
   TEST_AND_RETURN_FALSE(!key.empty());
   for (size_t i = 0; i < key.size(); ++i) {
@@ -140,4 +164,33 @@
   return true;
 }
 
+// MemoryPrefs
+
+bool MemoryPrefs::MemoryStorage::GetKey(const string& key,
+                                        string* value) const {
+  auto it = values_.find(key);
+  if (it == values_.end())
+    return false;
+  *value = it->second;
+  return true;
+}
+
+bool MemoryPrefs::MemoryStorage::SetKey(const string& key,
+                                        const string& value) {
+  values_[key] = value;
+  return true;
+}
+
+bool MemoryPrefs::MemoryStorage::KeyExists(const string& key) const {
+  return values_.find(key) != values_.end();
+}
+
+bool MemoryPrefs::MemoryStorage::DeleteKey(const string& key) {
+  auto it = values_.find(key);
+  if (it == values_.end())
+    return false;
+  values_.erase(it);
+  return true;
+}
+
 }  // namespace chromeos_update_engine
diff --git a/common/prefs.h b/common/prefs.h
index f11abc3..0116454 100644
--- a/common/prefs.h
+++ b/common/prefs.h
@@ -28,18 +28,36 @@
 
 namespace chromeos_update_engine {
 
-// Implements a preference store by storing the value associated with
-// a key in a separate file named after the key under a preference
-// store directory.
-
-class Prefs : public PrefsInterface {
+// Implements a preference store by storing the value associated with a key
+// in a given storage passed during construction.
+class PrefsBase : public PrefsInterface {
  public:
-  Prefs() = default;
+  // Storage interface used to set and retrieve keys.
+  class StorageInterface {
+   public:
+    StorageInterface() = default;
+    virtual ~StorageInterface() = default;
 
-  // Initializes the store by associating this object with |prefs_dir|
-  // as the preference store directory. Returns true on success, false
-  // otherwise.
-  bool Init(const base::FilePath& prefs_dir);
+    // Get the key named |key| and store its value in the referenced |value|.
+    // Returns whether the operation succeeded.
+    virtual bool GetKey(const std::string& key, std::string* value) const = 0;
+
+    // Set the value of the key named |key| to |value| regardless of the
+    // previous value. Returns whether the operation succeeded.
+    virtual bool SetKey(const std::string& key, const std::string& value) = 0;
+
+    // Returns whether the key named |key| exists.
+    virtual bool KeyExists(const std::string& key) const = 0;
+
+    // Deletes the value associated with the key name |key|. Returns whether the
+    // key was deleted.
+    virtual bool DeleteKey(const std::string& key) = 0;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(StorageInterface);
+  };
+
+  explicit PrefsBase(StorageInterface* storage) : storage_(storage) {}
 
   // PrefsInterface methods.
   bool GetString(const std::string& key, std::string* value) const override;
@@ -58,24 +76,93 @@
                       ObserverInterface* observer) override;
 
  private:
+  // The registered observers watching for changes.
+  std::map<std::string, std::vector<ObserverInterface*>> observers_;
+
+  // The concrete implementation of the storage used for the keys.
+  StorageInterface* storage_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrefsBase);
+};
+
+// Implements a preference store by storing the value associated with
+// a key in a separate file named after the key under a preference
+// store directory.
+
+class Prefs : public PrefsBase {
+ public:
+  Prefs() : PrefsBase(&file_storage_) {}
+
+  // Initializes the store by associating this object with |prefs_dir|
+  // as the preference store directory. Returns true on success, false
+  // otherwise.
+  bool Init(const base::FilePath& prefs_dir);
+
+ private:
   FRIEND_TEST(PrefsTest, GetFileNameForKey);
   FRIEND_TEST(PrefsTest, GetFileNameForKeyBadCharacter);
   FRIEND_TEST(PrefsTest, GetFileNameForKeyEmpty);
 
-  // Sets |filename| to the full path to the file containing the data
-  // associated with |key|. Returns true on success, false otherwise.
-  bool GetFileNameForKey(const std::string& key,
-                         base::FilePath* filename) const;
+  class FileStorage : public PrefsBase::StorageInterface {
+   public:
+    FileStorage() = default;
 
-  // Preference store directory.
-  base::FilePath prefs_dir_;
+    bool Init(const base::FilePath& prefs_dir);
 
-  // The registered observers watching for changes.
-  std::map<std::string, std::vector<ObserverInterface*>> observers_;
+    // PrefsBase::StorageInterface overrides.
+    bool GetKey(const std::string& key, std::string* value) const override;
+    bool SetKey(const std::string& key, const std::string& value) override;
+    bool KeyExists(const std::string& key) const override;
+    bool DeleteKey(const std::string& key) override;
+
+   private:
+    FRIEND_TEST(PrefsTest, GetFileNameForKey);
+    FRIEND_TEST(PrefsTest, GetFileNameForKeyBadCharacter);
+    FRIEND_TEST(PrefsTest, GetFileNameForKeyEmpty);
+
+    // Sets |filename| to the full path to the file containing the data
+    // associated with |key|. Returns true on success, false otherwise.
+    bool GetFileNameForKey(const std::string& key,
+                           base::FilePath* filename) const;
+
+    // Preference store directory.
+    base::FilePath prefs_dir_;
+  };
+
+  // The concrete file storage implementation.
+  FileStorage file_storage_;
 
   DISALLOW_COPY_AND_ASSIGN(Prefs);
 };
 
+// Implements a preference store in memory. The stored values are lost when the
+// object is destroyed.
+
+class MemoryPrefs : public PrefsBase {
+ public:
+  MemoryPrefs() : PrefsBase(&mem_storage_) {}
+
+ private:
+  class MemoryStorage : public PrefsBase::StorageInterface {
+   public:
+    MemoryStorage() = default;
+
+    // PrefsBase::StorageInterface overrides.
+    bool GetKey(const std::string& key, std::string* value) const override;
+    bool SetKey(const std::string& key, const std::string& value) override;
+    bool KeyExists(const std::string& key) const override;
+    bool DeleteKey(const std::string& key) override;
+
+   private:
+    // The std::map holding the values in memory.
+    std::map<std::string, std::string> values_;
+  };
+
+  // The concrete memory storage implementation.
+  MemoryStorage mem_storage_;
+
+  DISALLOW_COPY_AND_ASSIGN(MemoryPrefs);
+};
 }  // namespace chromeos_update_engine
 
 #endif  // UPDATE_ENGINE_COMMON_PREFS_H_
diff --git a/common/prefs_unittest.cc b/common/prefs_unittest.cc
index d94623a..0822599 100644
--- a/common/prefs_unittest.cc
+++ b/common/prefs_unittest.cc
@@ -18,6 +18,7 @@
 
 #include <inttypes.h>
 
+#include <limits>
 #include <string>
 
 #include <base/files/file_util.h>
@@ -62,18 +63,18 @@
   const char kAllvalidCharsKey[] =
       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-";
   base::FilePath path;
-  EXPECT_TRUE(prefs_.GetFileNameForKey(kAllvalidCharsKey, &path));
+  EXPECT_TRUE(prefs_.file_storage_.GetFileNameForKey(kAllvalidCharsKey, &path));
   EXPECT_EQ(prefs_dir_.Append(kAllvalidCharsKey).value(), path.value());
 }
 
 TEST_F(PrefsTest, GetFileNameForKeyBadCharacter) {
   base::FilePath path;
-  EXPECT_FALSE(prefs_.GetFileNameForKey("ABC abc", &path));
+  EXPECT_FALSE(prefs_.file_storage_.GetFileNameForKey("ABC abc", &path));
 }
 
 TEST_F(PrefsTest, GetFileNameForKeyEmpty) {
   base::FilePath path;
-  EXPECT_FALSE(prefs_.GetFileNameForKey("", &path));
+  EXPECT_FALSE(prefs_.file_storage_.GetFileNameForKey("", &path));
 }
 
 TEST_F(PrefsTest, GetString) {
@@ -338,4 +339,24 @@
   prefs_.RemoveObserver(kInvalidKey, &mock_obserser);
 }
 
+class MemoryPrefsTest : public ::testing::Test {
+ protected:
+  MemoryPrefs prefs_;
+};
+
+TEST_F(MemoryPrefsTest, BasicTest) {
+  EXPECT_FALSE(prefs_.Exists(kKey));
+  int64_t value = 0;
+  EXPECT_FALSE(prefs_.GetInt64(kKey, &value));
+
+  EXPECT_TRUE(prefs_.SetInt64(kKey, 1234));
+  EXPECT_TRUE(prefs_.Exists(kKey));
+  EXPECT_TRUE(prefs_.GetInt64(kKey, &value));
+  EXPECT_EQ(1234, value);
+
+  EXPECT_TRUE(prefs_.Delete(kKey));
+  EXPECT_FALSE(prefs_.Exists(kKey));
+  EXPECT_FALSE(prefs_.Delete(kKey));
+}
+
 }  // namespace chromeos_update_engine
diff --git a/common/test_utils.h b/common/test_utils.h
index 60ec90e..ed64c80 100644
--- a/common/test_utils.h
+++ b/common/test_utils.h
@@ -174,7 +174,7 @@
     unlinker_.reset(new ScopedPathUnlinker(path_));
   }
 
-  const std::string& path() { return path_; }
+  const std::string& path() const { return path_; }
 
  private:
   std::string path_;
diff --git a/common/utils.cc b/common/utils.cc
index 5d15da0..1338268 100644
--- a/common/utils.cc
+++ b/common/utils.cc
@@ -87,6 +87,11 @@
 // The path to the kernel's boot_id.
 const char kBootIdPath[] = "/proc/sys/kernel/random/boot_id";
 
+// A pointer to a null-terminated string containing the root directory where all
+// the temporary files should be created. If null, the system default is used
+// instead.
+const char* root_temp_dir = nullptr;
+
 // Return true if |disk_name| is an MTD or a UBI device. Note that this test is
 // simply based on the name of the device.
 bool IsMtdDeviceName(const string& disk_name) {
@@ -143,13 +148,17 @@
   }
 
   base::FilePath temp_dir;
+  if (root_temp_dir) {
+    temp_dir = base::FilePath(root_temp_dir);
+  } else {
 #ifdef __ANDROID__
-  temp_dir = base::FilePath(constants::kNonVolatileDirectory).Append("tmp");
+    temp_dir = base::FilePath(constants::kNonVolatileDirectory).Append("tmp");
+#else
+    TEST_AND_RETURN_FALSE(base::GetTempDir(&temp_dir));
+#endif  // __ANDROID__
+  }
   if (!base::PathExists(temp_dir))
     TEST_AND_RETURN_FALSE(base::CreateDirectory(temp_dir));
-#else
-  TEST_AND_RETURN_FALSE(base::GetTempDir(&temp_dir));
-#endif  // __ANDROID__
   *template_path = temp_dir.Append(path);
   return true;
 }
@@ -158,6 +167,10 @@
 
 namespace utils {
 
+void SetRootTempDir(const char* new_root_temp_dir) {
+  root_temp_dir = new_root_temp_dir;
+}
+
 string ParseECVersion(string input_line) {
   base::TrimWhitespaceASCII(input_line, base::TRIM_ALL, &input_line);
 
@@ -692,14 +705,24 @@
 }
 
 bool UnmountFilesystem(const string& mountpoint) {
-  for (int num_retries = 0; ; ++num_retries) {
+  int num_retries = 1;
+  for (;; ++num_retries) {
     if (umount(mountpoint.c_str()) == 0)
+      return true;
+    if (errno != EBUSY || num_retries >= kUnmountMaxNumOfRetries)
       break;
-
-    TEST_AND_RETURN_FALSE_ERRNO(errno == EBUSY &&
-                                num_retries < kUnmountMaxNumOfRetries);
     usleep(kUnmountRetryIntervalInMicroseconds);
   }
+  if (errno == EINVAL) {
+    LOG(INFO) << "Not a mountpoint: " << mountpoint;
+    return false;
+  }
+  PLOG(WARNING) << "Error unmounting " << mountpoint << " after " << num_retries
+                << " attempts. Lazy unmounting instead, error was";
+  if (umount2(mountpoint.c_str(), MNT_DETACH) != 0) {
+    PLOG(ERROR) << "Lazy unmount failed";
+    return false;
+  }
   return true;
 }
 
@@ -1055,35 +1078,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 88c33b7..24bf702 100644
--- a/common/utils.h
+++ b/common/utils.h
@@ -131,6 +131,13 @@
 // only returns true if "/dev/ubi%d_0" becomes available in |timeout| seconds.
 bool TryAttachingUbiVolume(int volume_num, int timeout);
 
+// Setup the directory |new_root_temp_dir| to be used as the root directory for
+// temporary files instead of the system's default. If the directory doesn't
+// exists, it will be created when first used.
+// NOTE: The memory pointed by |new_root_temp_dir| must be available until this
+// function is called again with a different value.
+void SetRootTempDir(const char* new_root_temp_dir);
+
 // If |base_filename_template| is neither absolute (starts with "/") nor
 // explicitly relative to the current working directory (starts with "./" or
 // "../"), then it is prepended the system's temporary directory. On success,
@@ -315,16 +322,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/common/utils_unittest.cc b/common/utils_unittest.cc
index f840a75..b0beff9 100644
--- a/common/utils_unittest.cc
+++ b/common/utils_unittest.cc
@@ -17,7 +17,9 @@
 #include "update_engine/common/utils.h"
 
 #include <errno.h>
+#include <fcntl.h>
 #include <stdint.h>
+#include <sys/mount.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 
@@ -545,4 +547,37 @@
   EXPECT_TRUE(BoolMacroTestHelper());
 }
 
+TEST(UtilsTest, UnmountFilesystemFailureTest) {
+  EXPECT_FALSE(utils::UnmountFilesystem("/path/to/non-existing-dir"));
+}
+
+TEST(UtilsTest, UnmountFilesystemBusyFailureTest) {
+  string tmp_image;
+  EXPECT_TRUE(utils::MakeTempFile("img.XXXXXX", &tmp_image, nullptr));
+  ScopedPathUnlinker tmp_image_unlinker(tmp_image);
+
+  EXPECT_TRUE(base::CopyFile(
+      test_utils::GetBuildArtifactsPath().Append("gen/disk_ext2_4k.img"),
+      base::FilePath(tmp_image)));
+
+  base::ScopedTempDir mnt_dir;
+  EXPECT_TRUE(mnt_dir.CreateUniqueTempDir());
+
+  string loop_dev;
+  test_utils::ScopedLoopbackDeviceBinder loop_binder(
+      tmp_image, true, &loop_dev);
+
+  // This is the actual test part. While we hold a file descriptor open for the
+  // mounted filesystem, umount should still succeed.
+  EXPECT_TRUE(utils::MountFilesystem(
+      loop_dev, mnt_dir.path().value(), MS_RDONLY, "ext4", ""));
+  string target_file = mnt_dir.path().Append("empty-file").value();
+  int fd = HANDLE_EINTR(open(target_file.c_str(), O_RDONLY));
+  EXPECT_GE(fd, 0);
+  EXPECT_TRUE(utils::UnmountFilesystem(mnt_dir.path().value()));
+  IGNORE_EINTR(close(fd));
+  // The filesystem was already unmounted so this call should fail.
+  EXPECT_FALSE(utils::UnmountFilesystem(mnt_dir.path().value()));
+}
+
 }  // namespace chromeos_update_engine
diff --git a/daemon_state_android.cc b/daemon_state_android.cc
index cf1e892..0960b1a 100644
--- a/daemon_state_android.cc
+++ b/daemon_state_android.cc
@@ -58,6 +58,11 @@
     return false;
   }
 
+  // The CertificateChecker singleton is used by the update attempter.
+  certificate_checker_.reset(
+      new CertificateChecker(prefs_.get(), &openssl_wrapper_));
+  certificate_checker_->Init();
+
   // Initialize the UpdateAttempter before the UpdateManager.
   update_attempter_.reset(new UpdateAttempterAndroid(
       this, prefs_.get(), boot_control_.get(), hardware_.get()));
diff --git a/daemon_state_android.h b/daemon_state_android.h
index 5db3ef0..928a14e 100644
--- a/daemon_state_android.h
+++ b/daemon_state_android.h
@@ -20,8 +20,8 @@
 #include <memory>
 #include <set>
 
+#include "update_engine/certificate_checker.h"
 #include "update_engine/common/boot_control_interface.h"
-#include "update_engine/common/certificate_checker.h"
 #include "update_engine/common/hardware_interface.h"
 #include "update_engine/common/prefs_interface.h"
 #include "update_engine/daemon_state_interface.h"
@@ -43,7 +43,7 @@
   void AddObserver(ServiceObserverInterface* observer) override;
   void RemoveObserver(ServiceObserverInterface* observer) override;
 
-  const std::set<ServiceObserverInterface*>& service_observers() {
+  const std::set<ServiceObserverInterface*>& service_observers() override {
     return service_observers_;
   }
 
@@ -64,6 +64,11 @@
 
   // The main class handling the updates.
   std::unique_ptr<UpdateAttempterAndroid> update_attempter_;
+
+  // OpenSSLWrapper and CertificateChecker used for checking changes in SSL
+  // certificates.
+  OpenSSLWrapper openssl_wrapper_;
+  std::unique_ptr<CertificateChecker> certificate_checker_;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/daemon_state_interface.h b/daemon_state_interface.h
index a0944aa..2356816 100644
--- a/daemon_state_interface.h
+++ b/daemon_state_interface.h
@@ -20,6 +20,7 @@
 #include "update_engine/service_observer_interface.h"
 
 #include <memory>
+#include <set>
 
 namespace chromeos_update_engine {
 
@@ -36,6 +37,9 @@
   virtual void AddObserver(ServiceObserverInterface* observer) = 0;
   virtual void RemoveObserver(ServiceObserverInterface* observer) = 0;
 
+  // Return the set of current observers.
+  virtual const std::set<ServiceObserverInterface*>& service_observers() = 0;
+
  protected:
   DaemonStateInterface() = default;
 };
diff --git a/hardware_android.cc b/hardware_android.cc
index 277a6c6..5b13eec 100644
--- a/hardware_android.cc
+++ b/hardware_android.cc
@@ -16,12 +16,22 @@
 
 #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;
 
@@ -29,8 +39,48 @@
 
 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";
+
 const char kPropBuildDateUTC[] = "ro.build.date.utc";
 
+// 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 {
@@ -101,6 +151,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 c239ee3..6561377 100644
--- a/hardware_android.h
+++ b/hardware_android.h
@@ -41,6 +41,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;
   int64_t GetBuildTimestamp() const override;
diff --git a/hardware_chromeos.cc b/hardware_chromeos.cc
index 4d467ef..88b2783 100644
--- a/hardware_chromeos.cc
+++ b/hardware_chromeos.cc
@@ -50,6 +50,12 @@
 // a powerwash is performed.
 const char kPowerwashCountMarker[] = "powerwash_count";
 
+// UpdateManager config path.
+const char* kConfigFilePath = "/etc/update_manager.conf";
+
+// UpdateManager config options:
+const char* kConfigOptsIsOOBEEnabled = "is_oobe_enabled";
+
 }  // namespace
 
 namespace chromeos_update_engine {
@@ -140,6 +146,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 c5b306b..e3f086f 100644
--- a/hardware_chromeos.h
+++ b/hardware_chromeos.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;
   int64_t GetBuildTimestamp() const override;
diff --git a/common/libcurl_http_fetcher.cc b/libcurl_http_fetcher.cc
similarity index 99%
rename from common/libcurl_http_fetcher.cc
rename to libcurl_http_fetcher.cc
index b2dffc2..04d5c06 100644
--- a/common/libcurl_http_fetcher.cc
+++ b/libcurl_http_fetcher.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/common/libcurl_http_fetcher.h"
+#include "update_engine/libcurl_http_fetcher.h"
 
 #include <algorithm>
 #include <string>
@@ -26,7 +26,7 @@
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
 
-#include "update_engine/common/certificate_checker.h"
+#include "update_engine/certificate_checker.h"
 #include "update_engine/common/hardware_interface.h"
 #include "update_engine/common/platform_constants.h"
 
diff --git a/common/libcurl_http_fetcher.h b/libcurl_http_fetcher.h
similarity index 97%
rename from common/libcurl_http_fetcher.h
rename to libcurl_http_fetcher.h
index d126171..199c495 100644
--- a/common/libcurl_http_fetcher.h
+++ b/libcurl_http_fetcher.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_COMMON_LIBCURL_HTTP_FETCHER_H_
-#define UPDATE_ENGINE_COMMON_LIBCURL_HTTP_FETCHER_H_
+#ifndef UPDATE_ENGINE_LIBCURL_HTTP_FETCHER_H_
+#define UPDATE_ENGINE_LIBCURL_HTTP_FETCHER_H_
 
 #include <map>
 #include <memory>
@@ -28,7 +28,7 @@
 #include <base/macros.h>
 #include <brillo/message_loops/message_loop.h>
 
-#include "update_engine/common/certificate_checker.h"
+#include "update_engine/certificate_checker.h"
 #include "update_engine/common/hardware_interface.h"
 #include "update_engine/common/http_fetcher.h"
 
@@ -266,4 +266,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_COMMON_LIBCURL_HTTP_FETCHER_H_
+#endif  // UPDATE_ENGINE_LIBCURL_HTTP_FETCHER_H_
diff --git a/metrics.h b/metrics.h
index bb9fa5e..7c369ee 100644
--- a/metrics.h
+++ b/metrics.h
@@ -19,7 +19,7 @@
 
 #include <base/time/time.h>
 
-#include "update_engine/common/certificate_checker.h"
+#include "update_engine/certificate_checker.h"
 #include "update_engine/common/constants.h"
 #include "update_engine/common/error_code.h"
 
diff --git a/common/mock_certificate_checker.h b/mock_certificate_checker.h
similarity index 81%
rename from common/mock_certificate_checker.h
rename to mock_certificate_checker.h
index 1f55ca1..c86f502 100644
--- a/common/mock_certificate_checker.h
+++ b/mock_certificate_checker.h
@@ -14,13 +14,13 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_COMMON_MOCK_CERTIFICATE_CHECKER_H_
-#define UPDATE_ENGINE_COMMON_MOCK_CERTIFICATE_CHECKER_H_
+#ifndef UPDATE_ENGINE_MOCK_CERTIFICATE_CHECKER_H_
+#define UPDATE_ENGINE_MOCK_CERTIFICATE_CHECKER_H_
 
 #include <gmock/gmock.h>
 #include <openssl/ssl.h>
 
-#include "update_engine/common/certificate_checker.h"
+#include "update_engine/certificate_checker.h"
 
 namespace chromeos_update_engine {
 
@@ -35,4 +35,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_COMMON_MOCK_CERTIFICATE_CHECKER_H_
+#endif  // UPDATE_ENGINE_MOCK_CERTIFICATE_CHECKER_H_
diff --git a/network_selector.h b/network_selector.h
new file mode 100644
index 0000000..22aed8e
--- /dev/null
+++ b/network_selector.h
@@ -0,0 +1,33 @@
+//
+// 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_NETWORK_SELECTOR_H_
+#define UPDATE_ENGINE_NETWORK_SELECTOR_H_
+
+#include <memory>
+
+#include "update_engine/network_selector_interface.h"
+
+namespace chromeos_update_engine {
+namespace network {
+
+// Creates the NetworkSelectorInterface instance for the given platform.
+std::unique_ptr<NetworkSelectorInterface> CreateNetworkSelector();
+
+}  // namespace network
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_NETWORK_SELECTOR_H_
diff --git a/network_selector_android.cc b/network_selector_android.cc
new file mode 100644
index 0000000..6879b69
--- /dev/null
+++ b/network_selector_android.cc
@@ -0,0 +1,45 @@
+//
+// 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/network_selector_android.h"
+
+#include <android/multinetwork.h>
+#include <base/logging.h>
+#include <brillo/make_unique_ptr.h>
+
+namespace chromeos_update_engine {
+
+namespace network {
+
+// Factory defined in network_selector.h.
+std::unique_ptr<NetworkSelectorInterface> CreateNetworkSelector() {
+  return brillo::make_unique_ptr(new NetworkSelectorAndroid());
+}
+
+}  // namespace network
+
+// Defined in network_selector_interface.h.
+const NetworkId kDefaultNetworkId = NETWORK_UNSPECIFIED;
+
+bool NetworkSelectorAndroid::SetProcessNetwork(NetworkId network_id) {
+  if (android_setprocnetwork(network_id) < 0) {
+    PLOG(ERROR) << "Binding the network to " << network_id;
+    return false;
+  }
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/network_selector_android.h b/network_selector_android.h
new file mode 100644
index 0000000..135536c
--- /dev/null
+++ b/network_selector_android.h
@@ -0,0 +1,40 @@
+//
+// 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_NETWORK_SELECTOR_ANDROID_H_
+#define UPDATE_ENGINE_NETWORK_SELECTOR_ANDROID_H_
+
+#include <base/macros.h>
+
+#include "update_engine/network_selector_interface.h"
+
+namespace chromeos_update_engine {
+
+class NetworkSelectorAndroid final : public NetworkSelectorInterface {
+ public:
+  NetworkSelectorAndroid() = default;
+  ~NetworkSelectorAndroid() override = default;
+
+  // NetworkSelectorInterface overrides.
+  bool SetProcessNetwork(NetworkId network_id) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NetworkSelectorAndroid);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_NETWORK_SELECTOR_ANDROID_H_
diff --git a/network_selector_interface.h b/network_selector_interface.h
new file mode 100644
index 0000000..6c17b2c
--- /dev/null
+++ b/network_selector_interface.h
@@ -0,0 +1,49 @@
+//
+// 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_NETWORK_SELECTOR_INTERFACE_H_
+#define UPDATE_ENGINE_NETWORK_SELECTOR_INTERFACE_H_
+
+#include <cstdint>
+
+namespace chromeos_update_engine {
+
+typedef uint64_t NetworkId;
+
+// A constant value used to indicate the default network id. Defined in the
+// network_selector_*.cc file.
+extern const NetworkId kDefaultNetworkId;
+
+// A class that handles the network used for the connections performed from this
+// process in a platform-specific way.
+
+class NetworkSelectorInterface {
+ public:
+
+  virtual ~NetworkSelectorInterface() = default;
+
+  // Set the current process network. All sockets created in the future will be
+  // bound to this particular network. Call this with the special value
+  // kNetworkId to use the default network.
+  virtual bool SetProcessNetwork(NetworkId network_id) = 0;
+
+ protected:
+  NetworkSelectorInterface() = default;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_NETWORK_SELECTOR_INTERFACE_H_
diff --git a/network_selector_stub.cc b/network_selector_stub.cc
new file mode 100644
index 0000000..218d454
--- /dev/null
+++ b/network_selector_stub.cc
@@ -0,0 +1,44 @@
+//
+// 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/network_selector_stub.h"
+
+#include <base/logging.h>
+#include <brillo/make_unique_ptr.h>
+
+namespace chromeos_update_engine {
+
+namespace network {
+
+// Factory defined in network_selector.h.
+std::unique_ptr<NetworkSelectorInterface> CreateNetworkSelector() {
+  return brillo::make_unique_ptr(new NetworkSelectorStub());
+}
+
+}  // namespace network
+
+// Defined in network_selector_interface.h.
+const NetworkId kDefaultNetworkId = 0;
+
+bool NetworkSelectorStub::SetProcessNetwork(NetworkId network_id) {
+  if (network_id != kDefaultNetworkId) {
+    LOG(ERROR) << "SetProcessNetwork not implemented.";
+    return false;
+  }
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/network_selector_stub.h b/network_selector_stub.h
new file mode 100644
index 0000000..b3f7b48
--- /dev/null
+++ b/network_selector_stub.h
@@ -0,0 +1,40 @@
+//
+// 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_NETWORK_SELECTOR_STUB_H_
+#define UPDATE_ENGINE_NETWORK_SELECTOR_STUB_H_
+
+#include <base/macros.h>
+
+#include "update_engine/network_selector_interface.h"
+
+namespace chromeos_update_engine {
+
+class NetworkSelectorStub final : public NetworkSelectorInterface {
+ public:
+  NetworkSelectorStub() = default;
+  ~NetworkSelectorStub() override = default;
+
+  // NetworkSelectorInterface overrides.
+  bool SetProcessNetwork(NetworkId network_id) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NetworkSelectorStub);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_NETWORK_SELECTOR_STUB_H_
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index a0ac43b..b338d34 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -28,6 +28,7 @@
 
 #include <base/files/file_util.h>
 #include <base/format_macros.h>
+#include <base/strings/string_number_conversions.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
 #include <brillo/data_encoding.h>
@@ -129,6 +130,35 @@
   *err = 0;
   return fd;
 }
+
+// Discard the tail of the block device referenced by |fd|, from the offset
+// |data_size| until the end of the block device. Returns whether the data was
+// discarded.
+bool DiscardPartitionTail(FileDescriptorPtr fd, uint64_t data_size) {
+  uint64_t part_size = fd->BlockDevSize();
+  if (!part_size || part_size <= data_size)
+    return false;
+
+  const vector<int> requests = {
+      BLKSECDISCARD,
+      BLKDISCARD,
+#ifdef BLKZEROOUT
+      BLKZEROOUT,
+#endif
+  };
+  for (int request : requests) {
+    int error = 0;
+    if (fd->BlkIoctl(request, data_size, part_size - data_size, &error) &&
+        error == 0) {
+      return true;
+    }
+    LOG(WARNING) << "Error discarding the last "
+                 << (part_size - data_size) / 1024 << " KiB using ioctl("
+                 << request << ")";
+  }
+  return false;
+}
+
 }  // namespace
 
 
@@ -248,9 +278,15 @@
   if (op_result)
     return true;
 
+  size_t partition_first_op_num =
+      current_partition_ ? acc_num_operations_[current_partition_ - 1] : 0;
   LOG(ERROR) << "Failed to perform " << op_type_name << " operation "
-             << next_operation_num_;
-  *error = ErrorCode::kDownloadOperationExecutionError;
+             << next_operation_num_ << ", which is the operation "
+             << next_operation_num_ - partition_first_op_num
+             << " in partition \""
+             << partitions_[current_partition_].partition_name() << "\"";
+  if (*error == ErrorCode::kSuccess)
+    *error = ErrorCode::kDownloadOperationExecutionError;
   return false;
 }
 
@@ -319,6 +355,15 @@
                << ", file " << target_path_;
     return false;
   }
+
+  LOG(INFO) << "Applying " << partition.operations().size()
+            << " operations to partition \"" << partition.partition_name()
+            << "\"";
+
+  // Discard the end of the partition, but ignore failures.
+  DiscardPartitionTail(
+      target_fd_, install_plan_->partitions[current_partition_].target_size);
+
   return true;
 }
 
@@ -676,10 +721,10 @@
         op_result = PerformBsdiffOperation(op);
         break;
       case InstallOperation::SOURCE_COPY:
-        op_result = PerformSourceCopyOperation(op);
+        op_result = PerformSourceCopyOperation(op, error);
         break;
       case InstallOperation::SOURCE_BSDIFF:
-        op_result = PerformSourceBsdiffOperation(op);
+        op_result = PerformSourceBsdiffOperation(op, error);
         break;
       default:
        op_result = false;
@@ -799,6 +844,7 @@
           (partition.has_postinstall_path() ? partition.postinstall_path()
                                             : kPostinstallDefaultScript);
       install_part.filesystem_type = partition.filesystem_type();
+      install_part.postinstall_optional = partition.postinstall_optional();
     }
 
     if (partition.has_old_partition_info()) {
@@ -1004,16 +1050,34 @@
 }
 
 // Compare |calculated_hash| with source hash in |operation|, return false and
-// dump hash if don't match.
+// dump hash and set |error| if don't match.
 bool ValidateSourceHash(const brillo::Blob& calculated_hash,
-                        const InstallOperation& operation) {
+                        const InstallOperation& operation,
+                        ErrorCode* error) {
   brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
                                     operation.src_sha256_hash().end());
   if (calculated_hash != expected_source_hash) {
-    LOG(ERROR) << "Hash verification failed. Expected hash = ";
-    utils::HexDumpVector(expected_source_hash);
-    LOG(ERROR) << "Calculated hash = ";
-    utils::HexDumpVector(calculated_hash);
+    LOG(ERROR) << "The hash of the source data on disk for this operation "
+               << "doesn't match the expected value. This could mean that the "
+               << "delta update payload was targeted for another version, or "
+               << "that the source partition was modified after it was "
+               << "installed, for example, by mounting a filesystem.";
+    LOG(ERROR) << "Expected:   sha256|hex = "
+               << base::HexEncode(expected_source_hash.data(),
+                                  expected_source_hash.size());
+    LOG(ERROR) << "Calculated: sha256|hex = "
+               << base::HexEncode(calculated_hash.data(),
+                                  calculated_hash.size());
+
+    vector<string> source_extents;
+    for (const Extent& ext : operation.src_extents()) {
+      source_extents.push_back(base::StringPrintf(
+          "%" PRIu64 ":%" PRIu64, ext.start_block(), ext.num_blocks()));
+    }
+    LOG(ERROR) << "Operation source (offset:size) in blocks: "
+               << base::JoinString(source_extents, ",");
+
+    *error = ErrorCode::kDownloadStateInitializationError;
     return false;
   }
   return true;
@@ -1022,7 +1086,7 @@
 }  // namespace
 
 bool DeltaPerformer::PerformSourceCopyOperation(
-    const InstallOperation& operation) {
+    const InstallOperation& operation, ErrorCode* error) {
   if (operation.has_src_length())
     TEST_AND_RETURN_FALSE(operation.src_length() % block_size_ == 0);
   if (operation.has_dst_length())
@@ -1075,7 +1139,7 @@
   if (operation.has_src_sha256_hash()) {
     TEST_AND_RETURN_FALSE(source_hasher.Finalize());
     TEST_AND_RETURN_FALSE(
-        ValidateSourceHash(source_hasher.raw_hash(), operation));
+        ValidateSourceHash(source_hasher.raw_hash(), operation, error));
   }
 
   DCHECK_EQ(bytes_read, static_cast<ssize_t>(blocks_to_read * block_size_));
@@ -1163,7 +1227,7 @@
 }
 
 bool DeltaPerformer::PerformSourceBsdiffOperation(
-    const InstallOperation& operation) {
+    const InstallOperation& operation, ErrorCode* error) {
   // Since we delete data off the beginning of the buffer as we use it,
   // the data we need should be exactly at the beginning of the buffer.
   TEST_AND_RETURN_FALSE(buffer_offset_ == operation.data_offset());
@@ -1193,7 +1257,7 @@
     }
     TEST_AND_RETURN_FALSE(source_hasher.Finalize());
     TEST_AND_RETURN_FALSE(
-        ValidateSourceHash(source_hasher.raw_hash(), operation));
+        ValidateSourceHash(source_hasher.raw_hash(), operation, error));
   }
 
   string input_positions;
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index 0a60020..fdfcb5b 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -250,12 +250,16 @@
   bool PerformInstallOperation(const InstallOperation& operation);
 
   // These perform a specific type of operation and return true on success.
+  // |error| will be set if source hash mismatch, otherwise |error| might not be
+  // set even if it fails.
   bool PerformReplaceOperation(const InstallOperation& operation);
   bool PerformZeroOrDiscardOperation(const InstallOperation& operation);
   bool PerformMoveOperation(const InstallOperation& operation);
   bool PerformBsdiffOperation(const InstallOperation& operation);
-  bool PerformSourceCopyOperation(const InstallOperation& operation);
-  bool PerformSourceBsdiffOperation(const InstallOperation& operation);
+  bool PerformSourceCopyOperation(const InstallOperation& operation,
+                                  ErrorCode* error);
+  bool PerformSourceBsdiffOperation(const InstallOperation& operation,
+                                    ErrorCode* error);
 
   // Extracts the payload signature message from the blob on the |operation| if
   // the offset matches the one specified by the manifest. Returns whether the
diff --git a/payload_consumer/download_action.h b/payload_consumer/download_action.h
index fc32068..285930a 100644
--- a/payload_consumer/download_action.h
+++ b/payload_consumer/download_action.h
@@ -24,8 +24,6 @@
 #include <memory>
 #include <string>
 
-#include <curl/curl.h>
-
 #include "update_engine/common/action.h"
 #include "update_engine/common/boot_control_interface.h"
 #include "update_engine/common/http_fetcher.h"
diff --git a/payload_consumer/file_descriptor.cc b/payload_consumer/file_descriptor.cc
index 309c60d..8a23dea 100644
--- a/payload_consumer/file_descriptor.cc
+++ b/payload_consumer/file_descriptor.cc
@@ -24,6 +24,8 @@
 
 #include <base/posix/eintr_wrapper.h>
 
+#include "update_engine/common/utils.h"
+
 namespace chromeos_update_engine {
 
 bool EintrSafeFileDescriptor::Open(const char* path, int flags, mode_t mode) {
@@ -65,6 +67,20 @@
   return lseek64(fd_, offset, whence);
 }
 
+uint64_t EintrSafeFileDescriptor::BlockDevSize() {
+  if (fd_ < 0)
+    return 0;
+  struct stat stbuf;
+  if (fstat(fd_, &stbuf) < 0) {
+    PLOG(ERROR) << "Error stat-ing fd " << fd_;
+    return 0;
+  }
+  if (!S_ISBLK(stbuf.st_mode))
+    return 0;
+  off_t block_size = utils::BlockDevSize(fd_);
+  return block_size < 0 ? 0 : block_size;
+}
+
 bool EintrSafeFileDescriptor::BlkIoctl(int request,
                                        uint64_t start,
                                        uint64_t length,
diff --git a/payload_consumer/file_descriptor.h b/payload_consumer/file_descriptor.h
index 3c15415..7bb2974 100644
--- a/payload_consumer/file_descriptor.h
+++ b/payload_consumer/file_descriptor.h
@@ -78,6 +78,10 @@
   // may set errno accordingly.
   virtual off64_t Seek(off64_t offset, int whence) = 0;
 
+  // Return the size of the block device in bytes, or 0 if the device is not a
+  // block device or an error occurred.
+  virtual uint64_t BlockDevSize() = 0;
+
   // Runs a ioctl() on the file descriptor if supported. Returns whether
   // the operation is supported. The |request| can be one of BLKDISCARD,
   // BLKZEROOUT and BLKSECDISCARD to discard, write zeros or securely discard
@@ -119,6 +123,7 @@
   ssize_t Read(void* buf, size_t count) override;
   ssize_t Write(const void* buf, size_t count) override;
   off64_t Seek(off64_t offset, int whence) override;
+  uint64_t BlockDevSize() override;
   bool BlkIoctl(int request,
                 uint64_t start,
                 uint64_t length,
diff --git a/payload_consumer/install_plan.cc b/payload_consumer/install_plan.cc
index 51e85b3..b04da74 100644
--- a/payload_consumer/install_plan.cc
+++ b/payload_consumer/install_plan.cc
@@ -115,7 +115,8 @@
           target_hash == that.target_hash &&
           run_postinstall == that.run_postinstall &&
           postinstall_path == that.postinstall_path &&
-          filesystem_type == that.filesystem_type);
+          filesystem_type == that.filesystem_type &&
+          postinstall_optional == that.postinstall_optional);
 }
 
 }  // namespace chromeos_update_engine
diff --git a/payload_consumer/install_plan.h b/payload_consumer/install_plan.h
index 454dd78..f15775e 100644
--- a/payload_consumer/install_plan.h
+++ b/payload_consumer/install_plan.h
@@ -95,6 +95,7 @@
     bool run_postinstall{false};
     std::string postinstall_path;
     std::string filesystem_type;
+    bool postinstall_optional{false};
   };
   std::vector<Partition> partitions;
 
diff --git a/payload_consumer/mtd_file_descriptor.h b/payload_consumer/mtd_file_descriptor.h
index 9ac1ec1..6c945b2 100644
--- a/payload_consumer/mtd_file_descriptor.h
+++ b/payload_consumer/mtd_file_descriptor.h
@@ -40,6 +40,7 @@
   ssize_t Read(void* buf, size_t count) override;
   ssize_t Write(const void* buf, size_t count) override;
   off64_t Seek(off64_t offset, int whence) override;
+  uint64_t BlockDevSize() override { return 0; }
   bool BlkIoctl(int request,
                 uint64_t start,
                 uint64_t length,
@@ -75,6 +76,7 @@
   ssize_t Read(void* buf, size_t count) override;
   ssize_t Write(const void* buf, size_t count) override;
   off64_t Seek(off64_t offset, int whence) override;
+  uint64_t BlockDevSize() override { return 0; }
   bool BlkIoctl(int request,
                 uint64_t start,
                 uint64_t length,
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index 98f4eaf..84f1edf 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -56,8 +56,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);
     }
@@ -301,7 +301,14 @@
       // to get back to FW A.
       error_code = ErrorCode::kPostinstallFirmwareRONotUpdatable;
     }
-    return CompletePostinstall(error_code);
+
+    // If postinstall script for this partition is optional we can ignore the
+    // result.
+    if (install_plan_.partitions[current_partition_].postinstall_optional) {
+      LOG(INFO) << "Ignoring postinstall failure since it is optional";
+    } else {
+      return CompletePostinstall(error_code);
+    }
   }
   accumulated_weight_ += partition_weight_[current_partition_];
   current_partition_++;
@@ -324,9 +331,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 d5b05a6..26fcbd9 100644
--- a/payload_consumer/postinstall_runner_action_unittest.cc
+++ b/payload_consumer/postinstall_runner_action_unittest.cc
@@ -37,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"
 
@@ -87,10 +88,6 @@
     loop_.SetAsCurrent();
     async_signal_handler_.Init();
     subprocess_.Init(&async_signal_handler_);
-    ASSERT_TRUE(utils::MakeTempDirectory(
-        "postinstall_runner_action_unittest-XXXXXX", &working_dir_));
-    // We use a test-specific powerwash marker file, to avoid race conditions.
-    powerwash_marker_file_ = working_dir_ + "/factory_install_reset";
     // These tests use the postinstall files generated by "generate_images.sh"
     // stored in the "disk_ext2_unittest.img" image.
     postinstall_image_ = test_utils::GetBuildArtifactsPath()
@@ -160,14 +157,11 @@
   brillo::AsynchronousSignalHandler async_signal_handler_;
   Subprocess subprocess_;
 
-  // A temporary working directory used for the test.
-  string 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.
@@ -195,8 +189,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);
@@ -222,8 +215,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_);
 
@@ -252,7 +244,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) {
@@ -267,11 +259,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
@@ -282,7 +271,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/payload_generator/delta_diff_generator.cc b/payload_generator/delta_diff_generator.cc
index 7ca42fc..3295df0 100644
--- a/payload_generator/delta_diff_generator.cc
+++ b/payload_generator/delta_diff_generator.cc
@@ -97,15 +97,6 @@
       if (!old_part.path.empty() &&
           !utils::IsSquashfsFilesystem(new_part.path)) {
         // Delta update.
-        if (utils::IsExtFilesystem(new_part.path)) {
-          LOG_IF(WARNING, old_part.size != new_part.size)
-              << "Old and new filesystems have different size.";
-          // TODO(deymo): Our tools only support growing the filesystem size
-          // during an update. Remove this check when that's fixed.
-          // crbug.com/192136
-          LOG_IF(FATAL, old_part.size > new_part.size)
-              << "Shirking the filesystem size is not supported at the moment.";
-        }
         if (config.version.minor == kInPlaceMinorPayloadVersion) {
           LOG(INFO) << "Using generator InplaceGenerator().";
           strategy.reset(new InplaceGenerator());
diff --git a/payload_generator/delta_diff_utils.cc b/payload_generator/delta_diff_utils.cc
index 0782a8c..8fce055 100644
--- a/payload_generator/delta_diff_utils.cc
+++ b/payload_generator/delta_diff_utils.cc
@@ -180,8 +180,8 @@
       aops,
       old_part.path,
       new_part.path,
-      old_part.fs_interface ? old_part.fs_interface->GetBlockCount() : 0,
-      new_part.fs_interface->GetBlockCount(),
+      old_part.size / kBlockSize,
+      new_part.size / kBlockSize,
       soft_chunk_blocks,
       version,
       blob_file,
@@ -320,6 +320,13 @@
   for (uint64_t block = old_num_blocks; block-- > 0; ) {
     if (old_block_ids[block] != 0 && !old_visited_blocks->ContainsBlock(block))
       old_blocks_map[old_block_ids[block]].push_back(block);
+
+    // Mark all zeroed blocks in the old image as "used" since it doesn't make
+    // any sense to spend I/O to read zeros from the source partition and more
+    // importantly, these could sometimes be blocks discarded in the SSD which
+    // would read non-zero values.
+    if (old_block_ids[block] == 0)
+      old_visited_blocks->AddBlock(block);
   }
 
   // The collection of blocks in the new partition with just zeros. This is a
diff --git a/payload_generator/inplace_generator.cc b/payload_generator/inplace_generator.cc
index 30dbafc..be8b487 100644
--- a/payload_generator/inplace_generator.cc
+++ b/payload_generator/inplace_generator.cc
@@ -677,17 +677,13 @@
 
   enum BlockField { READER = 0, WRITER, BLOCK_FIELD_COUNT };
   for (int field = READER; field < BLOCK_FIELD_COUNT; field++) {
-    const int extents_size =
-        (field == READER) ? operation.src_extents_size() :
-        operation.dst_extents_size();
     const char* past_participle = (field == READER) ? "read" : "written";
     const google::protobuf::RepeatedPtrField<Extent>& extents =
         (field == READER) ? operation.src_extents() : operation.dst_extents();
     Vertex::Index Block::*access_type = (field == READER) ?
         &Block::reader : &Block::writer;
 
-    for (int i = 0; i < extents_size; i++) {
-      const Extent& extent = extents.Get(i);
+    for (const Extent& extent : extents) {
       for (uint64_t block = extent.start_block();
            block < (extent.start_block() + extent.num_blocks()); block++) {
         if ((*blocks)[block].*access_type != Vertex::kInvalidIndex) {
@@ -738,6 +734,7 @@
 }
 
 bool InplaceGenerator::ResolveReadAfterWriteDependencies(
+    const PartitionConfig& old_part,
     const PartitionConfig& new_part,
     uint64_t partition_size,
     size_t block_size,
@@ -746,7 +743,7 @@
   // Convert the operations to the graph.
   Graph graph;
   CheckGraph(graph);
-  vector<Block> blocks(new_part.size / block_size);
+  vector<Block> blocks(std::max(old_part.size, new_part.size) / block_size);
   for (const auto& aop : *aops) {
     AddInstallOpToGraph(
         &graph, Vertex::kInvalidIndex, &blocks, aop.op, aop.name);
@@ -815,12 +812,8 @@
                                                        blob_file));
   LOG(INFO) << "Done reading " << new_part.name;
 
-  TEST_AND_RETURN_FALSE(
-    ResolveReadAfterWriteDependencies(new_part,
-                                      partition_size,
-                                      config.block_size,
-                                      blob_file,
-                                      aops));
+  TEST_AND_RETURN_FALSE(ResolveReadAfterWriteDependencies(
+      old_part, new_part, partition_size, config.block_size, blob_file, aops));
   LOG(INFO) << "Done reordering " << new_part.name;
   return true;
 }
diff --git a/payload_generator/inplace_generator.h b/payload_generator/inplace_generator.h
index 4839824..48a1fac 100644
--- a/payload_generator/inplace_generator.h
+++ b/payload_generator/inplace_generator.h
@@ -212,6 +212,7 @@
   // On success, stores the new operations in |aops| in the right order and
   // returns true.
   static bool ResolveReadAfterWriteDependencies(
+      const PartitionConfig& old_part,
       const PartitionConfig& new_part,
       uint64_t partition_size,
       size_t block_size,
diff --git a/payload_generator/inplace_generator_unittest.cc b/payload_generator/inplace_generator_unittest.cc
index cde4bfc..20ac50b 100644
--- a/payload_generator/inplace_generator_unittest.cc
+++ b/payload_generator/inplace_generator_unittest.cc
@@ -33,6 +33,7 @@
 #include "update_engine/common/utils.h"
 #include "update_engine/payload_generator/cycle_breaker.h"
 #include "update_engine/payload_generator/delta_diff_generator.h"
+#include "update_engine/payload_generator/delta_diff_utils.h"
 #include "update_engine/payload_generator/extent_ranges.h"
 #include "update_engine/payload_generator/graph_types.h"
 #include "update_engine/payload_generator/graph_utils.h"
@@ -118,6 +119,16 @@
     blob_file_.reset(new BlobFileWriter(blob_fd_, &blob_file_size_));
   }
 
+  // Dump the list of operations |aops| in case of test failure.
+  void DumpAopsOnFailure(const vector<AnnotatedOperation>& aops) {
+    if (HasNonfatalFailure()) {
+      LOG(INFO) << "Result operation list:";
+      for (const auto& aop : aops) {
+        LOG(INFO) << aop;
+      }
+    }
+  }
+
   // Blob file name, file descriptor and file size used to store operation
   // blobs.
   string blob_path_;
@@ -618,19 +629,21 @@
   // space, forcing it to create a new full operation and the second case with
   // one extra block in the partition that can be used for the move operation.
   for (const auto part_blocks : vector<uint64_t>{num_blocks, num_blocks + 1}) {
-    SCOPED_TRACE(base::StringPrintf("Using partition_blocs=%" PRIu64,
-                                    part_blocks));
+    SCOPED_TRACE(
+        base::StringPrintf("Using partition_blocks=%" PRIu64, part_blocks));
     vector<AnnotatedOperation> result_aops = aops;
     EXPECT_TRUE(InplaceGenerator::ResolveReadAfterWriteDependencies(
-      part, part_blocks * block_size, block_size, blob_file_.get(),
-      &result_aops));
+        part,
+        part,
+        part_blocks * block_size,
+        block_size,
+        blob_file_.get(),
+        &result_aops));
 
     size_t full_ops = 0;
     for (const auto& aop : result_aops) {
-      if (aop.op.type() == InstallOperation::REPLACE ||
-          aop.op.type() == InstallOperation::REPLACE_BZ) {
+      if (diff_utils::IsAReplaceOperation(aop.op.type()))
         full_ops++;
-      }
 
       if (aop.op.type() != InstallOperation::MOVE)
         continue;
@@ -648,13 +661,90 @@
     // operation for it.
     EXPECT_EQ(part_blocks == num_blocks ? 2U : 1U, full_ops);
 
-    if (HasNonfatalFailure()) {
-      LOG(INFO) << "Result operation list:";
-      for (const auto& aop : result_aops) {
-        LOG(INFO) << aop;
+    DumpAopsOnFailure(result_aops);
+  }
+}
+
+// Test that we can shrink a filesystem and break cycles.
+TEST_F(InplaceGeneratorTest, ResolveReadAfterWriteDependenciesShrinkData) {
+  size_t block_size = 4096;
+  size_t old_blocks = 10;
+  size_t new_blocks = 8;
+  vector<AnnotatedOperation> aops;
+
+  // Create a loop using the blocks 1-6 and one other operation writing to the
+  // block 7 from outside the new partition. The loop in the blocks 1-6 uses
+  // two-block operations, so it needs two blocks of scratch space. It can't use
+  // the block 0 as scratch space (see previous test) and it can't use the
+  // blocks 7 or 8 due the last move operation.
+
+  aops.emplace_back();
+  aops.back().name = base::StringPrintf("<bz-block-0>");
+  aops.back().op.set_type(InstallOperation::REPLACE_BZ);
+  StoreExtents({ExtentForRange(0, 1)}, aops.back().op.mutable_dst_extents());
+
+  const size_t num_ops = 3;
+  for (size_t i = 0; i < num_ops; i++) {
+    AnnotatedOperation aop;
+    aop.name = base::StringPrintf("<op-%" PRIuS ">", i);
+    aop.op.set_type(InstallOperation::BSDIFF);
+    StoreExtents({ExtentForRange(1 + 2 * i, 2)}, aop.op.mutable_src_extents());
+    StoreExtents({ExtentForRange(1 + 2 * ((i + 1) % num_ops), 2)},
+                 aop.op.mutable_dst_extents());
+    aops.push_back(aop);
+  }
+
+  {
+    AnnotatedOperation aop;
+    aop.name = "<op-shrink>";
+    aop.op.set_type(InstallOperation::BSDIFF);
+    StoreExtents({ExtentForRange(8, 1)}, aop.op.mutable_src_extents());
+    StoreExtents({ExtentForRange(7, 1)}, aop.op.mutable_dst_extents());
+    aops.push_back(aop);
+  }
+
+  PartitionConfig old_part("part");
+  old_part.path = "/dev/zero";
+  old_part.size = old_blocks * block_size;
+
+  PartitionConfig new_part("part");
+  new_part.path = "/dev/zero";
+  new_part.size = new_blocks * block_size;
+
+  CreateBlobFile();
+
+  EXPECT_TRUE(InplaceGenerator::ResolveReadAfterWriteDependencies(
+      old_part,
+      new_part,
+      (old_blocks + 2) * block_size,  // enough scratch space.
+      block_size,
+      blob_file_.get(),
+      &aops));
+
+  size_t full_ops = 0;
+  for (const auto& aop : aops) {
+    if (diff_utils::IsAReplaceOperation(aop.op.type()))
+      full_ops++;
+  }
+  // There should be only one REPLACE* operation, the one we added for block 0.
+  EXPECT_EQ(1U, full_ops);
+
+  // There should be only one MOVE operation, the one used to break the loop
+  // which should write to scratch space past the block 7 (the last block of the
+  // new partition) which is being written later.
+  size_t move_ops = 0;
+  for (const auto& aop : aops) {
+    if (aop.op.type() == InstallOperation::MOVE) {
+      move_ops++;
+      for (const Extent& extent : aop.op.dst_extents()) {
+        EXPECT_LE(7U, extent.start_block()) << "On dst extents for aop: "
+                                            << aop;
       }
     }
   }
+  EXPECT_EQ(1U, move_ops);
+
+  DumpAopsOnFailure(aops);
 }
 
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/payload_file.cc b/payload_generator/payload_file.cc
index d7046eb..d2ae706 100644
--- a/payload_generator/payload_file.cc
+++ b/payload_generator/payload_file.cc
@@ -139,6 +139,7 @@
           partition->set_postinstall_path(part.postinstall.path);
         if (!part.postinstall.filesystem_type.empty())
           partition->set_filesystem_type(part.postinstall.filesystem_type);
+        partition->set_postinstall_optional(part.postinstall.optional);
       }
       for (const AnnotatedOperation& aop : part.aops) {
         *partition->add_operations() = aop.op;
diff --git a/payload_generator/payload_generation_config.cc b/payload_generator/payload_generation_config.cc
index dc2ced6..8ef30a0 100644
--- a/payload_generator/payload_generation_config.cc
+++ b/payload_generator/payload_generation_config.cc
@@ -27,7 +27,7 @@
 namespace chromeos_update_engine {
 
 bool PostInstallConfig::IsEmpty() const {
-  return run == false && path.empty() && filesystem_type.empty();
+  return !run && path.empty() && filesystem_type.empty() && !optional;
 }
 
 bool PartitionConfig::ValidateExists() const {
@@ -95,6 +95,8 @@
     store.GetString("POSTINSTALL_PATH_" + part.name, &part.postinstall.path);
     store.GetString("FILESYSTEM_TYPE_" + part.name,
                     &part.postinstall.filesystem_type);
+    store.GetBoolean("POSTINSTALL_OPTIONAL_" + part.name,
+                     &part.postinstall.optional);
   }
   if (!found_postinstall) {
     LOG(ERROR) << "No valid postinstall config found.";
diff --git a/payload_generator/payload_generation_config.h b/payload_generator/payload_generation_config.h
index 8bb7737..074738b 100644
--- a/payload_generator/payload_generation_config.h
+++ b/payload_generator/payload_generation_config.h
@@ -46,6 +46,9 @@
   // The filesystem type used to mount the partition in order to run the
   // post-install program.
   std::string filesystem_type;
+
+  // Whether this postinstall script should be ignored if it fails.
+  bool optional = false;
 };
 
 struct PartitionConfig {
diff --git a/payload_generator/payload_generation_config_unittest.cc b/payload_generator/payload_generation_config_unittest.cc
index 122d94a..3545056 100644
--- a/payload_generator/payload_generation_config_unittest.cc
+++ b/payload_generator/payload_generation_config_unittest.cc
@@ -29,12 +29,14 @@
   EXPECT_TRUE(
       store.LoadFromString("RUN_POSTINSTALL_root=true\n"
                            "POSTINSTALL_PATH_root=postinstall\n"
-                           "FILESYSTEM_TYPE_root=ext4"));
+                           "FILESYSTEM_TYPE_root=ext4\n"
+                           "POSTINSTALL_OPTIONAL_root=true"));
   EXPECT_TRUE(image_config.LoadPostInstallConfig(store));
   EXPECT_FALSE(image_config.partitions[0].postinstall.IsEmpty());
   EXPECT_EQ(true, image_config.partitions[0].postinstall.run);
   EXPECT_EQ("postinstall", image_config.partitions[0].postinstall.path);
   EXPECT_EQ("ext4", image_config.partitions[0].postinstall.filesystem_type);
+  EXPECT_TRUE(image_config.partitions[0].postinstall.optional);
 }
 
 TEST_F(PayloadGenerationConfigTest, LoadPostInstallConfigNameMismatchTest) {
diff --git a/real_system_state.h b/real_system_state.h
index 071e3e0..480b4b7 100644
--- a/real_system_state.h
+++ b/real_system_state.h
@@ -20,6 +20,7 @@
 #include "update_engine/system_state.h"
 
 #include <memory>
+#include <set>
 
 #include <debugd/dbus-proxies.h>
 #include <metrics/metrics_library.h>
@@ -27,8 +28,8 @@
 #include <power_manager/dbus-proxies.h>
 #include <session_manager/dbus-proxies.h>
 
+#include "update_engine/certificate_checker.h"
 #include "update_engine/common/boot_control_interface.h"
-#include "update_engine/common/certificate_checker.h"
 #include "update_engine/common/clock.h"
 #include "update_engine/common/hardware_interface.h"
 #include "update_engine/common/prefs.h"
@@ -63,6 +64,10 @@
 
   void AddObserver(ServiceObserverInterface* observer) override;
   void RemoveObserver(ServiceObserverInterface* observer) override;
+  const std::set<ServiceObserverInterface*>& service_observers() override {
+    CHECK(update_attempter_.get());
+    return update_attempter_->service_observers();
+  }
 
   // SystemState overrides.
   inline void set_device_policy(
diff --git a/scripts/brillo_update_payload b/scripts/brillo_update_payload
index f1044c0..1c49138 100755
--- a/scripts/brillo_update_payload
+++ b/scripts/brillo_update_payload
@@ -223,6 +223,18 @@
   echo "${default_value}"
 }
 
+# truncate_file <file_path> <file_size>
+#
+# Truncate the given |file_path| to |file_size| using perl.
+# The truncate binary might not be available.
+truncate_file() {
+  local file_path="$1"
+  local file_size="$2"
+  perl -e "open(FILE, \"+<\", \$ARGV[0]); \
+           truncate(FILE, ${file_size}); \
+           close(FILE);" "${file_path}"
+}
+
 # Create a temporary file in the work_dir with an optional pattern name.
 # Prints the name of the newly created file.
 create_tempfile() {
@@ -412,13 +424,20 @@
       part_file="${temp_raw}"
     fi
 
-    # delta_generator only supports images multiple of 4 KiB, so we pad with
-    # zeros if needed.
+    # delta_generator only supports images multiple of 4 KiB. For target images
+    # we pad the data with zeros if needed, but for source images we truncate
+    # down the data since the last block of the old image could be padded on
+    # disk with unknown data.
     filesize=$(stat -c%s "${part_file}")
     if [[ $(( filesize % 4096 )) -ne 0 ]]; then
-      echo "Rounding up partition ${part}.img to multiple of 4 KiB."
-      : $(( filesize = (filesize + 4095) & -4096 ))
-      truncate --size="${filesize}" "${part_file}"
+      if [[ "${partitions_array}" == "SRC_PARTITIONS" ]]; then
+        echo "Rounding DOWN partition ${part}.img to a multiple of 4 KiB."
+        : $(( filesize = filesize & -4096 ))
+      else
+        echo "Rounding UP partition ${part}.img to a multiple of 4 KiB."
+        : $(( filesize = (filesize + 4095) & -4096 ))
+      fi
+      truncate_file "${part_file}" "${filesize}"
     fi
 
     eval "${partitions_array}[\"${part}\"]=\"${part_file}\""
diff --git a/sideload_main.cc b/sideload_main.cc
new file mode 100644
index 0000000..35ed11b
--- /dev/null
+++ b/sideload_main.cc
@@ -0,0 +1,227 @@
+//
+// 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 <xz.h>
+
+#include <string>
+#include <vector>
+
+#include <base/command_line.h>
+#include <base/logging.h>
+#include <base/strings/string_split.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/asynchronous_signal_handler.h>
+#include <brillo/flag_helper.h>
+#include <brillo/make_unique_ptr.h>
+#include <brillo/message_loops/base_message_loop.h>
+#include <brillo/streams/file_stream.h>
+#include <brillo/streams/stream.h>
+
+#include "update_engine/common/boot_control.h"
+#include "update_engine/common/error_code_utils.h"
+#include "update_engine/common/hardware.h"
+#include "update_engine/common/prefs.h"
+#include "update_engine/common/subprocess.h"
+#include "update_engine/common/terminator.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/update_attempter_android.h"
+
+using std::string;
+using std::vector;
+using update_engine::UpdateStatus;
+
+namespace {
+// The root directory used for temporary files in update_engine_sideload.
+const char kSideloadRootTempDir[] = "/tmp/update_engine_sideload";
+}  // namespace
+
+namespace chromeos_update_engine {
+namespace {
+
+void SetupLogging() {
+  string log_file;
+  logging::LoggingSettings log_settings;
+  log_settings.lock_log = logging::DONT_LOCK_LOG_FILE;
+  log_settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE;
+  log_settings.log_file = nullptr;
+  log_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+
+  logging::InitLogging(log_settings);
+}
+
+class SideloadDaemonState : public DaemonStateInterface,
+                            public ServiceObserverInterface {
+ public:
+  explicit SideloadDaemonState(brillo::StreamPtr status_stream)
+      : status_stream_(std::move(status_stream)) {
+    // Add this class as the only observer.
+    observers_.insert(this);
+  }
+  ~SideloadDaemonState() override = default;
+
+  // DaemonStateInterface overrides.
+  bool StartUpdater() override { return true; }
+  void AddObserver(ServiceObserverInterface* observer) override {}
+  void RemoveObserver(ServiceObserverInterface* observer) override {}
+  const std::set<ServiceObserverInterface*>& service_observers() override {
+    return observers_;
+  }
+
+  // ServiceObserverInterface overrides.
+  void SendStatusUpdate(int64_t last_checked_time,
+                        double progress,
+                        UpdateStatus status,
+                        const string& new_version,
+                        int64_t new_size) override {
+    if (status_ != status && (status == UpdateStatus::DOWNLOADING ||
+                              status == UpdateStatus::FINALIZING)) {
+      // Split the progress bar in two parts for the two stages DOWNLOADING and
+      // FINALIZING.
+      ReportStatus(base::StringPrintf(
+          "ui_print Step %d/2", status == UpdateStatus::DOWNLOADING ? 1 : 2));
+      ReportStatus(base::StringPrintf("progress 0.5 0"));
+    }
+    if (status_ != status || fabs(progress - progress_) > 0.005) {
+      ReportStatus(base::StringPrintf("set_progress %.lf", progress));
+    }
+    progress_ = progress;
+    status_ = status;
+  }
+
+  void SendPayloadApplicationComplete(ErrorCode error_code) override {
+    if (error_code != ErrorCode::kSuccess) {
+      ReportStatus(
+          base::StringPrintf("ui_print Error applying update: %d (%s)",
+                             error_code,
+                             utils::ErrorCodeToString(error_code).c_str()));
+    }
+    error_code_ = error_code;
+    brillo::MessageLoop::current()->BreakLoop();
+  }
+
+  void SendChannelChangeUpdate(const string& tracking_channel) override {}
+
+  // Getters.
+  UpdateStatus status() { return status_; }
+  ErrorCode error_code() { return error_code_; }
+
+ private:
+  // Report a status message in the status_stream_, if any. These messages
+  // should conform to the specification defined in the Android recovery.
+  void ReportStatus(const string& message) {
+    if (!status_stream_)
+      return;
+    string status_line = message + "\n";
+    status_stream_->WriteAllBlocking(
+        status_line.data(), status_line.size(), nullptr);
+  }
+
+  std::set<ServiceObserverInterface*> observers_;
+  brillo::StreamPtr status_stream_;
+
+  // The last status and error code reported.
+  UpdateStatus status_{UpdateStatus::IDLE};
+  ErrorCode error_code_{ErrorCode::kSuccess};
+  double progress_{-1.};
+};
+
+// Apply an update payload directly from the given payload URI.
+bool ApplyUpdatePayload(const string& payload,
+                        int64_t payload_offset,
+                        int64_t payload_size,
+                        const vector<string>& headers,
+                        int64_t status_fd) {
+  base::MessageLoopForIO base_loop;
+  brillo::BaseMessageLoop loop(&base_loop);
+  loop.SetAsCurrent();
+
+  // Setup the subprocess handler.
+  brillo::AsynchronousSignalHandler handler;
+  handler.Init();
+  Subprocess subprocess;
+  subprocess.Init(&handler);
+
+  SideloadDaemonState sideload_daemon_state(
+      brillo::FileStream::FromFileDescriptor(status_fd, true, nullptr));
+
+  // During the sideload we don't access the prefs persisted on disk but instead
+  // use a temporary memory storage.
+  MemoryPrefs prefs;
+
+  std::unique_ptr<BootControlInterface> boot_control =
+      boot_control::CreateBootControl();
+  if (!boot_control) {
+    LOG(ERROR) << "Error initializing the BootControlInterface.";
+    return false;
+  }
+
+  std::unique_ptr<HardwareInterface> hardware = hardware::CreateHardware();
+  if (!hardware) {
+    LOG(ERROR) << "Error initializing the HardwareInterface.";
+    return false;
+  }
+
+  UpdateAttempterAndroid update_attempter(
+      &sideload_daemon_state, &prefs, boot_control.get(), hardware.get());
+  update_attempter.Init();
+
+  TEST_AND_RETURN_FALSE(update_attempter.ApplyPayload(
+      payload, payload_offset, payload_size, headers, nullptr));
+
+  loop.Run();
+  return sideload_daemon_state.status() == UpdateStatus::UPDATED_NEED_REBOOT;
+}
+
+}  // namespace
+}  // namespace chromeos_update_engine
+
+int main(int argc, char** argv) {
+  DEFINE_string(payload,
+                "file:///data/payload.bin",
+                "The URI to the update payload to use.");
+  DEFINE_int64(
+      offset, 0, "The offset in the payload where the CrAU update starts. ");
+  DEFINE_int64(size,
+               0,
+               "The size of the CrAU part of the payload. If 0 is passed, it "
+               "will be autodetected.");
+  DEFINE_string(headers,
+                "",
+                "A list of key-value pairs, one element of the list per line.");
+  DEFINE_int64(status_fd, -1, "A file descriptor to notify the update status.");
+
+  chromeos_update_engine::Terminator::Init();
+  chromeos_update_engine::SetupLogging();
+  brillo::FlagHelper::Init(argc, argv, "Update Engine Sideload");
+
+  LOG(INFO) << "Update Engine Sideloading starting";
+
+  // xz-embedded requires to initialize its CRC-32 table once on startup.
+  xz_crc32_init();
+
+  // When called from recovery, /data is not accessible, so we need to use
+  // /tmp for temporary files.
+  chromeos_update_engine::utils::SetRootTempDir(kSideloadRootTempDir);
+
+  vector<string> headers = base::SplitString(
+      FLAGS_headers, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+
+  if (!chromeos_update_engine::ApplyUpdatePayload(
+          FLAGS_payload, FLAGS_offset, FLAGS_size, headers, FLAGS_status_fd))
+    return 1;
+
+  return 0;
+}
diff --git a/update_attempter.cc b/update_attempter.cc
index a0f3bd4..c773063 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -41,18 +41,18 @@
 #include <power_manager/dbus-proxies.h>
 #include <update_engine/dbus-constants.h>
 
+#include "update_engine/certificate_checker.h"
 #include "update_engine/common/boot_control_interface.h"
-#include "update_engine/common/certificate_checker.h"
 #include "update_engine/common/clock_interface.h"
 #include "update_engine/common/constants.h"
 #include "update_engine/common/hardware_interface.h"
-#include "update_engine/common/libcurl_http_fetcher.h"
 #include "update_engine/common/multi_range_http_fetcher.h"
 #include "update_engine/common/platform_constants.h"
 #include "update_engine/common/prefs_interface.h"
 #include "update_engine/common/subprocess.h"
 #include "update_engine/common/utils.h"
 #include "update_engine/dbus_service.h"
+#include "update_engine/libcurl_http_fetcher.h"
 #include "update_engine/metrics.h"
 #include "update_engine/omaha_request_action.h"
 #include "update_engine/omaha_request_params.h"
@@ -585,7 +585,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.h b/update_attempter.h
index b045614..92683e6 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -31,9 +31,9 @@
 
 #include "debugd/dbus-proxies.h"
 #include "update_engine/chrome_browser_proxy_resolver.h"
+#include "update_engine/certificate_checker.h"
 #include "update_engine/client_library/include/update_engine/update_status.h"
 #include "update_engine/common/action_processor.h"
-#include "update_engine/common/certificate_checker.h"
 #include "update_engine/common/cpu_limiter.h"
 #include "update_engine/libcros_proxy.h"
 #include "update_engine/omaha_request_params.h"
@@ -244,6 +244,10 @@
     service_observers_.erase(observer);
   }
 
+  const std::set<ServiceObserverInterface*>& service_observers() {
+    return service_observers_;
+  }
+
   // Remove all the observers.
   void ClearObservers() { service_observers_.clear(); }
 
diff --git a/update_attempter_android.cc b/update_attempter_android.cc
index e42f569..dcc2dfd 100644
--- a/update_attempter_android.cc
+++ b/update_attempter_android.cc
@@ -26,17 +26,25 @@
 #include <brillo/bind_lambda.h>
 #include <brillo/message_loops/message_loop.h>
 #include <brillo/strings/string_utils.h>
+#include <log/log.h>
 
 #include "update_engine/common/constants.h"
-#include "update_engine/common/libcurl_http_fetcher.h"
+#include "update_engine/common/file_fetcher.h"
 #include "update_engine/common/multi_range_http_fetcher.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/daemon_state_android.h"
+#include "update_engine/daemon_state_interface.h"
+#include "update_engine/network_selector.h"
 #include "update_engine/payload_consumer/download_action.h"
 #include "update_engine/payload_consumer/filesystem_verifier_action.h"
 #include "update_engine/payload_consumer/postinstall_runner_action.h"
 #include "update_engine/update_status_utils.h"
 
+#ifndef _UE_SIDELOAD
+// Do not include support for external HTTP(s) urls when building
+// update_engine_sideload.
+#include "update_engine/libcurl_http_fetcher.h"
+#endif
+
 using base::Bind;
 using base::TimeDelta;
 using base::TimeTicks;
@@ -70,7 +78,7 @@
 }  // namespace
 
 UpdateAttempterAndroid::UpdateAttempterAndroid(
-    DaemonStateAndroid* daemon_state,
+    DaemonStateInterface* daemon_state,
     PrefsInterface* prefs,
     BootControlInterface* boot_control,
     HardwareInterface* hardware)
@@ -79,6 +87,7 @@
       boot_control_(boot_control),
       hardware_(hardware),
       processor_(new ActionProcessor()) {
+  network_selector_ = network::CreateNetworkSelector();
 }
 
 UpdateAttempterAndroid::~UpdateAttempterAndroid() {
@@ -168,12 +177,30 @@
 
   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;
+
+  NetworkId network_id = kDefaultNetworkId;
+  if (!headers[kPayloadPropertyNetworkId].empty()) {
+    if (!base::StringToUint64(headers[kPayloadPropertyNetworkId],
+                              &network_id)) {
+      return LogAndSetError(
+          error,
+          FROM_HERE,
+          "Invalid network_id: " + headers[kPayloadPropertyNetworkId]);
+    }
+    if (!network_selector_->SetProcessNetwork(network_id)) {
+      LOG(WARNING) << "Unable to set network_id, continuing with the update.";
+    }
+  }
 
   LOG(INFO) << "Using this install plan:";
   install_plan_.Dump();
 
-  BuildUpdateActions();
+  BuildUpdateActions(payload_url);
   SetupDownload();
   // Setup extra headers.
   HttpFetcher* fetcher = download_action_->http_fetcher();
@@ -255,13 +282,35 @@
                                             ErrorCode code) {
   LOG(INFO) << "Processing Done.";
 
-  if (code == ErrorCode::kSuccess) {
-    // Update succeeded.
-    WriteUpdateCompletedMarker();
-    prefs_->SetInt64(kPrefsDeltaUpdateFailures, 0);
-    DeltaPerformer::ResetUpdateProgress(prefs_, false);
+  switch (code) {
+    case ErrorCode::kSuccess:
+      // Update succeeded.
+      WriteUpdateCompletedMarker();
+      prefs_->SetInt64(kPrefsDeltaUpdateFailures, 0);
+      DeltaPerformer::ResetUpdateProgress(prefs_, false);
 
-    LOG(INFO) << "Update successfully applied, waiting to reboot.";
+      LOG(INFO) << "Update successfully applied, waiting to reboot.";
+      break;
+
+    case ErrorCode::kFilesystemCopierError:
+    case ErrorCode::kNewRootfsVerificationError:
+    case ErrorCode::kNewKernelVerificationError:
+    case ErrorCode::kFilesystemVerifierError:
+    case ErrorCode::kDownloadStateInitializationError:
+      // Reset the ongoing update for these errors so it starts from the
+      // beginning next time.
+      DeltaPerformer::ResetUpdateProgress(prefs_, false);
+      LOG(INFO) << "Resetting update progress.";
+      break;
+
+    case ErrorCode::kPayloadTimestampError:
+      // SafetyNet logging, b/36232423
+      android_errorWriteLog(0x534e4554, "36232423");
+      break;
+
+    default:
+      // Ignore all other error codes.
+      break;
   }
 
   TerminateUpdateAndNotify(code);
@@ -382,7 +431,7 @@
   last_notify_time_ = TimeTicks::Now();
 }
 
-void UpdateAttempterAndroid::BuildUpdateActions() {
+void UpdateAttempterAndroid::BuildUpdateActions(const string& url) {
   CHECK(!processor_->IsRunning());
   processor_->set_delegate(this);
 
@@ -390,9 +439,20 @@
   shared_ptr<InstallPlanAction> install_plan_action(
       new InstallPlanAction(install_plan_));
 
-  LibcurlHttpFetcher* download_fetcher =
-      new LibcurlHttpFetcher(&proxy_resolver_, hardware_);
-  download_fetcher->set_server_to_check(ServerToCheck::kDownload);
+  HttpFetcher* download_fetcher = nullptr;
+  if (FileFetcher::SupportedUrl(url)) {
+    DLOG(INFO) << "Using FileFetcher for file URL.";
+    download_fetcher = new FileFetcher();
+  } else {
+#ifdef _UE_SIDELOAD
+    LOG(FATAL) << "Unsupported sideload URI: " << url;
+#else
+    LibcurlHttpFetcher* libcurl_fetcher =
+        new LibcurlHttpFetcher(&proxy_resolver_, hardware_);
+    libcurl_fetcher->set_server_to_check(ServerToCheck::kDownload);
+    download_fetcher = libcurl_fetcher;
+#endif  // _UE_SIDELOAD
+  }
   shared_ptr<DownloadAction> download_action(new DownloadAction(
       prefs_,
       boot_control_,
@@ -404,10 +464,11 @@
                                    VerifierMode::kVerifyTargetHash));
 
   shared_ptr<PostinstallRunnerAction> postinstall_runner_action(
-      new PostinstallRunnerAction(boot_control_));
+      new PostinstallRunnerAction(boot_control_, hardware_));
 
   download_action->set_delegate(this);
   download_action_ = download_action;
+  postinstall_runner_action->set_delegate(this);
 
   actions_.push_back(shared_ptr<AbstractAction>(install_plan_action));
   actions_.push_back(shared_ptr<AbstractAction>(download_action));
diff --git a/update_attempter_android.h b/update_attempter_android.h
index 4fdac2c..2617318 100644
--- a/update_attempter_android.h
+++ b/update_attempter_android.h
@@ -31,6 +31,8 @@
 #include "update_engine/common/cpu_limiter.h"
 #include "update_engine/common/hardware_interface.h"
 #include "update_engine/common/prefs_interface.h"
+#include "update_engine/daemon_state_interface.h"
+#include "update_engine/network_selector_interface.h"
 #include "update_engine/payload_consumer/download_action.h"
 #include "update_engine/payload_consumer/postinstall_runner_action.h"
 #include "update_engine/service_delegate_android_interface.h"
@@ -38,8 +40,6 @@
 
 namespace chromeos_update_engine {
 
-class DaemonStateAndroid;
-
 class UpdateAttempterAndroid
     : public ServiceDelegateAndroidInterface,
       public ActionProcessorDelegate,
@@ -48,7 +48,7 @@
  public:
   using UpdateStatus = update_engine::UpdateStatus;
 
-  UpdateAttempterAndroid(DaemonStateAndroid* daemon_state,
+  UpdateAttempterAndroid(DaemonStateInterface* daemon_state,
                          PrefsInterface* prefs,
                          BootControlInterface* boot_control_,
                          HardwareInterface* hardware_);
@@ -108,8 +108,8 @@
   void SetStatusAndNotify(UpdateStatus status);
 
   // Helper method to construct the sequence of actions to be performed for
-  // applying an update.
-  void BuildUpdateActions();
+  // applying an update from the given |url|.
+  void BuildUpdateActions(const std::string& url);
 
   // Sets up the download parameters based on the update requested on the
   // |install_plan_|.
@@ -122,7 +122,7 @@
   // Returns whether an update was completed in the current boot.
   bool UpdateCompletedOnThisBoot();
 
-  DaemonStateAndroid* daemon_state_;
+  DaemonStateInterface* daemon_state_;
 
   // DaemonStateAndroid pointers.
   PrefsInterface* prefs_;
@@ -163,6 +163,9 @@
   // CPU limiter during the update.
   CPULimiter cpu_limiter_;
 
+  // Helper class to select the network to use during the update.
+  std::unique_ptr<NetworkSelectorInterface> network_selector_;
+
   // Whether we have marked the current slot as good. This step is required
   // before applying an update to the other slot.
   bool updated_boot_flags_ = false;
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 35bd206..c8f103d 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -291,7 +291,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/update_engine.gyp b/update_engine.gyp
index 3a1d635..1bd83b1 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -484,6 +484,7 @@
             'common/certificate_checker_unittest.cc',
             'common/cpu_limiter_unittest.cc',
             'common/fake_prefs.cc',
+            'common/file_fetcher.cc',  # Only required for tests.
             'common/hash_calculator_unittest.cc',
             'common/http_fetcher_unittest.cc',
             'common/hwid_override_unittest.cc',
diff --git a/update_engine.rc b/update_engine.rc
index 62bbcc7..88cf120 100644
--- a/update_engine.rc
+++ b/update_engine.rc
@@ -1,5 +1,5 @@
 service update_engine /system/bin/update_engine --logtostderr --foreground
     class late_start
     user root
-    group root system wakelock dbus inet
+    group root system wakelock dbus inet cache
     writepid /dev/cpuset/system-background/tasks
diff --git a/update_metadata.proto b/update_metadata.proto
index de7e668..596a04e 100644
--- a/update_metadata.proto
+++ b/update_metadata.proto
@@ -235,6 +235,10 @@
   // associated operation blobs (in operations[i].data_offset, data_length)
   // should be stored contiguously and in the same order.
   repeated InstallOperation operations = 8;
+
+  // Whether a failure in the postinstall step for this partition should be
+  // ignored.
+  optional bool postinstall_optional = 9;
 }
 
 message DeltaArchiveManifest {
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_