Merge Android U (ab/10368041)

Bug: 291102124
Merged-In: I4c2345c227697f76adf8623bb4bd57bcfd97901f
Change-Id: I1a6a4fc7ba09b97ef605ab3ec13f4b4b8656f1b3
diff --git a/Android.bp b/Android.bp
index a33aa8f..4db429d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -75,12 +75,6 @@
     ],
     ldflags: ["-Wl,--gc-sections"],
 
-    product_variables: {
-        pdk: {
-            enabled: false,
-        },
-    },
-
     target: {
         android: {
             cflags: [
@@ -348,8 +342,7 @@
         "libcutils",
         "libfs_mgr_binder",
         "libgsi",
-        "libsnapshot",
-        "libsnapshot_cow",
+        "libsnapshot_static",
         "libstatslog_ue",
         "libz",
     ],
@@ -375,7 +368,7 @@
             ],
             exclude_static_libs: [
                 "libfs_mgr_binder",
-                "libsnapshot",
+                "libsnapshot_static",
                 "libstatslog_ue"
             ],
         },
@@ -1048,6 +1041,8 @@
         "unittest_key_RSA4096.pem",
         "unittest_key_EC.pem",
         "update_engine.conf",
+        "testdata/cycle_nodes_product.bin",
+        "testdata/cycle_nodes_product_no_xor.bin",
     ],
     static_libs: [
         "libgmock",
@@ -1164,6 +1159,8 @@
         "unittest_key_RSA4096.pem",
         "unittest_key_EC.pem",
         "update_engine.conf",
+        "testdata/cycle_nodes_product.bin",
+        "testdata/cycle_nodes_product_no_xor.bin",
     ],
 
     // We cannot use the default generated AndroidTest.xml because of the use of helper modules
diff --git a/aosp/binder_service_android.cc b/aosp/binder_service_android.cc
index 84b5b7a..37df9a5 100644
--- a/aosp/binder_service_android.cc
+++ b/aosp/binder_service_android.cc
@@ -21,7 +21,6 @@
 #include <base/bind.h>
 #include <base/logging.h>
 #include <binderwrapper/binder_wrapper.h>
-#include <brillo/errors/error.h>
 #include <utils/String8.h>
 
 #include "update_engine/aosp/binder_service_android_common.h"
@@ -78,10 +77,7 @@
   auto binder_wrapper = android::BinderWrapper::Get();
   binder_wrapper->RegisterForDeathNotifications(
       callback_binder,
-      base::Bind(
-          base::IgnoreResult(&BinderUpdateEngineAndroidService::UnbindCallback),
-          base::Unretained(this),
-          base::Unretained(callback_binder.get())));
+      [this, callback = callback_binder.get()]() { UnbindCallback(callback); });
 
   *return_value = true;
   return Status::ok();
@@ -103,10 +99,10 @@
     int64_t payload_offset,
     int64_t payload_size,
     const vector<android::String16>& header_kv_pairs) {
-  const string payload_url{android::String8{url}.string()};
+  const string payload_url{android::String8{url}.c_str()};
   vector<string> str_headers = ToVecString(header_kv_pairs);
 
-  brillo::ErrorPtr error;
+  Error error;
   if (!service_delegate_->ApplyPayload(
           payload_url, payload_offset, payload_size, str_headers, &error)) {
     return ErrorPtrToStatus(error);
@@ -121,7 +117,7 @@
     const vector<android::String16>& header_kv_pairs) {
   vector<string> str_headers = ToVecString(header_kv_pairs);
 
-  brillo::ErrorPtr error;
+  Error error;
   if (!service_delegate_->ApplyPayload(
           pfd.get(), payload_offset, payload_size, str_headers, &error)) {
     return ErrorPtrToStatus(error);
@@ -130,28 +126,28 @@
 }
 
 Status BinderUpdateEngineAndroidService::suspend() {
-  brillo::ErrorPtr error;
+  Error error;
   if (!service_delegate_->SuspendUpdate(&error))
     return ErrorPtrToStatus(error);
   return Status::ok();
 }
 
 Status BinderUpdateEngineAndroidService::resume() {
-  brillo::ErrorPtr error;
+  Error error;
   if (!service_delegate_->ResumeUpdate(&error))
     return ErrorPtrToStatus(error);
   return Status::ok();
 }
 
 Status BinderUpdateEngineAndroidService::cancel() {
-  brillo::ErrorPtr error;
+  Error error;
   if (!service_delegate_->CancelUpdate(&error))
     return ErrorPtrToStatus(error);
   return Status::ok();
 }
 
 Status BinderUpdateEngineAndroidService::resetStatus() {
-  brillo::ErrorPtr error;
+  Error error;
   if (!service_delegate_->ResetStatus(&error))
     return ErrorPtrToStatus(error);
   return Status::ok();
@@ -159,16 +155,16 @@
 
 Status BinderUpdateEngineAndroidService::setShouldSwitchSlotOnReboot(
     const android::String16& metadata_filename) {
-  brillo::ErrorPtr error;
+  Error error;
   if (!service_delegate_->setShouldSwitchSlotOnReboot(
-          android::String8(metadata_filename).string(), &error)) {
+          android::String8(metadata_filename).c_str(), &error)) {
     return ErrorPtrToStatus(error);
   }
   return Status::ok();
 }
 
 Status BinderUpdateEngineAndroidService::resetShouldSwitchSlotOnReboot() {
-  brillo::ErrorPtr error;
+  Error error;
   if (!service_delegate_->resetShouldSwitchSlotOnReboot(&error)) {
     return ErrorPtrToStatus(error);
   }
@@ -178,13 +174,13 @@
 Status BinderUpdateEngineAndroidService::verifyPayloadApplicable(
     const android::String16& metadata_filename, bool* return_value) {
   const std::string payload_metadata{
-      android::String8{metadata_filename}.string()};
+      android::String8{metadata_filename}.c_str()};
   LOG(INFO) << "Received a request of verifying payload metadata in "
             << payload_metadata << ".";
-  brillo::ErrorPtr error;
+  Error error;
   *return_value =
       service_delegate_->VerifyPayloadApplicable(payload_metadata, &error);
-  if (error != nullptr)
+  if (error.error_code != ErrorCode::kSuccess)
     return ErrorPtrToStatus(error);
   return Status::ok();
 }
@@ -209,15 +205,15 @@
     const vector<android::String16>& header_kv_pairs,
     int64_t* return_value) {
   const std::string payload_metadata{
-      android::String8{metadata_filename}.string()};
+      android::String8{metadata_filename}.c_str()};
   vector<string> str_headers = ToVecString(header_kv_pairs);
   LOG(INFO) << "Received a request of allocating space for " << payload_metadata
             << ".";
-  brillo::ErrorPtr error;
+  Error error;
   *return_value =
       static_cast<int64_t>(service_delegate_->AllocateSpaceForPayload(
           payload_metadata, str_headers, &error));
-  if (error != nullptr)
+  if (error.error_code != ErrorCode::kSuccess)
     return ErrorPtrToStatus(error);
   return Status::ok();
 }
@@ -237,7 +233,7 @@
             update_engine::UpdateStatus::CLEANUP_PREVIOUS_UPDATE),
         progress));
   }
-  void RegisterForDeathNotifications(base::Closure unbind) {
+  void RegisterForDeathNotifications(const std::function<void()>& unbind) {
     const android::sp<android::IBinder>& callback_binder =
         IUpdateEngineCallback::asBinder(callback_);
     auto binder_wrapper = android::BinderWrapper::Get();
@@ -250,10 +246,10 @@
 
 Status BinderUpdateEngineAndroidService::cleanupSuccessfulUpdate(
     const android::sp<IUpdateEngineCallback>& callback) {
-  brillo::ErrorPtr error;
+  Error error;
   service_delegate_->CleanupSuccessfulUpdate(
       std::make_unique<CleanupSuccessfulUpdateCallback>(callback), &error);
-  if (error != nullptr)
+  if (error.error_code != ErrorCode::kSuccess)
     return ErrorPtrToStatus(error);
   return Status::ok();
 }
diff --git a/aosp/binder_service_android_common.h b/aosp/binder_service_android_common.h
index 223b32e..636e98e 100644
--- a/aosp/binder_service_android_common.h
+++ b/aosp/binder_service_android_common.h
@@ -20,14 +20,16 @@
 #include <string>
 #include <vector>
 
+#include "update_engine/common/error.h"
+
 #include <binder/Status.h>
 
 namespace chromeos_update_engine {
 
-static inline android::binder::Status ErrorPtrToStatus(
-    const brillo::ErrorPtr& error) {
+static inline android::binder::Status ErrorPtrToStatus(const Error& error) {
   return android::binder::Status::fromServiceSpecificError(
-      1, android::String8{error->GetMessage().c_str()});
+      static_cast<int>(error.error_code),
+      android::String8{error.message.c_str()});
 }
 
 static inline std::vector<std::string> ToVecString(
@@ -35,7 +37,7 @@
   std::vector<std::string> out;
   out.reserve(inp.size());
   for (const auto& e : inp) {
-    out.emplace_back(android::String8{e}.string());
+    out.emplace_back(android::String8{e}.c_str());
   }
   return out;
 }
diff --git a/aosp/binder_service_stable_android.cc b/aosp/binder_service_stable_android.cc
index 17b35ee..3bc7f6c 100644
--- a/aosp/binder_service_stable_android.cc
+++ b/aosp/binder_service_stable_android.cc
@@ -21,7 +21,6 @@
 #include <base/bind.h>
 #include <base/logging.h>
 #include <binderwrapper/binder_wrapper.h>
-#include <brillo/errors/error.h>
 #include <utils/String8.h>
 
 #include "update_engine/aosp/binder_service_android_common.h"
@@ -83,10 +82,7 @@
   auto binder_wrapper = android::BinderWrapper::Get();
   binder_wrapper->RegisterForDeathNotifications(
       callback_binder,
-      base::Bind(base::IgnoreResult(
-                     &BinderUpdateEngineAndroidStableService::UnbindCallback),
-                 base::Unretained(this),
-                 base::Unretained(callback_binder.get())));
+      [this, callback = callback_binder.get()]() { UnbindCallback(callback); });
 
   *return_value = true;
   return Status::ok();
@@ -111,7 +107,7 @@
     const vector<android::String16>& header_kv_pairs) {
   vector<string> str_headers = ToVecString(header_kv_pairs);
 
-  brillo::ErrorPtr error;
+  Error error;
   if (!service_delegate_->ApplyPayload(
           pfd.get(), payload_offset, payload_size, str_headers, &error)) {
     return ErrorPtrToStatus(error);
diff --git a/aosp/dynamic_partition_control_android.cc b/aosp/dynamic_partition_control_android.cc
index a309b6e..d8df520 100644
--- a/aosp/dynamic_partition_control_android.cc
+++ b/aosp/dynamic_partition_control_android.cc
@@ -48,6 +48,7 @@
 #include "update_engine/aosp/dynamic_partition_utils.h"
 #include "update_engine/common/boot_control_interface.h"
 #include "update_engine/common/dynamic_partition_control_interface.h"
+#include "update_engine/common/error_code.h"
 #include "update_engine/common/platform_constants.h"
 #include "update_engine/common/utils.h"
 #include "update_engine/payload_consumer/cow_writer_file_descriptor.h"
@@ -449,7 +450,8 @@
     uint32_t target_slot,
     const DeltaArchiveManifest& manifest,
     bool update,
-    uint64_t* required_size) {
+    uint64_t* required_size,
+    ErrorCode* error) {
   source_slot_ = source_slot;
   target_slot_ = target_slot;
   if (required_size != nullptr) {
@@ -458,10 +460,14 @@
 
   if (fs_mgr_overlayfs_is_setup()) {
     // Non DAP devices can use overlayfs as well.
-    LOG(WARNING)
+    LOG(ERROR)
         << "overlayfs overrides are active and can interfere with our "
            "resources.\n"
         << "run adb enable-verity to deactivate if required and try again.";
+    if (error) {
+      *error = ErrorCode::kOverlayfsenabledError;
+      return false;
+    }
   }
 
   // If metadata is erased but not formatted, it is possible to not mount
@@ -852,6 +858,7 @@
 
   auto target_device =
       device_dir.Append(GetSuperPartitionName(target_slot)).value();
+
   return StoreMetadata(target_device, builder.get(), target_slot);
 }
 
diff --git a/aosp/dynamic_partition_control_android.h b/aosp/dynamic_partition_control_android.h
index 830901b..35a79fd 100644
--- a/aosp/dynamic_partition_control_android.h
+++ b/aosp/dynamic_partition_control_android.h
@@ -55,7 +55,8 @@
                                   uint32_t target_slot,
                                   const DeltaArchiveManifest& manifest,
                                   bool update,
-                                  uint64_t* required_size) override;
+                                  uint64_t* required_size,
+                                  ErrorCode* error = nullptr) override;
   bool FinishUpdate(bool powerwash_required) override;
   std::unique_ptr<AbstractAction> GetCleanupPreviousUpdateAction(
       BootControlInterface* boot_control,
diff --git a/aosp/dynamic_partition_control_android_unittest.cc b/aosp/dynamic_partition_control_android_unittest.cc
index 6f1d4ef..30780f0 100644
--- a/aosp/dynamic_partition_control_android_unittest.cc
+++ b/aosp/dynamic_partition_control_android_unittest.cc
@@ -164,6 +164,7 @@
         target(),
         PartitionSizesToManifest(partition_sizes),
         true,
+        nullptr,
         nullptr);
   }
   void SetSlots(const TestParam& slots) { slots_ = slots; }
@@ -363,7 +364,7 @@
   // DynamicPartitionControlAndroidTest::PreparePartitionsForUpdate(), since we
   // don't want any default group in the PartitionMetadata.
   EXPECT_TRUE(dynamicControl().PreparePartitionsForUpdate(
-      source(), target(), {}, true, nullptr));
+      source(), target(), {}, true, nullptr, nullptr));
 
   // Should use dynamic source partitions.
   EXPECT_CALL(dynamicControl(), GetState(S("system")))
@@ -504,6 +505,7 @@
       target(),
       PartitionSizesToManifest({{"system", 2_GiB}, {"vendor", 1_GiB}}),
       false,
+      nullptr,
       nullptr));
 
   // Dynamic partition "system".
@@ -759,6 +761,7 @@
       target(),
       PartitionSizesToManifest({{"foo", 4_MiB}}),
       false,
+      nullptr,
       nullptr));
   dynamicControl().set_fake_mapped_devices({T("foo")});
 
@@ -1041,8 +1044,12 @@
         }));
   }
   bool PreparePartitionsForUpdate(uint64_t* required_size) {
-    return dynamicControl().PreparePartitionsForUpdate(
-        source(), target(), manifest_, true /* update */, required_size);
+    return dynamicControl().PreparePartitionsForUpdate(source(),
+                                                       target(),
+                                                       manifest_,
+                                                       true /* update */,
+                                                       required_size,
+                                                       nullptr);
   }
   MockSnapshotManager* snapshot_ = nullptr;
   DeltaArchiveManifest manifest_;
diff --git a/aosp/service_delegate_android_interface.h b/aosp/service_delegate_android_interface.h
index b3660ab..45c0274 100644
--- a/aosp/service_delegate_android_interface.h
+++ b/aosp/service_delegate_android_interface.h
@@ -23,7 +23,7 @@
 #include <string>
 #include <vector>
 
-#include <brillo/errors/error.h>
+#include "update_engine/common/error.h"
 
 namespace chromeos_update_engine {
 
@@ -36,7 +36,8 @@
   virtual void OnCleanupProgressUpdate(double progress) = 0;
   virtual void OnCleanupComplete(int32_t error_code) = 0;
   // Call RegisterForDeathNotifications on the internal binder object.
-  virtual void RegisterForDeathNotifications(base::Closure unbind) = 0;
+  virtual void RegisterForDeathNotifications(
+      const std::function<void()>& unbind) = 0;
 };
 
 // This class defines the interface exposed by the Android version of the
@@ -58,54 +59,54 @@
       int64_t payload_offset,
       int64_t payload_size,
       const std::vector<std::string>& key_value_pair_headers,
-      brillo::ErrorPtr* error) = 0;
+      Error* error) = 0;
 
   virtual bool ApplyPayload(
       int fd,
       int64_t payload_offset,
       int64_t payload_size,
       const std::vector<std::string>& key_value_pair_headers,
-      brillo::ErrorPtr* error) = 0;
+      Error* error) = 0;
 
   // Suspend an ongoing update. Returns true if there was an update ongoing and
   // it was suspended. In case of failure, it returns false and sets |error|
   // accordingly.
-  virtual bool SuspendUpdate(brillo::ErrorPtr* error) = 0;
+  virtual bool SuspendUpdate(Error* error) = 0;
 
   // Resumes an update suspended with SuspendUpdate(). The update can't be
   // suspended after it finished and this method will fail in that case.
   // Returns whether the resume operation was successful, which only implies
   // that there was a suspended update. In case of error, returns false and sets
   // |error| accordingly.
-  virtual bool ResumeUpdate(brillo::ErrorPtr* error) = 0;
+  virtual bool ResumeUpdate(Error* error) = 0;
 
   // Cancel the ongoing update. The update could be running or suspended, but it
   // can't be canceled after it was done. In case of error, returns false and
   // sets |error| accordingly.
-  virtual bool CancelUpdate(brillo::ErrorPtr* error) = 0;
+  virtual bool CancelUpdate(Error* error) = 0;
 
   // Reset the already applied update back to an idle state. This method can
   // only be called when no update attempt is going on, and it will reset the
   // status back to idle, deleting the currently applied update if any. In case
   // of error, returns false and sets |error| accordingly.
-  virtual bool ResetStatus(brillo::ErrorPtr* error) = 0;
+  virtual bool ResetStatus(Error* error) = 0;
 
   // Verifies whether a payload (delegated by the payload metadata) can be
   // applied to the current device. Returns whether the payload is applicable.
   // In case of error, returns false and sets |error| accordingly.
   virtual bool VerifyPayloadApplicable(const std::string& metadata_filename,
-                                       brillo::ErrorPtr* error) = 0;
+                                       Error* error) = 0;
   // Sets the A/B slot switch for the next boot after applying an ota update.
   // If applyPayload hasn't switched the slot by itself, the client can call
   // this API to switch the slot and apply the update on next boot. Returns
   // true on success.
   virtual bool setShouldSwitchSlotOnReboot(const std::string& metadata_filename,
-                                           brillo::ErrorPtr* error) = 0;
+                                           Error* error) = 0;
 
   // Resets the boot slot to the source/current slot, without cancelling the
   // update progress. This can be called after the update is installed, and to
   // prevent the device from accidentally taking the update when it reboots.
-  virtual bool resetShouldSwitchSlotOnReboot(brillo::ErrorPtr* error) = 0;
+  virtual bool resetShouldSwitchSlotOnReboot(Error* error) = 0;
 
   // Allocates space for a payload.
   // Returns 0 if space is successfully preallocated.
@@ -118,7 +119,7 @@
   virtual uint64_t AllocateSpaceForPayload(
       const std::string& metadata_filename,
       const std::vector<std::string>& key_value_pair_headers,
-      brillo::ErrorPtr* error) = 0;
+      Error* error) = 0;
 
   // Wait for merge to complete, then clean up merge after an update has been
   // successful.
@@ -127,7 +128,7 @@
   // |callback|.
   virtual void CleanupSuccessfulUpdate(
       std::unique_ptr<CleanupSuccessfulUpdateCallbackInterface> callback,
-      brillo::ErrorPtr* error) = 0;
+      Error* error) = 0;
 
  protected:
   ServiceDelegateAndroidInterface() = default;
diff --git a/aosp/update_attempter_android.cc b/aosp/update_attempter_android.cc
index 6134885..e52f26a 100644
--- a/aosp/update_attempter_android.cc
+++ b/aosp/update_attempter_android.cc
@@ -81,18 +81,32 @@
 const double kBroadcastThresholdProgress = 0.01;  // 1%
 const int kBroadcastThresholdSeconds = 10;
 
-const char* const kErrorDomain = "update_engine";
-// TODO(deymo): Convert the different errors to a numeric value to report them
-// back on the service error.
-const char* const kGenericError = "generic_error";
+// Log and set the error on the passed ErrorPtr.
+bool LogAndSetGenericError(Error* error,
+                           int line_number,
+                           const char* file_name,
+                           const string& reason) {
+  LOG(ERROR) << "Replying with failure: " << file_name << " " << line_number
+             << ": " << reason;
+  error->line_number = line_number;
+  error->file_name = file_name;
+  error->message = reason;
+  error->error_code = ErrorCode::kError;
+  return false;
+}
 
 // Log and set the error on the passed ErrorPtr.
-bool LogAndSetError(brillo::ErrorPtr* error,
-                    const base::Location& location,
-                    const string& reason) {
-  brillo::Error::AddTo(error, location, kErrorDomain, kGenericError, reason);
-  LOG(ERROR) << "Replying with failure: " << location.ToString() << ": "
-             << reason;
+bool LogAndSetError(Error* error,
+                    int line_number,
+                    const char* file_name,
+                    const string& reason,
+                    ErrorCode error_code) {
+  LOG(ERROR) << "Replying with failure: " << file_name << " " << line_number
+             << ": " << reason;
+  error->line_number = line_number;
+  error->file_name = file_name;
+  error->message = reason;
+  error->error_code = error_code;
   return false;
 }
 
@@ -105,17 +119,20 @@
 
 bool ParseKeyValuePairHeaders(const vector<string>& key_value_pair_headers,
                               std::map<string, string>* headers,
-                              brillo::ErrorPtr* error) {
+                              Error* error) {
   for (const string& key_value_pair : key_value_pair_headers) {
     string key;
     string value;
     if (!brillo::string_utils::SplitAtFirst(
             key_value_pair, "=", &key, &value, false)) {
-      return LogAndSetError(
-          error, FROM_HERE, "Passed invalid header: " + key_value_pair);
+      return LogAndSetGenericError(error,
+                                   __LINE__,
+                                   __FILE__,
+                                   "Passed invalid header: " + key_value_pair);
     }
     if (!headers->emplace(key, value).second)
-      return LogAndSetError(error, FROM_HERE, "Passed repeated key: " + key);
+      return LogAndSetGenericError(
+          error, __LINE__, __FILE__, "Passed repeated key: " + key);
   }
   return true;
 }
@@ -131,6 +148,12 @@
               : "");
 }
 
+std::string GetCurrentBuildVersion() {
+  // Example: [ro.build.fingerprint]:
+  // [generic/aosp_cf_x86_64_phone/vsoc_x86_64:VanillaIceCream/AOSP.MAIN/user08011303:userdebug/test-keys]
+  return android::base::GetProperty("ro.build.fingerprint", "");
+}
+
 }  // namespace
 
 UpdateAttempterAndroid::UpdateAttempterAndroid(
@@ -217,14 +240,20 @@
     int64_t payload_offset,
     int64_t payload_size,
     const vector<string>& key_value_pair_headers,
-    brillo::ErrorPtr* error) {
+    Error* error) {
   if (status_ == UpdateStatus::UPDATED_NEED_REBOOT) {
-    return LogAndSetError(
-        error, FROM_HERE, "An update already applied, waiting for reboot");
+    return LogAndSetGenericError(
+        error,
+        __LINE__,
+        __FILE__,
+        "An update already applied, waiting for reboot");
   }
   if (processor_->IsRunning()) {
-    return LogAndSetError(
-        error, FROM_HERE, "Already processing an update, cancel it first.");
+    return LogAndSetGenericError(
+        error,
+        __LINE__,
+        __FILE__,
+        "Already processing an update, cancel it first.");
   }
   DCHECK_EQ(status_, UpdateStatus::IDLE);
 
@@ -307,15 +336,17 @@
   if (!headers[kPayloadPropertyNetworkId].empty()) {
     if (!base::StringToUint64(headers[kPayloadPropertyNetworkId],
                               &network_id)) {
-      return LogAndSetError(
+      return LogAndSetGenericError(
           error,
-          FROM_HERE,
+          __LINE__,
+          __FILE__,
           "Invalid network_id: " + headers[kPayloadPropertyNetworkId]);
     }
     if (!network_selector_->SetProcessNetwork(network_id)) {
-      return LogAndSetError(
+      return LogAndSetGenericError(
           error,
-          FROM_HERE,
+          __LINE__,
+          __FILE__,
           "Unable to set network_id: " + headers[kPayloadPropertyNetworkId]);
     }
   }
@@ -379,17 +410,23 @@
     int64_t payload_offset,
     int64_t payload_size,
     const vector<string>& key_value_pair_headers,
-    brillo::ErrorPtr* error) {
+    Error* error) {
   // update_engine state must be checked before modifying payload_fd_ otherwise
   // already running update will be terminated (existing file descriptor will be
   // closed)
   if (status_ == UpdateStatus::UPDATED_NEED_REBOOT) {
-    return LogAndSetError(
-        error, FROM_HERE, "An update already applied, waiting for reboot");
+    return LogAndSetGenericError(
+        error,
+        __LINE__,
+        __FILE__,
+        "An update already applied, waiting for reboot");
   }
   if (processor_->IsRunning()) {
-    return LogAndSetError(
-        error, FROM_HERE, "Already processing an update, cancel it first.");
+    return LogAndSetGenericError(
+        error,
+        __LINE__,
+        __FILE__,
+        "Already processing an update, cancel it first.");
   }
   DCHECK_EQ(status_, UpdateStatus::IDLE);
 
@@ -400,40 +437,48 @@
       payload_url, payload_offset, payload_size, key_value_pair_headers, error);
 }
 
-bool UpdateAttempterAndroid::SuspendUpdate(brillo::ErrorPtr* error) {
+bool UpdateAttempterAndroid::SuspendUpdate(Error* error) {
   if (!processor_->IsRunning())
-    return LogAndSetError(error, FROM_HERE, "No ongoing update to suspend.");
+    return LogAndSetGenericError(
+        error, __LINE__, __FILE__, "No ongoing update to suspend.");
   processor_->SuspendProcessing();
   return true;
 }
 
-bool UpdateAttempterAndroid::ResumeUpdate(brillo::ErrorPtr* error) {
+bool UpdateAttempterAndroid::ResumeUpdate(Error* error) {
   if (!processor_->IsRunning())
-    return LogAndSetError(error, FROM_HERE, "No ongoing update to resume.");
+    return LogAndSetGenericError(
+        error, __LINE__, __FILE__, "No ongoing update to resume.");
   processor_->ResumeProcessing();
   return true;
 }
 
-bool UpdateAttempterAndroid::CancelUpdate(brillo::ErrorPtr* error) {
+bool UpdateAttempterAndroid::CancelUpdate(Error* error) {
   if (!processor_->IsRunning())
-    return LogAndSetError(error, FROM_HERE, "No ongoing update to cancel.");
+    return LogAndSetGenericError(
+        error, __LINE__, __FILE__, "No ongoing update to cancel.");
   processor_->StopProcessing();
   return true;
 }
 
-bool UpdateAttempterAndroid::ResetStatus(brillo::ErrorPtr* error) {
+bool UpdateAttempterAndroid::ResetStatus(Error* error) {
   LOG(INFO) << "Attempting to reset state from "
             << UpdateStatusToString(status_) << " to UpdateStatus::IDLE";
   if (processor_->IsRunning()) {
-    return LogAndSetError(
-        error, FROM_HERE, "Already processing an update, cancel it first.");
+    return LogAndSetGenericError(
+        error,
+        __LINE__,
+        __FILE__,
+        "Already processing an update, cancel it first.");
   }
   if (status_ != UpdateStatus::IDLE &&
       status_ != UpdateStatus::UPDATED_NEED_REBOOT) {
-    return LogAndSetError(error,
-                          FROM_HERE,
-                          "Status reset not allowed in this state, please "
-                          "cancel on going OTA first.");
+    return LogAndSetGenericError(
+        error,
+        __LINE__,
+        __FILE__,
+        "Status reset not allowed in this state, please "
+        "cancel on going OTA first.");
   }
 
   if (apex_handler_android_ != nullptr) {
@@ -445,10 +490,11 @@
   // after resetting to idle state, it doesn't go back to
   // UpdateStatus::UPDATED_NEED_REBOOT state.
   if (!ClearUpdateCompletedMarker()) {
-    return LogAndSetError(error,
-                          FROM_HERE,
-                          "Failed to reset the status because "
-                          "ClearUpdateCompletedMarker() failed");
+    return LogAndSetGenericError(error,
+                                 __LINE__,
+                                 __FILE__,
+                                 "Failed to reset the status because "
+                                 "ClearUpdateCompletedMarker() failed");
   }
   if (status_ == UpdateStatus::UPDATED_NEED_REBOOT) {
     if (!resetShouldSwitchSlotOnReboot(error)) {
@@ -478,27 +524,34 @@
     const std::string& metadata_filename,
     std::string_view expected_metadata_hash,
     DeltaArchiveManifest* manifest,
-    brillo::ErrorPtr* error) {
+    Error* error) {
   FileDescriptorPtr fd(new EintrSafeFileDescriptor);
   if (!fd->Open(metadata_filename.c_str(), O_RDONLY)) {
-    return LogAndSetError(
-        error, FROM_HERE, "Failed to open " + metadata_filename);
+    return LogAndSetError(error,
+                          __LINE__,
+                          __FILE__,
+                          "Failed to open " + metadata_filename,
+                          ErrorCode::kDownloadManifestParseError);
   }
   brillo::Blob metadata(kMaxPayloadHeaderSize);
   if (!fd->Read(metadata.data(), metadata.size())) {
     return LogAndSetError(
         error,
-        FROM_HERE,
-        "Failed to read payload header from " + metadata_filename);
+        __LINE__,
+        __FILE__,
+        "Failed to read payload header from " + metadata_filename,
+        ErrorCode::kDownloadManifestParseError);
   }
   ErrorCode errorcode{};
   PayloadMetadata payload_metadata;
   if (payload_metadata.ParsePayloadHeader(metadata, &errorcode) !=
       MetadataParseResult::kSuccess) {
     return LogAndSetError(error,
-                          FROM_HERE,
+                          __LINE__,
+                          __FILE__,
                           "Failed to parse payload header: " +
-                              utils::ErrorCodeToString(errorcode));
+                              utils::ErrorCodeToString(errorcode),
+                          errorcode);
   }
   uint64_t metadata_size = payload_metadata.GetMetadataSize() +
                            payload_metadata.GetMetadataSignatureSize();
@@ -507,16 +560,20 @@
           static_cast<uint64_t>(utils::FileSize(metadata_filename))) {
     return LogAndSetError(
         error,
-        FROM_HERE,
-        "Invalid metadata size: " + std::to_string(metadata_size));
+        __LINE__,
+        __FILE__,
+        "Invalid metadata size: " + std::to_string(metadata_size),
+        ErrorCode::kDownloadManifestParseError);
   }
   metadata.resize(metadata_size);
   if (!fd->Read(metadata.data() + kMaxPayloadHeaderSize,
                 metadata.size() - kMaxPayloadHeaderSize)) {
     return LogAndSetError(
         error,
-        FROM_HERE,
-        "Failed to read metadata and signature from " + metadata_filename);
+        __LINE__,
+        __FILE__,
+        "Failed to read metadata and signature from " + metadata_filename,
+        ErrorCode::kDownloadManifestParseError);
   }
   fd->Close();
   if (!expected_metadata_hash.empty()) {
@@ -525,10 +582,12 @@
         metadata.data(), payload_metadata.GetMetadataSize(), &metadata_hash));
     if (metadata_hash != expected_metadata_hash) {
       return LogAndSetError(error,
-                            FROM_HERE,
+                            __LINE__,
+                            __FILE__,
                             "Metadata hash mismatch. Expected hash: " +
                                 HexEncode(expected_metadata_hash) +
-                                " actual hash: " + HexEncode(metadata_hash));
+                                " actual hash: " + HexEncode(metadata_hash),
+                            ErrorCode::kDownloadManifestParseError);
     } else {
       LOG(INFO) << "Payload metadata hash check passed : "
                 << HexEncode(metadata_hash);
@@ -539,27 +598,35 @@
       constants::kUpdateCertificatesPath);
   if (!payload_verifier) {
     return LogAndSetError(error,
-                          FROM_HERE,
+                          __LINE__,
+                          __FILE__,
                           "Failed to create the payload verifier from " +
-                              std::string(constants::kUpdateCertificatesPath));
+                              std::string(constants::kUpdateCertificatesPath),
+                          ErrorCode::kDownloadManifestParseError);
   }
   errorcode = payload_metadata.ValidateMetadataSignature(
       metadata, "", *payload_verifier);
   if (errorcode != ErrorCode::kSuccess) {
     return LogAndSetError(error,
-                          FROM_HERE,
+                          __LINE__,
+                          __FILE__,
                           "Failed to validate metadata signature: " +
-                              utils::ErrorCodeToString(errorcode));
+                              utils::ErrorCodeToString(errorcode),
+                          errorcode);
   }
   if (!payload_metadata.GetManifest(metadata, manifest)) {
-    return LogAndSetError(error, FROM_HERE, "Failed to parse manifest.");
+    return LogAndSetError(error,
+                          __LINE__,
+                          __FILE__,
+                          "Failed to parse manifest.",
+                          ErrorCode::kDownloadManifestParseError);
   }
 
   return true;
 }
 
 bool UpdateAttempterAndroid::VerifyPayloadApplicable(
-    const std::string& metadata_filename, brillo::ErrorPtr* error) {
+    const std::string& metadata_filename, Error* error) {
   DeltaArchiveManifest manifest;
   TEST_AND_RETURN_FALSE(
       VerifyPayloadParseManifest(metadata_filename, &manifest, error));
@@ -574,14 +641,15 @@
     string partition_path;
     if (!boot_control_->GetPartitionDevice(
             partition.partition_name(), current_slot, &partition_path)) {
-      return LogAndSetError(
+      return LogAndSetGenericError(
           error,
-          FROM_HERE,
+          __LINE__,
+          __FILE__,
           "Failed to get partition device for " + partition.partition_name());
     }
     if (!fd->Open(partition_path.c_str(), O_RDONLY)) {
-      return LogAndSetError(
-          error, FROM_HERE, "Failed to open " + partition_path);
+      return LogAndSetGenericError(
+          error, __LINE__, __FILE__, "Failed to open " + partition_path);
     }
     for (const InstallOperation& operation : partition.operations()) {
       if (!operation.has_src_sha256_hash())
@@ -591,8 +659,8 @@
                                         operation.src_extents(),
                                         manifest.block_size(),
                                         &source_hash)) {
-        return LogAndSetError(
-            error, FROM_HERE, "Failed to hash " + partition_path);
+        return LogAndSetGenericError(
+            error, __LINE__, __FILE__, "Failed to hash " + partition_path);
       }
       if (!PartitionWriter::ValidateSourceHash(
               source_hash, operation, fd, &errorcode)) {
@@ -964,8 +1032,7 @@
 
 bool UpdateAttempterAndroid::OTARebootSucceeded() const {
   const auto current_slot = boot_control_->GetCurrentSlot();
-  const string current_version =
-      android::base::GetProperty("ro.build.version.incremental", "");
+  const string current_version = GetCurrentBuildVersion();
   int64_t previous_slot = -1;
   TEST_AND_RETURN_FALSE(prefs_->GetInt64(kPrefsPreviousSlot, &previous_slot));
   string previous_version;
@@ -1018,9 +1085,7 @@
 }
 
 void UpdateAttempterAndroid::UpdateStateAfterReboot(const OTAResult result) {
-  // Example: [ro.build.version.incremental]: [4292972]
-  string current_version =
-      android::base::GetProperty("ro.build.version.incremental", "");
+  const string current_version = GetCurrentBuildVersion();
   TEST_AND_RETURN(!current_version.empty());
 
   // |UpdateStateAfterReboot()| is only called after system reboot, so record
@@ -1122,7 +1187,7 @@
 uint64_t UpdateAttempterAndroid::AllocateSpaceForPayload(
     const std::string& metadata_filename,
     const vector<string>& key_value_pair_headers,
-    brillo::ErrorPtr* error) {
+    Error* error) {
   std::map<string, string> headers;
   if (!ParseKeyValuePairHeaders(key_value_pair_headers, &headers, error)) {
     return 0;
@@ -1144,9 +1209,11 @@
   if (apex_handler_android_ != nullptr) {
     auto result = apex_handler_android_->CalculateSize(apex_infos);
     if (!result.ok()) {
-      LogAndSetError(error,
-                     FROM_HERE,
-                     "Failed to calculate size required for compressed APEX");
+      LogAndSetGenericError(
+          error,
+          __LINE__,
+          __FILE__,
+          "Failed to calculate size required for compressed APEX");
       return 0;
     }
     apex_size_required = *result;
@@ -1154,14 +1221,26 @@
 
   string payload_id = GetPayloadId(headers);
   uint64_t required_size = 0;
+  ErrorCode error_code{};
+
   if (!DeltaPerformer::PreparePartitionsForUpdate(prefs_,
                                                   boot_control_,
                                                   GetTargetSlot(),
                                                   manifest,
                                                   payload_id,
-                                                  &required_size)) {
+                                                  &required_size,
+                                                  &error_code)) {
+    if (error_code == ErrorCode::kOverlayfsenabledError) {
+      LogAndSetError(error,
+                     __LINE__,
+                     __FILE__,
+                     "OverlayFS Shouldn't be enabled for OTA.",
+                     error_code);
+      return 0;
+    }
     if (required_size == 0) {
-      LogAndSetError(error, FROM_HERE, "Failed to allocate space for payload.");
+      LogAndSetGenericError(
+          error, __LINE__, __FILE__, "Failed to allocate space for payload.");
       return 0;
     } else {
       LOG(ERROR) << "Insufficient space for payload: " << required_size
@@ -1184,7 +1263,7 @@
 
 void UpdateAttempterAndroid::CleanupSuccessfulUpdate(
     std::unique_ptr<CleanupSuccessfulUpdateCallbackInterface> callback,
-    brillo::ErrorPtr* error) {
+    Error* error) {
   if (cleanup_previous_update_code_.has_value()) {
     LOG(INFO) << "CleanupSuccessfulUpdate has previously completed with "
               << utils::ErrorCodeToString(*cleanup_previous_update_code_);
@@ -1197,20 +1276,22 @@
   if (callback) {
     auto callback_ptr = callback.get();
     cleanup_previous_update_callbacks_.emplace_back(std::move(callback));
-    callback_ptr->RegisterForDeathNotifications(
-        base::Bind(&UpdateAttempterAndroid::RemoveCleanupPreviousUpdateCallback,
-                   base::Unretained(this),
-                   base::Unretained(callback_ptr)));
+    callback_ptr->RegisterForDeathNotifications([this, callback_ptr]() {
+      RemoveCleanupPreviousUpdateCallback(callback_ptr);
+    });
   }
   ScheduleCleanupPreviousUpdate();
 }
 
 bool UpdateAttempterAndroid::setShouldSwitchSlotOnReboot(
-    const std::string& metadata_filename, brillo::ErrorPtr* error) {
+    const std::string& metadata_filename, Error* error) {
   LOG(INFO) << "setShouldSwitchSlotOnReboot(" << metadata_filename << ")";
   if (processor_->IsRunning()) {
-    return LogAndSetError(
-        error, FROM_HERE, "Already processing an update, cancel it first.");
+    return LogAndSetGenericError(
+        error,
+        __LINE__,
+        __FILE__,
+        "Already processing an update, cancel it first.");
   }
   DeltaArchiveManifest manifest;
   TEST_AND_RETURN_FALSE(
@@ -1233,6 +1314,7 @@
       std::make_unique<PostinstallRunnerAction>(boot_control_, hardware_);
   SetStatusAndNotify(UpdateStatus::VERIFYING);
   postinstall_runner_action->set_delegate(this);
+  ErrorCode error_code{};
 
   // If last error code is kUpdatedButNotActive, we know that we reached this
   // state by calling applyPayload() with switch_slot=false. That applyPayload()
@@ -1247,19 +1329,21 @@
                                           GetTargetSlot(),
                                           manifest,
                                           false /* should update */,
-                                          nullptr)) {
-      return LogAndSetError(
-          error, FROM_HERE, "Failed to PreparePartitionsForUpdate");
+                                          nullptr,
+                                          &error_code)) {
+      return LogAndSetGenericError(
+          error, __LINE__, __FILE__, "Failed to PreparePartitionsForUpdate");
     }
-    ErrorCode error_code{};
     if (!install_plan_.ParsePartitions(manifest.partitions(),
                                        boot_control_,
                                        manifest.block_size(),
                                        &error_code)) {
       return LogAndSetError(error,
-                            FROM_HERE,
+                            __LINE__,
+                            __FILE__,
                             "Failed to LoadPartitionsFromSlots " +
-                                utils::ErrorCodeToString(error_code));
+                                utils::ErrorCodeToString(error_code),
+                            error_code);
     }
 
     auto filesystem_verifier_action =
@@ -1278,24 +1362,27 @@
   return true;
 }
 
-bool UpdateAttempterAndroid::resetShouldSwitchSlotOnReboot(
-    brillo::ErrorPtr* error) {
+bool UpdateAttempterAndroid::resetShouldSwitchSlotOnReboot(Error* error) {
   if (processor_->IsRunning()) {
-    return LogAndSetError(
-        error, FROM_HERE, "Already processing an update, cancel it first.");
+    return LogAndSetGenericError(
+        error,
+        __LINE__,
+        __FILE__,
+        "Already processing an update, cancel it first.");
   }
   TEST_AND_RETURN_FALSE(ClearUpdateCompletedMarker());
   // Update the boot flags so the current slot has higher priority.
   if (!boot_control_->SetActiveBootSlot(GetCurrentSlot())) {
-    return LogAndSetError(error, FROM_HERE, "Failed to SetActiveBootSlot");
+    return LogAndSetGenericError(
+        error, __LINE__, __FILE__, "Failed to SetActiveBootSlot");
   }
 
   // Mark the current slot as successful again, since marking it as active
   // may reset the successful bit. We ignore the result of whether marking
   // the current slot as successful worked.
   if (!boot_control_->MarkBootSuccessfulAsync(Bind([](bool successful) {}))) {
-    return LogAndSetError(
-        error, FROM_HERE, "Failed to MarkBootSuccessfulAsync");
+    return LogAndSetGenericError(
+        error, __LINE__, __FILE__, "Failed to MarkBootSuccessfulAsync");
   }
 
   // Resets the warm reset property since we won't switch the slot.
diff --git a/aosp/update_attempter_android.h b/aosp/update_attempter_android.h
index bbffbe9..b7851f1 100644
--- a/aosp/update_attempter_android.h
+++ b/aosp/update_attempter_android.h
@@ -77,28 +77,28 @@
                     int64_t payload_offset,
                     int64_t payload_size,
                     const std::vector<std::string>& key_value_pair_headers,
-                    brillo::ErrorPtr* error) override;
+                    Error* error) override;
   bool ApplyPayload(int fd,
                     int64_t payload_offset,
                     int64_t payload_size,
                     const std::vector<std::string>& key_value_pair_headers,
-                    brillo::ErrorPtr* error) override;
-  bool SuspendUpdate(brillo::ErrorPtr* error) override;
-  bool ResumeUpdate(brillo::ErrorPtr* error) override;
-  bool CancelUpdate(brillo::ErrorPtr* error) override;
-  bool ResetStatus(brillo::ErrorPtr* error) override;
+                    Error* error) override;
+  bool SuspendUpdate(Error* error) override;
+  bool ResumeUpdate(Error* error) override;
+  bool CancelUpdate(Error* error) override;
+  bool ResetStatus(Error* error) override;
   bool VerifyPayloadApplicable(const std::string& metadata_filename,
-                               brillo::ErrorPtr* error) override;
+                               Error* error) override;
   uint64_t AllocateSpaceForPayload(
       const std::string& metadata_filename,
       const std::vector<std::string>& key_value_pair_headers,
-      brillo::ErrorPtr* error) override;
+      Error* error) override;
   void CleanupSuccessfulUpdate(
       std::unique_ptr<CleanupSuccessfulUpdateCallbackInterface> callback,
-      brillo::ErrorPtr* error) override;
+      Error* error) override;
   bool setShouldSwitchSlotOnReboot(const std::string& metadata_filename,
-                                   brillo::ErrorPtr* error) override;
-  bool resetShouldSwitchSlotOnReboot(brillo::ErrorPtr* error) override;
+                                   Error* error) override;
+  bool resetShouldSwitchSlotOnReboot(Error* error) override;
 
   // ActionProcessorDelegate methods:
   void ProcessingDone(const ActionProcessor* processor,
@@ -223,10 +223,10 @@
   static bool VerifyPayloadParseManifest(const std::string& metadata_filename,
                                          std::string_view metadata_hash,
                                          DeltaArchiveManifest* manifest,
-                                         brillo::ErrorPtr* error);
+                                         Error* error);
   static bool VerifyPayloadParseManifest(const std::string& metadata_filename,
                                          DeltaArchiveManifest* manifest,
-                                         brillo::ErrorPtr* error) {
+                                         Error* error) {
     return VerifyPayloadParseManifest(metadata_filename, "", manifest, error);
   }
 
diff --git a/aosp/update_attempter_android_integration_test.cc b/aosp/update_attempter_android_integration_test.cc
index 909aa3c..8f3bad7 100644
--- a/aosp/update_attempter_android_integration_test.cc
+++ b/aosp/update_attempter_android_integration_test.cc
@@ -36,6 +36,7 @@
 #include "update_engine/aosp/daemon_state_android.h"
 #include "update_engine/aosp/update_attempter_android.h"
 #include "update_engine/common/constants.h"
+#include "update_engine/common/error.h"
 #include "update_engine/common/fake_boot_control.h"
 #include "update_engine/common/fake_hardware.h"
 #include "update_engine/common/hash_calculator.h"
@@ -299,7 +300,7 @@
                                           &metadata_size));
     LOG(INFO) << "Signature offset: " << manifest->signatures_offset()
               << ", Signature size: " << manifest->signatures_size();
-    brillo::ErrorPtr error;
+    Error error;
     HashCalculator::RawHashOfFile(payload_file_.path(), &hash);
     daemon_state_.AddObserver(this);
     ASSERT_TRUE(update_attempter_android_.ApplyPayload(
@@ -311,10 +312,10 @@
              ("=" + brillo::data_encoding::Base64Encode(hash))},
         &error));
     brillo::MessageLoop::current()->Run();
-    if (error) {
-      LOG(ERROR) << error->GetMessage();
+    if (error.error_code != ErrorCode::kSuccess) {
+      LOG(ERROR) << error.message;
     }
-    ASSERT_EQ(error, nullptr);
+    ASSERT_EQ(error.error_code, ErrorCode::kSuccess) << error.message;
     ASSERT_EQ(completion_code_, ErrorCode::kSuccess);
   }
 
diff --git a/aosp/update_engine_client_android.cc b/aosp/update_engine_client_android.cc
index c00e9f5..81736a7 100644
--- a/aosp/update_engine_client_android.cc
+++ b/aosp/update_engine_client_android.cc
@@ -117,8 +117,7 @@
   android::BinderWrapper::Create();
   android::BinderWrapper::Get()->RegisterForDeathNotifications(
       android::os::IUpdateEngine::asBinder(service_),
-      base::Bind(&UpdateEngineClientAndroid::UpdateEngineServiceDied,
-                 base::Unretained(this)));
+      [this]() { UpdateEngineServiceDied(); });
 }
 
 int UpdateEngineClientAndroid::OnInit() {
@@ -207,7 +206,7 @@
   if (FLAGS_follow) {
     // Register a callback object with the service.
     callback_ = new UECallback(this);
-    bool bound;
+    bool bound = false;
     if (!service_->bind(callback_, &bound).isOk() || !bound) {
       LOG(ERROR) << "Failed to bind() the UpdateEngine daemon.";
       return 1;
diff --git a/common/action_processor.h b/common/action_processor.h
index 6108c3e..5a4286f 100644
--- a/common/action_processor.h
+++ b/common/action_processor.h
@@ -22,7 +22,6 @@
 #include <vector>
 
 #include <base/macros.h>
-#include <brillo/errors/error.h>
 
 #include "update_engine/common/error_code.h"
 
diff --git a/common/dynamic_partition_control_interface.h b/common/dynamic_partition_control_interface.h
index 490892e..5c0a03b 100644
--- a/common/dynamic_partition_control_interface.h
+++ b/common/dynamic_partition_control_interface.h
@@ -106,7 +106,8 @@
                                           uint32_t target_slot,
                                           const DeltaArchiveManifest& manifest,
                                           bool update,
-                                          uint64_t* required_size) = 0;
+                                          uint64_t* required_size,
+                                          ErrorCode* error = nullptr) = 0;
 
   // After writing to new partitions, before rebooting into the new slot, call
   // this function to indicate writes to new partitions are done.
diff --git a/common/dynamic_partition_control_stub.cc b/common/dynamic_partition_control_stub.cc
index fd9a3b4..7d681cf 100644
--- a/common/dynamic_partition_control_stub.cc
+++ b/common/dynamic_partition_control_stub.cc
@@ -62,7 +62,8 @@
     uint32_t target_slot,
     const DeltaArchiveManifest& manifest,
     bool update,
-    uint64_t* required_size) {
+    uint64_t* required_size,
+    ErrorCode* error) {
   return true;
 }
 
diff --git a/common/dynamic_partition_control_stub.h b/common/dynamic_partition_control_stub.h
index 1db6a78..610a82a 100644
--- a/common/dynamic_partition_control_stub.h
+++ b/common/dynamic_partition_control_stub.h
@@ -43,7 +43,8 @@
                                   uint32_t target_slot,
                                   const DeltaArchiveManifest& manifest,
                                   bool update,
-                                  uint64_t* required_size) override;
+                                  uint64_t* required_size,
+                                  ErrorCode* error = nullptr) override;
 
   bool FinishUpdate(bool powerwash_required) override;
   std::unique_ptr<AbstractAction> GetCleanupPreviousUpdateAction(
diff --git a/common/error.h b/common/error.h
new file mode 100644
index 0000000..ab38e64
--- /dev/null
+++ b/common/error.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 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.
+//
+#pragma once
+
+#include "update_engine/common/error_code.h"
+#include <string>
+
+namespace chromeos_update_engine {
+
+struct Error {
+  ErrorCode error_code;
+  std::string message;
+  int line_number;
+  const char* file_name;
+};
+
+}  // namespace chromeos_update_engine
diff --git a/common/error_code.h b/common/error_code.h
index a889888..7924579 100644
--- a/common/error_code.h
+++ b/common/error_code.h
@@ -87,6 +87,7 @@
   kDeviceCorrupted = 61,
   kPackageExcludedFromUpdate = 62,
   kPostInstallMountError = 63,
+  kOverlayfsenabledError = 64,
 
   // VERY IMPORTANT! When adding new error codes:
   //
diff --git a/common/error_code_utils.cc b/common/error_code_utils.cc
index 421544a..12a98bf 100644
--- a/common/error_code_utils.cc
+++ b/common/error_code_utils.cc
@@ -175,6 +175,8 @@
       return "ErrorCode::kPackageExcludedFromUpdate";
     case ErrorCode::kPostInstallMountError:
       return "ErrorCode::kPostInstallMountError";
+    case ErrorCode::kOverlayfsenabledError:
+      return "ErrorCode::kOverlayfsenabledError";
       // Don't add a default case to let the compiler warn about newly added
       // error codes which should be added here.
   }
diff --git a/common/mock_dynamic_partition_control.h b/common/mock_dynamic_partition_control.h
index 79909b4..968246a 100644
--- a/common/mock_dynamic_partition_control.h
+++ b/common/mock_dynamic_partition_control.h
@@ -66,11 +66,15 @@
                std::optional<uint64_t> label),
               (override));
 
-  MOCK_METHOD(
-      bool,
-      PreparePartitionsForUpdate,
-      (uint32_t, uint32_t, const DeltaArchiveManifest&, bool, uint64_t*),
-      (override));
+  MOCK_METHOD(bool,
+              PreparePartitionsForUpdate,
+              (uint32_t,
+               uint32_t,
+               const DeltaArchiveManifest&,
+               bool,
+               uint64_t*,
+               ErrorCode*),
+              (override));
 
   MOCK_METHOD(bool, ResetUpdate, (PrefsInterface*), (override));
   MOCK_METHOD(std::unique_ptr<AbstractAction>,
diff --git a/libbinderwrapper/Android.bp b/libbinderwrapper/Android.bp
new file mode 100644
index 0000000..87c55fa
--- /dev/null
+++ b/libbinderwrapper/Android.bp
@@ -0,0 +1,63 @@
+//
+// Copyright (C) 2015 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.
+//
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_defaults {
+    name: "libbinderwrapper_defaults",
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+
+    ],
+    export_include_dirs: ["include"],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+    ],
+}
+
+// libbinderwrapper shared library
+// ========================================================
+cc_library_shared {
+    name: "libbinderwrapper",
+    defaults: ["libbinderwrapper_defaults"],
+    vendor_available: true,
+
+    srcs: [
+        "binder_wrapper.cc",
+        "real_binder_wrapper.cc",
+    ],
+}
+
+// libbinderwrapper_test_support static library
+// ========================================================
+cc_library_static {
+    name: "libbinderwrapper_test_support",
+    defaults: ["libbinderwrapper_defaults"],
+
+    static_libs: ["libgtest"],
+    shared_libs: ["libbinderwrapper"],
+
+    srcs: [
+        "binder_test_base.cc",
+        "stub_binder_wrapper.cc",
+    ],
+}
diff --git a/libbinderwrapper/binder_test_base.cc b/libbinderwrapper/binder_test_base.cc
new file mode 100644
index 0000000..af93a04
--- /dev/null
+++ b/libbinderwrapper/binder_test_base.cc
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 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 <binderwrapper/binder_test_base.h>
+
+#include <binderwrapper/binder_wrapper.h>
+#include <binderwrapper/stub_binder_wrapper.h>
+
+namespace android {
+
+BinderTestBase::BinderTestBase() : binder_wrapper_(new StubBinderWrapper()) {
+  // Pass ownership.
+  BinderWrapper::InitForTesting(binder_wrapper_);
+}
+
+BinderTestBase::~BinderTestBase() {
+  BinderWrapper::Destroy();
+}
+
+}  // namespace android
diff --git a/libbinderwrapper/binder_wrapper.cc b/libbinderwrapper/binder_wrapper.cc
new file mode 100644
index 0000000..f5e7476
--- /dev/null
+++ b/libbinderwrapper/binder_wrapper.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 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 <binderwrapper/binder_wrapper.h>
+
+#include <android-base/logging.h>
+
+#include "real_binder_wrapper.h"
+
+namespace android {
+
+// Singleton instance.
+BinderWrapper* BinderWrapper::instance_ = nullptr;
+
+// static
+void BinderWrapper::Create() {
+  CHECK(!instance_) << "Already initialized; missing call to Destroy()?";
+  instance_ = new RealBinderWrapper();
+}
+
+// static
+void BinderWrapper::InitForTesting(BinderWrapper* wrapper) {
+  CHECK(!instance_) << "Already initialized; missing call to Destroy()?";
+  instance_ = wrapper;
+}
+
+// static
+void BinderWrapper::Destroy() {
+  CHECK(instance_) << "Not initialized; missing call to Create()?";
+  delete instance_;
+  instance_ = nullptr;
+}
+
+// static
+BinderWrapper* BinderWrapper::Get() {
+  CHECK(instance_) << "Not initialized; missing call to Create()?";
+  return instance_;
+}
+
+// static
+BinderWrapper* BinderWrapper::GetOrCreateInstance() {
+  if (!instance_)
+    instance_ = new RealBinderWrapper();
+  return instance_;
+}
+
+}  // namespace android
diff --git a/libbinderwrapper/include/binderwrapper/binder_test_base.h b/libbinderwrapper/include/binderwrapper/binder_test_base.h
new file mode 100644
index 0000000..46eca3f
--- /dev/null
+++ b/libbinderwrapper/include/binderwrapper/binder_test_base.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 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 SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_TEST_BASE_H_
+#define SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_TEST_BASE_H_
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class StubBinderWrapper;
+
+// Class that can be inherited from (or aliased via typedef/using) when writing
+// tests that use StubBinderManager.
+class BinderTestBase : public ::testing::Test {
+ public:
+  BinderTestBase();
+  ~BinderTestBase() override;
+
+  StubBinderWrapper* binder_wrapper() { return binder_wrapper_; }
+
+ protected:
+  StubBinderWrapper* binder_wrapper_;  // Not owned.
+
+ private:
+   BinderTestBase(const BinderTestBase&) = delete;
+};
+
+}  // namespace android
+
+#endif  // SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_TEST_BASE_H_
diff --git a/libbinderwrapper/include/binderwrapper/binder_wrapper.h b/libbinderwrapper/include/binderwrapper/binder_wrapper.h
new file mode 100644
index 0000000..fc5708b
--- /dev/null
+++ b/libbinderwrapper/include/binderwrapper/binder_wrapper.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 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 SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_WRAPPER_H_
+#define SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_WRAPPER_H_
+
+#include <sys/types.h>
+
+#include <functional>
+#include <string>
+
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class BBinder;
+class IBinder;
+
+// Wraps libbinder to make it testable.
+// NOTE: Static methods of this class are not thread-safe.
+class BinderWrapper {
+ public:
+  virtual ~BinderWrapper() {}
+
+  // Creates and initializes the singleton (using a wrapper that communicates
+  // with the real binder system).
+  static void Create();
+
+  // Initializes |wrapper| as the singleton, taking ownership of it. Tests that
+  // want to inject their own wrappers should call this instead of Create().
+  static void InitForTesting(BinderWrapper* wrapper);
+
+  // Destroys the singleton. Must be called before calling Create() or
+  // InitForTesting() a second time.
+  static void Destroy();
+
+  // Returns the singleton instance previously created by Create() or set by
+  // InitForTesting().
+  static BinderWrapper* Get();
+
+  // Returns the singleton instance if it was previously created by Create() or
+  // set by InitForTesting(), or creates a new one by calling Create().
+  static BinderWrapper* GetOrCreateInstance();
+
+  // Gets the binder for communicating with the service identified by
+  // |service_name|, returning null immediately if it doesn't exist.
+  virtual sp<IBinder> GetService(const std::string& service_name) = 0;
+
+  // Registers |binder| as |service_name| with the service manager.
+  virtual bool RegisterService(const std::string& service_name,
+                               const sp<IBinder>& binder) = 0;
+
+  // Creates a local binder object.
+  virtual sp<BBinder> CreateLocalBinder() = 0;
+
+  // Registers |callback| to be invoked when |binder| dies. If another callback
+  // is currently registered for |binder|, it will be replaced.
+  virtual bool RegisterForDeathNotifications(const sp<IBinder>& binder,
+                                             const std::function<void()>&) = 0;
+
+  // Unregisters the callback, if any, for |binder|.
+  virtual bool UnregisterForDeathNotifications(const sp<IBinder>& binder) = 0;
+
+  // When called while in a transaction, returns the caller's UID or PID.
+  virtual uid_t GetCallingUid() = 0;
+  virtual pid_t GetCallingPid() = 0;
+
+ private:
+  static BinderWrapper* instance_;
+};
+
+}  // namespace android
+
+#endif  // SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_WRAPPER_H_
diff --git a/libbinderwrapper/include/binderwrapper/stub_binder_wrapper.h b/libbinderwrapper/include/binderwrapper/stub_binder_wrapper.h
new file mode 100644
index 0000000..2a7f77e
--- /dev/null
+++ b/libbinderwrapper/include/binderwrapper/stub_binder_wrapper.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2015 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 SYSTEM_CORE_INCLUDE_BINDERWRAPPER_STUB_BINDER_WRAPPER_H_
+#define SYSTEM_CORE_INCLUDE_BINDERWRAPPER_STUB_BINDER_WRAPPER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binderwrapper/binder_wrapper.h>
+
+namespace android {
+
+// Stub implementation of BinderWrapper for testing.
+//
+// Example usage:
+//
+// First, assuming a base IFoo binder interface, create a stub class that
+// derives from BnFoo to implement the receiver side of the communication:
+//
+//   class StubFoo : public BnFoo {
+//    public:
+//     ...
+//     status_t doSomething(int arg) override {
+//       // e.g. save passed-in value for later inspection by tests.
+//       return OK;
+//     }
+//   };
+//
+// Next, from your test code, inject a StubBinderManager either directly or by
+// inheriting from the BinderTestBase class:
+//
+//   StubBinderWrapper* wrapper = new StubBinderWrapper();
+//   BinderWrapper::InitForTesting(wrapper);  // Takes ownership.
+//
+// Also from your test, create a StubFoo and register it with the wrapper:
+//
+//   StubFoo* foo = new StubFoo();
+//   sp<IBinder> binder(foo);
+//   wrapper->SetBinderForService("foo", binder);
+//
+// The code being tested can now use the wrapper to get the stub and call it:
+//
+//   sp<IBinder> binder = BinderWrapper::Get()->GetService("foo");
+//   CHECK(binder.get());
+//   sp<IFoo> foo = interface_cast<IFoo>(binder);
+//   CHECK_EQ(foo->doSomething(3), OK);
+//
+// To create a local BBinder object, production code can call
+// CreateLocalBinder(). Then, a test can get the BBinder's address via
+// local_binders() to check that they're passed as expected in binder calls.
+//
+class StubBinderWrapper : public BinderWrapper {
+ public:
+  StubBinderWrapper();
+  ~StubBinderWrapper() override;
+
+  const std::vector<sp<BBinder>>& local_binders() const {
+    return local_binders_;
+  }
+  void clear_local_binders() { local_binders_.clear(); }
+
+  void set_calling_uid(uid_t uid) { calling_uid_ = uid; }
+  void set_calling_pid(pid_t pid) { calling_pid_ = pid; }
+
+  // Sets the binder to return when |service_name| is passed to GetService() or
+  // WaitForService().
+  void SetBinderForService(const std::string& service_name,
+                           const sp<IBinder>& binder);
+
+  // Returns the binder previously registered for |service_name| via
+  // RegisterService(), or null if the service hasn't been registered.
+  sp<IBinder> GetRegisteredService(const std::string& service_name) const;
+
+  // Run the calback in |death_callbacks_| corresponding to |binder|.
+  void NotifyAboutBinderDeath(const sp<IBinder>& binder);
+
+  // BinderWrapper:
+  sp<IBinder> GetService(const std::string& service_name) override;
+  bool RegisterService(const std::string& service_name,
+                       const sp<IBinder>& binder) override;
+  sp<BBinder> CreateLocalBinder() override;
+  bool RegisterForDeathNotifications(const sp<IBinder>& binder,
+                                     const std::function<void()>& callback) override;
+  bool UnregisterForDeathNotifications(const sp<IBinder>& binder) override;
+  uid_t GetCallingUid() override;
+  pid_t GetCallingPid() override;
+
+ private:
+  using ServiceMap = std::map<std::string, sp<IBinder>>;
+
+  // Map from service name to associated binder handle. Used by GetService() and
+  // WaitForService().
+  ServiceMap services_to_return_;
+
+  // Map from service name to associated binder handle. Updated by
+  // RegisterService().
+  ServiceMap registered_services_;
+
+  // Local binders returned by CreateLocalBinder().
+  std::vector<sp<BBinder>> local_binders_;
+
+  // Map from binder handle to the callback that should be invoked on binder
+  // death.
+  std::map<sp<IBinder>, std::function<void()>> death_callbacks_;
+
+  // Values to return from GetCallingUid() and GetCallingPid();
+  uid_t calling_uid_;
+  pid_t calling_pid_;
+
+  StubBinderWrapper(const StubBinderWrapper&) = delete;
+};
+
+}  // namespace android
+
+#endif  // SYSTEM_CORE_INCLUDE_BINDERWRAPPER_STUB_BINDER_WRAPPER_H_
diff --git a/libbinderwrapper/real_binder_wrapper.cc b/libbinderwrapper/real_binder_wrapper.cc
new file mode 100644
index 0000000..d214ce3
--- /dev/null
+++ b/libbinderwrapper/real_binder_wrapper.cc
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2015 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 "real_binder_wrapper.h"
+
+#include <android-base/logging.h>
+
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+// Class that handles binder death notifications. libbinder wants the recipient
+// to be wrapped in sp<>, so registering RealBinderWrapper as a recipient would
+// be awkward.
+class RealBinderWrapper::DeathRecipient : public IBinder::DeathRecipient {
+ public:
+   explicit DeathRecipient(const std::function<void()>& callback)
+       : callback_(std::move(callback)) {}
+   ~DeathRecipient() = default;
+
+   // IBinder::DeathRecipient:
+   void binderDied(const wp<IBinder>& who) override { callback_(); }
+
+ private:
+  // Callback to run in response to binder death.
+   std::function<void()> callback_;
+
+   DISALLOW_COPY_AND_ASSIGN(DeathRecipient);
+};
+
+RealBinderWrapper::RealBinderWrapper() = default;
+
+RealBinderWrapper::~RealBinderWrapper() = default;
+
+sp<IBinder> RealBinderWrapper::GetService(const std::string& service_name) {
+  sp<IServiceManager> service_manager = defaultServiceManager();
+  if (!service_manager.get()) {
+    LOG(ERROR) << "Unable to get service manager";
+    return sp<IBinder>();
+  }
+  sp<IBinder> binder =
+      service_manager->checkService(String16(service_name.c_str()));
+  if (!binder.get())
+    LOG(ERROR) << "Unable to get \"" << service_name << "\" service";
+  return binder;
+}
+
+bool RealBinderWrapper::RegisterService(const std::string& service_name,
+                                        const sp<IBinder>& binder) {
+  sp<IServiceManager> service_manager = defaultServiceManager();
+  if (!service_manager.get()) {
+    LOG(ERROR) << "Unable to get service manager";
+    return false;
+  }
+  status_t status = defaultServiceManager()->addService(
+      String16(service_name.c_str()), binder);
+  if (status != OK) {
+    LOG(ERROR) << "Failed to register \"" << service_name << "\" with service "
+               << "manager";
+    return false;
+  }
+  return true;
+}
+
+sp<BBinder> RealBinderWrapper::CreateLocalBinder() {
+  return sp<BBinder>(new BBinder());
+}
+
+bool RealBinderWrapper::RegisterForDeathNotifications(const sp<IBinder>& binder,
+                                                      const std::function<void()>& callback) {
+  sp<DeathRecipient> recipient(new DeathRecipient(callback));
+  if (binder->linkToDeath(recipient) != OK) {
+    LOG(ERROR) << "Failed to register for death notifications on "
+               << binder.get();
+    return false;
+  }
+  death_recipients_[binder] = recipient;
+  return true;
+}
+
+bool RealBinderWrapper::UnregisterForDeathNotifications(
+    const sp<IBinder>& binder) {
+  auto it = death_recipients_.find(binder);
+  if (it == death_recipients_.end()) {
+    LOG(ERROR) << "Not registered for death notifications on " << binder.get();
+    return false;
+  }
+  if (binder->unlinkToDeath(it->second) != OK) {
+    LOG(ERROR) << "Failed to unregister for death notifications on "
+               << binder.get();
+    return false;
+  }
+  death_recipients_.erase(it);
+  return true;
+}
+
+uid_t RealBinderWrapper::GetCallingUid() {
+  return IPCThreadState::self()->getCallingUid();
+}
+
+pid_t RealBinderWrapper::GetCallingPid() {
+  return IPCThreadState::self()->getCallingPid();
+}
+
+}  // namespace android
diff --git a/libbinderwrapper/real_binder_wrapper.h b/libbinderwrapper/real_binder_wrapper.h
new file mode 100644
index 0000000..d0468f0
--- /dev/null
+++ b/libbinderwrapper/real_binder_wrapper.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 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 SYSTEM_CORE_LIBBINDERWRAPPER_REAL_BINDER_WRAPPER_H_
+#define SYSTEM_CORE_LIBBINDERWRAPPER_REAL_BINDER_WRAPPER_H_
+
+#include <map>
+
+#include <binderwrapper/binder_wrapper.h>
+
+namespace android {
+
+class IBinder;
+
+// Real implementation of BinderWrapper.
+class RealBinderWrapper : public BinderWrapper {
+ public:
+  RealBinderWrapper();
+  ~RealBinderWrapper() override;
+
+  // BinderWrapper:
+  sp<IBinder> GetService(const std::string& service_name) override;
+  bool RegisterService(const std::string& service_name,
+                       const sp<IBinder>& binder) override;
+  sp<BBinder> CreateLocalBinder() override;
+  bool RegisterForDeathNotifications(const sp<IBinder>& binder,
+                                     const std::function<void()>& callback) override;
+  bool UnregisterForDeathNotifications(const sp<IBinder>& binder) override;
+  uid_t GetCallingUid() override;
+  pid_t GetCallingPid() override;
+
+ private:
+  class DeathRecipient;
+
+  // Map from binder handle to object that should be notified of the binder's
+  // death.
+  std::map<sp<IBinder>, sp<DeathRecipient>> death_recipients_;
+
+  RealBinderWrapper(const RealBinderWrapper&) = delete;
+};
+
+}  // namespace android
+
+#endif  // SYSTEM_CORE_LIBBINDER_WRAPPER_REAL_BINDER_WRAPPER_H_
diff --git a/libbinderwrapper/stub_binder_wrapper.cc b/libbinderwrapper/stub_binder_wrapper.cc
new file mode 100644
index 0000000..fabf122
--- /dev/null
+++ b/libbinderwrapper/stub_binder_wrapper.cc
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2015 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 <binderwrapper/stub_binder_wrapper.h>
+
+#include <android-base/logging.h>
+
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+
+namespace android {
+
+StubBinderWrapper::StubBinderWrapper()
+    : calling_uid_(-1),
+      calling_pid_(-1) {}
+
+StubBinderWrapper::~StubBinderWrapper() = default;
+
+void StubBinderWrapper::SetBinderForService(const std::string& service_name,
+                                            const sp<IBinder>& binder) {
+  services_to_return_[service_name] = binder;
+}
+
+sp<IBinder> StubBinderWrapper::GetRegisteredService(
+    const std::string& service_name) const {
+  const auto it = registered_services_.find(service_name);
+  return it != registered_services_.end() ? it->second : sp<IBinder>();
+}
+
+void StubBinderWrapper::NotifyAboutBinderDeath(const sp<IBinder>& binder) {
+  const auto it = death_callbacks_.find(binder);
+  if (it != death_callbacks_.end()) it->second();
+}
+
+sp<IBinder> StubBinderWrapper::GetService(const std::string& service_name) {
+  const auto it = services_to_return_.find(service_name);
+  return it != services_to_return_.end() ? it->second : sp<IBinder>();
+}
+
+bool StubBinderWrapper::RegisterService(const std::string& service_name,
+                                        const sp<IBinder>& binder) {
+  registered_services_[service_name] = binder;
+  return true;
+}
+
+sp<BBinder> StubBinderWrapper::CreateLocalBinder() {
+  sp<BBinder> binder(new BBinder());
+  local_binders_.push_back(binder);
+  return binder;
+}
+
+bool StubBinderWrapper::RegisterForDeathNotifications(const sp<IBinder>& binder,
+                                                      const std::function<void()>& callback) {
+  death_callbacks_[binder] = callback;
+  return true;
+}
+
+bool StubBinderWrapper::UnregisterForDeathNotifications(
+    const sp<IBinder>& binder) {
+  death_callbacks_.erase(binder);
+  return true;
+}
+
+uid_t StubBinderWrapper::GetCallingUid() {
+  return calling_uid_;
+}
+
+pid_t StubBinderWrapper::GetCallingPid() {
+  return calling_pid_;
+}
+
+}  // namespace android
diff --git a/metrics_utils.cc b/metrics_utils.cc
index ec35fe2..94a0520 100644
--- a/metrics_utils.cc
+++ b/metrics_utils.cc
@@ -73,6 +73,7 @@
     case ErrorCode::kVerityCalculationError:
     case ErrorCode::kNotEnoughSpace:
     case ErrorCode::kDeviceCorrupted:
+    case ErrorCode::kOverlayfsenabledError:
       return metrics::AttemptResult::kOperationExecutionError;
 
     case ErrorCode::kDownloadMetadataSignatureMismatch:
@@ -190,6 +191,7 @@
     case ErrorCode::kFilesystemCopierError:
     case ErrorCode::kPostinstallRunnerError:
     case ErrorCode::kPostInstallMountError:
+    case ErrorCode::kOverlayfsenabledError:
     case ErrorCode::kPayloadMismatchedType:
     case ErrorCode::kInstallDeviceOpenError:
     case ErrorCode::kKernelDeviceOpenError:
@@ -367,8 +369,8 @@
   TimeDelta time_to_reboot = current_time - system_updated_at;
   if (time_to_reboot.ToInternalValue() < 0) {
     LOG(WARNING) << "time_to_reboot is negative - system_updated_at: "
-                 << utils::ToString(system_updated_at) << " current time: "
-                 << utils::ToString(current_time);
+                 << utils::ToString(system_updated_at)
+                 << " current time: " << utils::ToString(current_time);
     return false;
   }
   metrics_reporter->ReportTimeToReboot(time_to_reboot.InMinutes());
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 298bde1..267805e 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -440,6 +440,9 @@
 // were written, or false on any error, regardless of progress
 // and stores an action exit code in |error|.
 bool DeltaPerformer::Write(const void* bytes, size_t count, ErrorCode* error) {
+  if (!error) {
+    return false;
+  }
   *error = ErrorCode::kSuccess;
   const char* c_bytes = reinterpret_cast<const char*>(bytes);
 
@@ -730,8 +733,10 @@
   // slot suffix of the partitions in the metadata.
   if (install_plan_->target_slot != BootControlInterface::kInvalidSlot) {
     uint64_t required_size = 0;
-    if (!PreparePartitionsForUpdate(&required_size)) {
-      if (required_size > 0) {
+    if (!PreparePartitionsForUpdate(&required_size, error)) {
+      if (*error == ErrorCode::kOverlayfsenabledError) {
+        return false;
+      } else if (required_size > 0) {
         *error = ErrorCode::kNotEnoughSpace;
       } else {
         *error = ErrorCode::kInstallDeviceOpenError;
@@ -802,7 +807,8 @@
   return true;
 }
 
-bool DeltaPerformer::PreparePartitionsForUpdate(uint64_t* required_size) {
+bool DeltaPerformer::PreparePartitionsForUpdate(uint64_t* required_size,
+                                                ErrorCode* error) {
   // Call static PreparePartitionsForUpdate with hash from
   // kPrefsUpdateCheckResponseHash to ensure hash of payload that space is
   // preallocated for is the same as the hash of payload being applied.
@@ -814,7 +820,8 @@
                                     install_plan_->target_slot,
                                     manifest_,
                                     update_check_response_hash,
-                                    required_size);
+                                    required_size,
+                                    error);
 }
 
 bool DeltaPerformer::PreparePartitionsForUpdate(
@@ -823,7 +830,8 @@
     BootControlInterface::Slot target_slot,
     const DeltaArchiveManifest& manifest,
     const std::string& update_check_response_hash,
-    uint64_t* required_size) {
+    uint64_t* required_size,
+    ErrorCode* error) {
   string last_hash;
   ignore_result(
       prefs->GetString(kPrefsDynamicPartitionMetadataUpdated, &last_hash));
@@ -845,9 +853,11 @@
           target_slot,
           manifest,
           !is_resume /* should update */,
-          required_size)) {
+          required_size,
+          error)) {
     LOG(ERROR) << "Unable to initialize partition metadata for slot "
-               << BootControlInterface::SlotName(target_slot);
+               << BootControlInterface::SlotName(target_slot) << " "
+               << utils::ErrorCodeToString(*error);
     return false;
   }
 
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index 2616b6e..e83e000 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -158,7 +158,7 @@
   // complete metadata. Returns kMetadataParseError if the metadata can't be
   // parsed given the payload.
   MetadataParseResult ParsePayloadMetadata(const brillo::Blob& payload,
-                                           ErrorCode* error);
+                                           ErrorCode* error = nullptr);
 
   void set_public_key_path(const std::string& public_key_path) {
     public_key_path_ = public_key_path;
@@ -189,7 +189,8 @@
       BootControlInterface::Slot target_slot,
       const DeltaArchiveManifest& manifest,
       const std::string& update_check_response_hash,
-      uint64_t* required_size);
+      uint64_t* required_size,
+      ErrorCode* error = nullptr);
 
  protected:
   // Exposed as virtual for testing purposes.
@@ -221,7 +222,7 @@
   // Parse and move the update instructions of all partitions into our local
   // |partitions_| variable based on the version of the payload. Requires the
   // manifest to be parsed and valid.
-  bool ParseManifestPartitions(ErrorCode* error);
+  bool ParseManifestPartitions(ErrorCode* error = nullptr);
 
   // Appends up to |*count_p| bytes from |*bytes_p| to |buffer_|, but only to
   // the extent that the size of |buffer_| does not exceed |max|. Advances
@@ -233,7 +234,7 @@
   // sets |*error| accordingly. Otherwise does nothing. Returns |op_result|.
   bool HandleOpResult(bool op_result,
                       const char* op_type_name,
-                      ErrorCode* error);
+                      ErrorCode* error = nullptr);
 
   // Logs the progress of downloading/applying an update.
   void LogProgress(const char* message_prefix);
@@ -263,9 +264,9 @@
   bool PerformReplaceOperation(const InstallOperation& operation);
   bool PerformZeroOrDiscardOperation(const InstallOperation& operation);
   bool PerformSourceCopyOperation(const InstallOperation& operation,
-                                  ErrorCode* error);
+                                  ErrorCode* error = nullptr);
   bool PerformDiffOperation(const InstallOperation& operation,
-                            ErrorCode* error);
+                            ErrorCode* error = nullptr);
 
   // Extracts the payload signature message from the current |buffer_| if the
   // offset matches the one specified by the manifest. Returns whether the
@@ -300,7 +301,8 @@
   // After install_plan_ is filled with partition names and sizes, initialize
   // metadata of partitions and map necessary devices before opening devices.
   // Also see comment for the static PreparePartitionsForUpdate().
-  bool PreparePartitionsForUpdate(uint64_t* required_size);
+  bool PreparePartitionsForUpdate(uint64_t* required_size,
+                                  ErrorCode* error = nullptr);
 
   // Check if current manifest contains timestamp errors.
   // Return:
diff --git a/payload_consumer/file_descriptor.cc b/payload_consumer/file_descriptor.cc
index da76327..ff27b73 100644
--- a/payload_consumer/file_descriptor.cc
+++ b/payload_consumer/file_descriptor.cc
@@ -77,7 +77,7 @@
 uint64_t EintrSafeFileDescriptor::BlockDevSize() {
   if (fd_ < 0)
     return 0;
-  struct stat stbuf;
+  struct stat stbuf {};
   if (fstat(fd_, &stbuf) < 0) {
     PLOG(ERROR) << "Error stat-ing fd " << fd_;
     return 0;
@@ -102,7 +102,7 @@
   // On some devices, the BLKDISCARD will actually read back as zeros, instead
   // of "undefined" data. The BLKDISCARDZEROES ioctl tells whether that's the
   // case, so we issue a BLKDISCARD in those cases to speed up the writes.
-  unsigned int arg;
+  unsigned int arg{};
   if (request == BLKZEROOUT && ioctl(fd_, BLKDISCARDZEROES, &arg) == 0 && arg)
     request = BLKDISCARD;
 
diff --git a/payload_consumer/verified_source_fd.cc b/payload_consumer/verified_source_fd.cc
index 09ac95b..3f17ad7 100644
--- a/payload_consumer/verified_source_fd.cc
+++ b/payload_consumer/verified_source_fd.cc
@@ -159,7 +159,9 @@
   source_fd_ = std::make_shared<EintrSafeFileDescriptor>();
   if (source_fd_ == nullptr)
     return false;
-  TEST_AND_RETURN_FALSE_ERRNO(source_fd_->Open(source_path_.c_str(), O_RDONLY));
+  if (!source_fd_->Open(source_path_.c_str(), O_RDONLY)) {
+    PLOG(ERROR) << "Failed to open " << source_path_;
+  }
   return true;
 }
 
diff --git a/payload_consumer/xor_extent_writer.cc b/payload_consumer/xor_extent_writer.cc
index 4534c05..fe7eca7 100644
--- a/payload_consumer/xor_extent_writer.cc
+++ b/payload_consumer/xor_extent_writer.cc
@@ -20,15 +20,95 @@
 
 #include "update_engine/common/utils.h"
 #include "update_engine/payload_consumer/xor_extent_writer.h"
+#include "update_engine/payload_generator/extent_ranges.h"
 #include "update_engine/payload_generator/extent_utils.h"
+#include "update_engine/update_metadata.pb.h"
 
 namespace chromeos_update_engine {
+bool XORExtentWriter::WriteXorCowOp(const uint8_t* bytes,
+                                    const size_t size,
+                                    const Extent& xor_ext,
+                                    const size_t src_offset) {
+  xor_block_data.resize(BlockSize() * xor_ext.num_blocks());
+  const auto src_block = src_offset / BlockSize();
+  ssize_t bytes_read = 0;
+  TEST_AND_RETURN_FALSE_ERRNO(utils::PReadAll(source_fd_,
+                                              xor_block_data.data(),
+                                              xor_block_data.size(),
+                                              src_offset,
+                                              &bytes_read));
+  if (bytes_read != static_cast<ssize_t>(xor_block_data.size())) {
+    LOG(ERROR) << "bytes_read: " << bytes_read << ", expected to read "
+               << xor_block_data.size() << " at block " << src_block
+               << " offset " << src_offset % BlockSize();
+    return false;
+  }
+
+  std::transform(xor_block_data.cbegin(),
+                 xor_block_data.cbegin() + xor_block_data.size(),
+                 bytes,
+                 xor_block_data.begin(),
+                 std::bit_xor<unsigned char>{});
+  TEST_AND_RETURN_FALSE(cow_writer_->AddXorBlocks(xor_ext.start_block(),
+                                                  xor_block_data.data(),
+                                                  xor_block_data.size(),
+                                                  src_block,
+                                                  src_offset % BlockSize()));
+  return true;
+}
+
+bool XORExtentWriter::WriteXorExtent(const uint8_t* bytes,
+                                     const size_t size,
+                                     const Extent& xor_ext,
+                                     const CowMergeOperation* merge_op) {
+  const auto src_block = merge_op->src_extent().start_block() +
+                         xor_ext.start_block() -
+                         merge_op->dst_extent().start_block();
+  const auto read_end_offset =
+      (src_block + xor_ext.num_blocks()) * BlockSize() + merge_op->src_offset();
+  const auto is_out_of_bound_read =
+      read_end_offset > partition_size_ && partition_size_ != 0;
+  const auto oob_bytes =
+      is_out_of_bound_read ? read_end_offset - partition_size_ : 0;
+  if (is_out_of_bound_read) {
+    if (oob_bytes >= BlockSize()) {
+      LOG(ERROR) << "XOR op overflowed source partition by more than "
+                 << BlockSize() << ", " << xor_ext << ", " << merge_op
+                 << ", out of bound bytes: " << oob_bytes
+                 << ", partition size: " << partition_size_;
+      return false;
+    }
+    if (oob_bytes > merge_op->src_offset()) {
+      LOG(ERROR) << "XOR op overflowed source offset, out of bound bytes: "
+                 << oob_bytes << ", source offset: " << merge_op->src_offset();
+    }
+    Extent non_oob_extent =
+        ExtentForRange(xor_ext.start_block(), xor_ext.num_blocks() - 1);
+    if (non_oob_extent.num_blocks() > 0) {
+      TEST_AND_RETURN_FALSE(
+          WriteXorCowOp(bytes,
+                        BlockSize() * non_oob_extent.num_blocks(),
+                        non_oob_extent,
+                        src_block * BlockSize() + merge_op->src_offset()));
+    }
+    const Extent last_block =
+        ExtentForRange(xor_ext.start_block() + xor_ext.num_blocks() - 1, 1);
+    TEST_AND_RETURN_FALSE(
+        WriteXorCowOp(bytes + (xor_ext.num_blocks() - 1) * BlockSize(),
+                      BlockSize(),
+                      last_block,
+                      (src_block + xor_ext.num_blocks() - 1) * BlockSize()));
+    return true;
+  }
+  TEST_AND_RETURN_FALSE(WriteXorCowOp(
+      bytes, size, xor_ext, src_block * BlockSize() + merge_op->src_offset()));
+  return true;
+}
 
 // Returns true on success.
 bool XORExtentWriter::WriteExtent(const void* bytes,
                                   const Extent& extent,
                                   const size_t size) {
-  brillo::Blob xor_block_data;
   const auto xor_extents = xor_map_.GetIntersectingExtents(extent);
   for (const auto& xor_ext : xor_extents) {
     const auto merge_op_opt = xor_map_.Get(xor_ext);
@@ -60,52 +140,16 @@
                  << xor_ext << " xor_map extent: " << merge_op->dst_extent();
       return false;
     }
-    const auto src_offset = merge_op->src_offset();
-    const auto src_block = merge_op->src_extent().start_block() +
-                           xor_ext.start_block() -
-                           merge_op->dst_extent().start_block();
     const auto i = xor_ext.start_block() - extent.start_block();
     const auto dst_block_data =
         static_cast<const unsigned char*>(bytes) + i * BlockSize();
-    const auto is_out_of_bound_read =
-        (src_block + xor_ext.num_blocks()) * BlockSize() + src_offset >
-            partition_size_ &&
-        partition_size_ != 0;
-    if (is_out_of_bound_read) {
-      LOG(INFO) << "Getting partial read for last block, converting "
-                   "XOR operation to a regular replace "
-                << xor_ext;
-      TEST_AND_RETURN_FALSE(
-          cow_writer_->AddRawBlocks(xor_ext.start_block(),
-                                    dst_block_data,
-                                    xor_ext.num_blocks() * BlockSize()));
-      continue;
-    }
-    xor_block_data.resize(BlockSize() * xor_ext.num_blocks());
-    ssize_t bytes_read = 0;
-    TEST_AND_RETURN_FALSE_ERRNO(
-        utils::PReadAll(source_fd_,
-                        xor_block_data.data(),
-                        xor_block_data.size(),
-                        src_offset + src_block * BlockSize(),
-                        &bytes_read));
-    if (bytes_read != static_cast<ssize_t>(xor_block_data.size())) {
-      LOG(ERROR) << "bytes_read: " << bytes_read << ", expected to read "
-                 << xor_block_data.size() << " at block " << src_block
-                 << " offset " << src_offset;
+    if (!WriteXorExtent(dst_block_data,
+                        xor_ext.num_blocks() * BlockSize(),
+                        xor_ext,
+                        merge_op)) {
+      LOG(ERROR) << "Failed to write XOR extent " << xor_ext;
       return false;
     }
-
-    std::transform(xor_block_data.cbegin(),
-                   xor_block_data.cbegin() + xor_block_data.size(),
-                   dst_block_data,
-                   xor_block_data.begin(),
-                   std::bit_xor<unsigned char>{});
-    TEST_AND_RETURN_FALSE(cow_writer_->AddXorBlocks(xor_ext.start_block(),
-                                                    xor_block_data.data(),
-                                                    xor_block_data.size(),
-                                                    src_block,
-                                                    src_offset));
   }
   const auto replace_extents = xor_map_.GetNonIntersectingExtents(extent);
   return WriteReplaceExtents(replace_extents, extent, bytes, size);
diff --git a/payload_consumer/xor_extent_writer.h b/payload_consumer/xor_extent_writer.h
index 57c99c2..2074ee2 100644
--- a/payload_consumer/xor_extent_writer.h
+++ b/payload_consumer/xor_extent_writer.h
@@ -56,10 +56,19 @@
                            const Extent& extent,
                            const void* bytes,
                            size_t size);
+  bool WriteXorExtent(const uint8_t* bytes,
+                      const size_t size,
+                      const Extent& xor_ext,
+                      const CowMergeOperation* merge_op);
+  bool WriteXorCowOp(const uint8_t* bytes,
+                     const size_t size,
+                     const Extent& xor_ext,
+                     size_t src_offset);
   const google::protobuf::RepeatedPtrField<Extent>& src_extents_;
   const FileDescriptorPtr source_fd_;
   const ExtentMap<const CowMergeOperation*>& xor_map_;
   android::snapshot::ICowWriter* cow_writer_;
+  std::vector<uint8_t> xor_block_data;
   const size_t partition_size_;
 };
 
diff --git a/payload_consumer/xor_extent_writer_unittest.cc b/payload_consumer/xor_extent_writer_unittest.cc
index 45796a6..827030a 100644
--- a/payload_consumer/xor_extent_writer_unittest.cc
+++ b/payload_consumer/xor_extent_writer_unittest.cc
@@ -14,6 +14,7 @@
 // limitations under the License.
 //
 
+#include <algorithm>
 #include <memory>
 
 #include <unistd.h>
@@ -22,7 +23,7 @@
 #include <gtest/gtest.h>
 #include <libsnapshot/mock_cow_writer.h>
 
-#include "common/utils.h"
+#include "update_engine/common/utils.h"
 #include "update_engine/payload_consumer/extent_map.h"
 #include "update_engine/payload_consumer/file_descriptor.h"
 #include "update_engine/payload_consumer/xor_extent_writer.h"
@@ -44,11 +45,13 @@
     ASSERT_EQ(ftruncate64(source_part_.fd, kBlockSize * NUM_BLOCKS), 0);
     ASSERT_EQ(ftruncate64(target_part_.fd, kBlockSize * NUM_BLOCKS), 0);
 
-    // Fill source part with 1s, as we are computing XOR between source and
-    // target data later.
+    // Fill source part with arbitrary data, as we are computing XOR between
+    // source and target data later.
     ASSERT_EQ(lseek(source_part_.fd, 0, SEEK_SET), 0);
     brillo::Blob buffer(kBlockSize);
-    std::fill(buffer.begin(), buffer.end(), 1);
+    for (size_t i = 0; i < kBlockSize; i++) {
+      buffer[i] = i & 0xFF;
+    }
     for (size_t i = 0; i < NUM_BLOCKS; i++) {
       ASSERT_EQ(write(source_part_.fd, buffer.data(), buffer.size()),
                 static_cast<ssize_t>(buffer.size()));
@@ -195,12 +198,13 @@
   // [12-14] => [320-322], [20-22] => [420-422], [NUM_BLOCKS-3] => [2-5]
 
   // merge op:
-  // [NUM_BLOCKS-1] => [2-3]
+  // [NUM_BLOCKS-1] => [2]
 
   // Expected result:
-  // [12-16] should be REPLACE blocks
+  // [320-322] should be REPLACE blocks
   // [420-422] should be REPLACE blocks
-  // [2-4] should be REPLACE blocks
+  // [2] should be XOR blocks, with 0 offset to avoid out of bound read
+  // [3-5] should be REPLACE BLOCKS
 
   auto zeros = utils::GetReadonlyZeroBlock(kBlockSize * 9);
   EXPECT_CALL(cow_writer_, AddRawBlocks(320, zeros->data(), kBlockSize * 3))
@@ -209,15 +213,84 @@
               AddRawBlocks(420, zeros->data() + 3 * kBlockSize, kBlockSize * 3))
       .WillOnce(Return(true));
 
-  EXPECT_CALL(cow_writer_,
-              AddRawBlocks(2, zeros->data() + 6 * kBlockSize, kBlockSize))
+  EXPECT_CALL(cow_writer_, AddXorBlocks(2, _, kBlockSize, NUM_BLOCKS - 1, 0))
       .WillOnce(Return(true));
   EXPECT_CALL(cow_writer_,
-              AddRawBlocks(3, zeros->data() + 7 * kBlockSize, kBlockSize * 2))
+              AddRawBlocks(3, zeros->data() + kBlockSize * 7, kBlockSize * 2))
       .WillOnce(Return(true));
 
   ASSERT_TRUE(writer_.Init(op_.dst_extents(), kBlockSize));
   ASSERT_TRUE(writer_.Write(zeros->data(), zeros->size()));
 }
 
+TEST_F(XorExtentWriterTest, LastMultiBlockTest) {
+  constexpr auto COW_XOR = CowMergeOperation::COW_XOR;
+
+  const auto op3 = CreateCowMergeOperation(
+      ExtentForRange(NUM_BLOCKS - 4, 4), ExtentForRange(2, 4), COW_XOR, 777);
+  ASSERT_TRUE(xor_map_.AddExtent(op3.dst_extent(), &op3));
+
+  *op_.add_src_extents() = ExtentForRange(NUM_BLOCKS - 4, 4);
+  *op_.add_dst_extents() = ExtentForRange(2, 4);
+  XORExtentWriter writer_{
+      op_, source_fd_, &cow_writer_, xor_map_, NUM_BLOCKS * kBlockSize};
+
+  // OTA op:
+  // [NUM_BLOCKS-4] => [2-5]
+
+  // merge op:
+  // [NUM_BLOCKS-4] => [2-5]
+
+  // Expected result:
+  // [12-16] should be REPLACE blocks
+  // [420-422] should be REPLACE blocks
+  // [2-3] should be XOR blocks
+  // [4] should be XOR blocks with 0 offset to avoid out of bound read
+
+  // Send arbitrary data, just to confirm that XORExtentWriter did XOR the
+  // source data with target data
+  std::vector<uint8_t> op_data(kBlockSize * 4);
+  for (size_t i = 0; i < op_data.size(); i++) {
+    if (i % kBlockSize == 0) {
+      op_data[i] = 1;
+    } else {
+      op_data[i] = (op_data[i - 1] * 3) & 0xFF;
+    }
+  }
+  auto&& verify_xor_data = [source_fd_(source_fd_), &op_data](
+                               uint32_t new_block_start,
+                               const void* data,
+                               size_t size,
+                               uint32_t old_block,
+                               uint16_t offset) -> bool {
+    std::vector<uint8_t> source_data(size);
+    ssize_t bytes_read{};
+    TEST_AND_RETURN_FALSE_ERRNO(utils::PReadAll(source_fd_,
+                                                source_data.data(),
+                                                source_data.size(),
+                                                old_block * kBlockSize + offset,
+                                                &bytes_read));
+    TEST_EQ(bytes_read, static_cast<ssize_t>(source_data.size()));
+    std::transform(source_data.begin(),
+                   source_data.end(),
+                   static_cast<const uint8_t*>(data),
+                   source_data.begin(),
+                   std::bit_xor<uint8_t>{});
+    if (memcmp(source_data.data(), op_data.data(), source_data.size()) != 0) {
+      LOG(ERROR) << "XOR data received does not appear to be an XOR between "
+                    "source and target data";
+      return false;
+    }
+    return true;
+  };
+  EXPECT_CALL(cow_writer_,
+              AddXorBlocks(2, _, kBlockSize * 3, NUM_BLOCKS - 4, 777))
+      .WillOnce(verify_xor_data);
+  EXPECT_CALL(cow_writer_, AddXorBlocks(5, _, kBlockSize, NUM_BLOCKS - 1, 0))
+      .WillOnce(verify_xor_data);
+
+  ASSERT_TRUE(writer_.Init(op_.dst_extents(), kBlockSize));
+  ASSERT_TRUE(writer_.Write(op_data.data(), op_data.size()));
+}
+
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/delta_diff_generator.cc b/payload_generator/delta_diff_generator.cc
index 46dfb0f..cfec1b9 100644
--- a/payload_generator/delta_diff_generator.cc
+++ b/payload_generator/delta_diff_generator.cc
@@ -110,7 +110,7 @@
       return;
     }
     if (!old_part_.path.empty()) {
-      auto generator = MergeSequenceGenerator::Create(*aops_);
+      auto generator = MergeSequenceGenerator::Create(*aops_, new_part_.name);
       if (!generator || !generator->Generate(cow_merge_sequence_)) {
         LOG(FATAL) << "Failed to generate merge sequence";
       }
diff --git a/payload_generator/extent_utils_unittest.cc b/payload_generator/extent_utils_unittest.cc
index 5467aa5..2046bf5 100644
--- a/payload_generator/extent_utils_unittest.cc
+++ b/payload_generator/extent_utils_unittest.cc
@@ -21,7 +21,6 @@
 
 #include <gtest/gtest.h>
 
-#include "update_engine/common/test_utils.h"
 #include "update_engine/payload_consumer/payload_constants.h"
 #include "update_engine/payload_generator/extent_ranges.h"
 
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc
index 6daa36c..8fa86f0 100644
--- a/payload_generator/generate_delta_main.cc
+++ b/payload_generator/generate_delta_main.cc
@@ -445,6 +445,11 @@
             false,
             "Whether to enable LZ4diff feature when processing EROFS images.");
 
+DEFINE_bool(enable_puffdiff,
+            true,
+            "Whether to enable puffdiff feature. Enabling puffdiff will take "
+            "longer but generated OTA will be smaller.");
+
 DEFINE_bool(
     enable_zucchini,
     true,
@@ -612,6 +617,7 @@
   payload_config.enable_vabc_xor = FLAGS_enable_vabc_xor;
   payload_config.enable_lz4diff = FLAGS_enable_lz4diff;
   payload_config.enable_zucchini = FLAGS_enable_zucchini;
+  payload_config.enable_puffdiff = FLAGS_enable_puffdiff;
 
   payload_config.ParseCompressorTypes(FLAGS_compressor_types);
 
diff --git a/payload_generator/merge_sequence_generator.cc b/payload_generator/merge_sequence_generator.cc
index cb43221..fb43bfd 100644
--- a/payload_generator/merge_sequence_generator.cc
+++ b/payload_generator/merge_sequence_generator.cc
@@ -19,7 +19,6 @@
 #include <algorithm>
 #include <limits>
 
-#include "update_engine/payload_generator/delta_diff_generator.h"
 #include "update_engine/payload_generator/extent_ranges.h"
 #include "update_engine/payload_generator/extent_utils.h"
 #include "update_engine/update_metadata.pb.h"
@@ -177,7 +176,8 @@
 }
 
 std::unique_ptr<MergeSequenceGenerator> MergeSequenceGenerator::Create(
-    const std::vector<AnnotatedOperation>& aops) {
+    const std::vector<AnnotatedOperation>& aops,
+    std::string_view partition_name) {
   std::vector<CowMergeOperation> sequence;
 
   for (const auto& aop : aops) {
@@ -192,25 +192,123 @@
     }
   }
 
-  std::sort(sequence.begin(), sequence.end());
   return std::unique_ptr<MergeSequenceGenerator>(
-      new MergeSequenceGenerator(sequence));
+      new MergeSequenceGenerator(sequence, partition_name));
 }
 
-bool MergeSequenceGenerator::FindDependency(
-    std::map<CowMergeOperation, std::set<CowMergeOperation>>* result) const {
-  CHECK(result);
+template <typename T>
+CowMergeOperation MaxOutDegree(
+    const T& nodes,
+    const std::map<CowMergeOperation, std::set<CowMergeOperation>>&
+        merge_after) {
+  // Rationale for this algorithm:
+  // We only need to remove nodes from the graph if the graph contains a cycle.
+  // Any graph of N nodes has cycle iff number of edges >= N.
+  // So, to restore the graph back to an acyclic state, we need to keep removing
+  // edges until we have <N edges left. To minimize the number of nodes removed,
+  // we always remove the node with maximum out degree.
+  CowMergeOperation best;
+  size_t max_out_degree = 0;
+  const auto has_xor =
+      std::any_of(nodes.begin(),
+                  nodes.end(),
+                  [&merge_after](const CowMergeOperation& node) {
+                    if (node.type() != CowMergeOperation::COW_XOR) {
+                      return false;
+                    }
+                    auto it = merge_after.find(node);
+                    if (it == merge_after.end()) {
+                      return false;
+                    }
+                    return it->second.size() > 0;
+                  });
+  for (const auto& op : nodes) {
+    if (has_xor && op.type() != CowMergeOperation::COW_XOR) {
+      continue;
+    }
+    const auto out_degree = merge_after.at(op).size();
+    if (out_degree > max_out_degree) {
+      best = op;
+      max_out_degree = out_degree;
+    } else if (out_degree == max_out_degree) {
+      if (op.src_extent().num_blocks() < best.src_extent().num_blocks()) {
+        best = op;
+      }
+    }
+  }
+  CHECK_NE(max_out_degree, 0UL);
+  return best;
+}
+
+template <typename T>
+struct MapKeyIterator {
+  MapKeyIterator<T>& operator++() {
+    ++it;
+    return *this;
+  }
+  MapKeyIterator<T>& operator--() {
+    --it;
+    return *this;
+  }
+  bool operator==(const MapKeyIterator<T>& rhs) const { return it == rhs.it; }
+  bool operator!=(const MapKeyIterator<T>& rhs) const { return it != rhs.it; }
+  auto&& operator->() const { return it->first; }
+  auto&& operator*() const { return it->first; }
+  T it;
+};
+
+template <typename T, typename U>
+struct MapKeyRange {
+  auto begin() const {
+    return MapKeyIterator<typename std::map<T, U>::const_iterator>{map.begin()};
+  }
+  auto end() const {
+    return MapKeyIterator<typename std::map<T, U>::const_iterator>{map.end()};
+  }
+  std::map<T, U> map;
+};
+
+CowMergeOperation MaxOutDegree(
+    const std::map<CowMergeOperation, int>& incoming_edges,
+    const std::map<CowMergeOperation, std::set<CowMergeOperation>>&
+        merge_after) {
+  return MaxOutDegree(MapKeyRange<CowMergeOperation, int>{incoming_edges},
+                      merge_after);
+}
+
+// Given a potentially cyclic graph, return a node to remove to break cycles
+// |incoming_edges| stores nodes' in degree, and |merge_after| is an outgoing
+// edge list. For example, |merge_after[a]| returns all nodes which `a` has an
+// out going edge to.
+// The only requirement of this function is to return a node which is in
+// |incoming_edges| . As long as this is satisfied, merge sequence generation
+// will work. Caller will keep removing nodes returned by this function until
+// the graph has no cycles. However, the choice of which node to remove can
+// greatly impact COW sizes. Nodes removed from the graph will be converted to a
+// COW_REPLACE operation, taking more disk space. So this function should try to
+// pick a node which minimizes number of nodes we have to remove. (Modulo the
+// weight of each node, which is how many blocks a CowMergeOperation touches)
+CowMergeOperation PickConvertToRaw(
+    const std::map<CowMergeOperation, int>& incoming_edges,
+    const std::map<CowMergeOperation, std::set<CowMergeOperation>>&
+        merge_after) {
+  return MaxOutDegree(incoming_edges, merge_after);
+}
+
+std::map<CowMergeOperation, std::set<CowMergeOperation>>
+MergeSequenceGenerator::FindDependency(
+    const std::vector<CowMergeOperation>& operations) {
   LOG(INFO) << "Finding dependencies";
 
   // Since the OTA operation may reuse some source blocks, use the binary
   // search on sorted dst extents to find overlaps.
   std::map<CowMergeOperation, std::set<CowMergeOperation>> merge_after;
-  for (const auto& op : operations_) {
+  for (const auto& op : operations) {
     // lower bound (inclusive): dst extent's end block >= src extent's start
     // block.
     const auto lower_it = std::lower_bound(
-        operations_.begin(),
-        operations_.end(),
+        operations.begin(),
+        operations.end(),
         op,
         [](const CowMergeOperation& it, const CowMergeOperation& op) {
           auto dst_end_block =
@@ -220,7 +318,7 @@
     // upper bound: dst extent's start block > src extent's end block
     const auto upper_it = std::upper_bound(
         lower_it,
-        operations_.end(),
+        operations.end(),
         op,
         [](const CowMergeOperation& op, const CowMergeOperation& it) {
           auto src_end_block =
@@ -244,18 +342,12 @@
     }
   }
 
-  *result = std::move(merge_after);
-  return true;
+  return merge_after;
 }
 
 bool MergeSequenceGenerator::Generate(
     std::vector<CowMergeOperation>* sequence) const {
   sequence->clear();
-  std::map<CowMergeOperation, std::set<CowMergeOperation>> merge_after;
-  if (!FindDependency(&merge_after)) {
-    LOG(ERROR) << "Failed to find dependencies";
-    return false;
-  }
 
   LOG(INFO) << "Generating sequence";
 
@@ -263,7 +355,7 @@
   // operations to discard to break cycles; thus yielding a deterministic
   // sequence.
   std::map<CowMergeOperation, int> incoming_edges;
-  for (const auto& it : merge_after) {
+  for (const auto& it : merge_after_) {
     for (const auto& blocked : it.second) {
       // Value is default initialized to 0.
       incoming_edges[blocked] += 1;
@@ -289,7 +381,11 @@
       merge_sequence.insert(
           merge_sequence.end(), free_operations.begin(), free_operations.end());
     } else {
-      auto to_convert = incoming_edges.begin()->first;
+      auto to_convert = PickConvertToRaw(incoming_edges, merge_after_);
+      // The operation we pick must be one of the nodes not already in merge
+      // sequence.
+      CHECK(incoming_edges.find(to_convert) != incoming_edges.end());
+
       free_operations.insert(to_convert);
       convert_to_raw.insert(to_convert);
       LOG(INFO) << "Converting operation to raw " << to_convert;
@@ -302,7 +398,7 @@
       // Now that this particular operation is merged, other operations
       // blocked by this one may be free. Decrement the count of blocking
       // operations, and set up the free operations for the next iteration.
-      for (const auto& blocked : merge_after[op]) {
+      for (const auto& blocked : merge_after_.at(op)) {
         auto it = incoming_edges.find(blocked);
         if (it == incoming_edges.end()) {
           continue;
@@ -347,7 +443,8 @@
   }
 
   LOG(INFO) << "Blocks in merge sequence " << blocks_in_sequence
-            << ", blocks in raw " << blocks_in_raw;
+            << ", blocks in raw " << blocks_in_raw << ", partition "
+            << partition_name_;
   if (!ValidateSequence(merge_sequence)) {
     LOG(ERROR) << "Invalid Sequence";
     return false;
diff --git a/payload_generator/merge_sequence_generator.h b/payload_generator/merge_sequence_generator.h
index d3b5eb9..083eedf 100644
--- a/payload_generator/merge_sequence_generator.h
+++ b/payload_generator/merge_sequence_generator.h
@@ -24,8 +24,6 @@
 #include <vector>
 
 #include "update_engine/payload_generator/annotated_operation.h"
-#include "update_engine/payload_generator/extent_ranges.h"
-#include "update_engine/payload_generator/extent_utils.h"
 #include "update_engine/update_metadata.pb.h"
 
 namespace chromeos_update_engine {
@@ -46,12 +44,25 @@
 // read after write will happen by following the sequence. When there is a
 // cycle, we will omit some operations in the list. Therefore, the result
 // sequence may not contain all blocks in the input list.
+
+template <typename T>
+T&& Sort(T&& container) {
+  std::sort(container.begin(), container.end());
+  return container;
+}
+
 class MergeSequenceGenerator {
  public:
-  // Creates an object from a list of OTA InstallOperations. Returns nullptr on
-  // failure.
+  // Creates an object from a list of OTA InstallOperations. Returns nullptr
+  // on failure.
   static std::unique_ptr<MergeSequenceGenerator> Create(
-      const std::vector<AnnotatedOperation>& aops);
+      const std::vector<AnnotatedOperation>& aops,
+      std::string_view partition_name = "");
+  explicit MergeSequenceGenerator(std::vector<CowMergeOperation> transfers,
+                                  std::string_view partition_name)
+      : operations_(std::move(Sort(transfers))),
+        merge_after_(FindDependency(operations_)),
+        partition_name_(partition_name) {}
   // Checks that no read after write happens in the given sequence.
   static bool ValidateSequence(const std::vector<CowMergeOperation>& sequence);
 
@@ -59,17 +70,25 @@
   // |sequence|. Returns false on failure.
   bool Generate(std::vector<CowMergeOperation>* sequence) const;
 
+  const std::vector<CowMergeOperation>& GetOperations() const {
+    return operations_;
+  }
+  const std::map<CowMergeOperation, std::set<CowMergeOperation>>&
+  GetDependencyMap() const {
+    return merge_after_;
+  }
+
  private:
   friend class MergeSequenceGeneratorTest;
-  explicit MergeSequenceGenerator(std::vector<CowMergeOperation> transfers)
-      : operations_(std::move(transfers)) {}
 
   // For a given merge operation, finds all the operations that should merge
-  // after myself. Put the result in |merge_after|.
-  bool FindDependency(std::map<CowMergeOperation, std::set<CowMergeOperation>>*
-                          merge_after) const;
+  // after myself. Put the result in |merge_after|. |operations| must be sorted
+  static std::map<CowMergeOperation, std::set<CowMergeOperation>>
+  FindDependency(const std::vector<CowMergeOperation>& operations);
   // The list of CowMergeOperations to sort.
   const std::vector<CowMergeOperation> operations_;
+  const std::map<CowMergeOperation, std::set<CowMergeOperation>> merge_after_;
+  const std::string_view partition_name_;
 };
 
 void SplitSelfOverlapping(const Extent& src_extent,
diff --git a/payload_generator/merge_sequence_generator_unittest.cc b/payload_generator/merge_sequence_generator_unittest.cc
index 86d4fdd..b2c86a2 100644
--- a/payload_generator/merge_sequence_generator_unittest.cc
+++ b/payload_generator/merge_sequence_generator_unittest.cc
@@ -17,16 +17,19 @@
 #include <algorithm>
 #include <vector>
 
+#include <android-base/file.h>
 #include <gtest/gtest.h>
 
-#include "update_engine/payload_consumer/payload_constants.h"
-#include "update_engine/payload_generator/delta_diff_generator.h"
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
 #include "update_engine/payload_generator/extent_ranges.h"
 #include "update_engine/payload_generator/extent_utils.h"
 #include "update_engine/payload_generator/merge_sequence_generator.h"
 #include "update_engine/update_metadata.pb.h"
 
 namespace chromeos_update_engine {
+using test_utils::GetBuildArtifactsPath;
+
 CowMergeOperation CreateCowMergeOperation(const Extent& src_extent,
                                           const Extent& dst_extent) {
   return CreateCowMergeOperation(
@@ -43,17 +46,14 @@
       std::vector<CowMergeOperation> transfers,
       std::map<CowMergeOperation, std::set<CowMergeOperation>>* result) {
     std::sort(transfers.begin(), transfers.end());
-    MergeSequenceGenerator generator(std::move(transfers));
-    ASSERT_TRUE(generator.FindDependency(result));
+    *result = MergeSequenceGenerator::FindDependency(transfers);
   }
 
-  void GenerateSequence(std::vector<CowMergeOperation> transfers,
-                        const std::vector<CowMergeOperation>& expected) {
+  void GenerateSequence(std::vector<CowMergeOperation> transfers) {
     std::sort(transfers.begin(), transfers.end());
-    MergeSequenceGenerator generator(std::move(transfers));
+    MergeSequenceGenerator generator(std::move(transfers), "");
     std::vector<CowMergeOperation> sequence;
     ASSERT_TRUE(generator.Generate(&sequence));
-    ASSERT_EQ(expected, sequence);
   }
 };
 
@@ -177,24 +177,18 @@
       CreateCowMergeOperation(ExtentForRange(25, 10), ExtentForRange(30, 10)),
   };
 
-  std::vector<CowMergeOperation> expected{
-      transfers[0], transfers[2], transfers[1]};
-  GenerateSequence(transfers, expected);
+  GenerateSequence(transfers);
 }
 
 TEST_F(MergeSequenceGeneratorTest, GenerateSequenceWithCycles) {
   std::vector<CowMergeOperation> transfers = {
-      CreateCowMergeOperation(ExtentForRange(25, 10), ExtentForRange(30, 10)),
+      CreateCowMergeOperation(ExtentForRange(15, 10), ExtentForRange(30, 10)),
       CreateCowMergeOperation(ExtentForRange(30, 10), ExtentForRange(40, 10)),
-      CreateCowMergeOperation(ExtentForRange(40, 10), ExtentForRange(25, 10)),
-      CreateCowMergeOperation(ExtentForRange(10, 10), ExtentForRange(15, 10)),
+      CreateCowMergeOperation(ExtentForRange(40, 10), ExtentForRange(15, 10)),
+      CreateCowMergeOperation(ExtentForRange(10, 10), ExtentForRange(5, 10)),
   };
 
-  // file 1,2,3 form a cycle. And file3, whose dst ext has smallest offset,
-  // will be converted to raw blocks
-  std::vector<CowMergeOperation> expected{
-      transfers[3], transfers[1], transfers[0]};
-  GenerateSequence(transfers, expected);
+  GenerateSequence(transfers);
 }
 
 TEST_F(MergeSequenceGeneratorTest, GenerateSequenceMultipleCycles) {
@@ -204,15 +198,12 @@
       CreateCowMergeOperation(ExtentForRange(24, 5), ExtentForRange(35, 5)),
       CreateCowMergeOperation(ExtentForRange(30, 10), ExtentForRange(15, 10)),
       // cycle 2
-      CreateCowMergeOperation(ExtentForRange(55, 10), ExtentForRange(60, 10)),
+      CreateCowMergeOperation(ExtentForRange(50, 10), ExtentForRange(60, 10)),
       CreateCowMergeOperation(ExtentForRange(60, 10), ExtentForRange(70, 10)),
-      CreateCowMergeOperation(ExtentForRange(70, 10), ExtentForRange(55, 10)),
+      CreateCowMergeOperation(ExtentForRange(70, 10), ExtentForRange(50, 10)),
   };
 
-  // file 3, 6 will be converted to raw.
-  std::vector<CowMergeOperation> expected{
-      transfers[1], transfers[0], transfers[4], transfers[3]};
-  GenerateSequence(transfers, expected);
+  GenerateSequence(transfers);
 }
 
 void ValidateSplitSequence(const Extent& src_extent, const Extent& dst_extent) {
@@ -265,17 +256,14 @@
                               ExtentForRange(15, 10),
                               CowMergeOperation::COW_XOR),
       // cycle 2
-      CreateCowMergeOperation(ExtentForRange(55, 10), ExtentForRange(60, 10)),
+      CreateCowMergeOperation(ExtentForRange(50, 10), ExtentForRange(60, 10)),
       CreateCowMergeOperation(ExtentForRange(60, 10),
                               ExtentForRange(70, 10),
                               CowMergeOperation::COW_XOR),
-      CreateCowMergeOperation(ExtentForRange(70, 10), ExtentForRange(55, 10)),
+      CreateCowMergeOperation(ExtentForRange(70, 10), ExtentForRange(50, 10)),
   };
 
-  // file 3, 6 will be converted to raw.
-  std::vector<CowMergeOperation> expected{
-      transfers[1], transfers[0], transfers[4], transfers[3]};
-  GenerateSequence(transfers, expected);
+  GenerateSequence(transfers);
 }
 
 TEST_F(MergeSequenceGeneratorTest, CreateGeneratorWithXor) {
@@ -424,4 +412,24 @@
   ASSERT_TRUE(generator->ValidateSequence(sequence));
 }
 
+TEST_F(MergeSequenceGeneratorTest, ActualPayloadTest) {
+  auto payload_path =
+      GetBuildArtifactsPath("testdata/cycle_nodes_product_no_xor.bin");
+  ASSERT_FALSE(payload_path.empty());
+  ASSERT_TRUE(utils::FileExists(payload_path.c_str()));
+  PartitionUpdate part;
+  std::string payload;
+  android::base::ReadFileToString(payload_path, &payload);
+  part.ParseFromString(payload);
+  part.set_partition_name("product");
+  std::vector<CowMergeOperation> ops;
+  ops.reserve(part.merge_operations_size());
+  for (const auto& op : part.merge_operations()) {
+    ops.emplace_back(op);
+  }
+  MergeSequenceGenerator generator(ops, part.partition_name());
+  std::vector<CowMergeOperation> sequence;
+  ASSERT_TRUE(generator.Generate(&sequence));
+}
+
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/payload_generation_config.cc b/payload_generator/payload_generation_config.cc
index f8469ea..a974180 100644
--- a/payload_generator/payload_generation_config.cc
+++ b/payload_generator/payload_generation_config.cc
@@ -38,6 +38,7 @@
 #include "update_engine/payload_generator/mapfile_filesystem.h"
 #include "update_engine/payload_generator/raw_filesystem.h"
 #include "update_engine/payload_generator/squashfs_filesystem.h"
+#include "update_engine/update_metadata.pb.h"
 
 using std::string;
 
@@ -393,6 +394,8 @@
     case InstallOperation::LZ4DIFF_BSDIFF:
     case InstallOperation::LZ4DIFF_PUFFDIFF:
       return enable_lz4diff;
+    case InstallOperation::PUFFDIFF:
+      return enable_puffdiff;
     default:
       return true;
   }
diff --git a/payload_generator/payload_generation_config.h b/payload_generator/payload_generation_config.h
index 225237a..0256a9d 100644
--- a/payload_generator/payload_generation_config.h
+++ b/payload_generator/payload_generation_config.h
@@ -261,6 +261,9 @@
   // Whether to enable zucchini ops
   bool enable_zucchini = true;
 
+  // Whether to enable puffdiff ops
+  bool enable_puffdiff = true;
+
   std::string security_patch_level;
 
   uint32_t max_threads = 0;
diff --git a/testdata/cycle_nodes_product.bin b/testdata/cycle_nodes_product.bin
new file mode 100644
index 0000000..0042934
--- /dev/null
+++ b/testdata/cycle_nodes_product.bin
Binary files differ
diff --git a/testdata/cycle_nodes_product_no_xor.bin b/testdata/cycle_nodes_product_no_xor.bin
new file mode 100644
index 0000000..44ed4fc
--- /dev/null
+++ b/testdata/cycle_nodes_product_no_xor.bin
Binary files differ
diff --git a/testdata/cycle_nodes_system.bin b/testdata/cycle_nodes_system.bin
new file mode 100644
index 0000000..d1fc0be
--- /dev/null
+++ b/testdata/cycle_nodes_system.bin
Binary files differ
diff --git a/testdata/cycle_nodes_system_dlkm.bin b/testdata/cycle_nodes_system_dlkm.bin
new file mode 100644
index 0000000..1e6bdb7
--- /dev/null
+++ b/testdata/cycle_nodes_system_dlkm.bin
Binary files differ
diff --git a/testdata/cycle_nodes_system_ext.bin b/testdata/cycle_nodes_system_ext.bin
new file mode 100644
index 0000000..80ab049
--- /dev/null
+++ b/testdata/cycle_nodes_system_ext.bin
Binary files differ
diff --git a/testdata/cycle_nodes_vendor.bin b/testdata/cycle_nodes_vendor.bin
new file mode 100644
index 0000000..a5f72d9
--- /dev/null
+++ b/testdata/cycle_nodes_vendor.bin
Binary files differ
diff --git a/testdata/cycle_nodes_vendor_dlkm.bin b/testdata/cycle_nodes_vendor_dlkm.bin
new file mode 100644
index 0000000..88e6afc
--- /dev/null
+++ b/testdata/cycle_nodes_vendor_dlkm.bin
Binary files differ
diff --git a/update_engine.conf b/update_engine.conf
index 2d3a655..22b59d9 100644
--- a/update_engine.conf
+++ b/update_engine.conf
@@ -1,2 +1,2 @@
 PAYLOAD_MAJOR_VERSION=2
-PAYLOAD_MINOR_VERSION=8
+PAYLOAD_MINOR_VERSION=9