Merge remote-tracking branch 'aosp/upstream-master' into merge

git merge aosp/upstream-master --commit -s recursive

Test: treehugger
Change-Id: Ifab9f47e1c5bea3898e78df0139d10842b41a44f
diff --git a/aosp/binder_service_android.cc b/aosp/binder_service_android.cc
new file mode 100644
index 0000000..ed76c4a
--- /dev/null
+++ b/aosp/binder_service_android.cc
@@ -0,0 +1,243 @@
+//
+// 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 "update_engine/aosp/binder_service_android.h"
+
+#include <memory>
+
+#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"
+
+using android::binder::Status;
+using android::os::IUpdateEngineCallback;
+using android::os::ParcelFileDescriptor;
+using std::string;
+using std::vector;
+using update_engine::UpdateEngineStatus;
+
+namespace chromeos_update_engine {
+
+BinderUpdateEngineAndroidService::BinderUpdateEngineAndroidService(
+    ServiceDelegateAndroidInterface* service_delegate)
+    : service_delegate_(service_delegate) {}
+
+void BinderUpdateEngineAndroidService::SendStatusUpdate(
+    const UpdateEngineStatus& update_engine_status) {
+  last_status_ = static_cast<int>(update_engine_status.status);
+  last_progress_ = update_engine_status.progress;
+  for (auto& callback : callbacks_) {
+    callback->onStatusUpdate(last_status_, last_progress_);
+  }
+}
+
+void BinderUpdateEngineAndroidService::SendPayloadApplicationComplete(
+    ErrorCode error_code) {
+  for (auto& callback : callbacks_) {
+    callback->onPayloadApplicationComplete(static_cast<int>(error_code));
+  }
+}
+
+Status BinderUpdateEngineAndroidService::bind(
+    const android::sp<IUpdateEngineCallback>& callback, bool* return_value) {
+  // Send an status update on connection (except when no update sent so far).
+  // Even though the status update is oneway, it still returns an erroneous
+  // status in case of a selinux denial. We should at least check this status
+  // and fails the binding.
+  if (last_status_ != -1) {
+    auto status = callback->onStatusUpdate(last_status_, last_progress_);
+    if (!status.isOk()) {
+      LOG(ERROR) << "Failed to call onStatusUpdate() from callback: "
+                 << status.toString8();
+      *return_value = false;
+      return Status::ok();
+    }
+  }
+
+  callbacks_.emplace_back(callback);
+
+  const android::sp<IBinder>& callback_binder =
+      IUpdateEngineCallback::asBinder(callback);
+  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())));
+
+  *return_value = true;
+  return Status::ok();
+}
+
+Status BinderUpdateEngineAndroidService::unbind(
+    const android::sp<IUpdateEngineCallback>& callback, bool* return_value) {
+  const android::sp<IBinder>& callback_binder =
+      IUpdateEngineCallback::asBinder(callback);
+  auto binder_wrapper = android::BinderWrapper::Get();
+  binder_wrapper->UnregisterForDeathNotifications(callback_binder);
+
+  *return_value = UnbindCallback(callback_binder.get());
+  return Status::ok();
+}
+
+Status BinderUpdateEngineAndroidService::applyPayload(
+    const android::String16& url,
+    int64_t payload_offset,
+    int64_t payload_size,
+    const vector<android::String16>& header_kv_pairs) {
+  const string payload_url{android::String8{url}.string()};
+  vector<string> str_headers = ToVecString(header_kv_pairs);
+
+  brillo::ErrorPtr error;
+  if (!service_delegate_->ApplyPayload(
+          payload_url, payload_offset, payload_size, str_headers, &error)) {
+    return ErrorPtrToStatus(error);
+  }
+  return Status::ok();
+}
+
+Status BinderUpdateEngineAndroidService::applyPayloadFd(
+    const ParcelFileDescriptor& pfd,
+    int64_t payload_offset,
+    int64_t payload_size,
+    const vector<android::String16>& header_kv_pairs) {
+  vector<string> str_headers = ToVecString(header_kv_pairs);
+
+  brillo::ErrorPtr error;
+  if (!service_delegate_->ApplyPayload(
+          pfd.get(), payload_offset, payload_size, str_headers, &error)) {
+    return ErrorPtrToStatus(error);
+  }
+  return Status::ok();
+}
+
+Status BinderUpdateEngineAndroidService::suspend() {
+  brillo::ErrorPtr error;
+  if (!service_delegate_->SuspendUpdate(&error))
+    return ErrorPtrToStatus(error);
+  return Status::ok();
+}
+
+Status BinderUpdateEngineAndroidService::resume() {
+  brillo::ErrorPtr error;
+  if (!service_delegate_->ResumeUpdate(&error))
+    return ErrorPtrToStatus(error);
+  return Status::ok();
+}
+
+Status BinderUpdateEngineAndroidService::cancel() {
+  brillo::ErrorPtr error;
+  if (!service_delegate_->CancelUpdate(&error))
+    return ErrorPtrToStatus(error);
+  return Status::ok();
+}
+
+Status BinderUpdateEngineAndroidService::resetStatus() {
+  brillo::ErrorPtr error;
+  if (!service_delegate_->ResetStatus(&error))
+    return ErrorPtrToStatus(error);
+  return Status::ok();
+}
+
+Status BinderUpdateEngineAndroidService::verifyPayloadApplicable(
+    const android::String16& metadata_filename, bool* return_value) {
+  const std::string payload_metadata{
+      android::String8{metadata_filename}.string()};
+  LOG(INFO) << "Received a request of verifying payload metadata in "
+            << payload_metadata << ".";
+  brillo::ErrorPtr error;
+  *return_value =
+      service_delegate_->VerifyPayloadApplicable(payload_metadata, &error);
+  if (error != nullptr)
+    return ErrorPtrToStatus(error);
+  return Status::ok();
+}
+
+bool BinderUpdateEngineAndroidService::UnbindCallback(const IBinder* callback) {
+  auto it = std::find_if(
+      callbacks_.begin(),
+      callbacks_.end(),
+      [&callback](const android::sp<IUpdateEngineCallback>& elem) {
+        return IUpdateEngineCallback::asBinder(elem).get() == callback;
+      });
+  if (it == callbacks_.end()) {
+    LOG(ERROR) << "Unable to unbind unknown callback.";
+    return false;
+  }
+  callbacks_.erase(it);
+  return true;
+}
+
+Status BinderUpdateEngineAndroidService::allocateSpaceForPayload(
+    const android::String16& metadata_filename,
+    const vector<android::String16>& header_kv_pairs,
+    int64_t* return_value) {
+  const std::string payload_metadata{
+      android::String8{metadata_filename}.string()};
+  vector<string> str_headers = ToVecString(header_kv_pairs);
+  LOG(INFO) << "Received a request of allocating space for " << payload_metadata
+            << ".";
+  brillo::ErrorPtr error;
+  *return_value =
+      static_cast<int64_t>(service_delegate_->AllocateSpaceForPayload(
+          payload_metadata, str_headers, &error));
+  if (error != nullptr)
+    return ErrorPtrToStatus(error);
+  return Status::ok();
+}
+
+class CleanupSuccessfulUpdateCallback
+    : public CleanupSuccessfulUpdateCallbackInterface {
+ public:
+  CleanupSuccessfulUpdateCallback(
+      const android::sp<IUpdateEngineCallback>& callback)
+      : callback_(callback) {}
+  void OnCleanupComplete(int32_t error_code) {
+    ignore_result(callback_->onPayloadApplicationComplete(error_code));
+  }
+  void OnCleanupProgressUpdate(double progress) {
+    ignore_result(callback_->onStatusUpdate(
+        static_cast<int32_t>(
+            update_engine::UpdateStatus::CLEANUP_PREVIOUS_UPDATE),
+        progress));
+  }
+  void RegisterForDeathNotifications(base::Closure unbind) {
+    const android::sp<android::IBinder>& callback_binder =
+        IUpdateEngineCallback::asBinder(callback_);
+    auto binder_wrapper = android::BinderWrapper::Get();
+    binder_wrapper->RegisterForDeathNotifications(callback_binder, unbind);
+  }
+
+ private:
+  android::sp<IUpdateEngineCallback> callback_;
+};
+
+Status BinderUpdateEngineAndroidService::cleanupSuccessfulUpdate(
+    const android::sp<IUpdateEngineCallback>& callback) {
+  brillo::ErrorPtr error;
+  service_delegate_->CleanupSuccessfulUpdate(
+      std::make_unique<CleanupSuccessfulUpdateCallback>(callback), &error);
+  if (error != nullptr)
+    return ErrorPtrToStatus(error);
+  return Status::ok();
+}
+
+}  // namespace chromeos_update_engine
diff --git a/aosp/binder_service_android.h b/aosp/binder_service_android.h
new file mode 100644
index 0000000..f41fbdf
--- /dev/null
+++ b/aosp/binder_service_android.h
@@ -0,0 +1,99 @@
+//
+// 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 UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include <utils/Errors.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+
+#include "android/os/BnUpdateEngine.h"
+#include "android/os/IUpdateEngineCallback.h"
+#include "update_engine/aosp/service_delegate_android_interface.h"
+#include "update_engine/common/service_observer_interface.h"
+
+namespace chromeos_update_engine {
+
+class BinderUpdateEngineAndroidService : public android::os::BnUpdateEngine,
+                                         public ServiceObserverInterface {
+ public:
+  explicit BinderUpdateEngineAndroidService(
+      ServiceDelegateAndroidInterface* service_delegate);
+  ~BinderUpdateEngineAndroidService() override = default;
+
+  const char* ServiceName() const { return "android.os.UpdateEngineService"; }
+
+  // ServiceObserverInterface overrides.
+  void SendStatusUpdate(
+      const update_engine::UpdateEngineStatus& update_engine_status) override;
+  void SendPayloadApplicationComplete(ErrorCode error_code) override;
+
+  // android::os::BnUpdateEngine overrides.
+  android::binder::Status applyPayload(
+      const android::String16& url,
+      int64_t payload_offset,
+      int64_t payload_size,
+      const std::vector<android::String16>& header_kv_pairs) override;
+  android::binder::Status applyPayloadFd(
+      const ::android::os::ParcelFileDescriptor& pfd,
+      int64_t payload_offset,
+      int64_t payload_size,
+      const std::vector<android::String16>& header_kv_pairs) override;
+  android::binder::Status bind(
+      const android::sp<android::os::IUpdateEngineCallback>& callback,
+      bool* return_value) override;
+  android::binder::Status unbind(
+      const android::sp<android::os::IUpdateEngineCallback>& callback,
+      bool* return_value) override;
+  android::binder::Status suspend() override;
+  android::binder::Status resume() override;
+  android::binder::Status cancel() override;
+  android::binder::Status resetStatus() override;
+  android::binder::Status verifyPayloadApplicable(
+      const android::String16& metadata_filename, bool* return_value) override;
+  android::binder::Status allocateSpaceForPayload(
+      const android::String16& metadata_filename,
+      const std::vector<android::String16>& header_kv_pairs,
+      int64_t* return_value) override;
+  android::binder::Status cleanupSuccessfulUpdate(
+      const android::sp<android::os::IUpdateEngineCallback>& callback) override;
+
+ private:
+  // Remove the passed |callback| from the list of registered callbacks. Called
+  // on unbind() or whenever the callback object is destroyed.
+  // Returns true on success.
+  bool UnbindCallback(const IBinder* callback);
+
+  // List of currently bound callbacks.
+  std::vector<android::sp<android::os::IUpdateEngineCallback>> callbacks_;
+
+  // Cached copy of the last status update sent. Used to send an initial
+  // notification when bind() is called from the client.
+  int last_status_{-1};
+  double last_progress_{0.0};
+
+  ServiceDelegateAndroidInterface* service_delegate_;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_H_
diff --git a/aosp/binder_service_android_common.h b/aosp/binder_service_android_common.h
new file mode 100644
index 0000000..223b32e
--- /dev/null
+++ b/aosp/binder_service_android_common.h
@@ -0,0 +1,45 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_COMMON_H_
+#define UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_COMMON_H_
+
+#include <string>
+#include <vector>
+
+#include <binder/Status.h>
+
+namespace chromeos_update_engine {
+
+static inline android::binder::Status ErrorPtrToStatus(
+    const brillo::ErrorPtr& error) {
+  return android::binder::Status::fromServiceSpecificError(
+      1, android::String8{error->GetMessage().c_str()});
+}
+
+static inline std::vector<std::string> ToVecString(
+    const std::vector<android::String16>& inp) {
+  std::vector<std::string> out;
+  out.reserve(inp.size());
+  for (const auto& e : inp) {
+    out.emplace_back(android::String8{e}.string());
+  }
+  return out;
+}
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_COMMON_H_
diff --git a/aosp/binder_service_stable_android.cc b/aosp/binder_service_stable_android.cc
new file mode 100644
index 0000000..17b35ee
--- /dev/null
+++ b/aosp/binder_service_stable_android.cc
@@ -0,0 +1,132 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/binder_service_stable_android.h"
+
+#include <memory>
+
+#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"
+
+using android::binder::Status;
+using android::os::IUpdateEngineStableCallback;
+using android::os::ParcelFileDescriptor;
+using std::string;
+using std::vector;
+using update_engine::UpdateEngineStatus;
+
+namespace chromeos_update_engine {
+
+BinderUpdateEngineAndroidStableService::BinderUpdateEngineAndroidStableService(
+    ServiceDelegateAndroidInterface* service_delegate)
+    : service_delegate_(service_delegate) {}
+
+void BinderUpdateEngineAndroidStableService::SendStatusUpdate(
+    const UpdateEngineStatus& update_engine_status) {
+  last_status_ = static_cast<int>(update_engine_status.status);
+  last_progress_ = update_engine_status.progress;
+  if (callback_) {
+    callback_->onStatusUpdate(last_status_, last_progress_);
+  }
+}
+
+void BinderUpdateEngineAndroidStableService::SendPayloadApplicationComplete(
+    ErrorCode error_code) {
+  if (callback_) {
+    callback_->onPayloadApplicationComplete(static_cast<int>(error_code));
+  }
+}
+
+Status BinderUpdateEngineAndroidStableService::bind(
+    const android::sp<IUpdateEngineStableCallback>& callback,
+    bool* return_value) {
+  // Reject binding if another callback is already bound.
+  if (callback_ != nullptr) {
+    LOG(ERROR) << "Another callback is already bound. Can't bind new callback.";
+    *return_value = false;
+    return Status::ok();
+  }
+
+  // See BinderUpdateEngineAndroidService::bind.
+  if (last_status_ != -1) {
+    auto status = callback->onStatusUpdate(last_status_, last_progress_);
+    if (!status.isOk()) {
+      LOG(ERROR) << "Failed to call onStatusUpdate() from callback: "
+                 << status.toString8();
+      *return_value = false;
+      return Status::ok();
+    }
+  }
+
+  callback_ = callback;
+
+  const android::sp<IBinder>& callback_binder =
+      IUpdateEngineStableCallback::asBinder(callback);
+  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())));
+
+  *return_value = true;
+  return Status::ok();
+}
+
+Status BinderUpdateEngineAndroidStableService::unbind(
+    const android::sp<IUpdateEngineStableCallback>& callback,
+    bool* return_value) {
+  const android::sp<IBinder>& callback_binder =
+      IUpdateEngineStableCallback::asBinder(callback);
+  auto binder_wrapper = android::BinderWrapper::Get();
+  binder_wrapper->UnregisterForDeathNotifications(callback_binder);
+
+  *return_value = UnbindCallback(callback_binder.get());
+  return Status::ok();
+}
+
+Status BinderUpdateEngineAndroidStableService::applyPayloadFd(
+    const ParcelFileDescriptor& pfd,
+    int64_t payload_offset,
+    int64_t payload_size,
+    const vector<android::String16>& header_kv_pairs) {
+  vector<string> str_headers = ToVecString(header_kv_pairs);
+
+  brillo::ErrorPtr error;
+  if (!service_delegate_->ApplyPayload(
+          pfd.get(), payload_offset, payload_size, str_headers, &error)) {
+    return ErrorPtrToStatus(error);
+  }
+  return Status::ok();
+}
+
+bool BinderUpdateEngineAndroidStableService::UnbindCallback(
+    const IBinder* callback) {
+  if (IUpdateEngineStableCallback::asBinder(callback_).get() != callback) {
+    LOG(ERROR) << "Unable to unbind unknown callback.";
+    return false;
+  }
+  callback_ = nullptr;
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/aosp/binder_service_stable_android.h b/aosp/binder_service_stable_android.h
new file mode 100644
index 0000000..212afaa
--- /dev/null
+++ b/aosp/binder_service_stable_android.h
@@ -0,0 +1,85 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_BINDER_SERVICE_STABLE_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_BINDER_SERVICE_STABLE_ANDROID_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include <utils/Errors.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+
+#include "android/os/BnUpdateEngineStable.h"
+#include "android/os/IUpdateEngineStableCallback.h"
+#include "update_engine/aosp/service_delegate_android_interface.h"
+#include "update_engine/common/service_observer_interface.h"
+
+namespace chromeos_update_engine {
+
+class BinderUpdateEngineAndroidStableService
+    : public android::os::BnUpdateEngineStable,
+      public ServiceObserverInterface {
+ public:
+  explicit BinderUpdateEngineAndroidStableService(
+      ServiceDelegateAndroidInterface* service_delegate);
+  ~BinderUpdateEngineAndroidStableService() override = default;
+
+  const char* ServiceName() const {
+    return "android.os.UpdateEngineStableService";
+  }
+
+  // ServiceObserverInterface overrides.
+  void SendStatusUpdate(
+      const update_engine::UpdateEngineStatus& update_engine_status) override;
+  void SendPayloadApplicationComplete(ErrorCode error_code) override;
+
+  // android::os::BnUpdateEngineStable overrides.
+  android::binder::Status applyPayloadFd(
+      const ::android::os::ParcelFileDescriptor& pfd,
+      int64_t payload_offset,
+      int64_t payload_size,
+      const std::vector<android::String16>& header_kv_pairs) override;
+  android::binder::Status bind(
+      const android::sp<android::os::IUpdateEngineStableCallback>& callback,
+      bool* return_value) override;
+  android::binder::Status unbind(
+      const android::sp<android::os::IUpdateEngineStableCallback>& callback,
+      bool* return_value) override;
+
+ private:
+  // Remove the passed |callback| from the list of registered callbacks. Called
+  // on unbind() or whenever the callback object is destroyed.
+  // Returns true on success.
+  bool UnbindCallback(const IBinder* callback);
+
+  // Bound callback. The stable interface only supports one callback at a time.
+  android::sp<android::os::IUpdateEngineStableCallback> callback_;
+
+  // Cached copy of the last status update sent. Used to send an initial
+  // notification when bind() is called from the client.
+  int last_status_{-1};
+  double last_progress_{0.0};
+
+  ServiceDelegateAndroidInterface* service_delegate_;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_AOSP_BINDER_SERVICE_STABLE_ANDROID_H_
diff --git a/aosp/boot_control_android.cc b/aosp/boot_control_android.cc
new file mode 100644
index 0000000..bda65be
--- /dev/null
+++ b/aosp/boot_control_android.cc
@@ -0,0 +1,186 @@
+//
+// 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 "update_engine/aosp/boot_control_android.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/logging.h>
+#include <bootloader_message/bootloader_message.h>
+#include <brillo/message_loops/message_loop.h>
+
+#include "update_engine/aosp/dynamic_partition_control_android.h"
+#include "update_engine/common/utils.h"
+
+using std::string;
+
+using android::dm::DmDeviceState;
+using android::hardware::hidl_string;
+using android::hardware::Return;
+using android::hardware::boot::V1_0::BoolResult;
+using android::hardware::boot::V1_0::CommandResult;
+using android::hardware::boot::V1_0::IBootControl;
+using Slot = chromeos_update_engine::BootControlInterface::Slot;
+
+namespace {
+
+auto StoreResultCallback(CommandResult* dest) {
+  return [dest](const CommandResult& result) { *dest = result; };
+}
+}  // namespace
+
+namespace chromeos_update_engine {
+
+namespace boot_control {
+
+// Factory defined in boot_control.h.
+std::unique_ptr<BootControlInterface> CreateBootControl() {
+  auto boot_control = std::make_unique<BootControlAndroid>();
+  if (!boot_control->Init()) {
+    return nullptr;
+  }
+  return std::move(boot_control);
+}
+
+}  // namespace boot_control
+
+bool BootControlAndroid::Init() {
+  module_ = IBootControl::getService();
+  if (module_ == nullptr) {
+    LOG(ERROR) << "Error getting bootctrl HIDL module.";
+    return false;
+  }
+
+  LOG(INFO) << "Loaded boot control hidl hal.";
+
+  dynamic_control_ = std::make_unique<DynamicPartitionControlAndroid>();
+
+  return true;
+}
+
+unsigned int BootControlAndroid::GetNumSlots() const {
+  return module_->getNumberSlots();
+}
+
+BootControlInterface::Slot BootControlAndroid::GetCurrentSlot() const {
+  return module_->getCurrentSlot();
+}
+
+bool BootControlAndroid::GetPartitionDevice(const std::string& partition_name,
+                                            BootControlInterface::Slot slot,
+                                            bool not_in_payload,
+                                            std::string* device,
+                                            bool* is_dynamic) const {
+  return dynamic_control_->GetPartitionDevice(partition_name,
+                                              slot,
+                                              GetCurrentSlot(),
+                                              not_in_payload,
+                                              device,
+                                              is_dynamic);
+}
+
+bool BootControlAndroid::GetPartitionDevice(const string& partition_name,
+                                            BootControlInterface::Slot slot,
+                                            string* device) const {
+  return GetPartitionDevice(
+      partition_name, slot, false /* not_in_payload */, device, nullptr);
+}
+
+bool BootControlAndroid::IsSlotBootable(Slot slot) const {
+  Return<BoolResult> ret = module_->isSlotBootable(slot);
+  if (!ret.isOk()) {
+    LOG(ERROR) << "Unable to determine if slot " << SlotName(slot)
+               << " is bootable: " << ret.description();
+    return false;
+  }
+  if (ret == BoolResult::INVALID_SLOT) {
+    LOG(ERROR) << "Invalid slot: " << SlotName(slot);
+    return false;
+  }
+  return ret == BoolResult::TRUE;
+}
+
+bool BootControlAndroid::MarkSlotUnbootable(Slot slot) {
+  CommandResult result;
+  auto ret = module_->setSlotAsUnbootable(slot, StoreResultCallback(&result));
+  if (!ret.isOk()) {
+    LOG(ERROR) << "Unable to call MarkSlotUnbootable for slot "
+               << SlotName(slot) << ": " << ret.description();
+    return false;
+  }
+  if (!result.success) {
+    LOG(ERROR) << "Unable to mark slot " << SlotName(slot)
+               << " as unbootable: " << result.errMsg.c_str();
+  }
+  return result.success;
+}
+
+bool BootControlAndroid::SetActiveBootSlot(Slot slot) {
+  CommandResult result;
+  auto ret = module_->setActiveBootSlot(slot, StoreResultCallback(&result));
+  if (!ret.isOk()) {
+    LOG(ERROR) << "Unable to call SetActiveBootSlot for slot " << SlotName(slot)
+               << ": " << ret.description();
+    return false;
+  }
+  if (!result.success) {
+    LOG(ERROR) << "Unable to set the active slot to slot " << SlotName(slot)
+               << ": " << result.errMsg.c_str();
+  }
+  return result.success;
+}
+
+bool BootControlAndroid::MarkBootSuccessfulAsync(
+    base::Callback<void(bool)> callback) {
+  CommandResult result;
+  auto ret = module_->markBootSuccessful(StoreResultCallback(&result));
+  if (!ret.isOk()) {
+    LOG(ERROR) << "Unable to call MarkBootSuccessful: " << ret.description();
+    return false;
+  }
+  if (!result.success) {
+    LOG(ERROR) << "Unable to mark boot successful: " << result.errMsg.c_str();
+  }
+  return brillo::MessageLoop::current()->PostTask(
+             FROM_HERE, base::Bind(callback, result.success)) !=
+         brillo::MessageLoop::kTaskIdNull;
+}
+
+bool BootControlAndroid::IsSlotMarkedSuccessful(
+    BootControlInterface::Slot slot) const {
+  Return<BoolResult> ret = module_->isSlotMarkedSuccessful(slot);
+  CommandResult result;
+  if (!ret.isOk()) {
+    LOG(ERROR) << "Unable to determine if slot " << SlotName(slot)
+               << " is marked successful: " << ret.description();
+    return false;
+  }
+  if (ret == BoolResult::INVALID_SLOT) {
+    LOG(ERROR) << "Invalid slot: " << SlotName(slot);
+    return false;
+  }
+  return ret == BoolResult::TRUE;
+}
+
+DynamicPartitionControlInterface*
+BootControlAndroid::GetDynamicPartitionControl() {
+  return dynamic_control_.get();
+}
+
+}  // namespace chromeos_update_engine
diff --git a/aosp/boot_control_android.h b/aosp/boot_control_android.h
new file mode 100644
index 0000000..e288723
--- /dev/null
+++ b/aosp/boot_control_android.h
@@ -0,0 +1,73 @@
+//
+// 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 UPDATE_ENGINE_AOSP_BOOT_CONTROL_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_BOOT_CONTROL_ANDROID_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include <android/hardware/boot/1.0/IBootControl.h>
+#include <liblp/builder.h>
+
+#include "update_engine/aosp/dynamic_partition_control_android.h"
+#include "update_engine/common/boot_control.h"
+#include "update_engine/common/dynamic_partition_control_interface.h"
+
+namespace chromeos_update_engine {
+
+// The Android implementation of the BootControlInterface. This implementation
+// uses the libhardware's boot_control HAL to access the bootloader.
+class BootControlAndroid : public BootControlInterface {
+ public:
+  BootControlAndroid() = default;
+  ~BootControlAndroid() = default;
+
+  // Load boot_control HAL implementation using libhardware and
+  // initializes it. Returns false if an error occurred.
+  bool Init();
+
+  // BootControlInterface overrides.
+  unsigned int GetNumSlots() const override;
+  BootControlInterface::Slot GetCurrentSlot() const override;
+  bool GetPartitionDevice(const std::string& partition_name,
+                          BootControlInterface::Slot slot,
+                          bool not_in_payload,
+                          std::string* device,
+                          bool* is_dynamic) const override;
+  bool GetPartitionDevice(const std::string& partition_name,
+                          BootControlInterface::Slot slot,
+                          std::string* device) const override;
+  bool IsSlotBootable(BootControlInterface::Slot slot) const override;
+  bool MarkSlotUnbootable(BootControlInterface::Slot slot) override;
+  bool SetActiveBootSlot(BootControlInterface::Slot slot) override;
+  bool MarkBootSuccessfulAsync(base::Callback<void(bool)> callback) override;
+  bool IsSlotMarkedSuccessful(BootControlInterface::Slot slot) const override;
+  DynamicPartitionControlInterface* GetDynamicPartitionControl() override;
+
+ private:
+  ::android::sp<::android::hardware::boot::V1_0::IBootControl> module_;
+  std::unique_ptr<DynamicPartitionControlAndroid> dynamic_control_;
+
+  friend class BootControlAndroidTest;
+
+  DISALLOW_COPY_AND_ASSIGN(BootControlAndroid);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_AOSP_BOOT_CONTROL_ANDROID_H_
diff --git a/aosp/cleanup_previous_update_action.cc b/aosp/cleanup_previous_update_action.cc
new file mode 100644
index 0000000..16cb9fe
--- /dev/null
+++ b/aosp/cleanup_previous_update_action.cc
@@ -0,0 +1,474 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "update_engine/aosp/cleanup_previous_update_action.h"
+
+#include <chrono>  // NOLINT(build/c++11) -- for merge times
+#include <functional>
+#include <string>
+#include <type_traits>
+
+#include <android-base/properties.h>
+#include <base/bind.h>
+
+#ifndef __ANDROID_RECOVERY__
+#include <statslog.h>
+#endif
+
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/delta_performer.h"
+
+using android::base::GetBoolProperty;
+using android::snapshot::ISnapshotManager;
+using android::snapshot::SnapshotMergeStats;
+using android::snapshot::UpdateState;
+using brillo::MessageLoop;
+
+constexpr char kBootCompletedProp[] = "sys.boot_completed";
+// Interval to check sys.boot_completed.
+constexpr auto kCheckBootCompletedInterval = base::TimeDelta::FromSeconds(2);
+// Interval to check IBootControl::isSlotMarkedSuccessful
+constexpr auto kCheckSlotMarkedSuccessfulInterval =
+    base::TimeDelta::FromSeconds(2);
+// Interval to call SnapshotManager::ProcessUpdateState
+constexpr auto kWaitForMergeInterval = base::TimeDelta::FromSeconds(2);
+
+#ifdef __ANDROID_RECOVERY__
+static constexpr bool kIsRecovery = true;
+#else
+static constexpr bool kIsRecovery = false;
+#endif
+
+namespace chromeos_update_engine {
+
+CleanupPreviousUpdateAction::CleanupPreviousUpdateAction(
+    PrefsInterface* prefs,
+    BootControlInterface* boot_control,
+    android::snapshot::ISnapshotManager* snapshot,
+    CleanupPreviousUpdateActionDelegateInterface* delegate)
+    : prefs_(prefs),
+      boot_control_(boot_control),
+      snapshot_(snapshot),
+      delegate_(delegate),
+      running_(false),
+      cancel_failed_(false),
+      last_percentage_(0),
+      merge_stats_(nullptr) {}
+
+CleanupPreviousUpdateAction::~CleanupPreviousUpdateAction() {
+  StopActionInternal();
+}
+
+void CleanupPreviousUpdateAction::PerformAction() {
+  StartActionInternal();
+}
+
+void CleanupPreviousUpdateAction::TerminateProcessing() {
+  StopActionInternal();
+}
+
+void CleanupPreviousUpdateAction::ResumeAction() {
+  StartActionInternal();
+}
+
+void CleanupPreviousUpdateAction::SuspendAction() {
+  StopActionInternal();
+}
+
+void CleanupPreviousUpdateAction::ActionCompleted(ErrorCode error_code) {
+  StopActionInternal();
+  ReportMergeStats();
+  metadata_device_ = nullptr;
+}
+
+std::string CleanupPreviousUpdateAction::Type() const {
+  return StaticType();
+}
+
+std::string CleanupPreviousUpdateAction::StaticType() {
+  return "CleanupPreviousUpdateAction";
+}
+
+// This function is called at the beginning of all delayed functions. By
+// resetting |scheduled_task_|, the delayed function acknowledges that the task
+// has already been executed, therefore there's no need to cancel it in the
+// future. This avoids StopActionInternal() from resetting task IDs in an
+// unexpected way because task IDs could be reused.
+void CleanupPreviousUpdateAction::AcknowledgeTaskExecuted() {
+  if (scheduled_task_ != MessageLoop::kTaskIdNull) {
+    LOG(INFO) << "Executing task " << scheduled_task_;
+  }
+  scheduled_task_ = MessageLoop::kTaskIdNull;
+}
+
+// Check that scheduled_task_ is a valid task ID. Otherwise, terminate the
+// action.
+void CleanupPreviousUpdateAction::CheckTaskScheduled(std::string_view name) {
+  if (scheduled_task_ == MessageLoop::kTaskIdNull) {
+    LOG(ERROR) << "Unable to schedule " << name;
+    processor_->ActionComplete(this, ErrorCode::kError);
+  } else {
+    LOG(INFO) << "CleanupPreviousUpdateAction scheduled task ID "
+              << scheduled_task_ << " for " << name;
+  }
+}
+
+void CleanupPreviousUpdateAction::StopActionInternal() {
+  LOG(INFO) << "Stopping/suspending/completing CleanupPreviousUpdateAction";
+  running_ = false;
+
+  if (scheduled_task_ != MessageLoop::kTaskIdNull) {
+    if (MessageLoop::current()->CancelTask(scheduled_task_)) {
+      LOG(INFO) << "CleanupPreviousUpdateAction cancelled pending task ID "
+                << scheduled_task_;
+    } else {
+      LOG(ERROR) << "CleanupPreviousUpdateAction unable to cancel task ID "
+                 << scheduled_task_;
+    }
+  }
+  scheduled_task_ = MessageLoop::kTaskIdNull;
+}
+
+void CleanupPreviousUpdateAction::StartActionInternal() {
+  CHECK(prefs_);
+  CHECK(boot_control_);
+
+  LOG(INFO) << "Starting/resuming CleanupPreviousUpdateAction";
+  running_ = true;
+  // Do nothing on non-VAB device.
+  if (!boot_control_->GetDynamicPartitionControl()
+           ->GetVirtualAbFeatureFlag()
+           .IsEnabled()) {
+    processor_->ActionComplete(this, ErrorCode::kSuccess);
+    return;
+  }
+  // SnapshotManager must be available on VAB devices.
+  CHECK(snapshot_ != nullptr);
+  merge_stats_ = snapshot_->GetSnapshotMergeStatsInstance();
+  CHECK(merge_stats_ != nullptr);
+  WaitBootCompletedOrSchedule();
+}
+
+void CleanupPreviousUpdateAction::ScheduleWaitBootCompleted() {
+  TEST_AND_RETURN(running_);
+  scheduled_task_ = MessageLoop::current()->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&CleanupPreviousUpdateAction::WaitBootCompletedOrSchedule,
+                 base::Unretained(this)),
+      kCheckBootCompletedInterval);
+  CheckTaskScheduled("WaitBootCompleted");
+}
+
+void CleanupPreviousUpdateAction::WaitBootCompletedOrSchedule() {
+  AcknowledgeTaskExecuted();
+  TEST_AND_RETURN(running_);
+  if (!kIsRecovery &&
+      !android::base::GetBoolProperty(kBootCompletedProp, false)) {
+    // repeat
+    ScheduleWaitBootCompleted();
+    return;
+  }
+
+  LOG(INFO) << "Boot completed, waiting on markBootSuccessful()";
+  CheckSlotMarkedSuccessfulOrSchedule();
+}
+
+void CleanupPreviousUpdateAction::ScheduleWaitMarkBootSuccessful() {
+  TEST_AND_RETURN(running_);
+  scheduled_task_ = MessageLoop::current()->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(
+          &CleanupPreviousUpdateAction::CheckSlotMarkedSuccessfulOrSchedule,
+          base::Unretained(this)),
+      kCheckSlotMarkedSuccessfulInterval);
+  CheckTaskScheduled("WaitMarkBootSuccessful");
+}
+
+void CleanupPreviousUpdateAction::CheckSlotMarkedSuccessfulOrSchedule() {
+  AcknowledgeTaskExecuted();
+  TEST_AND_RETURN(running_);
+  if (!kIsRecovery &&
+      !boot_control_->IsSlotMarkedSuccessful(boot_control_->GetCurrentSlot())) {
+    ScheduleWaitMarkBootSuccessful();
+  }
+
+  if (metadata_device_ == nullptr) {
+    metadata_device_ = snapshot_->EnsureMetadataMounted();
+  }
+
+  if (metadata_device_ == nullptr) {
+    LOG(ERROR) << "Failed to mount /metadata.";
+    // If metadata is erased but not formatted, it is possible to not mount
+    // it in recovery. It is safe to skip CleanupPreviousUpdateAction.
+    processor_->ActionComplete(
+        this, kIsRecovery ? ErrorCode::kSuccess : ErrorCode::kError);
+    return;
+  }
+
+  if (kIsRecovery) {
+    auto snapshots_created =
+        snapshot_->RecoveryCreateSnapshotDevices(metadata_device_);
+    switch (snapshots_created) {
+      case android::snapshot::CreateResult::CREATED: {
+        // If previous update has not finished merging, snapshots exists and are
+        // created here so that ProcessUpdateState can proceed.
+        LOG(INFO) << "Snapshot devices are created";
+        break;
+      }
+      case android::snapshot::CreateResult::NOT_CREATED: {
+        // If there is no previous update, no snapshot devices are created and
+        // ProcessUpdateState will return immediately. Hence, NOT_CREATED is not
+        // considered an error.
+        LOG(INFO) << "Snapshot devices are not created";
+        break;
+      }
+      case android::snapshot::CreateResult::ERROR:
+      default: {
+        LOG(ERROR)
+            << "Failed to create snapshot devices (CreateResult = "
+            << static_cast<
+                   std::underlying_type_t<android::snapshot::CreateResult>>(
+                   snapshots_created);
+        processor_->ActionComplete(this, ErrorCode::kError);
+        return;
+      }
+    }
+  }
+
+  if (!merge_stats_->Start()) {
+    // Not an error because CleanupPreviousUpdateAction may be paused and
+    // resumed while kernel continues merging snapshots in the background.
+    LOG(WARNING) << "SnapshotMergeStats::Start failed.";
+  }
+  LOG(INFO) << "Waiting for any previous merge request to complete. "
+            << "This can take up to several minutes.";
+  WaitForMergeOrSchedule();
+}
+
+void CleanupPreviousUpdateAction::ScheduleWaitForMerge() {
+  TEST_AND_RETURN(running_);
+  scheduled_task_ = MessageLoop::current()->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&CleanupPreviousUpdateAction::WaitForMergeOrSchedule,
+                 base::Unretained(this)),
+      kWaitForMergeInterval);
+  CheckTaskScheduled("WaitForMerge");
+}
+
+void CleanupPreviousUpdateAction::WaitForMergeOrSchedule() {
+  AcknowledgeTaskExecuted();
+  TEST_AND_RETURN(running_);
+  auto state = snapshot_->ProcessUpdateState(
+      std::bind(&CleanupPreviousUpdateAction::OnMergePercentageUpdate, this),
+      std::bind(&CleanupPreviousUpdateAction::BeforeCancel, this));
+  merge_stats_->set_state(state);
+
+  switch (state) {
+    case UpdateState::None: {
+      LOG(INFO) << "Can't find any snapshot to merge.";
+      ErrorCode error_code = ErrorCode::kSuccess;
+      if (!snapshot_->CancelUpdate()) {
+        error_code = ErrorCode::kError;
+        LOG(INFO) << "Failed to call SnapshotManager::CancelUpdate().";
+      }
+      processor_->ActionComplete(this, error_code);
+      return;
+    }
+
+    case UpdateState::Initiated: {
+      LOG(ERROR) << "Previous update has not been completed, not cleaning up";
+      processor_->ActionComplete(this, ErrorCode::kSuccess);
+      return;
+    }
+
+    case UpdateState::Unverified: {
+      InitiateMergeAndWait();
+      return;
+    }
+
+    case UpdateState::Merging: {
+      ScheduleWaitForMerge();
+      return;
+    }
+
+    case UpdateState::MergeNeedsReboot: {
+      LOG(ERROR) << "Need reboot to finish merging.";
+      processor_->ActionComplete(this, ErrorCode::kError);
+      return;
+    }
+
+    case UpdateState::MergeCompleted: {
+      LOG(INFO) << "Merge finished with state MergeCompleted.";
+      processor_->ActionComplete(this, ErrorCode::kSuccess);
+      return;
+    }
+
+    case UpdateState::MergeFailed: {
+      LOG(ERROR) << "Merge failed. Device may be corrupted.";
+      processor_->ActionComplete(this, ErrorCode::kDeviceCorrupted);
+      return;
+    }
+
+    case UpdateState::Cancelled: {
+      // DeltaPerformer::ResetUpdateProgress failed, hence snapshots are
+      // not deleted to avoid inconsistency.
+      // Nothing can be done here; just try next time.
+      ErrorCode error_code =
+          cancel_failed_ ? ErrorCode::kError : ErrorCode::kSuccess;
+      processor_->ActionComplete(this, error_code);
+      return;
+    }
+
+    default: {
+      // Protobuf has some reserved enum values, so a default case is needed.
+      LOG(FATAL) << "SnapshotManager::ProcessUpdateState returns "
+                 << static_cast<int32_t>(state);
+    }
+  }
+}
+
+bool CleanupPreviousUpdateAction::OnMergePercentageUpdate() {
+  double percentage = 0.0;
+  snapshot_->GetUpdateState(&percentage);
+  if (delegate_) {
+    // libsnapshot uses [0, 100] percentage but update_engine uses [0, 1].
+    delegate_->OnCleanupProgressUpdate(percentage / 100);
+  }
+
+  // Log if percentage increments by at least 1.
+  if (last_percentage_ < static_cast<unsigned int>(percentage)) {
+    last_percentage_ = percentage;
+    LOG(INFO) << "Waiting for merge to complete: " << last_percentage_ << "%.";
+  }
+
+  // Do not continue to wait for merge. Instead, let ProcessUpdateState
+  // return Merging directly so that we can ScheduleWaitForMerge() in
+  // MessageLoop.
+  return false;
+}
+
+bool CleanupPreviousUpdateAction::BeforeCancel() {
+  if (DeltaPerformer::ResetUpdateProgress(
+          prefs_,
+          false /* quick */,
+          false /* skip dynamic partitions metadata*/)) {
+    return true;
+  }
+
+  // ResetUpdateProgress might not work on stub prefs. Do additional checks.
+  LOG(WARNING) << "ProcessUpdateState returns Cancelled but cleanup failed.";
+
+  std::string val;
+  ignore_result(prefs_->GetString(kPrefsDynamicPartitionMetadataUpdated, &val));
+  if (val.empty()) {
+    LOG(INFO) << kPrefsDynamicPartitionMetadataUpdated
+              << " is empty, assuming successful cleanup";
+    return true;
+  }
+  LOG(WARNING)
+      << kPrefsDynamicPartitionMetadataUpdated << " is " << val
+      << ", not deleting snapshots even though UpdateState is Cancelled.";
+  cancel_failed_ = true;
+  return false;
+}
+
+void CleanupPreviousUpdateAction::InitiateMergeAndWait() {
+  TEST_AND_RETURN(running_);
+  LOG(INFO) << "Attempting to initiate merge.";
+  // suspend the VAB merge when running a DSU
+  if (GetBoolProperty("ro.gsid.image_running", false)) {
+    LOG(WARNING) << "Suspend the VAB merge when running a DSU.";
+    processor_->ActionComplete(this, ErrorCode::kError);
+    return;
+  }
+
+  uint64_t cow_file_size;
+  if (snapshot_->InitiateMerge(&cow_file_size)) {
+    merge_stats_->set_cow_file_size(cow_file_size);
+    WaitForMergeOrSchedule();
+    return;
+  }
+
+  LOG(WARNING) << "InitiateMerge failed.";
+  auto state = snapshot_->GetUpdateState();
+  merge_stats_->set_state(state);
+  if (state == UpdateState::Unverified) {
+    // We are stuck at unverified state. This can happen if the update has
+    // been applied, but it has not even been attempted yet (in libsnapshot,
+    // rollback indicator does not exist); for example, if update_engine
+    // restarts before the device reboots, then this state may be reached.
+    // Nothing should be done here.
+    LOG(WARNING) << "InitiateMerge leaves the device at "
+                 << "UpdateState::Unverified. (Did update_engine "
+                 << "restarted?)";
+    processor_->ActionComplete(this, ErrorCode::kSuccess);
+    return;
+  }
+
+  // State does seems to be advanced.
+  // It is possibly racy. For example, on a userdebug build, the user may
+  // manually initiate a merge with snapshotctl between last time
+  // update_engine checks UpdateState. Hence, just call
+  // WaitForMergeOrSchedule one more time.
+  LOG(WARNING) << "IniitateMerge failed but GetUpdateState returned "
+               << android::snapshot::UpdateState_Name(state)
+               << ", try to wait for merge again.";
+  WaitForMergeOrSchedule();
+  return;
+}
+
+void CleanupPreviousUpdateAction::ReportMergeStats() {
+  auto result = merge_stats_->Finish();
+  if (result == nullptr) {
+    LOG(WARNING) << "Not reporting merge stats because "
+                    "SnapshotMergeStats::Finish failed.";
+    return;
+  }
+
+#ifdef __ANDROID_RECOVERY__
+  LOG(INFO) << "Skip reporting merge stats in recovery.";
+#else
+  const auto& report = result->report();
+
+  if (report.state() == UpdateState::None ||
+      report.state() == UpdateState::Initiated ||
+      report.state() == UpdateState::Unverified) {
+    LOG(INFO) << "Not reporting merge stats because state is "
+              << android::snapshot::UpdateState_Name(report.state());
+    return;
+  }
+
+  auto passed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
+      result->merge_time());
+
+  bool vab_retrofit = boot_control_->GetDynamicPartitionControl()
+                          ->GetVirtualAbFeatureFlag()
+                          .IsRetrofit();
+
+  LOG(INFO) << "Reporting merge stats: "
+            << android::snapshot::UpdateState_Name(report.state()) << " in "
+            << passed_ms.count() << "ms (resumed " << report.resume_count()
+            << " times), using " << report.cow_file_size()
+            << " bytes of COW image.";
+  android::util::stats_write(android::util::SNAPSHOT_MERGE_REPORTED,
+                             static_cast<int32_t>(report.state()),
+                             static_cast<int64_t>(passed_ms.count()),
+                             static_cast<int32_t>(report.resume_count()),
+                             vab_retrofit,
+                             static_cast<int64_t>(report.cow_file_size()));
+#endif
+}
+
+}  // namespace chromeos_update_engine
diff --git a/aosp/cleanup_previous_update_action.h b/aosp/cleanup_previous_update_action.h
new file mode 100644
index 0000000..b93c557
--- /dev/null
+++ b/aosp/cleanup_previous_update_action.h
@@ -0,0 +1,103 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_CLEANUP_PREVIOUS_UPDATE_ACTION_H_
+#define UPDATE_ENGINE_AOSP_CLEANUP_PREVIOUS_UPDATE_ACTION_H_
+
+#include <chrono>  // NOLINT(build/c++11) -- for merge times
+#include <memory>
+#include <string>
+#include <string_view>
+
+#include <brillo/message_loops/message_loop.h>
+#include <libsnapshot/snapshot.h>
+#include <libsnapshot/snapshot_stats.h>
+
+#include "update_engine/common/action.h"
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/cleanup_previous_update_action_delegate.h"
+#include "update_engine/common/error_code.h"
+#include "update_engine/common/prefs_interface.h"
+
+namespace chromeos_update_engine {
+
+class CleanupPreviousUpdateAction;
+
+template <>
+class ActionTraits<CleanupPreviousUpdateAction> {
+ public:
+  typedef NoneType InputObjectType;
+  typedef NoneType OutputObjectType;
+};
+
+// On Android Virtual A/B devices, clean up snapshots from previous update
+// attempt. See DynamicPartitionControlAndroid::CleanupSuccessfulUpdate.
+class CleanupPreviousUpdateAction : public Action<CleanupPreviousUpdateAction> {
+ public:
+  CleanupPreviousUpdateAction(
+      PrefsInterface* prefs,
+      BootControlInterface* boot_control,
+      android::snapshot::ISnapshotManager* snapshot,
+      CleanupPreviousUpdateActionDelegateInterface* delegate);
+  ~CleanupPreviousUpdateAction();
+
+  void PerformAction() override;
+  void SuspendAction() override;
+  void ResumeAction() override;
+  void TerminateProcessing() override;
+  void ActionCompleted(ErrorCode error_code) override;
+  std::string Type() const override;
+  static std::string StaticType();
+  typedef ActionTraits<CleanupPreviousUpdateAction>::InputObjectType
+      InputObjectType;
+  typedef ActionTraits<CleanupPreviousUpdateAction>::OutputObjectType
+      OutputObjectType;
+
+ private:
+  PrefsInterface* prefs_;
+  BootControlInterface* boot_control_;
+  android::snapshot::ISnapshotManager* snapshot_;
+  CleanupPreviousUpdateActionDelegateInterface* delegate_;
+  std::unique_ptr<android::snapshot::AutoDevice> metadata_device_;
+  bool running_{false};
+  bool cancel_failed_{false};
+  unsigned int last_percentage_{0};
+  android::snapshot::ISnapshotMergeStats* merge_stats_;
+  brillo::MessageLoop::TaskId scheduled_task_{brillo::MessageLoop::kTaskIdNull};
+
+  // Helpers for task management.
+  void AcknowledgeTaskExecuted();
+  void CheckTaskScheduled(std::string_view name);
+
+  void StopActionInternal();
+  void StartActionInternal();
+  void ScheduleWaitBootCompleted();
+  void WaitBootCompletedOrSchedule();
+  void ScheduleWaitMarkBootSuccessful();
+  void CheckSlotMarkedSuccessfulOrSchedule();
+  void ScheduleWaitForMerge();
+  void WaitForMergeOrSchedule();
+  void InitiateMergeAndWait();
+  void ReportMergeStats();
+
+  // Callbacks to ProcessUpdateState.
+  bool OnMergePercentageUpdate();
+  bool BeforeCancel();
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_AOSP_CLEANUP_PREVIOUS_UPDATE_ACTION_H_
diff --git a/aosp/daemon_android.cc b/aosp/daemon_android.cc
new file mode 100644
index 0000000..c102e3b
--- /dev/null
+++ b/aosp/daemon_android.cc
@@ -0,0 +1,74 @@
+//
+// 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 "update_engine/aosp/daemon_android.h"
+
+#include <sysexits.h>
+
+#include <binderwrapper/binder_wrapper.h>
+
+#include "update_engine/aosp/daemon_state_android.h"
+
+using std::unique_ptr;
+
+namespace chromeos_update_engine {
+
+unique_ptr<DaemonBase> DaemonBase::CreateInstance() {
+  return std::make_unique<DaemonAndroid>();
+}
+
+int DaemonAndroid::OnInit() {
+  // Register the |subprocess_| singleton with this Daemon as the signal
+  // handler.
+  subprocess_.Init(this);
+
+  int exit_code = brillo::Daemon::OnInit();
+  if (exit_code != EX_OK)
+    return exit_code;
+
+  android::BinderWrapper::Create();
+  binder_watcher_.Init();
+
+  DaemonStateAndroid* daemon_state_android = new DaemonStateAndroid();
+  daemon_state_.reset(daemon_state_android);
+  LOG_IF(ERROR, !daemon_state_android->Initialize())
+      << "Failed to initialize system state.";
+
+  auto binder_wrapper = android::BinderWrapper::Get();
+
+  // Create the Binder Service.
+  binder_service_ = new BinderUpdateEngineAndroidService{
+      daemon_state_android->service_delegate()};
+  if (!binder_wrapper->RegisterService(binder_service_->ServiceName(),
+                                       binder_service_)) {
+    LOG(ERROR) << "Failed to register binder service.";
+  }
+  daemon_state_->AddObserver(binder_service_.get());
+
+  // Create the stable binder service.
+  stable_binder_service_ = new BinderUpdateEngineAndroidStableService{
+      daemon_state_android->service_delegate()};
+  if (!binder_wrapper->RegisterService(stable_binder_service_->ServiceName(),
+                                       stable_binder_service_)) {
+    LOG(ERROR) << "Failed to register stable binder service.";
+  }
+  daemon_state_->AddObserver(stable_binder_service_.get());
+
+  daemon_state_->StartUpdater();
+  return EX_OK;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/aosp/daemon_android.h b/aosp/daemon_android.h
new file mode 100644
index 0000000..38a8689
--- /dev/null
+++ b/aosp/daemon_android.h
@@ -0,0 +1,58 @@
+//
+// 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 UPDATE_ENGINE_AOSP_DAEMON_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_DAEMON_ANDROID_H_
+
+#include <memory>
+
+#include <brillo/binder_watcher.h>
+
+#include "update_engine/aosp/binder_service_android.h"
+#include "update_engine/aosp/binder_service_stable_android.h"
+#include "update_engine/common/daemon_base.h"
+#include "update_engine/common/daemon_state_interface.h"
+#include "update_engine/common/subprocess.h"
+
+namespace chromeos_update_engine {
+
+class DaemonAndroid : public DaemonBase {
+ public:
+  DaemonAndroid() = default;
+
+ protected:
+  int OnInit() override;
+
+ private:
+  // The Subprocess singleton class requires a |brillo::MessageLoop| in the
+  // current thread, so we need to initialize it from this class instead of
+  // the main() function.
+  Subprocess subprocess_;
+
+  brillo::BinderWatcher binder_watcher_;
+  android::sp<BinderUpdateEngineAndroidService> binder_service_;
+  android::sp<BinderUpdateEngineAndroidStableService> stable_binder_service_;
+
+  // The daemon state with all the required daemon classes for the configured
+  // platform.
+  std::unique_ptr<DaemonStateInterface> daemon_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(DaemonAndroid);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_AOSP_DAEMON_ANDROID_H_
diff --git a/aosp/daemon_state_android.cc b/aosp/daemon_state_android.cc
new file mode 100644
index 0000000..9bdd175
--- /dev/null
+++ b/aosp/daemon_state_android.cc
@@ -0,0 +1,92 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/daemon_state_android.h"
+
+#include <base/logging.h>
+
+#include "update_engine/aosp/update_attempter_android.h"
+#include "update_engine/common/boot_control.h"
+#include "update_engine/common/boot_control_stub.h"
+#include "update_engine/common/hardware.h"
+#include "update_engine/common/prefs.h"
+
+namespace chromeos_update_engine {
+
+bool DaemonStateAndroid::Initialize() {
+  boot_control_ = boot_control::CreateBootControl();
+  if (!boot_control_) {
+    LOG(WARNING) << "Unable to create BootControl instance, using stub "
+                 << "instead. All update attempts will fail.";
+    boot_control_.reset(new BootControlStub());
+  }
+
+  hardware_ = hardware::CreateHardware();
+  if (!hardware_) {
+    LOG(ERROR) << "Error initializing the HardwareInterface.";
+    return false;
+  }
+
+  LOG_IF(INFO, !hardware_->IsNormalBootMode()) << "Booted in dev mode.";
+  LOG_IF(INFO, !hardware_->IsOfficialBuild()) << "Booted non-official build.";
+
+  // Initialize prefs.
+  base::FilePath non_volatile_path;
+  if (!hardware_->GetNonVolatileDirectory(&non_volatile_path)) {
+    prefs_.reset(new MemoryPrefs());
+    LOG(WARNING)
+        << "Could not get a non-volatile directory, fall back to memory prefs";
+  } else {
+    Prefs* prefs = new Prefs();
+    prefs_.reset(prefs);
+    if (!prefs->Init(non_volatile_path.Append(kPrefsSubDirectory))) {
+      LOG(ERROR) << "Failed to initialize preferences.";
+      return false;
+    }
+  }
+
+  // The CertificateChecker singleton is used by the update attempter.
+  certificate_checker_.reset(
+      new CertificateChecker(prefs_.get(), &openssl_wrapper_));
+  certificate_checker_->Init();
+
+  // Initialize the UpdateAttempter before the UpdateManager.
+  update_attempter_.reset(new UpdateAttempterAndroid(
+      this, prefs_.get(), boot_control_.get(), hardware_.get()));
+
+  return true;
+}
+
+bool DaemonStateAndroid::StartUpdater() {
+  // The DaemonState in Android is a passive daemon. It will only start applying
+  // an update when instructed to do so from the exposed binder API.
+  update_attempter_->Init();
+  return true;
+}
+
+void DaemonStateAndroid::AddObserver(ServiceObserverInterface* observer) {
+  service_observers_.insert(observer);
+}
+
+void DaemonStateAndroid::RemoveObserver(ServiceObserverInterface* observer) {
+  service_observers_.erase(observer);
+}
+
+ServiceDelegateAndroidInterface* DaemonStateAndroid::service_delegate() {
+  return update_attempter_.get();
+}
+
+}  // namespace chromeos_update_engine
diff --git a/aosp/daemon_state_android.h b/aosp/daemon_state_android.h
new file mode 100644
index 0000000..dea3a23
--- /dev/null
+++ b/aosp/daemon_state_android.h
@@ -0,0 +1,76 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_DAEMON_STATE_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_DAEMON_STATE_ANDROID_H_
+
+#include <memory>
+#include <set>
+
+#include "update_engine/aosp/service_delegate_android_interface.h"
+#include "update_engine/aosp/update_attempter_android.h"
+#include "update_engine/certificate_checker.h"
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/daemon_state_interface.h"
+#include "update_engine/common/hardware_interface.h"
+#include "update_engine/common/prefs_interface.h"
+#include "update_engine/common/service_observer_interface.h"
+
+namespace chromeos_update_engine {
+
+class DaemonStateAndroid : public DaemonStateInterface {
+ public:
+  DaemonStateAndroid() = default;
+  ~DaemonStateAndroid() override = default;
+
+  bool Initialize();
+
+  // DaemonStateInterface overrides.
+  bool StartUpdater() override;
+  void AddObserver(ServiceObserverInterface* observer) override;
+  void RemoveObserver(ServiceObserverInterface* observer) override;
+
+  const std::set<ServiceObserverInterface*>& service_observers() override {
+    return service_observers_;
+  }
+
+  // Return a pointer to the service delegate.
+  ServiceDelegateAndroidInterface* service_delegate();
+
+ protected:
+  std::set<ServiceObserverInterface*> service_observers_;
+
+  // Interface for the boot control functions.
+  std::unique_ptr<BootControlInterface> boot_control_;
+
+  // Interface for the hardware functions.
+  std::unique_ptr<HardwareInterface> hardware_;
+
+  // Interface for persisted store.
+  std::unique_ptr<PrefsInterface> prefs_;
+
+  // The main class handling the updates.
+  std::unique_ptr<UpdateAttempterAndroid> update_attempter_;
+
+  // OpenSSLWrapper and CertificateChecker used for checking changes in SSL
+  // certificates.
+  OpenSSLWrapper openssl_wrapper_;
+  std::unique_ptr<CertificateChecker> certificate_checker_;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_AOSP_DAEMON_STATE_ANDROID_H_
diff --git a/aosp/dynamic_partition_control_android.cc b/aosp/dynamic_partition_control_android.cc
new file mode 100644
index 0000000..d461807
--- /dev/null
+++ b/aosp/dynamic_partition_control_android.cc
@@ -0,0 +1,1274 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/dynamic_partition_control_android.h"
+
+#include <chrono>  // NOLINT(build/c++11) - using libsnapshot / liblp API
+#include <cstdint>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <bootloader_message/bootloader_message.h>
+#include <fs_mgr.h>
+#include <fs_mgr_dm_linear.h>
+#include <fs_mgr_overlayfs.h>
+#include <libavb/libavb.h>
+#include <libdm/dm.h>
+#include <liblp/liblp.h>
+#include <libsnapshot/cow_writer.h>
+#include <libsnapshot/snapshot.h>
+#include <libsnapshot/snapshot_stub.h>
+
+#include "update_engine/aosp/cleanup_previous_update_action.h"
+#include "update_engine/aosp/dynamic_partition_utils.h"
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/delta_performer.h"
+
+using android::base::GetBoolProperty;
+using android::base::GetProperty;
+using android::base::Join;
+using android::dm::DeviceMapper;
+using android::dm::DmDeviceState;
+using android::fs_mgr::CreateLogicalPartition;
+using android::fs_mgr::CreateLogicalPartitionParams;
+using android::fs_mgr::DestroyLogicalPartition;
+using android::fs_mgr::Fstab;
+using android::fs_mgr::MetadataBuilder;
+using android::fs_mgr::Partition;
+using android::fs_mgr::PartitionOpener;
+using android::fs_mgr::SlotSuffixForSlotNumber;
+using android::snapshot::OptimizeSourceCopyOperation;
+using android::snapshot::Return;
+using android::snapshot::SnapshotManager;
+using android::snapshot::SnapshotManagerStub;
+using android::snapshot::UpdateState;
+
+namespace chromeos_update_engine {
+
+constexpr char kUseDynamicPartitions[] = "ro.boot.dynamic_partitions";
+constexpr char kRetrfoitDynamicPartitions[] =
+    "ro.boot.dynamic_partitions_retrofit";
+constexpr char kVirtualAbEnabled[] = "ro.virtual_ab.enabled";
+constexpr char kVirtualAbRetrofit[] = "ro.virtual_ab.retrofit";
+constexpr char kVirtualAbCompressionEnabled[] =
+    "ro.virtual_ab.compression.enabled";
+
+// Currently, android doesn't have a retrofit prop for VAB Compression. However,
+// struct FeatureFlag forces us to determine if a feature is 'retrofit'. So this
+// is here just to simplify code. Replace it with real retrofit prop name once
+// there is one.
+constexpr char kVirtualAbCompressionRetrofit[] = "";
+constexpr char kPostinstallFstabPrefix[] = "ro.postinstall.fstab.prefix";
+// Map timeout for dynamic partitions.
+constexpr std::chrono::milliseconds kMapTimeout{1000};
+// Map timeout for dynamic partitions with snapshots. Since several devices
+// needs to be mapped, this timeout is longer than |kMapTimeout|.
+constexpr std::chrono::milliseconds kMapSnapshotTimeout{5000};
+
+DynamicPartitionControlAndroid::~DynamicPartitionControlAndroid() {
+  Cleanup();
+}
+
+static FeatureFlag GetFeatureFlag(const char* enable_prop,
+                                  const char* retrofit_prop) {
+  // Default retrofit to false if retrofit_prop is empty.
+  bool retrofit = retrofit_prop && retrofit_prop[0] != '\0' &&
+                  GetBoolProperty(retrofit_prop, false);
+  bool enabled = GetBoolProperty(enable_prop, false);
+  if (retrofit && !enabled) {
+    LOG(ERROR) << retrofit_prop << " is true but " << enable_prop
+               << " is not. These sysprops are inconsistent. Assume that "
+               << enable_prop << " is true from now on.";
+  }
+  if (retrofit) {
+    return FeatureFlag(FeatureFlag::Value::RETROFIT);
+  }
+  if (enabled) {
+    return FeatureFlag(FeatureFlag::Value::LAUNCH);
+  }
+  return FeatureFlag(FeatureFlag::Value::NONE);
+}
+
+DynamicPartitionControlAndroid::DynamicPartitionControlAndroid()
+    : dynamic_partitions_(
+          GetFeatureFlag(kUseDynamicPartitions, kRetrfoitDynamicPartitions)),
+      virtual_ab_(GetFeatureFlag(kVirtualAbEnabled, kVirtualAbRetrofit)),
+      virtual_ab_compression_(GetFeatureFlag(kVirtualAbCompressionEnabled,
+                                             kVirtualAbCompressionRetrofit)) {
+  if (GetVirtualAbFeatureFlag().IsEnabled()) {
+    snapshot_ = SnapshotManager::New();
+  } else {
+    snapshot_ = SnapshotManagerStub::New();
+  }
+  CHECK(snapshot_ != nullptr) << "Cannot initialize SnapshotManager.";
+}
+
+FeatureFlag DynamicPartitionControlAndroid::GetDynamicPartitionsFeatureFlag() {
+  return dynamic_partitions_;
+}
+
+FeatureFlag DynamicPartitionControlAndroid::GetVirtualAbFeatureFlag() {
+  return virtual_ab_;
+}
+
+FeatureFlag
+DynamicPartitionControlAndroid::GetVirtualAbCompressionFeatureFlag() {
+  return virtual_ab_compression_;
+}
+
+bool DynamicPartitionControlAndroid::OptimizeOperation(
+    const std::string& partition_name,
+    const InstallOperation& operation,
+    InstallOperation* optimized) {
+  switch (operation.type()) {
+    case InstallOperation::SOURCE_COPY:
+      return target_supports_snapshot_ &&
+             GetVirtualAbFeatureFlag().IsEnabled() &&
+             mapped_devices_.count(partition_name +
+                                   SlotSuffixForSlotNumber(target_slot_)) > 0 &&
+             OptimizeSourceCopyOperation(operation, optimized);
+      break;
+    default:
+      break;
+  }
+  return false;
+}
+
+bool DynamicPartitionControlAndroid::MapPartitionInternal(
+    const std::string& super_device,
+    const std::string& target_partition_name,
+    uint32_t slot,
+    bool force_writable,
+    std::string* path) {
+  CreateLogicalPartitionParams params = {
+      .block_device = super_device,
+      .metadata_slot = slot,
+      .partition_name = target_partition_name,
+      .force_writable = force_writable,
+  };
+  bool success = false;
+  if (GetVirtualAbFeatureFlag().IsEnabled() && target_supports_snapshot_ &&
+      force_writable && ExpectMetadataMounted()) {
+    // Only target partitions are mapped with force_writable. On Virtual
+    // A/B devices, target partitions may overlap with source partitions, so
+    // they must be mapped with snapshot.
+    // One exception is when /metadata is not mounted. Fallback to
+    // CreateLogicalPartition as snapshots are not created in the first place.
+    params.timeout_ms = kMapSnapshotTimeout;
+    success = snapshot_->MapUpdateSnapshot(params, path);
+  } else {
+    params.timeout_ms = kMapTimeout;
+    success = CreateLogicalPartition(params, path);
+  }
+
+  if (!success) {
+    LOG(ERROR) << "Cannot map " << target_partition_name << " in "
+               << super_device << " on device mapper.";
+    return false;
+  }
+  LOG(INFO) << "Succesfully mapped " << target_partition_name
+            << " to device mapper (force_writable = " << force_writable
+            << "); device path at " << *path;
+  mapped_devices_.insert(target_partition_name);
+  return true;
+}
+
+bool DynamicPartitionControlAndroid::MapPartitionOnDeviceMapper(
+    const std::string& super_device,
+    const std::string& target_partition_name,
+    uint32_t slot,
+    bool force_writable,
+    std::string* path) {
+  DmDeviceState state = GetState(target_partition_name);
+  if (state == DmDeviceState::ACTIVE) {
+    if (mapped_devices_.find(target_partition_name) != mapped_devices_.end()) {
+      if (GetDmDevicePathByName(target_partition_name, path)) {
+        LOG(INFO) << target_partition_name
+                  << " is mapped on device mapper: " << *path;
+        return true;
+      }
+      LOG(ERROR) << target_partition_name << " is mapped but path is unknown.";
+      return false;
+    }
+    // If target_partition_name is not in mapped_devices_ but state is ACTIVE,
+    // the device might be mapped incorrectly before. Attempt to unmap it.
+    // Note that for source partitions, if GetState() == ACTIVE, callers (e.g.
+    // BootControlAndroid) should not call MapPartitionOnDeviceMapper, but
+    // should directly call GetDmDevicePathByName.
+    if (!UnmapPartitionOnDeviceMapper(target_partition_name)) {
+      LOG(ERROR) << target_partition_name
+                 << " is mapped before the update, and it cannot be unmapped.";
+      return false;
+    }
+    state = GetState(target_partition_name);
+    if (state != DmDeviceState::INVALID) {
+      LOG(ERROR) << target_partition_name << " is unmapped but state is "
+                 << static_cast<std::underlying_type_t<DmDeviceState>>(state);
+      return false;
+    }
+  }
+  if (state == DmDeviceState::INVALID) {
+    return MapPartitionInternal(
+        super_device, target_partition_name, slot, force_writable, path);
+  }
+
+  LOG(ERROR) << target_partition_name
+             << " is mapped on device mapper but state is unknown: "
+             << static_cast<std::underlying_type_t<DmDeviceState>>(state);
+  return false;
+}
+
+bool DynamicPartitionControlAndroid::UnmapPartitionOnDeviceMapper(
+    const std::string& target_partition_name) {
+  if (DeviceMapper::Instance().GetState(target_partition_name) !=
+      DmDeviceState::INVALID) {
+    // Partitions at target slot on non-Virtual A/B devices are mapped as
+    // dm-linear. Also, on Virtual A/B devices, system_other may be mapped for
+    // preopt apps as dm-linear.
+    // Call DestroyLogicalPartition to handle these cases.
+    bool success = DestroyLogicalPartition(target_partition_name);
+
+    // On a Virtual A/B device, |target_partition_name| may be a leftover from
+    // a paused update. Clean up any underlying devices.
+    if (ExpectMetadataMounted()) {
+      success &= snapshot_->UnmapUpdateSnapshot(target_partition_name);
+    } else {
+      LOG(INFO) << "Skip UnmapUpdateSnapshot(" << target_partition_name
+                << ") because metadata is not mounted";
+    }
+
+    if (!success) {
+      LOG(ERROR) << "Cannot unmap " << target_partition_name
+                 << " from device mapper.";
+      return false;
+    }
+    LOG(INFO) << "Successfully unmapped " << target_partition_name
+              << " from device mapper.";
+  }
+  mapped_devices_.erase(target_partition_name);
+  return true;
+}
+
+bool DynamicPartitionControlAndroid::UnmapAllPartitions() {
+  if (mapped_devices_.empty()) {
+    return false;
+  }
+  // UnmapPartitionOnDeviceMapper removes objects from mapped_devices_, hence
+  // a copy is needed for the loop.
+  std::set<std::string> mapped = mapped_devices_;
+  LOG(INFO) << "Destroying [" << Join(mapped, ", ") << "] from device mapper";
+  for (const auto& partition_name : mapped) {
+    ignore_result(UnmapPartitionOnDeviceMapper(partition_name));
+  }
+  return true;
+}
+
+void DynamicPartitionControlAndroid::Cleanup() {
+  UnmapAllPartitions();
+  metadata_device_.reset();
+}
+
+bool DynamicPartitionControlAndroid::DeviceExists(const std::string& path) {
+  return base::PathExists(base::FilePath(path));
+}
+
+android::dm::DmDeviceState DynamicPartitionControlAndroid::GetState(
+    const std::string& name) {
+  return DeviceMapper::Instance().GetState(name);
+}
+
+bool DynamicPartitionControlAndroid::GetDmDevicePathByName(
+    const std::string& name, std::string* path) {
+  return DeviceMapper::Instance().GetDmDevicePathByName(name, path);
+}
+
+std::unique_ptr<MetadataBuilder>
+DynamicPartitionControlAndroid::LoadMetadataBuilder(
+    const std::string& super_device, uint32_t slot) {
+  auto builder = MetadataBuilder::New(PartitionOpener(), super_device, slot);
+  if (builder == nullptr) {
+    LOG(WARNING) << "No metadata slot " << BootControlInterface::SlotName(slot)
+                 << " in " << super_device;
+    return nullptr;
+  }
+  LOG(INFO) << "Loaded metadata from slot "
+            << BootControlInterface::SlotName(slot) << " in " << super_device;
+  return builder;
+}
+
+std::unique_ptr<MetadataBuilder>
+DynamicPartitionControlAndroid::LoadMetadataBuilder(
+    const std::string& super_device,
+    uint32_t source_slot,
+    uint32_t target_slot) {
+  bool always_keep_source_slot = !target_supports_snapshot_;
+  auto builder = MetadataBuilder::NewForUpdate(PartitionOpener(),
+                                               super_device,
+                                               source_slot,
+                                               target_slot,
+                                               always_keep_source_slot);
+  if (builder == nullptr) {
+    LOG(WARNING) << "No metadata slot "
+                 << BootControlInterface::SlotName(source_slot) << " in "
+                 << super_device;
+    return nullptr;
+  }
+  LOG(INFO) << "Created metadata for new update from slot "
+            << BootControlInterface::SlotName(source_slot) << " in "
+            << super_device;
+  return builder;
+}
+
+bool DynamicPartitionControlAndroid::StoreMetadata(
+    const std::string& super_device,
+    MetadataBuilder* builder,
+    uint32_t target_slot) {
+  auto metadata = builder->Export();
+  if (metadata == nullptr) {
+    LOG(ERROR) << "Cannot export metadata to slot "
+               << BootControlInterface::SlotName(target_slot) << " in "
+               << super_device;
+    return false;
+  }
+
+  if (GetDynamicPartitionsFeatureFlag().IsRetrofit()) {
+    if (!FlashPartitionTable(super_device, *metadata)) {
+      LOG(ERROR) << "Cannot write metadata to " << super_device;
+      return false;
+    }
+    LOG(INFO) << "Written metadata to " << super_device;
+  } else {
+    if (!UpdatePartitionTable(super_device, *metadata, target_slot)) {
+      LOG(ERROR) << "Cannot write metadata to slot "
+                 << BootControlInterface::SlotName(target_slot) << " in "
+                 << super_device;
+      return false;
+    }
+    LOG(INFO) << "Copied metadata to slot "
+              << BootControlInterface::SlotName(target_slot) << " in "
+              << super_device;
+  }
+
+  return true;
+}
+
+bool DynamicPartitionControlAndroid::GetDeviceDir(std::string* out) {
+  // We can't use fs_mgr to look up |partition_name| because fstab
+  // doesn't list every slot partition (it uses the slotselect option
+  // to mask the suffix).
+  //
+  // We can however assume that there's an entry for the /misc mount
+  // point and use that to get the device file for the misc
+  // partition. This helps us locate the disk that |partition_name|
+  // resides on. From there we'll assume that a by-name scheme is used
+  // so we can just replace the trailing "misc" by the given
+  // |partition_name| and suffix corresponding to |slot|, e.g.
+  //
+  //   /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
+  //   /dev/block/platform/soc.0/7824900.sdhci/by-name/boot_a
+  //
+  // If needed, it's possible to relax the by-name assumption in the
+  // future by trawling /sys/block looking for the appropriate sibling
+  // of misc and then finding an entry in /dev matching the sysfs
+  // entry.
+
+  std::string err, misc_device = get_bootloader_message_blk_device(&err);
+  if (misc_device.empty()) {
+    LOG(ERROR) << "Unable to get misc block device: " << err;
+    return false;
+  }
+
+  if (!utils::IsSymlink(misc_device.c_str())) {
+    LOG(ERROR) << "Device file " << misc_device << " for /misc "
+               << "is not a symlink.";
+    return false;
+  }
+  *out = base::FilePath(misc_device).DirName().value();
+  return true;
+}
+
+bool DynamicPartitionControlAndroid::PreparePartitionsForUpdate(
+    uint32_t source_slot,
+    uint32_t target_slot,
+    const DeltaArchiveManifest& manifest,
+    bool update,
+    uint64_t* required_size) {
+  source_slot_ = source_slot;
+  target_slot_ = target_slot;
+  if (required_size != nullptr) {
+    *required_size = 0;
+  }
+
+  if (fs_mgr_overlayfs_is_setup()) {
+    // Non DAP devices can use overlayfs as well.
+    LOG(WARNING)
+        << "overlayfs overrides are active and can interfere with our "
+           "resources.\n"
+        << "run adb enable-verity to deactivate if required and try again.";
+  }
+
+  // If metadata is erased but not formatted, it is possible to not mount
+  // it in recovery. It is acceptable to skip mounting and choose fallback path
+  // (PrepareDynamicPartitionsForUpdate) when sideloading full OTAs.
+  TEST_AND_RETURN_FALSE(EnsureMetadataMounted() || IsRecovery());
+
+  if (update) {
+    TEST_AND_RETURN_FALSE(EraseSystemOtherAvbFooter(source_slot, target_slot));
+  }
+
+  if (!GetDynamicPartitionsFeatureFlag().IsEnabled()) {
+    return true;
+  }
+
+  if (target_slot == source_slot) {
+    LOG(ERROR) << "Cannot call PreparePartitionsForUpdate on current slot.";
+    return false;
+  }
+
+  if (!SetTargetBuildVars(manifest)) {
+    return false;
+  }
+
+  // Although the current build supports dynamic partitions, the given payload
+  // doesn't use it for target partitions. This could happen when applying a
+  // retrofit update. Skip updating the partition metadata for the target slot.
+  if (!is_target_dynamic_) {
+    return true;
+  }
+
+  if (!update)
+    return true;
+
+  bool delete_source = false;
+
+  if (GetVirtualAbFeatureFlag().IsEnabled()) {
+    // On Virtual A/B device, either CancelUpdate() or BeginUpdate() must be
+    // called before calling UnmapUpdateSnapshot.
+    // - If target_supports_snapshot_, PrepareSnapshotPartitionsForUpdate()
+    //   calls BeginUpdate() which resets update state
+    // - If !target_supports_snapshot_ or PrepareSnapshotPartitionsForUpdate
+    //   failed in recovery, explicitly CancelUpdate().
+    if (target_supports_snapshot_) {
+      if (PrepareSnapshotPartitionsForUpdate(
+              source_slot, target_slot, manifest, required_size)) {
+        return true;
+      }
+
+      // Virtual A/B device doing Virtual A/B update in Android mode must use
+      // snapshots.
+      if (!IsRecovery()) {
+        LOG(ERROR) << "PrepareSnapshotPartitionsForUpdate failed in Android "
+                   << "mode";
+        return false;
+      }
+
+      delete_source = true;
+      LOG(INFO) << "PrepareSnapshotPartitionsForUpdate failed in recovery. "
+                << "Attempt to overwrite existing partitions if possible";
+    } else {
+      // Downgrading to an non-Virtual A/B build or is secondary OTA.
+      LOG(INFO) << "Using regular A/B on Virtual A/B because package disabled "
+                << "snapshots.";
+    }
+
+    // In recovery, if /metadata is not mounted, it is likely that metadata
+    // partition is erased and not formatted yet. After sideloading, when
+    // rebooting into the new version, init will erase metadata partition,
+    // hence the failure of CancelUpdate() can be ignored here.
+    // However, if metadata is mounted and CancelUpdate fails, sideloading
+    // should not proceed because during next boot, snapshots will overlay on
+    // the devices incorrectly.
+    if (ExpectMetadataMounted()) {
+      TEST_AND_RETURN_FALSE(snapshot_->CancelUpdate());
+    } else {
+      LOG(INFO) << "Skip canceling previous update because metadata is not "
+                << "mounted";
+    }
+  }
+
+  // TODO(xunchang) support partial update on non VAB enabled devices.
+  TEST_AND_RETURN_FALSE(PrepareDynamicPartitionsForUpdate(
+      source_slot, target_slot, manifest, delete_source));
+
+  if (required_size != nullptr) {
+    *required_size = 0;
+  }
+  return true;
+}
+
+bool DynamicPartitionControlAndroid::SetTargetBuildVars(
+    const DeltaArchiveManifest& manifest) {
+  // Precondition: current build supports dynamic partition.
+  CHECK(GetDynamicPartitionsFeatureFlag().IsEnabled());
+
+  bool is_target_dynamic =
+      !manifest.dynamic_partition_metadata().groups().empty();
+  bool target_supports_snapshot =
+      manifest.dynamic_partition_metadata().snapshot_enabled();
+
+  if (manifest.partial_update()) {
+    // Partial updates requires DAP. On partial updates that does not involve
+    // dynamic partitions, groups() can be empty, so also assume
+    // is_target_dynamic in this case. This assumption should be safe because we
+    // also check target_supports_snapshot below, which presumably also implies
+    // target build supports dynamic partition.
+    if (!is_target_dynamic) {
+      LOG(INFO) << "Assuming target build supports dynamic partitions for "
+                   "partial updates.";
+      is_target_dynamic = true;
+    }
+
+    // Partial updates requires Virtual A/B. Double check that both current
+    // build and target build supports Virtual A/B.
+    if (!GetVirtualAbFeatureFlag().IsEnabled()) {
+      LOG(ERROR) << "Partial update cannot be applied on a device that does "
+                    "not support snapshots.";
+      return false;
+    }
+    if (!target_supports_snapshot) {
+      LOG(ERROR) << "Cannot apply partial update to a build that does not "
+                    "support snapshots.";
+      return false;
+    }
+  }
+
+  // Store the flags.
+  is_target_dynamic_ = is_target_dynamic;
+  // If !is_target_dynamic_, leave target_supports_snapshot_ unset because
+  // snapshots would not work without dynamic partition.
+  if (is_target_dynamic_) {
+    target_supports_snapshot_ = target_supports_snapshot;
+  }
+  return true;
+}
+
+namespace {
+// Try our best to erase AVB footer.
+class AvbFooterEraser {
+ public:
+  explicit AvbFooterEraser(const std::string& path) : path_(path) {}
+  bool Erase() {
+    // Try to mark the block device read-only. Ignore any
+    // failure since this won't work when passing regular files.
+    ignore_result(utils::SetBlockDeviceReadOnly(path_, false /* readonly */));
+
+    fd_.reset(new EintrSafeFileDescriptor());
+    int flags = O_WRONLY | O_TRUNC | O_CLOEXEC | O_SYNC;
+    TEST_AND_RETURN_FALSE(fd_->Open(path_.c_str(), flags));
+
+    // Need to write end-AVB_FOOTER_SIZE to end.
+    static_assert(AVB_FOOTER_SIZE > 0);
+    off64_t offset = fd_->Seek(-AVB_FOOTER_SIZE, SEEK_END);
+    TEST_AND_RETURN_FALSE_ERRNO(offset >= 0);
+    uint64_t write_size = AVB_FOOTER_SIZE;
+    LOG(INFO) << "Zeroing " << path_ << " @ [" << offset << ", "
+              << (offset + write_size) << "] (" << write_size << " bytes)";
+    brillo::Blob zeros(write_size);
+    TEST_AND_RETURN_FALSE(utils::WriteAll(fd_, zeros.data(), zeros.size()));
+    return true;
+  }
+  ~AvbFooterEraser() {
+    TEST_AND_RETURN(fd_ != nullptr && fd_->IsOpen());
+    if (!fd_->Close()) {
+      LOG(WARNING) << "Failed to close fd for " << path_;
+    }
+  }
+
+ private:
+  std::string path_;
+  FileDescriptorPtr fd_;
+};
+
+}  // namespace
+
+std::optional<bool>
+DynamicPartitionControlAndroid::IsAvbEnabledOnSystemOther() {
+  auto prefix = GetProperty(kPostinstallFstabPrefix, "");
+  if (prefix.empty()) {
+    LOG(WARNING) << "Cannot get " << kPostinstallFstabPrefix;
+    return std::nullopt;
+  }
+  auto path = base::FilePath(prefix).Append("etc/fstab.postinstall").value();
+  return IsAvbEnabledInFstab(path);
+}
+
+std::optional<bool> DynamicPartitionControlAndroid::IsAvbEnabledInFstab(
+    const std::string& path) {
+  Fstab fstab;
+  if (!ReadFstabFromFile(path, &fstab)) {
+    PLOG(WARNING) << "Cannot read fstab from " << path;
+    if (errno == ENOENT) {
+      return false;
+    }
+    return std::nullopt;
+  }
+  for (const auto& entry : fstab) {
+    if (!entry.avb_keys.empty()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool DynamicPartitionControlAndroid::GetSystemOtherPath(
+    uint32_t source_slot,
+    uint32_t target_slot,
+    const std::string& partition_name_suffix,
+    std::string* path,
+    bool* should_unmap) {
+  path->clear();
+  *should_unmap = false;
+
+  // Check that AVB is enabled on system_other before erasing.
+  auto has_avb = IsAvbEnabledOnSystemOther();
+  TEST_AND_RETURN_FALSE(has_avb.has_value());
+  if (!has_avb.value()) {
+    LOG(INFO) << "AVB is not enabled on system_other. Skip erasing.";
+    return true;
+  }
+
+  if (!IsRecovery()) {
+    // Found unexpected avb_keys for system_other on devices retrofitting
+    // dynamic partitions. Previous crash in update_engine may leave logical
+    // partitions mapped on physical system_other partition. It is difficult to
+    // handle these cases. Just fail.
+    if (GetDynamicPartitionsFeatureFlag().IsRetrofit()) {
+      LOG(ERROR) << "Cannot erase AVB footer on system_other on devices with "
+                 << "retrofit dynamic partitions. They should not have AVB "
+                 << "enabled on system_other.";
+      return false;
+    }
+  }
+
+  std::string device_dir_str;
+  TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
+  base::FilePath device_dir(device_dir_str);
+
+  // On devices without dynamic partition, search for static partitions.
+  if (!GetDynamicPartitionsFeatureFlag().IsEnabled()) {
+    *path = device_dir.Append(partition_name_suffix).value();
+    TEST_AND_RETURN_FALSE(DeviceExists(*path));
+    return true;
+  }
+
+  auto source_super_device =
+      device_dir.Append(GetSuperPartitionName(source_slot)).value();
+
+  auto builder = LoadMetadataBuilder(source_super_device, source_slot);
+  if (builder == nullptr) {
+    if (IsRecovery()) {
+      // It might be corrupted for some reason. It should still be able to
+      // sideload.
+      LOG(WARNING) << "Super partition metadata cannot be read from the source "
+                   << "slot, skip erasing.";
+      return true;
+    } else {
+      // Device has booted into Android mode, indicating that the super
+      // partition metadata should be there.
+      LOG(ERROR) << "Super partition metadata cannot be read from the source "
+                 << "slot. This is unexpected on devices with dynamic "
+                 << "partitions enabled.";
+      return false;
+    }
+  }
+  auto p = builder->FindPartition(partition_name_suffix);
+  if (p == nullptr) {
+    // If the source slot is flashed without system_other, it does not exist
+    // in super partition metadata at source slot. It is safe to skip it.
+    LOG(INFO) << "Can't find " << partition_name_suffix
+              << " in metadata source slot, skip erasing.";
+    return true;
+  }
+  // System_other created by flashing tools should be erased.
+  // If partition is created by update_engine (via NewForUpdate), it is a
+  // left-over partition from the previous update and does not contain
+  // system_other, hence there is no need to erase.
+  // Note the reverse is not necessary true. If the flag is not set, we don't
+  // know if the partition is created by update_engine or by flashing tools
+  // because older versions of super partition metadata does not contain this
+  // flag. It is okay to erase the AVB footer anyways.
+  if (p->attributes() & LP_PARTITION_ATTR_UPDATED) {
+    LOG(INFO) << partition_name_suffix
+              << " does not contain system_other, skip erasing.";
+    return true;
+  }
+
+  if (p->size() < AVB_FOOTER_SIZE) {
+    LOG(INFO) << partition_name_suffix << " has length " << p->size()
+              << "( < AVB_FOOTER_SIZE " << AVB_FOOTER_SIZE
+              << "), skip erasing.";
+    return true;
+  }
+
+  // Delete any pre-existing device with name |partition_name_suffix| and
+  // also remove it from |mapped_devices_|.
+  // In recovery, metadata might not be mounted, and
+  // UnmapPartitionOnDeviceMapper might fail. However,
+  // it is unusual that system_other has already been mapped. Hence, just skip.
+  TEST_AND_RETURN_FALSE(UnmapPartitionOnDeviceMapper(partition_name_suffix));
+  // Use CreateLogicalPartition directly to avoid mapping with existing
+  // snapshots.
+  CreateLogicalPartitionParams params = {
+      .block_device = source_super_device,
+      .metadata_slot = source_slot,
+      .partition_name = partition_name_suffix,
+      .force_writable = true,
+      .timeout_ms = kMapTimeout,
+  };
+  TEST_AND_RETURN_FALSE(CreateLogicalPartition(params, path));
+  *should_unmap = true;
+  return true;
+}
+
+bool DynamicPartitionControlAndroid::EraseSystemOtherAvbFooter(
+    uint32_t source_slot, uint32_t target_slot) {
+  LOG(INFO) << "Erasing AVB footer of system_other partition before update.";
+
+  const std::string target_suffix = SlotSuffixForSlotNumber(target_slot);
+  const std::string partition_name_suffix = "system" + target_suffix;
+
+  std::string path;
+  bool should_unmap = false;
+
+  TEST_AND_RETURN_FALSE(GetSystemOtherPath(
+      source_slot, target_slot, partition_name_suffix, &path, &should_unmap));
+
+  if (path.empty()) {
+    return true;
+  }
+
+  bool ret = AvbFooterEraser(path).Erase();
+
+  // Delete |partition_name_suffix| from device mapper and from
+  // |mapped_devices_| again so that it does not interfere with update process.
+  // In recovery, metadata might not be mounted, and
+  // UnmapPartitionOnDeviceMapper might fail. However, DestroyLogicalPartition
+  // should be called. If DestroyLogicalPartition does fail, it is still okay
+  // to skip the error here and let Prepare*() fail later.
+  if (should_unmap) {
+    TEST_AND_RETURN_FALSE(UnmapPartitionOnDeviceMapper(partition_name_suffix));
+  }
+
+  return ret;
+}
+
+bool DynamicPartitionControlAndroid::PrepareDynamicPartitionsForUpdate(
+    uint32_t source_slot,
+    uint32_t target_slot,
+    const DeltaArchiveManifest& manifest,
+    bool delete_source) {
+  const std::string target_suffix = SlotSuffixForSlotNumber(target_slot);
+
+  // Unmap all the target dynamic partitions because they would become
+  // inconsistent with the new metadata.
+  for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
+    for (const auto& partition_name : group.partition_names()) {
+      if (!UnmapPartitionOnDeviceMapper(partition_name + target_suffix)) {
+        return false;
+      }
+    }
+  }
+
+  std::string device_dir_str;
+  if (!GetDeviceDir(&device_dir_str)) {
+    return false;
+  }
+  base::FilePath device_dir(device_dir_str);
+  auto source_device =
+      device_dir.Append(GetSuperPartitionName(source_slot)).value();
+
+  auto builder = LoadMetadataBuilder(source_device, source_slot, target_slot);
+  if (builder == nullptr) {
+    LOG(ERROR) << "No metadata at "
+               << BootControlInterface::SlotName(source_slot);
+    return false;
+  }
+
+  if (delete_source) {
+    TEST_AND_RETURN_FALSE(
+        DeleteSourcePartitions(builder.get(), source_slot, manifest));
+  }
+
+  if (!UpdatePartitionMetadata(builder.get(), target_slot, manifest)) {
+    return false;
+  }
+
+  auto target_device =
+      device_dir.Append(GetSuperPartitionName(target_slot)).value();
+  return StoreMetadata(target_device, builder.get(), target_slot);
+}
+
+bool DynamicPartitionControlAndroid::PrepareSnapshotPartitionsForUpdate(
+    uint32_t source_slot,
+    uint32_t target_slot,
+    const DeltaArchiveManifest& manifest,
+    uint64_t* required_size) {
+  TEST_AND_RETURN_FALSE(ExpectMetadataMounted());
+  if (!snapshot_->BeginUpdate()) {
+    LOG(ERROR) << "Cannot begin new update.";
+    return false;
+  }
+  auto ret = snapshot_->CreateUpdateSnapshots(manifest);
+  if (!ret) {
+    LOG(ERROR) << "Cannot create update snapshots: " << ret.string();
+    if (required_size != nullptr &&
+        ret.error_code() == Return::ErrorCode::NO_SPACE) {
+      *required_size = ret.required_size();
+    }
+    return false;
+  }
+  return true;
+}
+
+std::string DynamicPartitionControlAndroid::GetSuperPartitionName(
+    uint32_t slot) {
+  return fs_mgr_get_super_partition_name(slot);
+}
+
+bool DynamicPartitionControlAndroid::UpdatePartitionMetadata(
+    MetadataBuilder* builder,
+    uint32_t target_slot,
+    const DeltaArchiveManifest& manifest) {
+  // Check preconditions.
+  CHECK(!GetVirtualAbFeatureFlag().IsEnabled() || IsRecovery())
+      << "UpdatePartitionMetadata is called on a Virtual A/B device "
+         "but source partitions is not deleted. This is not allowed.";
+
+  // If applying downgrade from Virtual A/B to non-Virtual A/B, the left-over
+  // COW group needs to be deleted to ensure there are enough space to create
+  // target partitions.
+  builder->RemoveGroupAndPartitions(android::snapshot::kCowGroupName);
+
+  const std::string target_suffix = SlotSuffixForSlotNumber(target_slot);
+  DeleteGroupsWithSuffix(builder, target_suffix);
+
+  uint64_t total_size = 0;
+  for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
+    total_size += group.size();
+  }
+
+  std::string expr;
+  uint64_t allocatable_space = builder->AllocatableSpace();
+  // On device retrofitting dynamic partitions, allocatable_space = super.
+  // On device launching dynamic partitions w/o VAB,
+  //   allocatable_space = super / 2.
+  // On device launching dynamic partitions with VAB, allocatable_space = super.
+  if (!GetDynamicPartitionsFeatureFlag().IsRetrofit() &&
+      !GetVirtualAbFeatureFlag().IsEnabled()) {
+    allocatable_space /= 2;
+    expr = "half of ";
+  }
+  if (total_size > allocatable_space) {
+    LOG(ERROR) << "The maximum size of all groups with suffix " << target_suffix
+               << " (" << total_size << ") has exceeded " << expr
+               << "allocatable space for dynamic partitions "
+               << allocatable_space << ".";
+    return false;
+  }
+
+  // name of partition(e.g. "system") -> size in bytes
+  std::map<std::string, uint64_t> partition_sizes;
+  for (const auto& partition : manifest.partitions()) {
+    partition_sizes.emplace(partition.partition_name(),
+                            partition.new_partition_info().size());
+  }
+
+  for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
+    auto group_name_suffix = group.name() + target_suffix;
+    if (!builder->AddGroup(group_name_suffix, group.size())) {
+      LOG(ERROR) << "Cannot add group " << group_name_suffix << " with size "
+                 << group.size();
+      return false;
+    }
+    LOG(INFO) << "Added group " << group_name_suffix << " with size "
+              << group.size();
+
+    for (const auto& partition_name : group.partition_names()) {
+      auto partition_sizes_it = partition_sizes.find(partition_name);
+      if (partition_sizes_it == partition_sizes.end()) {
+        // TODO(tbao): Support auto-filling partition info for framework-only
+        // OTA.
+        LOG(ERROR) << "dynamic_partition_metadata contains partition "
+                   << partition_name << " but it is not part of the manifest. "
+                   << "This is not supported.";
+        return false;
+      }
+      uint64_t partition_size = partition_sizes_it->second;
+
+      auto partition_name_suffix = partition_name + target_suffix;
+      Partition* p = builder->AddPartition(
+          partition_name_suffix, group_name_suffix, LP_PARTITION_ATTR_READONLY);
+      if (!p) {
+        LOG(ERROR) << "Cannot add partition " << partition_name_suffix
+                   << " to group " << group_name_suffix;
+        return false;
+      }
+      if (!builder->ResizePartition(p, partition_size)) {
+        LOG(ERROR) << "Cannot resize partition " << partition_name_suffix
+                   << " to size " << partition_size << ". Not enough space?";
+        return false;
+      }
+      LOG(INFO) << "Added partition " << partition_name_suffix << " to group "
+                << group_name_suffix << " with size " << partition_size;
+    }
+  }
+
+  return true;
+}
+
+bool DynamicPartitionControlAndroid::FinishUpdate(bool powerwash_required) {
+  if (ExpectMetadataMounted()) {
+    if (snapshot_->GetUpdateState() == UpdateState::Initiated) {
+      LOG(INFO) << "Snapshot writes are done.";
+      return snapshot_->FinishedSnapshotWrites(powerwash_required);
+    }
+  } else {
+    LOG(INFO) << "Skip FinishedSnapshotWrites() because /metadata is not "
+              << "mounted";
+  }
+  return true;
+}
+
+bool DynamicPartitionControlAndroid::GetPartitionDevice(
+    const std::string& partition_name,
+    uint32_t slot,
+    uint32_t current_slot,
+    bool not_in_payload,
+    std::string* device,
+    bool* is_dynamic) {
+  const auto& partition_name_suffix =
+      partition_name + SlotSuffixForSlotNumber(slot);
+  std::string device_dir_str;
+  TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
+  base::FilePath device_dir(device_dir_str);
+
+  if (is_dynamic) {
+    *is_dynamic = false;
+  }
+
+  // When looking up target partition devices, treat them as static if the
+  // current payload doesn't encode them as dynamic partitions. This may happen
+  // when applying a retrofit update on top of a dynamic-partitions-enabled
+  // build.
+  if (GetDynamicPartitionsFeatureFlag().IsEnabled() &&
+      (slot == current_slot || is_target_dynamic_)) {
+    switch (GetDynamicPartitionDevice(device_dir,
+                                      partition_name_suffix,
+                                      slot,
+                                      current_slot,
+                                      not_in_payload,
+                                      device)) {
+      case DynamicPartitionDeviceStatus::SUCCESS:
+        if (is_dynamic) {
+          *is_dynamic = true;
+        }
+        return true;
+      case DynamicPartitionDeviceStatus::TRY_STATIC:
+        break;
+      case DynamicPartitionDeviceStatus::ERROR:  // fallthrough
+      default:
+        return false;
+    }
+  }
+  base::FilePath path = device_dir.Append(partition_name_suffix);
+  if (!DeviceExists(path.value())) {
+    LOG(ERROR) << "Device file " << path.value() << " does not exist.";
+    return false;
+  }
+
+  *device = path.value();
+  return true;
+}
+
+bool DynamicPartitionControlAndroid::GetPartitionDevice(
+    const std::string& partition_name,
+    uint32_t slot,
+    uint32_t current_slot,
+    std::string* device) {
+  return GetPartitionDevice(
+      partition_name, slot, current_slot, false, device, nullptr);
+}
+
+bool DynamicPartitionControlAndroid::IsSuperBlockDevice(
+    const base::FilePath& device_dir,
+    uint32_t current_slot,
+    const std::string& partition_name_suffix) {
+  std::string source_device =
+      device_dir.Append(GetSuperPartitionName(current_slot)).value();
+  auto source_metadata = LoadMetadataBuilder(source_device, current_slot);
+  return source_metadata->HasBlockDevice(partition_name_suffix);
+}
+
+DynamicPartitionControlAndroid::DynamicPartitionDeviceStatus
+DynamicPartitionControlAndroid::GetDynamicPartitionDevice(
+    const base::FilePath& device_dir,
+    const std::string& partition_name_suffix,
+    uint32_t slot,
+    uint32_t current_slot,
+    bool not_in_payload,
+    std::string* device) {
+  std::string super_device =
+      device_dir.Append(GetSuperPartitionName(slot)).value();
+
+  auto builder = LoadMetadataBuilder(super_device, slot);
+  if (builder == nullptr) {
+    LOG(ERROR) << "No metadata in slot "
+               << BootControlInterface::SlotName(slot);
+    return DynamicPartitionDeviceStatus::ERROR;
+  }
+  if (builder->FindPartition(partition_name_suffix) == nullptr) {
+    LOG(INFO) << partition_name_suffix
+              << " is not in super partition metadata.";
+
+    if (IsSuperBlockDevice(device_dir, current_slot, partition_name_suffix)) {
+      LOG(ERROR) << "The static partition " << partition_name_suffix
+                 << " is a block device for current metadata."
+                 << "It cannot be used as a logical partition.";
+      return DynamicPartitionDeviceStatus::ERROR;
+    }
+
+    return DynamicPartitionDeviceStatus::TRY_STATIC;
+  }
+
+  if (slot == current_slot) {
+    if (GetState(partition_name_suffix) != DmDeviceState::ACTIVE) {
+      LOG(WARNING) << partition_name_suffix << " is at current slot but it is "
+                   << "not mapped. Now try to map it.";
+    } else {
+      if (GetDmDevicePathByName(partition_name_suffix, device)) {
+        LOG(INFO) << partition_name_suffix
+                  << " is mapped on device mapper: " << *device;
+        return DynamicPartitionDeviceStatus::SUCCESS;
+      }
+      LOG(ERROR) << partition_name_suffix << "is mapped but path is unknown.";
+      return DynamicPartitionDeviceStatus::ERROR;
+    }
+  }
+
+  bool force_writable = (slot != current_slot) && !not_in_payload;
+  if (MapPartitionOnDeviceMapper(
+          super_device, partition_name_suffix, slot, force_writable, device)) {
+    return DynamicPartitionDeviceStatus::SUCCESS;
+  }
+  return DynamicPartitionDeviceStatus::ERROR;
+}
+
+void DynamicPartitionControlAndroid::set_fake_mapped_devices(
+    const std::set<std::string>& fake) {
+  mapped_devices_ = fake;
+}
+
+bool DynamicPartitionControlAndroid::IsRecovery() {
+  return constants::kIsRecovery;
+}
+
+static bool IsIncrementalUpdate(const DeltaArchiveManifest& manifest) {
+  const auto& partitions = manifest.partitions();
+  return std::any_of(partitions.begin(), partitions.end(), [](const auto& p) {
+    return p.has_old_partition_info();
+  });
+}
+
+bool DynamicPartitionControlAndroid::DeleteSourcePartitions(
+    MetadataBuilder* builder,
+    uint32_t source_slot,
+    const DeltaArchiveManifest& manifest) {
+  TEST_AND_RETURN_FALSE(IsRecovery());
+
+  if (IsIncrementalUpdate(manifest)) {
+    LOG(ERROR) << "Cannot sideload incremental OTA because snapshots cannot "
+               << "be created.";
+    if (GetVirtualAbFeatureFlag().IsLaunch()) {
+      LOG(ERROR) << "Sideloading incremental updates on devices launches "
+                 << " Virtual A/B is not supported.";
+    }
+    return false;
+  }
+
+  LOG(INFO) << "Will overwrite existing partitions. Slot "
+            << BootControlInterface::SlotName(source_slot)
+            << "may be unbootable until update finishes!";
+  const std::string source_suffix = SlotSuffixForSlotNumber(source_slot);
+  DeleteGroupsWithSuffix(builder, source_suffix);
+
+  return true;
+}
+
+std::unique_ptr<AbstractAction>
+DynamicPartitionControlAndroid::GetCleanupPreviousUpdateAction(
+    BootControlInterface* boot_control,
+    PrefsInterface* prefs,
+    CleanupPreviousUpdateActionDelegateInterface* delegate) {
+  if (!GetVirtualAbFeatureFlag().IsEnabled()) {
+    return std::make_unique<NoOpAction>();
+  }
+  return std::make_unique<CleanupPreviousUpdateAction>(
+      prefs, boot_control, snapshot_.get(), delegate);
+}
+
+bool DynamicPartitionControlAndroid::ResetUpdate(PrefsInterface* prefs) {
+  if (!GetVirtualAbFeatureFlag().IsEnabled()) {
+    return true;
+  }
+
+  LOG(INFO) << __func__ << " resetting update state and deleting snapshots.";
+  TEST_AND_RETURN_FALSE(prefs != nullptr);
+
+  // If the device has already booted into the target slot,
+  // ResetUpdateProgress may pass but CancelUpdate fails.
+  // This is expected. A scheduled CleanupPreviousUpdateAction should free
+  // space when it is done.
+  TEST_AND_RETURN_FALSE(DeltaPerformer::ResetUpdateProgress(
+      prefs, false /* quick */, false /* skip dynamic partitions metadata */));
+
+  if (ExpectMetadataMounted()) {
+    TEST_AND_RETURN_FALSE(snapshot_->CancelUpdate());
+  } else {
+    LOG(INFO) << "Skip cancelling update in ResetUpdate because /metadata is "
+              << "not mounted";
+  }
+
+  return true;
+}
+
+bool DynamicPartitionControlAndroid::ListDynamicPartitionsForSlot(
+    uint32_t current_slot, std::vector<std::string>* partitions) {
+  if (!GetDynamicPartitionsFeatureFlag().IsEnabled()) {
+    LOG(ERROR) << "Dynamic partition is not enabled";
+    return false;
+  }
+
+  std::string device_dir_str;
+  TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
+  base::FilePath device_dir(device_dir_str);
+  auto super_device =
+      device_dir.Append(GetSuperPartitionName(current_slot)).value();
+  auto builder = LoadMetadataBuilder(super_device, current_slot);
+  TEST_AND_RETURN_FALSE(builder != nullptr);
+
+  std::vector<std::string> result;
+  auto suffix = SlotSuffixForSlotNumber(current_slot);
+  for (const auto& group : builder->ListGroups()) {
+    for (const auto& partition : builder->ListPartitionsInGroup(group)) {
+      std::string_view partition_name = partition->name();
+      if (!android::base::ConsumeSuffix(&partition_name, suffix)) {
+        continue;
+      }
+      result.emplace_back(partition_name);
+    }
+  }
+  *partitions = std::move(result);
+  return true;
+}
+
+bool DynamicPartitionControlAndroid::VerifyExtentsForUntouchedPartitions(
+    uint32_t source_slot,
+    uint32_t target_slot,
+    const std::vector<std::string>& partitions) {
+  std::string device_dir_str;
+  TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
+  base::FilePath device_dir(device_dir_str);
+
+  auto source_super_device =
+      device_dir.Append(GetSuperPartitionName(source_slot)).value();
+  auto source_builder = LoadMetadataBuilder(source_super_device, source_slot);
+  TEST_AND_RETURN_FALSE(source_builder != nullptr);
+
+  auto target_super_device =
+      device_dir.Append(GetSuperPartitionName(target_slot)).value();
+  auto target_builder = LoadMetadataBuilder(target_super_device, target_slot);
+  TEST_AND_RETURN_FALSE(target_builder != nullptr);
+
+  return MetadataBuilder::VerifyExtentsAgainstSourceMetadata(
+      *source_builder, source_slot, *target_builder, target_slot, partitions);
+}
+
+bool DynamicPartitionControlAndroid::ExpectMetadataMounted() {
+  // No need to mount metadata for non-Virtual A/B devices.
+  if (!GetVirtualAbFeatureFlag().IsEnabled()) {
+    return false;
+  }
+  // Intentionally not checking |metadata_device_| in Android mode.
+  // /metadata should always be mounted in Android mode. If it isn't, let caller
+  // fails when calling into SnapshotManager.
+  if (!IsRecovery()) {
+    return true;
+  }
+  // In recovery mode, explicitly check |metadata_device_|.
+  return metadata_device_ != nullptr;
+}
+
+bool DynamicPartitionControlAndroid::EnsureMetadataMounted() {
+  // No need to mount metadata for non-Virtual A/B devices.
+  if (!GetVirtualAbFeatureFlag().IsEnabled()) {
+    return true;
+  }
+
+  if (metadata_device_ == nullptr) {
+    metadata_device_ = snapshot_->EnsureMetadataMounted();
+  }
+  return metadata_device_ != nullptr;
+}
+
+std::unique_ptr<android::snapshot::ISnapshotWriter>
+DynamicPartitionControlAndroid::OpenCowWriter(
+    const std::string& partition_name,
+    const std::optional<std::string>& source_path,
+    bool is_append) {
+  auto suffix = SlotSuffixForSlotNumber(target_slot_);
+
+  auto super_device = GetSuperDevice();
+  if (!super_device.has_value()) {
+    return nullptr;
+  }
+  CreateLogicalPartitionParams params = {
+      .block_device = super_device->value(),
+      .metadata_slot = target_slot_,
+      .partition_name = partition_name + suffix,
+      .force_writable = true,
+  };
+  // TODO(zhangkelvin) Open an APPEND mode CowWriter once there's an API to do
+  // it.
+  return snapshot_->OpenSnapshotWriter(params, std::move(source_path));
+}
+
+std::optional<base::FilePath> DynamicPartitionControlAndroid::GetSuperDevice() {
+  std::string device_dir_str;
+  if (!GetDeviceDir(&device_dir_str)) {
+    LOG(ERROR) << "Failed to get device dir!";
+    return {};
+  }
+  base::FilePath device_dir(device_dir_str);
+  auto super_device = device_dir.Append(GetSuperPartitionName(target_slot_));
+  return super_device;
+}
+
+bool DynamicPartitionControlAndroid::MapAllPartitions() {
+  return snapshot_->MapAllSnapshots();
+}
+
+}  // namespace chromeos_update_engine
diff --git a/aosp/dynamic_partition_control_android.h b/aosp/dynamic_partition_control_android.h
new file mode 100644
index 0000000..a4064e9
--- /dev/null
+++ b/aosp/dynamic_partition_control_android.h
@@ -0,0 +1,309 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_CONTROL_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_CONTROL_ANDROID_H_
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <libsnapshot/auto_device.h>
+#include <libsnapshot/snapshot.h>
+#include <libsnapshot/snapshot_writer.h>
+
+#include "update_engine/common/dynamic_partition_control_interface.h"
+
+namespace chromeos_update_engine {
+
+class DynamicPartitionControlAndroid : public DynamicPartitionControlInterface {
+ public:
+  DynamicPartitionControlAndroid();
+  ~DynamicPartitionControlAndroid();
+  FeatureFlag GetDynamicPartitionsFeatureFlag() override;
+  FeatureFlag GetVirtualAbFeatureFlag() override;
+  FeatureFlag GetVirtualAbCompressionFeatureFlag() override;
+  bool OptimizeOperation(const std::string& partition_name,
+                         const InstallOperation& operation,
+                         InstallOperation* optimized) override;
+  void Cleanup() override;
+
+  bool PreparePartitionsForUpdate(uint32_t source_slot,
+                                  uint32_t target_slot,
+                                  const DeltaArchiveManifest& manifest,
+                                  bool update,
+                                  uint64_t* required_size) override;
+  bool FinishUpdate(bool powerwash_required) override;
+  std::unique_ptr<AbstractAction> GetCleanupPreviousUpdateAction(
+      BootControlInterface* boot_control,
+      PrefsInterface* prefs,
+      CleanupPreviousUpdateActionDelegateInterface* delegate) override;
+
+  bool ResetUpdate(PrefsInterface* prefs) override;
+
+  bool ListDynamicPartitionsForSlot(
+      uint32_t current_slot, std::vector<std::string>* partitions) override;
+
+  bool VerifyExtentsForUntouchedPartitions(
+      uint32_t source_slot,
+      uint32_t target_slot,
+      const std::vector<std::string>& partitions) override;
+
+  bool GetDeviceDir(std::string* path) override;
+
+  // Return the device for partition |partition_name| at slot |slot|.
+  // |current_slot| should be set to the current active slot.
+  // Note: this function is only used by BootControl*::GetPartitionDevice.
+  // Other callers should prefer BootControl*::GetPartitionDevice over
+  // BootControl*::GetDynamicPartitionControl()->GetPartitionDevice().
+  bool GetPartitionDevice(const std::string& partition_name,
+                          uint32_t slot,
+                          uint32_t current_slot,
+                          bool not_in_payload,
+                          std::string* device,
+                          bool* is_dynamic);
+
+  bool GetPartitionDevice(const std::string& partition_name,
+                          uint32_t slot,
+                          uint32_t current_slot,
+                          std::string* device);
+
+  // Partition name is expected to be unsuffixed. e.g. system, vendor
+  // Return an interface to write to a snapshoted partition.
+  std::unique_ptr<android::snapshot::ISnapshotWriter> OpenCowWriter(
+      const std::string& unsuffixed_partition_name,
+      const std::optional<std::string>& source_path,
+      bool is_append) override;
+
+  bool UnmapAllPartitions() override;
+
+ protected:
+  // These functions are exposed for testing.
+
+  // Unmap logical partition on device mapper. This is the reverse operation
+  // of MapPartitionOnDeviceMapper.
+  // Returns true if unmapped successfully.
+  virtual bool UnmapPartitionOnDeviceMapper(
+      const std::string& target_partition_name);
+
+  // Retrieves metadata from |super_device| at slot |slot|.
+  virtual std::unique_ptr<android::fs_mgr::MetadataBuilder> LoadMetadataBuilder(
+      const std::string& super_device, uint32_t slot);
+
+  // Retrieves metadata from |super_device| at slot |source_slot|. And modifies
+  // the metadata so that during updates, the metadata can be written to
+  // |target_slot|. In particular, on retrofit devices, the returned metadata
+  // automatically includes block devices at |target_slot|.
+  virtual std::unique_ptr<android::fs_mgr::MetadataBuilder> LoadMetadataBuilder(
+      const std::string& super_device,
+      uint32_t source_slot,
+      uint32_t target_slot);
+
+  // Write metadata |builder| to |super_device| at slot |target_slot|.
+  virtual bool StoreMetadata(const std::string& super_device,
+                             android::fs_mgr::MetadataBuilder* builder,
+                             uint32_t target_slot);
+
+  // Map logical partition on device-mapper.
+  // |super_device| is the device path of the physical partition ("super").
+  // |target_partition_name| is the identifier used in metadata; for example,
+  // "vendor_a"
+  // |slot| is the selected slot to mount; for example, 0 for "_a".
+  // Returns true if mapped successfully; if so, |path| is set to the device
+  // path of the mapped logical partition.
+  virtual bool MapPartitionOnDeviceMapper(
+      const std::string& super_device,
+      const std::string& target_partition_name,
+      uint32_t slot,
+      bool force_writable,
+      std::string* path);
+
+  // Return true if a static partition exists at device path |path|.
+  virtual bool DeviceExists(const std::string& path);
+
+  // Returns the current state of the underlying device mapper device
+  // with given name.
+  // One of INVALID, SUSPENDED or ACTIVE.
+  virtual android::dm::DmDeviceState GetState(const std::string& name);
+
+  // Returns the path to the device mapper device node in '/dev' corresponding
+  // to 'name'. If the device does not exist, false is returned, and the path
+  // parameter is not set.
+  virtual bool GetDmDevicePathByName(const std::string& name,
+                                     std::string* path);
+
+  // Return the name of the super partition (which stores super partition
+  // metadata) for a given slot.
+  virtual std::string GetSuperPartitionName(uint32_t slot);
+
+  virtual void set_fake_mapped_devices(const std::set<std::string>& fake);
+
+  // Allow mock objects to override this to test recovery mode.
+  virtual bool IsRecovery();
+
+  // Determine path for system_other partition.
+  // |source_slot| should be current slot.
+  // |target_slot| should be "other" slot.
+  // |partition_name_suffix| should be "system" + suffix(|target_slot|).
+  // Return true and set |path| if successful.
+  // Set |path| to empty if no need to erase system_other.
+  // Set |should_unmap| to true if path needs to be unmapped later.
+  //
+  // Note: system_other cannot use GetPartitionDevice or
+  // GetDynamicPartitionDevice because:
+  // - super partition metadata may be loaded from the source slot
+  // - UPDATED flag needs to be check to skip erasing if partition is not
+  //   created by flashing tools
+  // - Snapshots from previous update attempts should not be used.
+  virtual bool GetSystemOtherPath(uint32_t source_slot,
+                                  uint32_t target_slot,
+                                  const std::string& partition_name_suffix,
+                                  std::string* path,
+                                  bool* should_unmap);
+
+  // Returns true if any entry in the fstab file in |path| has AVB enabled,
+  // false if not enabled, and nullopt for any error.
+  virtual std::optional<bool> IsAvbEnabledInFstab(const std::string& path);
+
+  // Returns true if system_other has AVB enabled, false if not enabled, and
+  // nullopt for any error.
+  virtual std::optional<bool> IsAvbEnabledOnSystemOther();
+
+  // Erase system_other partition that may contain system_other.img.
+  // After the update, the content of system_other may be corrupted but with
+  // valid AVB footer. If the update is rolled back and factory data reset is
+  // triggered, system_b fails to be mapped with verity errors (see
+  // b/152444348). Erase the system_other so that mapping system_other is
+  // skipped.
+  virtual bool EraseSystemOtherAvbFooter(uint32_t source_slot,
+                                         uint32_t target_slot);
+
+  // Helper for PreparePartitionsForUpdate. Used for devices with dynamic
+  // partitions updating without snapshots.
+  // If |delete_source| is set, source partitions are deleted before resizing
+  // target partitions (using DeleteSourcePartitions).
+  virtual bool PrepareDynamicPartitionsForUpdate(
+      uint32_t source_slot,
+      uint32_t target_slot,
+      const DeltaArchiveManifest& manifest,
+      bool delete_source);
+
+  bool MapAllPartitions() override;
+
+ private:
+  friend class DynamicPartitionControlAndroidTest;
+  friend class SnapshotPartitionTestP;
+
+  std::optional<base::FilePath> GetSuperDevice();
+
+  bool MapPartitionInternal(const std::string& super_device,
+                            const std::string& target_partition_name,
+                            uint32_t slot,
+                            bool force_writable,
+                            std::string* path);
+
+  // Update |builder| according to |partition_metadata|.
+  // - In Android mode, this is only called when the device
+  //   does not have Virtual A/B.
+  // - When sideloading, this maybe called as a fallback path if CoW cannot
+  //   be created.
+  bool UpdatePartitionMetadata(android::fs_mgr::MetadataBuilder* builder,
+                               uint32_t target_slot,
+                               const DeltaArchiveManifest& manifest);
+
+  // Helper for PreparePartitionsForUpdate. Used for snapshotted partitions for
+  // Virtual A/B update.
+  bool PrepareSnapshotPartitionsForUpdate(uint32_t source_slot,
+                                          uint32_t target_slot,
+                                          const DeltaArchiveManifest& manifest,
+                                          uint64_t* required_size);
+
+  enum class DynamicPartitionDeviceStatus {
+    SUCCESS,
+    ERROR,
+    TRY_STATIC,
+  };
+
+  // Return SUCCESS and path in |device| if partition is dynamic.
+  // Return ERROR if any error.
+  // Return TRY_STATIC if caller should resolve the partition as a static
+  // partition instead.
+  DynamicPartitionDeviceStatus GetDynamicPartitionDevice(
+      const base::FilePath& device_dir,
+      const std::string& partition_name_suffix,
+      uint32_t slot,
+      uint32_t current_slot,
+      bool not_in_payload,
+      std::string* device);
+
+  // Return true if |partition_name_suffix| is a block device of
+  // super partition metadata slot |slot|.
+  bool IsSuperBlockDevice(const base::FilePath& device_dir,
+                          uint32_t current_slot,
+                          const std::string& partition_name_suffix);
+
+  // If sideloading a full OTA, delete source partitions from |builder|.
+  bool DeleteSourcePartitions(android::fs_mgr::MetadataBuilder* builder,
+                              uint32_t source_slot,
+                              const DeltaArchiveManifest& manifest);
+
+  // Returns true if metadata is expected to be mounted, false otherwise.
+  // Note that it returns false on non-Virtual A/B devices.
+  //
+  // Almost all functions of SnapshotManager depends on metadata being mounted.
+  // - In Android mode for Virtual A/B devices, assume it is mounted. If not,
+  //   let caller fails when calling into SnapshotManager.
+  // - In recovery for Virtual A/B devices, it is possible that metadata is not
+  //   formatted, hence it cannot be mounted. Caller should not call into
+  //   SnapshotManager.
+  // - On non-Virtual A/B devices, updates do not depend on metadata partition.
+  //   Caller should not call into SnapshotManager.
+  //
+  // This function does NOT mount metadata partition. Use EnsureMetadataMounted
+  // to mount metadata partition.
+  bool ExpectMetadataMounted();
+
+  // Ensure /metadata is mounted. Returns true if successful, false otherwise.
+  //
+  // Note that this function returns true on non-Virtual A/B devices without
+  // doing anything.
+  bool EnsureMetadataMounted();
+
+  // Set boolean flags related to target build. This includes flags like
+  // target_supports_snapshot_ and is_target_dynamic_.
+  bool SetTargetBuildVars(const DeltaArchiveManifest& manifest);
+
+  std::set<std::string> mapped_devices_;
+  const FeatureFlag dynamic_partitions_;
+  const FeatureFlag virtual_ab_;
+  const FeatureFlag virtual_ab_compression_;
+  std::unique_ptr<android::snapshot::ISnapshotManager> snapshot_;
+  std::unique_ptr<android::snapshot::AutoDevice> metadata_device_;
+  bool target_supports_snapshot_ = false;
+  // Whether the target partitions should be loaded as dynamic partitions. Set
+  // by PreparePartitionsForUpdate() per each update.
+  bool is_target_dynamic_ = false;
+  uint32_t source_slot_ = UINT32_MAX;
+  uint32_t target_slot_ = UINT32_MAX;
+
+  DISALLOW_COPY_AND_ASSIGN(DynamicPartitionControlAndroid);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_CONTROL_ANDROID_H_
diff --git a/aosp/dynamic_partition_control_android_unittest.cc b/aosp/dynamic_partition_control_android_unittest.cc
new file mode 100644
index 0000000..5d6463b
--- /dev/null
+++ b/aosp/dynamic_partition_control_android_unittest.cc
@@ -0,0 +1,1028 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/dynamic_partition_control_android.h"
+
+#include <set>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <libavb/libavb.h>
+#include <libsnapshot/mock_snapshot.h>
+
+#include "update_engine/aosp/dynamic_partition_test_utils.h"
+#include "update_engine/aosp/mock_dynamic_partition_control.h"
+#include "update_engine/common/mock_prefs.h"
+#include "update_engine/common/test_utils.h"
+
+using android::dm::DmDeviceState;
+using android::snapshot::MockSnapshotManager;
+using chromeos_update_engine::test_utils::ScopedLoopbackDeviceBinder;
+using std::string;
+using testing::_;
+using testing::AnyNumber;
+using testing::AnyOf;
+using testing::Invoke;
+using testing::NiceMock;
+using testing::Not;
+using testing::Optional;
+using testing::Return;
+
+namespace chromeos_update_engine {
+
+class DynamicPartitionControlAndroidTest : public ::testing::Test {
+ public:
+  void SetUp() override {
+    module_ = std::make_unique<NiceMock<MockDynamicPartitionControlAndroid>>();
+
+    ON_CALL(dynamicControl(), GetDynamicPartitionsFeatureFlag())
+        .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+    ON_CALL(dynamicControl(), GetVirtualAbFeatureFlag())
+        .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::NONE)));
+
+    ON_CALL(dynamicControl(), GetDeviceDir(_))
+        .WillByDefault(Invoke([](auto path) {
+          *path = kFakeDevicePath;
+          return true;
+        }));
+
+    ON_CALL(dynamicControl(), GetSuperPartitionName(_))
+        .WillByDefault(Return(kFakeSuper));
+
+    ON_CALL(dynamicControl(), GetDmDevicePathByName(_, _))
+        .WillByDefault(Invoke([](auto partition_name_suffix, auto device) {
+          *device = GetDmDevice(partition_name_suffix);
+          return true;
+        }));
+
+    ON_CALL(dynamicControl(), EraseSystemOtherAvbFooter(_, _))
+        .WillByDefault(Return(true));
+
+    ON_CALL(dynamicControl(), IsRecovery()).WillByDefault(Return(false));
+
+    ON_CALL(dynamicControl(), PrepareDynamicPartitionsForUpdate(_, _, _, _))
+        .WillByDefault(Invoke([&](uint32_t source_slot,
+                                  uint32_t target_slot,
+                                  const DeltaArchiveManifest& manifest,
+                                  bool delete_source) {
+          return dynamicControl().RealPrepareDynamicPartitionsForUpdate(
+              source_slot, target_slot, manifest, delete_source);
+        }));
+  }
+
+  // Return the mocked DynamicPartitionControlInterface.
+  NiceMock<MockDynamicPartitionControlAndroid>& dynamicControl() {
+    return static_cast<NiceMock<MockDynamicPartitionControlAndroid>&>(*module_);
+  }
+
+  std::string GetSuperDevice(uint32_t slot) {
+    return GetDevice(dynamicControl().GetSuperPartitionName(slot));
+  }
+
+  uint32_t source() { return slots_.source; }
+  uint32_t target() { return slots_.target; }
+
+  // Return partition names with suffix of source().
+  std::string S(const std::string& name) {
+    return name + kSlotSuffixes[source()];
+  }
+
+  // Return partition names with suffix of target().
+  std::string T(const std::string& name) {
+    return name + kSlotSuffixes[target()];
+  }
+
+  // Set the fake metadata to return when LoadMetadataBuilder is called on
+  // |slot|.
+  void SetMetadata(uint32_t slot,
+                   const PartitionSuffixSizes& sizes,
+                   uint32_t partition_attr = 0,
+                   uint64_t super_size = kDefaultSuperSize) {
+    EXPECT_CALL(dynamicControl(),
+                LoadMetadataBuilder(GetSuperDevice(slot), slot))
+        .Times(AnyNumber())
+        .WillRepeatedly(Invoke([=](auto, auto) {
+          return NewFakeMetadata(PartitionSuffixSizesToManifest(sizes),
+                                 partition_attr,
+                                 super_size);
+        }));
+
+    EXPECT_CALL(dynamicControl(),
+                LoadMetadataBuilder(GetSuperDevice(slot), slot, _))
+        .Times(AnyNumber())
+        .WillRepeatedly(Invoke([=](auto, auto, auto) {
+          return NewFakeMetadata(PartitionSuffixSizesToManifest(sizes),
+                                 partition_attr,
+                                 super_size);
+        }));
+  }
+
+  void ExpectStoreMetadata(const PartitionSuffixSizes& partition_sizes) {
+    EXPECT_CALL(dynamicControl(),
+                StoreMetadata(GetSuperDevice(target()),
+                              MetadataMatches(partition_sizes),
+                              target()))
+        .WillOnce(Return(true));
+  }
+
+  // Expect that UnmapPartitionOnDeviceMapper is called on target() metadata
+  // slot with each partition in |partitions|.
+  void ExpectUnmap(const std::set<std::string>& partitions) {
+    // Error when UnmapPartitionOnDeviceMapper is called on unknown arguments.
+    ON_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(_))
+        .WillByDefault(Return(false));
+
+    for (const auto& partition : partitions) {
+      EXPECT_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(partition))
+          .WillOnce(Return(true));
+    }
+  }
+  bool PreparePartitionsForUpdate(const PartitionSizes& partition_sizes) {
+    return dynamicControl().PreparePartitionsForUpdate(
+        source(),
+        target(),
+        PartitionSizesToManifest(partition_sizes),
+        true,
+        nullptr);
+  }
+  void SetSlots(const TestParam& slots) { slots_ = slots; }
+
+  void SetSnapshotEnabled(bool enabled) {
+    dynamicControl().target_supports_snapshot_ = enabled;
+  }
+
+  struct Listener : public ::testing::MatchResultListener {
+    explicit Listener(std::ostream* os) : MatchResultListener(os) {}
+  };
+
+  testing::AssertionResult UpdatePartitionMetadata(
+      const PartitionSuffixSizes& source_metadata,
+      const PartitionSizes& update_metadata,
+      const PartitionSuffixSizes& expected) {
+    return UpdatePartitionMetadata(
+        PartitionSuffixSizesToManifest(source_metadata),
+        PartitionSizesToManifest(update_metadata),
+        PartitionSuffixSizesToManifest(expected));
+  }
+  testing::AssertionResult UpdatePartitionMetadata(
+      const DeltaArchiveManifest& source_manifest,
+      const DeltaArchiveManifest& update_manifest,
+      const DeltaArchiveManifest& expected) {
+    return UpdatePartitionMetadata(
+        source_manifest, update_manifest, MetadataMatches(expected));
+  }
+  testing::AssertionResult UpdatePartitionMetadata(
+      const DeltaArchiveManifest& source_manifest,
+      const DeltaArchiveManifest& update_manifest,
+      const Matcher<MetadataBuilder*>& matcher) {
+    auto super_metadata = NewFakeMetadata(source_manifest);
+    if (!module_->UpdatePartitionMetadata(
+            super_metadata.get(), target(), update_manifest)) {
+      return testing::AssertionFailure()
+             << "UpdatePartitionMetadataInternal failed";
+    }
+    std::stringstream ss;
+    Listener listener(&ss);
+    if (matcher.MatchAndExplain(super_metadata.get(), &listener)) {
+      return testing::AssertionSuccess() << ss.str();
+    } else {
+      return testing::AssertionFailure() << ss.str();
+    }
+  }
+
+  std::unique_ptr<DynamicPartitionControlAndroid> module_;
+  TestParam slots_;
+};
+
+class DynamicPartitionControlAndroidTestP
+    : public DynamicPartitionControlAndroidTest,
+      public ::testing::WithParamInterface<TestParam> {
+ public:
+  void SetUp() override {
+    DynamicPartitionControlAndroidTest::SetUp();
+    SetSlots(GetParam());
+  }
+};
+
+// Test resize case. Grow if target metadata contains a partition with a size
+// less than expected.
+TEST_P(DynamicPartitionControlAndroidTestP,
+       NeedGrowIfSizeNotMatchWhenResizing) {
+  PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
+                                       {S("vendor"), 1_GiB},
+                                       {T("system"), 2_GiB},
+                                       {T("vendor"), 1_GiB}};
+  PartitionSuffixSizes expected{{S("system"), 2_GiB},
+                                {S("vendor"), 1_GiB},
+                                {T("system"), 3_GiB},
+                                {T("vendor"), 1_GiB}};
+  PartitionSizes update_metadata{{"system", 3_GiB}, {"vendor", 1_GiB}};
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test resize case. Shrink if target metadata contains a partition with a size
+// greater than expected.
+TEST_P(DynamicPartitionControlAndroidTestP,
+       NeedShrinkIfSizeNotMatchWhenResizing) {
+  PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
+                                       {S("vendor"), 1_GiB},
+                                       {T("system"), 2_GiB},
+                                       {T("vendor"), 1_GiB}};
+  PartitionSuffixSizes expected{{S("system"), 2_GiB},
+                                {S("vendor"), 1_GiB},
+                                {T("system"), 2_GiB},
+                                {T("vendor"), 150_MiB}};
+  PartitionSizes update_metadata{{"system", 2_GiB}, {"vendor", 150_MiB}};
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test adding partitions on the first run.
+TEST_P(DynamicPartitionControlAndroidTestP, AddPartitionToEmptyMetadata) {
+  PartitionSuffixSizes source_metadata{};
+  PartitionSuffixSizes expected{{T("system"), 2_GiB}, {T("vendor"), 1_GiB}};
+  PartitionSizes update_metadata{{"system", 2_GiB}, {"vendor", 1_GiB}};
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test subsequent add case.
+TEST_P(DynamicPartitionControlAndroidTestP, AddAdditionalPartition) {
+  PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
+                                       {T("system"), 2_GiB}};
+  PartitionSuffixSizes expected{
+      {S("system"), 2_GiB}, {T("system"), 2_GiB}, {T("vendor"), 1_GiB}};
+  PartitionSizes update_metadata{{"system", 2_GiB}, {"vendor", 1_GiB}};
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test delete one partition.
+TEST_P(DynamicPartitionControlAndroidTestP, DeletePartition) {
+  PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
+                                       {S("vendor"), 1_GiB},
+                                       {T("system"), 2_GiB},
+                                       {T("vendor"), 1_GiB}};
+  // No T("vendor")
+  PartitionSuffixSizes expected{
+      {S("system"), 2_GiB}, {S("vendor"), 1_GiB}, {T("system"), 2_GiB}};
+  PartitionSizes update_metadata{{"system", 2_GiB}};
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test delete all partitions.
+TEST_P(DynamicPartitionControlAndroidTestP, DeleteAll) {
+  PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
+                                       {S("vendor"), 1_GiB},
+                                       {T("system"), 2_GiB},
+                                       {T("vendor"), 1_GiB}};
+  PartitionSuffixSizes expected{{S("system"), 2_GiB}, {S("vendor"), 1_GiB}};
+  PartitionSizes update_metadata{};
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test corrupt source metadata case.
+TEST_P(DynamicPartitionControlAndroidTestP, CorruptedSourceMetadata) {
+  EXPECT_CALL(dynamicControl(),
+              LoadMetadataBuilder(GetSuperDevice(source()), source(), _))
+      .WillOnce(Invoke([](auto, auto, auto) { return nullptr; }));
+  ExpectUnmap({T("system")});
+
+  EXPECT_FALSE(PreparePartitionsForUpdate({{"system", 1_GiB}}))
+      << "Should not be able to continue with corrupt source metadata";
+}
+
+// Test that UpdatePartitionMetadata fails if there is not enough space on the
+// device.
+TEST_P(DynamicPartitionControlAndroidTestP, NotEnoughSpace) {
+  PartitionSuffixSizes source_metadata{{S("system"), 3_GiB},
+                                       {S("vendor"), 2_GiB},
+                                       {T("system"), 0},
+                                       {T("vendor"), 0}};
+  PartitionSizes update_metadata{{"system", 3_GiB}, {"vendor", 3_GiB}};
+
+  EXPECT_FALSE(UpdatePartitionMetadata(source_metadata, update_metadata, {}))
+      << "Should not be able to fit 11GiB data into 10GiB space";
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP, NotEnoughSpaceForSlot) {
+  PartitionSuffixSizes source_metadata{{S("system"), 1_GiB},
+                                       {S("vendor"), 1_GiB},
+                                       {T("system"), 0},
+                                       {T("vendor"), 0}};
+  PartitionSizes update_metadata{{"system", 3_GiB}, {"vendor", 3_GiB}};
+  EXPECT_FALSE(UpdatePartitionMetadata(source_metadata, update_metadata, {}))
+      << "Should not be able to grow over size of super / 2";
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP,
+       ApplyRetrofitUpdateOnDynamicPartitionsEnabledBuild) {
+  ON_CALL(dynamicControl(), GetDynamicPartitionsFeatureFlag())
+      .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::RETROFIT)));
+  // Static partition {system,bar}_{a,b} exists.
+  EXPECT_CALL(dynamicControl(),
+              DeviceExists(AnyOf(GetDevice(S("bar")),
+                                 GetDevice(T("bar")),
+                                 GetDevice(S("system")),
+                                 GetDevice(T("system")))))
+      .WillRepeatedly(Return(true));
+
+  SetMetadata(source(),
+              {{S("system"), 2_GiB},
+               {S("vendor"), 1_GiB},
+               {T("system"), 2_GiB},
+               {T("vendor"), 1_GiB}});
+
+  // Not calling through
+  // DynamicPartitionControlAndroidTest::PreparePartitionsForUpdate(), since we
+  // don't want any default group in the PartitionMetadata.
+  EXPECT_TRUE(dynamicControl().PreparePartitionsForUpdate(
+      source(), target(), {}, true, nullptr));
+
+  // Should use dynamic source partitions.
+  EXPECT_CALL(dynamicControl(), GetState(S("system")))
+      .Times(1)
+      .WillOnce(Return(DmDeviceState::ACTIVE));
+  string system_device;
+  EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+      "system", source(), source(), &system_device));
+  EXPECT_EQ(GetDmDevice(S("system")), system_device);
+
+  // Should use static target partitions without querying dynamic control.
+  EXPECT_CALL(dynamicControl(), GetState(T("system"))).Times(0);
+  EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+      "system", target(), source(), &system_device));
+  EXPECT_EQ(GetDevice(T("system")), system_device);
+
+  // Static partition "bar".
+  EXPECT_CALL(dynamicControl(), GetState(S("bar"))).Times(0);
+  std::string bar_device;
+  EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+      "bar", source(), source(), &bar_device));
+  EXPECT_EQ(GetDevice(S("bar")), bar_device);
+
+  EXPECT_CALL(dynamicControl(), GetState(T("bar"))).Times(0);
+  EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+      "bar", target(), source(), &bar_device));
+  EXPECT_EQ(GetDevice(T("bar")), bar_device);
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP,
+       GetPartitionDeviceWhenResumingUpdate) {
+  // Static partition bar_{a,b} exists.
+  EXPECT_CALL(dynamicControl(),
+              DeviceExists(AnyOf(GetDevice(S("bar")), GetDevice(T("bar")))))
+      .WillRepeatedly(Return(true));
+
+  // Both of the two slots contain valid partition metadata, since this is
+  // resuming an update.
+  SetMetadata(source(),
+              {{S("system"), 2_GiB},
+               {S("vendor"), 1_GiB},
+               {T("system"), 2_GiB},
+               {T("vendor"), 1_GiB}});
+  SetMetadata(target(),
+              {{S("system"), 2_GiB},
+               {S("vendor"), 1_GiB},
+               {T("system"), 2_GiB},
+               {T("vendor"), 1_GiB}});
+
+  EXPECT_TRUE(dynamicControl().PreparePartitionsForUpdate(
+      source(),
+      target(),
+      PartitionSizesToManifest({{"system", 2_GiB}, {"vendor", 1_GiB}}),
+      false,
+      nullptr));
+
+  // Dynamic partition "system".
+  EXPECT_CALL(dynamicControl(), GetState(S("system")))
+      .Times(1)
+      .WillOnce(Return(DmDeviceState::ACTIVE));
+  string system_device;
+  EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+      "system", source(), source(), &system_device));
+  EXPECT_EQ(GetDmDevice(S("system")), system_device);
+
+  EXPECT_CALL(dynamicControl(), GetState(T("system")))
+      .Times(AnyNumber())
+      .WillOnce(Return(DmDeviceState::ACTIVE));
+  EXPECT_CALL(dynamicControl(),
+              MapPartitionOnDeviceMapper(
+                  GetSuperDevice(target()), T("system"), target(), _, _))
+      .Times(AnyNumber())
+      .WillRepeatedly(
+          Invoke([](const auto&, const auto& name, auto, auto, auto* device) {
+            *device = "/fake/remapped/" + name;
+            return true;
+          }));
+  EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+      "system", target(), source(), &system_device));
+  EXPECT_EQ("/fake/remapped/" + T("system"), system_device);
+
+  // Static partition "bar".
+  EXPECT_CALL(dynamicControl(), GetState(S("bar"))).Times(0);
+  std::string bar_device;
+  EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+      "bar", source(), source(), &bar_device));
+  EXPECT_EQ(GetDevice(S("bar")), bar_device);
+
+  EXPECT_CALL(dynamicControl(), GetState(T("bar"))).Times(0);
+  EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+      "bar", target(), source(), &bar_device));
+  EXPECT_EQ(GetDevice(T("bar")), bar_device);
+}
+
+INSTANTIATE_TEST_CASE_P(DynamicPartitionControlAndroidTest,
+                        DynamicPartitionControlAndroidTestP,
+                        testing::Values(TestParam{0, 1}, TestParam{1, 0}));
+
+class DynamicPartitionControlAndroidGroupTestP
+    : public DynamicPartitionControlAndroidTestP {
+ public:
+  DeltaArchiveManifest source_manifest;
+  void SetUp() override {
+    DynamicPartitionControlAndroidTestP::SetUp();
+    AddGroupAndPartition(
+        &source_manifest, S("android"), 3_GiB, S("system"), 2_GiB);
+    AddGroupAndPartition(&source_manifest, S("oem"), 2_GiB, S("vendor"), 1_GiB);
+    AddGroupAndPartition(&source_manifest, T("android"), 3_GiB, T("system"), 0);
+    AddGroupAndPartition(&source_manifest, T("oem"), 2_GiB, T("vendor"), 0);
+  }
+
+  void AddGroupAndPartition(DeltaArchiveManifest* manifest,
+                            const string& group,
+                            uint64_t group_size,
+                            const string& partition,
+                            uint64_t partition_size) {
+    auto* g = AddGroup(manifest, group, group_size);
+    AddPartition(manifest, g, partition, partition_size);
+  }
+};
+
+// Allow to resize within group.
+TEST_P(DynamicPartitionControlAndroidGroupTestP, ResizeWithinGroup) {
+  DeltaArchiveManifest expected;
+  AddGroupAndPartition(&expected, T("android"), 3_GiB, T("system"), 3_GiB);
+  AddGroupAndPartition(&expected, T("oem"), 2_GiB, T("vendor"), 2_GiB);
+
+  DeltaArchiveManifest update_manifest;
+  AddGroupAndPartition(&update_manifest, "android", 3_GiB, "system", 3_GiB);
+  AddGroupAndPartition(&update_manifest, "oem", 2_GiB, "vendor", 2_GiB);
+
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_manifest, update_manifest, expected));
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, NotEnoughSpaceForGroup) {
+  DeltaArchiveManifest update_manifest;
+  AddGroupAndPartition(&update_manifest, "android", 3_GiB, "system", 1_GiB),
+      AddGroupAndPartition(&update_manifest, "oem", 2_GiB, "vendor", 3_GiB);
+  EXPECT_FALSE(UpdatePartitionMetadata(source_manifest, update_manifest, {}))
+      << "Should not be able to grow over maximum size of group";
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, GroupTooBig) {
+  DeltaArchiveManifest update_manifest;
+  AddGroup(&update_manifest, "android", 3_GiB);
+  AddGroup(&update_manifest, "oem", 3_GiB);
+  EXPECT_FALSE(UpdatePartitionMetadata(source_manifest, update_manifest, {}))
+      << "Should not be able to grow over size of super / 2";
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, AddPartitionToGroup) {
+  DeltaArchiveManifest expected;
+  auto* g = AddGroup(&expected, T("android"), 3_GiB);
+  AddPartition(&expected, g, T("system"), 2_GiB);
+  AddPartition(&expected, g, T("system_ext"), 1_GiB);
+
+  DeltaArchiveManifest update_manifest;
+  g = AddGroup(&update_manifest, "android", 3_GiB);
+  AddPartition(&update_manifest, g, "system", 2_GiB);
+  AddPartition(&update_manifest, g, "system_ext", 1_GiB);
+  AddGroupAndPartition(&update_manifest, "oem", 2_GiB, "vendor", 2_GiB);
+
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_manifest, update_manifest, expected));
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, RemovePartitionFromGroup) {
+  DeltaArchiveManifest expected;
+  AddGroup(&expected, T("android"), 3_GiB);
+
+  DeltaArchiveManifest update_manifest;
+  AddGroup(&update_manifest, "android", 3_GiB);
+  AddGroupAndPartition(&update_manifest, "oem", 2_GiB, "vendor", 2_GiB);
+
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_manifest, update_manifest, expected));
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, AddGroup) {
+  DeltaArchiveManifest expected;
+  AddGroupAndPartition(
+      &expected, T("new_group"), 2_GiB, T("new_partition"), 2_GiB);
+
+  DeltaArchiveManifest update_manifest;
+  AddGroupAndPartition(&update_manifest, "android", 2_GiB, "system", 2_GiB);
+  AddGroupAndPartition(&update_manifest, "oem", 1_GiB, "vendor", 1_GiB);
+  AddGroupAndPartition(
+      &update_manifest, "new_group", 2_GiB, "new_partition", 2_GiB);
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_manifest, update_manifest, expected));
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, RemoveGroup) {
+  DeltaArchiveManifest update_manifest;
+  AddGroupAndPartition(&update_manifest, "android", 2_GiB, "system", 2_GiB);
+
+  EXPECT_TRUE(UpdatePartitionMetadata(
+      source_manifest, update_manifest, Not(HasGroup(T("oem")))));
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, ResizeGroup) {
+  DeltaArchiveManifest expected;
+  AddGroupAndPartition(&expected, T("android"), 2_GiB, T("system"), 2_GiB);
+  AddGroupAndPartition(&expected, T("oem"), 3_GiB, T("vendor"), 3_GiB);
+  DeltaArchiveManifest update_manifest;
+  AddGroupAndPartition(&update_manifest, "android", 2_GiB, "system", 2_GiB),
+      AddGroupAndPartition(&update_manifest, "oem", 3_GiB, "vendor", 3_GiB);
+  EXPECT_TRUE(
+      UpdatePartitionMetadata(source_manifest, update_manifest, expected));
+}
+
+INSTANTIATE_TEST_CASE_P(DynamicPartitionControlAndroidTest,
+                        DynamicPartitionControlAndroidGroupTestP,
+                        testing::Values(TestParam{0, 1}, TestParam{1, 0}));
+
+const PartitionSuffixSizes update_sizes_0() {
+  // Initial state is 0 for "other" slot.
+  return {
+      {"grown_a", 2_GiB},
+      {"shrunk_a", 1_GiB},
+      {"same_a", 100_MiB},
+      {"deleted_a", 150_MiB},
+      // no added_a
+      {"grown_b", 200_MiB},
+      // simulate system_other
+      {"shrunk_b", 0},
+      {"same_b", 0},
+      {"deleted_b", 0},
+      // no added_b
+  };
+}
+
+const PartitionSuffixSizes update_sizes_1() {
+  return {
+      {"grown_a", 2_GiB},
+      {"shrunk_a", 1_GiB},
+      {"same_a", 100_MiB},
+      {"deleted_a", 150_MiB},
+      // no added_a
+      {"grown_b", 3_GiB},
+      {"shrunk_b", 150_MiB},
+      {"same_b", 100_MiB},
+      {"added_b", 150_MiB},
+      // no deleted_b
+  };
+}
+
+const PartitionSuffixSizes update_sizes_2() {
+  return {
+      {"grown_a", 4_GiB},
+      {"shrunk_a", 100_MiB},
+      {"same_a", 100_MiB},
+      {"deleted_a", 64_MiB},
+      // no added_a
+      {"grown_b", 3_GiB},
+      {"shrunk_b", 150_MiB},
+      {"same_b", 100_MiB},
+      {"added_b", 150_MiB},
+      // no deleted_b
+  };
+}
+
+// Test case for first update after the device is manufactured, in which
+// case the "other" slot is likely of size "0" (except system, which is
+// non-zero because of system_other partition)
+TEST_F(DynamicPartitionControlAndroidTest, SimulatedFirstUpdate) {
+  SetSlots({0, 1});
+
+  SetMetadata(source(), update_sizes_0());
+  SetMetadata(target(), update_sizes_0());
+  ExpectStoreMetadata(update_sizes_1());
+  ExpectUnmap({"grown_b", "shrunk_b", "same_b", "added_b"});
+
+  EXPECT_TRUE(PreparePartitionsForUpdate({{"grown", 3_GiB},
+                                          {"shrunk", 150_MiB},
+                                          {"same", 100_MiB},
+                                          {"added", 150_MiB}}));
+}
+
+// After first update, test for the second update. In the second update, the
+// "added" partition is deleted and "deleted" partition is re-added.
+TEST_F(DynamicPartitionControlAndroidTest, SimulatedSecondUpdate) {
+  SetSlots({1, 0});
+
+  SetMetadata(source(), update_sizes_1());
+  SetMetadata(target(), update_sizes_0());
+
+  ExpectStoreMetadata(update_sizes_2());
+  ExpectUnmap({"grown_a", "shrunk_a", "same_a", "deleted_a"});
+
+  EXPECT_TRUE(PreparePartitionsForUpdate({{"grown", 4_GiB},
+                                          {"shrunk", 100_MiB},
+                                          {"same", 100_MiB},
+                                          {"deleted", 64_MiB}}));
+}
+
+TEST_F(DynamicPartitionControlAndroidTest, ApplyingToCurrentSlot) {
+  SetSlots({1, 1});
+  EXPECT_FALSE(PreparePartitionsForUpdate({}))
+      << "Should not be able to apply to current slot.";
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP, OptimizeOperationTest) {
+  ASSERT_TRUE(dynamicControl().PreparePartitionsForUpdate(
+      source(),
+      target(),
+      PartitionSizesToManifest({{"foo", 4_MiB}}),
+      false,
+      nullptr));
+  dynamicControl().set_fake_mapped_devices({T("foo")});
+
+  InstallOperation iop;
+  InstallOperation optimized;
+  Extent *se, *de;
+
+  // Not a SOURCE_COPY operation, cannot skip.
+  iop.set_type(InstallOperation::REPLACE);
+  EXPECT_FALSE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+
+  iop.set_type(InstallOperation::SOURCE_COPY);
+
+  // By default GetVirtualAbFeatureFlag is disabled. Cannot skip operation.
+  EXPECT_FALSE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+
+  // Enable GetVirtualAbFeatureFlag in the mock interface.
+  ON_CALL(dynamicControl(), GetVirtualAbFeatureFlag())
+      .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+
+  // By default target_supports_snapshot_ is set to false. Cannot skip
+  // operation.
+  EXPECT_FALSE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+
+  SetSnapshotEnabled(true);
+
+  // Empty source and destination. Skip.
+  EXPECT_TRUE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+  EXPECT_TRUE(optimized.src_extents().empty());
+  EXPECT_TRUE(optimized.dst_extents().empty());
+
+  se = iop.add_src_extents();
+  se->set_start_block(0);
+  se->set_num_blocks(1);
+
+  // There is something in sources, but destinations are empty. Cannot skip.
+  EXPECT_FALSE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+
+  InstallOperation iop2;
+
+  de = iop2.add_dst_extents();
+  de->set_start_block(0);
+  de->set_num_blocks(1);
+
+  // There is something in destinations, but sources are empty. Cannot skip.
+  EXPECT_FALSE(dynamicControl().OptimizeOperation("foo", iop2, &optimized));
+
+  de = iop.add_dst_extents();
+  de->set_start_block(0);
+  de->set_num_blocks(1);
+
+  // Sources and destinations are identical. Skip.
+  EXPECT_TRUE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+  EXPECT_TRUE(optimized.src_extents().empty());
+  EXPECT_TRUE(optimized.dst_extents().empty());
+
+  se = iop.add_src_extents();
+  se->set_start_block(1);
+  se->set_num_blocks(5);
+
+  // There is something in source, but not in destination. Cannot skip.
+  EXPECT_FALSE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+
+  de = iop.add_dst_extents();
+  de->set_start_block(1);
+  de->set_num_blocks(5);
+
+  // There is source and destination are equal. Skip.
+  EXPECT_TRUE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+  EXPECT_TRUE(optimized.src_extents().empty());
+  EXPECT_TRUE(optimized.dst_extents().empty());
+
+  de = iop.add_dst_extents();
+  de->set_start_block(6);
+  de->set_num_blocks(5);
+
+  // There is something extra in dest. Cannot skip.
+  EXPECT_FALSE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+
+  se = iop.add_src_extents();
+  se->set_start_block(6);
+  se->set_num_blocks(5);
+
+  // Source and dest are identical again. Skip.
+  EXPECT_TRUE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+  EXPECT_TRUE(optimized.src_extents().empty());
+  EXPECT_TRUE(optimized.dst_extents().empty());
+
+  iop.Clear();
+  iop.set_type(InstallOperation::SOURCE_COPY);
+  se = iop.add_src_extents();
+  se->set_start_block(1);
+  se->set_num_blocks(1);
+  se = iop.add_src_extents();
+  se->set_start_block(3);
+  se->set_num_blocks(2);
+  se = iop.add_src_extents();
+  se->set_start_block(7);
+  se->set_num_blocks(2);
+  de = iop.add_dst_extents();
+  de->set_start_block(2);
+  de->set_num_blocks(5);
+
+  // [1, 3, 4, 7, 8] -> [2, 3, 4, 5, 6] should return [1, 7, 8] -> [2, 5, 6]
+  EXPECT_TRUE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+  ASSERT_EQ(2, optimized.src_extents_size());
+  ASSERT_EQ(2, optimized.dst_extents_size());
+  EXPECT_EQ(1u, optimized.src_extents(0).start_block());
+  EXPECT_EQ(1u, optimized.src_extents(0).num_blocks());
+  EXPECT_EQ(2u, optimized.dst_extents(0).start_block());
+  EXPECT_EQ(1u, optimized.dst_extents(0).num_blocks());
+  EXPECT_EQ(7u, optimized.src_extents(1).start_block());
+  EXPECT_EQ(2u, optimized.src_extents(1).num_blocks());
+  EXPECT_EQ(5u, optimized.dst_extents(1).start_block());
+  EXPECT_EQ(2u, optimized.dst_extents(1).num_blocks());
+
+  // Don't skip for static partitions.
+  EXPECT_FALSE(dynamicControl().OptimizeOperation("bar", iop, &optimized));
+}
+
+TEST_F(DynamicPartitionControlAndroidTest, ResetUpdate) {
+  MockPrefs prefs;
+  ASSERT_TRUE(dynamicControl().ResetUpdate(&prefs));
+}
+
+TEST_F(DynamicPartitionControlAndroidTest, IsAvbNotEnabledInFstab) {
+  std::string fstab_content =
+      "system /postinstall ext4 ro,nosuid,nodev,noexec "
+      "slotselect_other,logical\n"
+      "/dev/block/by-name/system /postinstall ext4 "
+      "ro,nosuid,nodev,noexec slotselect_other\n";
+  ScopedTempFile fstab;
+  ASSERT_TRUE(test_utils::WriteFileString(fstab.path(), fstab_content));
+  ASSERT_THAT(dynamicControl().RealIsAvbEnabledInFstab(fstab.path()),
+              Optional(false));
+}
+
+TEST_F(DynamicPartitionControlAndroidTest, IsAvbEnabledInFstab) {
+  std::string fstab_content =
+      "system /postinstall ext4 ro,nosuid,nodev,noexec "
+      "slotselect_other,logical,avb_keys=/foo\n";
+  ScopedTempFile fstab;
+  ASSERT_TRUE(test_utils::WriteFileString(fstab.path(), fstab_content));
+  ASSERT_THAT(dynamicControl().RealIsAvbEnabledInFstab(fstab.path()),
+              Optional(true));
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP, AvbNotEnabledOnSystemOther) {
+  ON_CALL(dynamicControl(), GetSystemOtherPath(_, _, _, _, _))
+      .WillByDefault(Invoke([&](auto source_slot,
+                                auto target_slot,
+                                const auto& name,
+                                auto path,
+                                auto should_unmap) {
+        return dynamicControl().RealGetSystemOtherPath(
+            source_slot, target_slot, name, path, should_unmap);
+      }));
+  ON_CALL(dynamicControl(), IsAvbEnabledOnSystemOther())
+      .WillByDefault(Return(false));
+  EXPECT_TRUE(
+      dynamicControl().RealEraseSystemOtherAvbFooter(source(), target()));
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP, NoSystemOtherToErase) {
+  SetMetadata(source(), {{S("system"), 100_MiB}});
+  ON_CALL(dynamicControl(), IsAvbEnabledOnSystemOther())
+      .WillByDefault(Return(true));
+  std::string path;
+  bool should_unmap;
+  ASSERT_TRUE(dynamicControl().RealGetSystemOtherPath(
+      source(), target(), T("system"), &path, &should_unmap));
+  ASSERT_TRUE(path.empty()) << path;
+  ASSERT_FALSE(should_unmap);
+  ON_CALL(dynamicControl(), GetSystemOtherPath(_, _, _, _, _))
+      .WillByDefault(Invoke([&](auto source_slot,
+                                auto target_slot,
+                                const auto& name,
+                                auto path,
+                                auto should_unmap) {
+        return dynamicControl().RealGetSystemOtherPath(
+            source_slot, target_slot, name, path, should_unmap);
+      }));
+  EXPECT_TRUE(
+      dynamicControl().RealEraseSystemOtherAvbFooter(source(), target()));
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP, SkipEraseUpdatedSystemOther) {
+  PartitionSuffixSizes sizes{{S("system"), 100_MiB}, {T("system"), 100_MiB}};
+  SetMetadata(source(), sizes, LP_PARTITION_ATTR_UPDATED);
+  ON_CALL(dynamicControl(), IsAvbEnabledOnSystemOther())
+      .WillByDefault(Return(true));
+  std::string path;
+  bool should_unmap;
+  ASSERT_TRUE(dynamicControl().RealGetSystemOtherPath(
+      source(), target(), T("system"), &path, &should_unmap));
+  ASSERT_TRUE(path.empty()) << path;
+  ASSERT_FALSE(should_unmap);
+  ON_CALL(dynamicControl(), GetSystemOtherPath(_, _, _, _, _))
+      .WillByDefault(Invoke([&](auto source_slot,
+                                auto target_slot,
+                                const auto& name,
+                                auto path,
+                                auto should_unmap) {
+        return dynamicControl().RealGetSystemOtherPath(
+            source_slot, target_slot, name, path, should_unmap);
+      }));
+  EXPECT_TRUE(
+      dynamicControl().RealEraseSystemOtherAvbFooter(source(), target()));
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP, EraseSystemOtherAvbFooter) {
+  constexpr uint64_t file_size = 1_MiB;
+  static_assert(file_size > AVB_FOOTER_SIZE);
+  ScopedTempFile system_other;
+  brillo::Blob original(file_size, 'X');
+  ASSERT_TRUE(test_utils::WriteFileVector(system_other.path(), original));
+  std::string mnt_path;
+  ScopedLoopbackDeviceBinder dev(system_other.path(), true, &mnt_path);
+  ASSERT_TRUE(dev.is_bound());
+
+  brillo::Blob device_content;
+  ASSERT_TRUE(utils::ReadFile(mnt_path, &device_content));
+  ASSERT_EQ(original, device_content);
+
+  PartitionSuffixSizes sizes{{S("system"), 100_MiB}, {T("system"), file_size}};
+  SetMetadata(source(), sizes);
+  ON_CALL(dynamicControl(), IsAvbEnabledOnSystemOther())
+      .WillByDefault(Return(true));
+  EXPECT_CALL(dynamicControl(),
+              GetSystemOtherPath(source(), target(), T("system"), _, _))
+      .WillRepeatedly(
+          Invoke([&](auto, auto, const auto&, auto path, auto should_unmap) {
+            *path = mnt_path;
+            *should_unmap = false;
+            return true;
+          }));
+  ASSERT_TRUE(
+      dynamicControl().RealEraseSystemOtherAvbFooter(source(), target()));
+
+  device_content.clear();
+  ASSERT_TRUE(utils::ReadFile(mnt_path, &device_content));
+  brillo::Blob new_expected(original);
+  // Clear the last AVB_FOOTER_SIZE bytes.
+  new_expected.resize(file_size - AVB_FOOTER_SIZE);
+  new_expected.resize(file_size, '\0');
+  ASSERT_EQ(new_expected, device_content);
+}
+
+class FakeAutoDevice : public android::snapshot::AutoDevice {
+ public:
+  FakeAutoDevice() : AutoDevice("") {}
+};
+
+class SnapshotPartitionTestP : public DynamicPartitionControlAndroidTestP {
+ public:
+  void SetUp() override {
+    DynamicPartitionControlAndroidTestP::SetUp();
+    ON_CALL(dynamicControl(), GetVirtualAbFeatureFlag())
+        .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+
+    snapshot_ = new NiceMock<MockSnapshotManager>();
+    dynamicControl().snapshot_.reset(snapshot_);  // takes ownership
+    EXPECT_CALL(*snapshot_, BeginUpdate()).WillOnce(Return(true));
+    EXPECT_CALL(*snapshot_, EnsureMetadataMounted())
+        .WillRepeatedly(
+            Invoke([]() { return std::make_unique<FakeAutoDevice>(); }));
+
+    manifest_ =
+        PartitionSizesToManifest({{"system", 3_GiB}, {"vendor", 1_GiB}});
+  }
+  void ExpectCreateUpdateSnapshots(android::snapshot::Return val) {
+    manifest_.mutable_dynamic_partition_metadata()->set_snapshot_enabled(true);
+    EXPECT_CALL(*snapshot_, CreateUpdateSnapshots(_))
+        .WillRepeatedly(Invoke([&, val](const auto& manifest) {
+          // Deep comparison requires full protobuf library. Comparing the
+          // pointers are sufficient.
+          EXPECT_EQ(&manifest_, &manifest);
+          LOG(WARNING) << "CreateUpdateSnapshots returning " << val.string();
+          return val;
+        }));
+  }
+  bool PreparePartitionsForUpdate(uint64_t* required_size) {
+    return dynamicControl().PreparePartitionsForUpdate(
+        source(), target(), manifest_, true /* update */, required_size);
+  }
+  MockSnapshotManager* snapshot_ = nullptr;
+  DeltaArchiveManifest manifest_;
+};
+
+// Test happy path of PreparePartitionsForUpdate on a Virtual A/B device.
+TEST_P(SnapshotPartitionTestP, PreparePartitions) {
+  ExpectCreateUpdateSnapshots(android::snapshot::Return::Ok());
+  uint64_t required_size = 0;
+  EXPECT_TRUE(PreparePartitionsForUpdate(&required_size));
+  EXPECT_EQ(0u, required_size);
+}
+
+// Test that if not enough space, required size returned by SnapshotManager is
+// passed up.
+TEST_P(SnapshotPartitionTestP, PreparePartitionsNoSpace) {
+  ExpectCreateUpdateSnapshots(android::snapshot::Return::NoSpace(1_GiB));
+  uint64_t required_size = 0;
+  EXPECT_FALSE(PreparePartitionsForUpdate(&required_size));
+  EXPECT_EQ(1_GiB, required_size);
+}
+
+// Test that in recovery, use empty space in super partition for a snapshot
+// update first.
+TEST_P(SnapshotPartitionTestP, RecoveryUseSuperEmpty) {
+  ExpectCreateUpdateSnapshots(android::snapshot::Return::Ok());
+  EXPECT_CALL(dynamicControl(), IsRecovery()).WillRepeatedly(Return(true));
+  // Must not call PrepareDynamicPartitionsForUpdate if
+  // PrepareSnapshotPartitionsForUpdate succeeds.
+  EXPECT_CALL(dynamicControl(), PrepareDynamicPartitionsForUpdate(_, _, _, _))
+      .Times(0);
+  uint64_t required_size = 0;
+  EXPECT_TRUE(PreparePartitionsForUpdate(&required_size));
+  EXPECT_EQ(0u, required_size);
+}
+
+// Test that in recovery, if CreateUpdateSnapshots throws an error, try
+// the flashing path for full updates.
+TEST_P(SnapshotPartitionTestP, RecoveryErrorShouldDeleteSource) {
+  // Expectation on PreparePartitionsForUpdate
+  ExpectCreateUpdateSnapshots(android::snapshot::Return::NoSpace(1_GiB));
+  EXPECT_CALL(dynamicControl(), IsRecovery()).WillRepeatedly(Return(true));
+  EXPECT_CALL(*snapshot_, CancelUpdate()).WillOnce(Return(true));
+  EXPECT_CALL(dynamicControl(), PrepareDynamicPartitionsForUpdate(_, _, _, _))
+      .WillRepeatedly(Invoke([&](auto source_slot,
+                                 auto target_slot,
+                                 const auto& manifest,
+                                 auto delete_source) {
+        EXPECT_EQ(source(), source_slot);
+        EXPECT_EQ(target(), target_slot);
+        // Deep comparison requires full protobuf library. Comparing the
+        // pointers are sufficient.
+        EXPECT_EQ(&manifest_, &manifest);
+        EXPECT_TRUE(delete_source);
+        return dynamicControl().RealPrepareDynamicPartitionsForUpdate(
+            source_slot, target_slot, manifest, delete_source);
+      }));
+  // Only one slot of space in super
+  uint64_t super_size = kDefaultGroupSize + 1_MiB;
+  // Expectation on PrepareDynamicPartitionsForUpdate
+  SetMetadata(
+      source(), {{S("system"), 2_GiB}, {S("vendor"), 1_GiB}}, 0, super_size);
+  ExpectUnmap({T("system"), T("vendor")});
+  // Expect that the source partitions aren't present in target super metadata.
+  ExpectStoreMetadata({{T("system"), 3_GiB}, {T("vendor"), 1_GiB}});
+
+  uint64_t required_size = 0;
+  EXPECT_TRUE(PreparePartitionsForUpdate(&required_size));
+  EXPECT_EQ(0u, required_size);
+}
+
+INSTANTIATE_TEST_CASE_P(DynamicPartitionControlAndroidTest,
+                        SnapshotPartitionTestP,
+                        testing::Values(TestParam{0, 1}, TestParam{1, 0}));
+
+}  // namespace chromeos_update_engine
diff --git a/aosp/dynamic_partition_test_utils.h b/aosp/dynamic_partition_test_utils.h
new file mode 100644
index 0000000..c7be1cb
--- /dev/null
+++ b/aosp/dynamic_partition_test_utils.h
@@ -0,0 +1,288 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_TEST_UTILS_H_
+#define UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_TEST_UTILS_H_
+
+#include <stdint.h>
+
+#include <iostream>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/strings/string_util.h>
+#include <fs_mgr.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <liblp/builder.h>
+#include <storage_literals/storage_literals.h>
+
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+using android::fs_mgr::MetadataBuilder;
+using testing::_;
+using testing::MakeMatcher;
+using testing::Matcher;
+using testing::MatcherInterface;
+using testing::MatchResultListener;
+using namespace android::storage_literals;  // NOLINT(build/namespaces)
+
+constexpr const uint32_t kMaxNumSlots = 2;
+constexpr const char* kSlotSuffixes[kMaxNumSlots] = {"_a", "_b"};
+constexpr const char* kFakeDevicePath = "/fake/dev/path/";
+constexpr const char* kFakeDmDevicePath = "/fake/dm/dev/path/";
+constexpr const uint32_t kFakeMetadataSize = 65536;
+constexpr const char* kDefaultGroup = "foo";
+constexpr const char* kFakeSuper = "fake_super";
+
+// A map describing the size of each partition.
+// "{name, size}"
+using PartitionSizes = std::map<std::string, uint64_t>;
+
+// "{name_a, size}"
+using PartitionSuffixSizes = std::map<std::string, uint64_t>;
+
+constexpr uint64_t kDefaultGroupSize = 5_GiB;
+// Super device size. 1 MiB for metadata.
+constexpr uint64_t kDefaultSuperSize = kDefaultGroupSize * 2 + 1_MiB;
+
+template <typename U, typename V>
+inline std::ostream& operator<<(std::ostream& os, const std::map<U, V>& param) {
+  os << "{";
+  bool first = true;
+  for (const auto& pair : param) {
+    if (!first)
+      os << ", ";
+    os << pair.first << ":" << pair.second;
+    first = false;
+  }
+  return os << "}";
+}
+
+template <typename V>
+inline void VectorToStream(std::ostream& os, const V& param) {
+  os << "[";
+  bool first = true;
+  for (const auto& e : param) {
+    if (!first)
+      os << ", ";
+    os << e;
+    first = false;
+  }
+  os << "]";
+}
+
+inline std::ostream& operator<<(std::ostream& os, const PartitionUpdate& p) {
+  return os << "{" << p.partition_name() << ", "
+            << p.new_partition_info().size() << "}";
+}
+
+inline std::ostream& operator<<(std::ostream& os,
+                                const DynamicPartitionGroup& g) {
+  os << "{" << g.name() << ", " << g.size() << ", ";
+  VectorToStream(os, g.partition_names());
+  return os << "}";
+}
+
+inline std::ostream& operator<<(std::ostream& os,
+                                const DeltaArchiveManifest& m) {
+  os << "{.groups = ";
+  VectorToStream(os, m.dynamic_partition_metadata().groups());
+  os << ", .partitions = ";
+  VectorToStream(os, m.partitions());
+  return os;
+}
+
+inline std::string GetDevice(const std::string& name) {
+  return kFakeDevicePath + name;
+}
+
+inline std::string GetDmDevice(const std::string& name) {
+  return kFakeDmDevicePath + name;
+}
+
+inline DynamicPartitionGroup* AddGroup(DeltaArchiveManifest* manifest,
+                                       const std::string& group,
+                                       uint64_t group_size) {
+  auto* g = manifest->mutable_dynamic_partition_metadata()->add_groups();
+  g->set_name(group);
+  g->set_size(group_size);
+  return g;
+}
+
+inline void AddPartition(DeltaArchiveManifest* manifest,
+                         DynamicPartitionGroup* group,
+                         const std::string& partition,
+                         uint64_t partition_size) {
+  group->add_partition_names(partition);
+  auto* p = manifest->add_partitions();
+  p->set_partition_name(partition);
+  p->mutable_new_partition_info()->set_size(partition_size);
+}
+
+// To support legacy tests, auto-convert {name_a: size} map to
+// DeltaArchiveManifest.
+inline DeltaArchiveManifest PartitionSuffixSizesToManifest(
+    const PartitionSuffixSizes& partition_sizes) {
+  DeltaArchiveManifest manifest;
+  for (const char* suffix : kSlotSuffixes) {
+    AddGroup(&manifest, std::string(kDefaultGroup) + suffix, kDefaultGroupSize);
+  }
+  for (const auto& pair : partition_sizes) {
+    for (size_t suffix_idx = 0; suffix_idx < kMaxNumSlots; ++suffix_idx) {
+      if (base::EndsWith(pair.first,
+                         kSlotSuffixes[suffix_idx],
+                         base::CompareCase::SENSITIVE)) {
+        AddPartition(
+            &manifest,
+            manifest.mutable_dynamic_partition_metadata()->mutable_groups(
+                suffix_idx),
+            pair.first,
+            pair.second);
+      }
+    }
+  }
+  return manifest;
+}
+
+// To support legacy tests, auto-convert {name: size} map to PartitionMetadata.
+inline DeltaArchiveManifest PartitionSizesToManifest(
+    const PartitionSizes& partition_sizes) {
+  DeltaArchiveManifest manifest;
+  auto* g = AddGroup(&manifest, std::string(kDefaultGroup), kDefaultGroupSize);
+  for (const auto& pair : partition_sizes) {
+    AddPartition(&manifest, g, pair.first, pair.second);
+  }
+  return manifest;
+}
+
+inline std::unique_ptr<MetadataBuilder> NewFakeMetadata(
+    const DeltaArchiveManifest& manifest,
+    uint32_t partition_attr = 0,
+    uint64_t super_size = kDefaultSuperSize) {
+  auto builder =
+      MetadataBuilder::New(super_size, kFakeMetadataSize, kMaxNumSlots);
+  for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
+    EXPECT_TRUE(builder->AddGroup(group.name(), group.size()));
+    for (const auto& partition_name : group.partition_names()) {
+      EXPECT_NE(
+          nullptr,
+          builder->AddPartition(partition_name, group.name(), partition_attr));
+    }
+  }
+  for (const auto& partition : manifest.partitions()) {
+    auto p = builder->FindPartition(partition.partition_name());
+    EXPECT_TRUE(p && builder->ResizePartition(
+                         p, partition.new_partition_info().size()));
+  }
+  return builder;
+}
+
+class MetadataMatcher : public MatcherInterface<MetadataBuilder*> {
+ public:
+  explicit MetadataMatcher(const PartitionSuffixSizes& partition_sizes)
+      : manifest_(PartitionSuffixSizesToManifest(partition_sizes)) {}
+  explicit MetadataMatcher(const DeltaArchiveManifest& manifest)
+      : manifest_(manifest) {}
+
+  bool MatchAndExplain(MetadataBuilder* metadata,
+                       MatchResultListener* listener) const override {
+    bool success = true;
+    for (const auto& group : manifest_.dynamic_partition_metadata().groups()) {
+      for (const auto& partition_name : group.partition_names()) {
+        auto p = metadata->FindPartition(partition_name);
+        if (p == nullptr) {
+          if (!success)
+            *listener << "; ";
+          *listener << "No partition " << partition_name;
+          success = false;
+          continue;
+        }
+        const auto& partition_updates = manifest_.partitions();
+        auto it = std::find_if(partition_updates.begin(),
+                               partition_updates.end(),
+                               [&](const auto& p) {
+                                 return p.partition_name() == partition_name;
+                               });
+        if (it == partition_updates.end()) {
+          *listener << "Can't find partition update " << partition_name;
+          success = false;
+          continue;
+        }
+        auto partition_size = it->new_partition_info().size();
+        if (p->size() != partition_size) {
+          if (!success)
+            *listener << "; ";
+          *listener << "Partition " << partition_name << " has size "
+                    << p->size() << ", expected " << partition_size;
+          success = false;
+        }
+        if (p->group_name() != group.name()) {
+          if (!success)
+            *listener << "; ";
+          *listener << "Partition " << partition_name << " has group "
+                    << p->group_name() << ", expected " << group.name();
+          success = false;
+        }
+      }
+    }
+    return success;
+  }
+
+  void DescribeTo(std::ostream* os) const override {
+    *os << "expect: " << manifest_;
+  }
+
+  void DescribeNegationTo(std::ostream* os) const override {
+    *os << "expect not: " << manifest_;
+  }
+
+ private:
+  DeltaArchiveManifest manifest_;
+};
+
+inline Matcher<MetadataBuilder*> MetadataMatches(
+    const PartitionSuffixSizes& partition_sizes) {
+  return MakeMatcher(new MetadataMatcher(partition_sizes));
+}
+
+inline Matcher<MetadataBuilder*> MetadataMatches(
+    const DeltaArchiveManifest& manifest) {
+  return MakeMatcher(new MetadataMatcher(manifest));
+}
+
+MATCHER_P(HasGroup, group, " has group " + group) {
+  auto groups = arg->ListGroups();
+  return std::find(groups.begin(), groups.end(), group) != groups.end();
+}
+
+struct TestParam {
+  uint32_t source;
+  uint32_t target;
+};
+inline std::ostream& operator<<(std::ostream& os, const TestParam& param) {
+  return os << "{source: " << param.source << ", target:" << param.target
+            << "}";
+}
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_TEST_UTILS_H_
diff --git a/aosp/dynamic_partition_utils.cc b/aosp/dynamic_partition_utils.cc
new file mode 100644
index 0000000..6b77a45
--- /dev/null
+++ b/aosp/dynamic_partition_utils.cc
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/dynamic_partition_utils.h"
+
+#include <vector>
+
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+
+using android::fs_mgr::MetadataBuilder;
+
+namespace chromeos_update_engine {
+
+void DeleteGroupsWithSuffix(MetadataBuilder* builder,
+                            const std::string& suffix) {
+  std::vector<std::string> groups = builder->ListGroups();
+  for (const auto& group_name : groups) {
+    if (base::EndsWith(group_name, suffix, base::CompareCase::SENSITIVE)) {
+      LOG(INFO) << "Removing group " << group_name;
+      builder->RemoveGroupAndPartitions(group_name);
+    }
+  }
+}
+
+}  // namespace chromeos_update_engine
diff --git a/aosp/dynamic_partition_utils.h b/aosp/dynamic_partition_utils.h
new file mode 100644
index 0000000..5a51d5e
--- /dev/null
+++ b/aosp/dynamic_partition_utils.h
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_UTILS_H_
+#define UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_UTILS_H_
+
+#include <string>
+
+#include <liblp/builder.h>
+
+namespace chromeos_update_engine {
+
+// Delete all groups (and their partitions) in |builder| that have names
+// ending with |suffix|.
+void DeleteGroupsWithSuffix(android::fs_mgr::MetadataBuilder* builder,
+                            const std::string& suffix);
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_UTILS_H_
diff --git a/aosp/hardware_android.cc b/aosp/hardware_android.cc
new file mode 100644
index 0000000..6f884d4
--- /dev/null
+++ b/aosp/hardware_android.cc
@@ -0,0 +1,283 @@
+//
+// 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 "update_engine/aosp/hardware_android.h"
+
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <string_view>
+
+#include <android/sysprop/GkiProperties.sysprop.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <base/files/file_util.h>
+#include <bootloader_message/bootloader_message.h>
+
+#include "update_engine/common/error_code_utils.h"
+#include "update_engine/common/hardware.h"
+#include "update_engine/common/platform_constants.h"
+#include "update_engine/common/utils.h"
+
+using android::base::GetBoolProperty;
+using android::base::GetIntProperty;
+using android::base::GetProperty;
+using std::string;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+// Android properties that identify the hardware and potentially non-updatable
+// parts of the bootloader (such as the bootloader version and the baseband
+// version).
+const char kPropProductManufacturer[] = "ro.product.manufacturer";
+const char kPropBootHardwareSKU[] = "ro.boot.hardware.sku";
+const char kPropBootRevision[] = "ro.boot.revision";
+const char kPropBuildDateUTC[] = "ro.build.date.utc";
+
+string GetPartitionBuildDate(const string& partition_name) {
+  return android::base::GetProperty("ro." + partition_name + ".build.date.utc",
+                                    "");
+}
+
+ErrorCode IsTimestampNewerLogged(const std::string& partition_name,
+                                 const std::string& old_version,
+                                 const std::string& new_version) {
+  auto error_code = utils::IsTimestampNewer(old_version, new_version);
+  if (error_code != ErrorCode::kSuccess) {
+    LOG(WARNING) << "Timestamp check failed with "
+                 << utils::ErrorCodeToString(error_code) << ": "
+                 << partition_name << " Partition timestamp: " << old_version
+                 << " Update timestamp: " << new_version;
+  }
+  return error_code;
+}
+
+}  // namespace
+
+namespace hardware {
+
+// Factory defined in hardware.h.
+std::unique_ptr<HardwareInterface> CreateHardware() {
+  return std::make_unique<HardwareAndroid>();
+}
+
+}  // namespace hardware
+
+// In Android there are normally three kinds of builds: eng, userdebug and user.
+// These builds target respectively a developer build, a debuggable version of
+// the final product and the pristine final product the end user will run.
+// Apart from the ro.build.type property name, they differ in the following
+// properties that characterize the builds:
+// * eng builds: ro.secure=0 and ro.debuggable=1
+// * userdebug builds: ro.secure=1 and ro.debuggable=1
+// * user builds: ro.secure=1 and ro.debuggable=0
+//
+// See IsOfficialBuild() and IsNormalMode() for the meaning of these options in
+// Android.
+
+bool HardwareAndroid::IsOfficialBuild() const {
+  // We run an official build iff ro.secure == 1, because we expect the build to
+  // behave like the end user product and check for updates. Note that while
+  // developers are able to build "official builds" by just running "make user",
+  // that will only result in a more restrictive environment. The important part
+  // is that we don't produce and push "non-official" builds to the end user.
+  //
+  // In case of a non-bool value, we take the most restrictive option and
+  // assume we are in an official-build.
+  return GetBoolProperty("ro.secure", true);
+}
+
+bool HardwareAndroid::IsNormalBootMode() const {
+  // We are running in "dev-mode" iff ro.debuggable == 1. In dev-mode the
+  // update_engine will allow extra developers options, such as providing a
+  // different update URL. In case of error, we assume the build is in
+  // normal-mode.
+  return !GetBoolProperty("ro.debuggable", false);
+}
+
+bool HardwareAndroid::AreDevFeaturesEnabled() const {
+  return !IsNormalBootMode();
+}
+
+bool HardwareAndroid::IsOOBEEnabled() const {
+  // No OOBE flow blocking updates for Android-based boards.
+  return false;
+}
+
+bool HardwareAndroid::IsOOBEComplete(base::Time* out_time_of_oobe) const {
+  LOG(WARNING) << "OOBE is not enabled but IsOOBEComplete() called.";
+  if (out_time_of_oobe)
+    *out_time_of_oobe = base::Time();
+  return true;
+}
+
+string HardwareAndroid::GetHardwareClass() const {
+  auto manufacturer = GetProperty(kPropProductManufacturer, "");
+  auto sku = GetProperty(kPropBootHardwareSKU, "");
+  auto revision = GetProperty(kPropBootRevision, "");
+
+  return manufacturer + ":" + sku + ":" + revision;
+}
+
+string HardwareAndroid::GetDeviceRequisition() const {
+  LOG(WARNING) << "STUB: Getting requisition is not supported.";
+  return "";
+}
+
+int HardwareAndroid::GetMinKernelKeyVersion() const {
+  LOG(WARNING) << "STUB: No Kernel key version is available.";
+  return -1;
+}
+
+int HardwareAndroid::GetMinFirmwareKeyVersion() const {
+  LOG(WARNING) << "STUB: No Firmware key version is available.";
+  return -1;
+}
+
+int HardwareAndroid::GetMaxFirmwareKeyRollforward() const {
+  LOG(WARNING) << "STUB: Getting firmware_max_rollforward is not supported.";
+  return -1;
+}
+
+bool HardwareAndroid::SetMaxFirmwareKeyRollforward(
+    int firmware_max_rollforward) {
+  LOG(WARNING) << "STUB: Setting firmware_max_rollforward is not supported.";
+  return false;
+}
+
+bool HardwareAndroid::SetMaxKernelKeyRollforward(int kernel_max_rollforward) {
+  LOG(WARNING) << "STUB: Setting kernel_max_rollforward is not supported.";
+  return false;
+}
+
+int HardwareAndroid::GetPowerwashCount() const {
+  LOG(WARNING) << "STUB: Assuming no factory reset was performed.";
+  return 0;
+}
+
+bool HardwareAndroid::SchedulePowerwash(bool save_rollback_data) {
+  LOG(INFO) << "Scheduling a powerwash to BCB.";
+  LOG_IF(WARNING, save_rollback_data) << "save_rollback_data was true but "
+                                      << "isn't supported.";
+  string err;
+  if (!update_bootloader_message({"--wipe_data", "--reason=wipe_data_from_ota"},
+                                 &err)) {
+    LOG(ERROR) << "Failed to update bootloader message: " << err;
+    return false;
+  }
+  return true;
+}
+
+bool HardwareAndroid::CancelPowerwash() {
+  string err;
+  if (!clear_bootloader_message(&err)) {
+    LOG(ERROR) << "Failed to clear bootloader message: " << err;
+    return false;
+  }
+  return true;
+}
+
+bool HardwareAndroid::GetNonVolatileDirectory(base::FilePath* path) const {
+  base::FilePath local_path(constants::kNonVolatileDirectory);
+  if (!base::DirectoryExists(local_path)) {
+    LOG(ERROR) << "Non-volatile directory not found: " << local_path.value();
+    return false;
+  }
+  *path = local_path;
+  return true;
+}
+
+bool HardwareAndroid::GetPowerwashSafeDirectory(base::FilePath* path) const {
+  // On Android, we don't have a directory persisted across powerwash.
+  return false;
+}
+
+int64_t HardwareAndroid::GetBuildTimestamp() const {
+  return GetIntProperty<int64_t>(kPropBuildDateUTC, 0);
+}
+
+// Returns true if the device runs an userdebug build, and explicitly allows OTA
+// downgrade.
+bool HardwareAndroid::AllowDowngrade() const {
+  return GetBoolProperty("ro.ota.allow_downgrade", false) &&
+         GetBoolProperty("ro.debuggable", false);
+}
+
+bool HardwareAndroid::GetFirstActiveOmahaPingSent() const {
+  LOG(WARNING) << "STUB: Assuming first active omaha was never set.";
+  return false;
+}
+
+bool HardwareAndroid::SetFirstActiveOmahaPingSent() {
+  LOG(WARNING) << "STUB: Assuming first active omaha is set.";
+  // We will set it true, so its failure doesn't cause escalation.
+  return true;
+}
+
+void HardwareAndroid::SetWarmReset(bool warm_reset) {
+  if constexpr (!constants::kIsRecovery) {
+    constexpr char warm_reset_prop[] = "ota.warm_reset";
+    if (!android::base::SetProperty(warm_reset_prop, warm_reset ? "1" : "0")) {
+      LOG(WARNING) << "Failed to set prop " << warm_reset_prop;
+    }
+  }
+}
+
+string HardwareAndroid::GetVersionForLogging(
+    const string& partition_name) const {
+  if (partition_name == "boot") {
+    // ro.bootimage.build.date.utc
+    return GetPartitionBuildDate("bootimage");
+  }
+  return GetPartitionBuildDate(partition_name);
+}
+
+ErrorCode HardwareAndroid::IsPartitionUpdateValid(
+    const string& partition_name, const string& new_version) const {
+  if (partition_name == "boot") {
+    const auto old_version = GetPartitionBuildDate("bootimage");
+    auto error_code =
+        IsTimestampNewerLogged(partition_name, old_version, new_version);
+    if (error_code == ErrorCode::kPayloadTimestampError) {
+      bool prevent_downgrade =
+          android::sysprop::GkiProperties::prevent_downgrade_version().value_or(
+              false);
+      if (!prevent_downgrade) {
+        LOG(WARNING) << "Downgrade of boot image is detected, but permitting "
+                        "update because device does not prevent boot image "
+                        "downgrade";
+        // If prevent_downgrade_version sysprop is not explicitly set, permit
+        // downgrade in boot image version.
+        // Even though error_code is overridden here, always call
+        // IsTimestampNewerLogged to produce log messages.
+        error_code = ErrorCode::kSuccess;
+      }
+    }
+    return error_code;
+  }
+
+  const auto old_version = GetPartitionBuildDate(partition_name);
+  // TODO(zhangkelvin)  for some partitions, missing a current timestamp should
+  // be an error, e.g. system, vendor, product etc.
+  auto error_code =
+      IsTimestampNewerLogged(partition_name, old_version, new_version);
+  return error_code;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/aosp/hardware_android.h b/aosp/hardware_android.h
new file mode 100644
index 0000000..5ffd7c5
--- /dev/null
+++ b/aosp/hardware_android.h
@@ -0,0 +1,73 @@
+//
+// 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 UPDATE_ENGINE_AOSP_HARDWARE_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_HARDWARE_ANDROID_H_
+
+#include <string>
+#include <string_view>
+
+#include <base/macros.h>
+#include <base/time/time.h>
+
+#include "update_engine/common/error_code.h"
+#include "update_engine/common/hardware.h"
+#include "update_engine/common/hardware_interface.h"
+
+namespace chromeos_update_engine {
+
+// Implements the real interface with the hardware in the Android platform.
+class HardwareAndroid : public HardwareInterface {
+ public:
+  HardwareAndroid() = default;
+  ~HardwareAndroid() override = default;
+
+  // HardwareInterface methods.
+  bool IsOfficialBuild() const override;
+  bool IsNormalBootMode() const override;
+  bool AreDevFeaturesEnabled() const override;
+  bool IsOOBEEnabled() const override;
+  bool IsOOBEComplete(base::Time* out_time_of_oobe) const override;
+  std::string GetHardwareClass() const override;
+  std::string GetDeviceRequisition() const override;
+  int GetMinKernelKeyVersion() const override;
+  int GetMinFirmwareKeyVersion() const override;
+  int GetMaxFirmwareKeyRollforward() const override;
+  bool SetMaxFirmwareKeyRollforward(int firmware_max_rollforward) override;
+  bool SetMaxKernelKeyRollforward(int kernel_max_rollforward) override;
+  int GetPowerwashCount() const override;
+  bool SchedulePowerwash(bool save_rollback_data) override;
+  bool CancelPowerwash() override;
+  bool GetNonVolatileDirectory(base::FilePath* path) const override;
+  bool GetPowerwashSafeDirectory(base::FilePath* path) const override;
+  int64_t GetBuildTimestamp() const override;
+  bool AllowDowngrade() const override;
+  bool GetFirstActiveOmahaPingSent() const override;
+  bool SetFirstActiveOmahaPingSent() override;
+  void SetWarmReset(bool warm_reset) override;
+  [[nodiscard]] std::string GetVersionForLogging(
+      const std::string& partition_name) const override;
+  [[nodiscard]] ErrorCode IsPartitionUpdateValid(
+      const std::string& partition_name,
+      const std::string& new_version) const override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HardwareAndroid);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_AOSP_HARDWARE_ANDROID_H_
diff --git a/aosp/logging_android.cc b/aosp/logging_android.cc
new file mode 100644
index 0000000..0219075
--- /dev/null
+++ b/aosp/logging_android.cc
@@ -0,0 +1,276 @@
+//
+// Copyright (C) 2020 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 <inttypes.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <functional>
+#include <iomanip>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <base/files/dir_reader_posix.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <log/log.h>
+
+#include "update_engine/common/utils.h"
+
+using std::string;
+
+#ifdef _UE_SIDELOAD
+constexpr bool kSideload = true;
+#else
+constexpr bool kSideload = false;
+#endif
+
+namespace chromeos_update_engine {
+namespace {
+
+constexpr char kSystemLogsRoot[] = "/data/misc/update_engine_log";
+constexpr size_t kLogCount = 5;
+
+// Keep the most recent |kLogCount| logs but remove the old ones in
+// "/data/misc/update_engine_log/".
+void DeleteOldLogs(const string& kLogsRoot) {
+  base::DirReaderPosix reader(kLogsRoot.c_str());
+  if (!reader.IsValid()) {
+    LOG(ERROR) << "Failed to read " << kLogsRoot;
+    return;
+  }
+
+  std::vector<string> old_logs;
+  while (reader.Next()) {
+    if (reader.name()[0] == '.')
+      continue;
+
+    // Log files are in format "update_engine.%Y%m%d-%H%M%S",
+    // e.g. update_engine.20090103-231425
+    uint64_t date;
+    uint64_t local_time;
+    if (sscanf(reader.name(),
+               "update_engine.%" PRIu64 "-%" PRIu64 "",
+               &date,
+               &local_time) == 2) {
+      old_logs.push_back(reader.name());
+    } else {
+      LOG(WARNING) << "Unrecognized log file " << reader.name();
+    }
+  }
+
+  std::sort(old_logs.begin(), old_logs.end(), std::greater<string>());
+  for (size_t i = kLogCount; i < old_logs.size(); i++) {
+    string log_path = kLogsRoot + "/" + old_logs[i];
+    if (unlink(log_path.c_str()) == -1) {
+      PLOG(WARNING) << "Failed to unlink " << log_path;
+    }
+  }
+}
+
+string SetupLogFile(const string& kLogsRoot) {
+  DeleteOldLogs(kLogsRoot);
+
+  return base::StringPrintf("%s/update_engine.%s",
+                            kLogsRoot.c_str(),
+                            utils::GetTimeAsString(::time(nullptr)).c_str());
+}
+
+const char* LogPriorityToCString(int priority) {
+  switch (priority) {
+    case ANDROID_LOG_VERBOSE:
+      return "VERBOSE";
+    case ANDROID_LOG_DEBUG:
+      return "DEBUG";
+    case ANDROID_LOG_INFO:
+      return "INFO";
+    case ANDROID_LOG_WARN:
+      return "WARN";
+    case ANDROID_LOG_ERROR:
+      return "ERROR";
+    case ANDROID_LOG_FATAL:
+      return "FATAL";
+    default:
+      return "UNKNOWN";
+  }
+}
+
+using LoggerFunction = std::function<void(const struct __android_log_message*)>;
+
+class FileLogger {
+ public:
+  explicit FileLogger(const string& path) {
+    fd_.reset(TEMP_FAILURE_RETRY(
+        open(path.c_str(),
+             O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_SYNC,
+             0644)));
+    if (fd_ == -1) {
+      // Use ALOGE that logs to logd before __android_log_set_logger.
+      ALOGE("Cannot open persistent log %s: %s", path.c_str(), strerror(errno));
+      return;
+    }
+    // The log file will have AID_LOG as group ID; this GID is inherited from
+    // the parent directory "/data/misc/update_engine_log" which sets the SGID
+    // bit.
+    if (fchmod(fd_.get(), 0640) == -1) {
+      // Use ALOGE that logs to logd before __android_log_set_logger.
+      ALOGE("Cannot chmod 0640 persistent log %s: %s",
+            path.c_str(),
+            strerror(errno));
+      return;
+    }
+  }
+  // Copy-constructor needed to be converted to std::function.
+  FileLogger(const FileLogger& other) { fd_.reset(dup(other.fd_)); }
+  void operator()(const struct __android_log_message* log_message) {
+    if (fd_ == -1) {
+      return;
+    }
+
+    std::string_view message_str =
+        log_message->message != nullptr ? log_message->message : "";
+
+    WriteToFd(GetPrefix(log_message));
+    WriteToFd(message_str);
+    WriteToFd("\n");
+  }
+
+ private:
+  android::base::unique_fd fd_;
+  void WriteToFd(std::string_view message) {
+    ignore_result(
+        android::base::WriteFully(fd_, message.data(), message.size()));
+  }
+
+  string GetPrefix(const struct __android_log_message* log_message) {
+    std::stringstream ss;
+    timeval tv;
+    gettimeofday(&tv, nullptr);
+    time_t t = tv.tv_sec;
+    struct tm local_time;
+    localtime_r(&t, &local_time);
+    struct tm* tm_time = &local_time;
+    ss << "[" << std::setfill('0') << std::setw(2) << 1 + tm_time->tm_mon
+       << std::setw(2) << tm_time->tm_mday << '/' << std::setw(2)
+       << tm_time->tm_hour << std::setw(2) << tm_time->tm_min << std::setw(2)
+       << tm_time->tm_sec << '.' << std::setw(6) << tv.tv_usec << "] ";
+    // libchrome logs prepends |message| with severity, file and line, but
+    // leave logger_data->file as nullptr.
+    // libbase / liblog logs doesn't. Hence, add them to match the style.
+    // For liblog logs that doesn't set logger_data->file, not printing the
+    // priority is acceptable.
+    if (log_message->file) {
+      ss << "[" << LogPriorityToCString(log_message->priority) << ':'
+         << log_message->file << '(' << log_message->line << ")] ";
+    }
+    return ss.str();
+  }
+};
+
+class CombinedLogger {
+ public:
+  CombinedLogger(bool log_to_system, bool log_to_file) {
+    if (log_to_system) {
+      if (kSideload) {
+        // No logd in sideload. Use stdout.
+        // recovery has already redirected stdio properly.
+        loggers_.push_back(__android_log_stderr_logger);
+      } else {
+        loggers_.push_back(__android_log_logd_logger);
+      }
+    }
+    if (log_to_file) {
+      loggers_.push_back(std::move(FileLogger(SetupLogFile(kSystemLogsRoot))));
+    }
+  }
+  void operator()(const struct __android_log_message* log_message) {
+    for (auto&& logger : loggers_) {
+      logger(log_message);
+    }
+  }
+
+ private:
+  std::vector<LoggerFunction> loggers_;
+};
+
+// Redirect all libchrome logs to liblog using our custom handler that does
+// not call __android_log_write and explicitly write to stderr at the same
+// time. The preset CombinedLogger already writes to stderr properly.
+bool RedirectToLiblog(int severity,
+                      const char* file,
+                      int line,
+                      size_t message_start,
+                      const std::string& str_newline) {
+  android_LogPriority priority =
+      (severity < 0) ? ANDROID_LOG_VERBOSE : ANDROID_LOG_UNKNOWN;
+  switch (severity) {
+    case logging::LOG_INFO:
+      priority = ANDROID_LOG_INFO;
+      break;
+    case logging::LOG_WARNING:
+      priority = ANDROID_LOG_WARN;
+      break;
+    case logging::LOG_ERROR:
+      priority = ANDROID_LOG_ERROR;
+      break;
+    case logging::LOG_FATAL:
+      priority = ANDROID_LOG_FATAL;
+      break;
+  }
+  std::string_view sv = str_newline;
+  ignore_result(android::base::ConsumeSuffix(&sv, "\n"));
+  std::string str(sv.data(), sv.size());
+  // This will eventually be redirected to CombinedLogger.
+  // Use nullptr as tag so that liblog infers log tag from getprogname().
+  __android_log_write(priority, nullptr /* tag */, str.c_str());
+  return true;
+}
+
+}  // namespace
+
+void SetupLogging(bool log_to_system, bool log_to_file) {
+  // Note that libchrome logging uses liblog.
+  // By calling liblog's __android_log_set_logger function, all of libchrome
+  // (used by update_engine) / libbase / liblog (used by depended modules)
+  // logging eventually redirects to CombinedLogger.
+  static auto g_logger =
+      std::make_unique<CombinedLogger>(log_to_system, log_to_file);
+  __android_log_set_logger([](const struct __android_log_message* log_message) {
+    (*g_logger)(log_message);
+  });
+
+  // libchrome logging should not log to file.
+  logging::LoggingSettings log_settings;
+  log_settings.lock_log = logging::DONT_LOCK_LOG_FILE;
+  log_settings.logging_dest =
+      static_cast<logging::LoggingDestination>(logging::LOG_NONE);
+  log_settings.log_file = nullptr;
+  logging::InitLogging(log_settings);
+  logging::SetLogItems(false /* enable_process_id */,
+                       false /* enable_thread_id */,
+                       false /* enable_timestamp */,
+                       false /* enable_tickcount */);
+  logging::SetLogMessageHandler(&RedirectToLiblog);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/aosp/metrics_reporter_android.cc b/aosp/metrics_reporter_android.cc
new file mode 100644
index 0000000..ea3bb6d
--- /dev/null
+++ b/aosp/metrics_reporter_android.cc
@@ -0,0 +1,170 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/metrics_reporter_android.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include <android-base/properties.h>
+#include <base/strings/string_util.h>
+#include <fs_mgr.h>
+#include <libdm/dm.h>
+#include <liblp/builder.h>
+#include <liblp/liblp.h>
+#include <statslog.h>
+
+#include "update_engine/common/constants.h"
+
+using android::fs_mgr::GetPartitionGroupName;
+using android::fs_mgr::LpMetadata;
+using android::fs_mgr::MetadataBuilder;
+using android::fs_mgr::ReadMetadata;
+using android::fs_mgr::SlotNumberForSlotSuffix;
+using base::EndsWith;
+
+namespace {
+// A number offset adds on top of the enum value. e.g. ErrorCode::SUCCESS will
+// be reported as 10000, and AttemptResult::UPDATE_CANCELED will be reported as
+// 10011. This keeps the ordering of update engine's enum definition when statsd
+// atoms reserve the value 0 for unknown state.
+constexpr auto kMetricsReporterEnumOffset = 10000;
+
+int32_t GetStatsdEnumValue(int32_t value) {
+  return kMetricsReporterEnumOffset + value;
+}
+}  // namespace
+
+namespace chromeos_update_engine {
+
+namespace metrics {
+
+std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter() {
+  return std::make_unique<MetricsReporterAndroid>();
+}
+
+}  // namespace metrics
+
+void MetricsReporterAndroid::ReportUpdateAttemptMetrics(
+    SystemState* /* system_state */,
+    int attempt_number,
+    PayloadType payload_type,
+    base::TimeDelta duration,
+    base::TimeDelta duration_uptime,
+    int64_t payload_size,
+    metrics::AttemptResult attempt_result,
+    ErrorCode error_code) {
+  int64_t payload_size_mib = payload_size / kNumBytesInOneMiB;
+
+  int64_t super_partition_size_bytes = 0;
+  int64_t super_free_space = 0;
+  int64_t slot_size_bytes = 0;
+
+  if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
+    uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
+    auto super_device = fs_mgr_get_super_partition_name();
+    std::unique_ptr<LpMetadata> metadata = ReadMetadata(super_device, slot);
+    if (metadata) {
+      super_partition_size_bytes = GetTotalSuperPartitionSize(*metadata);
+
+      for (const auto& group : metadata->groups) {
+        if (EndsWith(GetPartitionGroupName(group),
+                     fs_mgr_get_slot_suffix(),
+                     base::CompareCase::SENSITIVE)) {
+          slot_size_bytes += group.maximum_size;
+        }
+      }
+
+      auto metadata_builder = MetadataBuilder::New(*metadata);
+      if (metadata_builder) {
+        auto free_regions = metadata_builder->GetFreeRegions();
+        for (const auto& interval : free_regions) {
+          super_free_space += interval.length();
+        }
+        super_free_space *= android::dm::kSectorSize;
+      } else {
+        LOG(ERROR) << "Cannot create metadata builder.";
+      }
+    } else {
+      LOG(ERROR) << "Could not read dynamic partition metadata for device: "
+                 << super_device;
+    }
+  }
+
+  android::util::stats_write(
+      android::util::UPDATE_ENGINE_UPDATE_ATTEMPT_REPORTED,
+      attempt_number,
+      GetStatsdEnumValue(static_cast<int32_t>(payload_type)),
+      duration.InMinutes(),
+      duration_uptime.InMinutes(),
+      payload_size_mib,
+      GetStatsdEnumValue(static_cast<int32_t>(attempt_result)),
+      GetStatsdEnumValue(static_cast<int32_t>(error_code)),
+      android::base::GetProperty("ro.build.fingerprint", "").c_str(),
+      super_partition_size_bytes,
+      slot_size_bytes,
+      super_free_space);
+}
+
+void MetricsReporterAndroid::ReportUpdateAttemptDownloadMetrics(
+    int64_t payload_bytes_downloaded,
+    int64_t /* payload_download_speed_bps */,
+    DownloadSource /* download_source */,
+    metrics::DownloadErrorCode /* payload_download_error_code */,
+    metrics::ConnectionType /* connection_type */) {
+  // TODO(xunchang) add statsd reporting
+  LOG(INFO) << "Current update attempt downloads "
+            << payload_bytes_downloaded / kNumBytesInOneMiB << " bytes data";
+}
+
+void MetricsReporterAndroid::ReportSuccessfulUpdateMetrics(
+    int attempt_count,
+    int /* updates_abandoned_count */,
+    PayloadType payload_type,
+    int64_t payload_size,
+    int64_t num_bytes_downloaded[kNumDownloadSources],
+    int download_overhead_percentage,
+    base::TimeDelta total_duration,
+    base::TimeDelta /* total_duration_uptime */,
+    int reboot_count,
+    int /* url_switch_count */) {
+  int64_t payload_size_mib = payload_size / kNumBytesInOneMiB;
+  int64_t total_bytes_downloaded = 0;
+  for (size_t i = 0; i < kNumDownloadSources; i++) {
+    total_bytes_downloaded += num_bytes_downloaded[i] / kNumBytesInOneMiB;
+  }
+
+  android::util::stats_write(
+      android::util::UPDATE_ENGINE_SUCCESSFUL_UPDATE_REPORTED,
+      static_cast<int32_t>(attempt_count),
+      GetStatsdEnumValue(static_cast<int32_t>(payload_type)),
+      static_cast<int32_t>(payload_size_mib),
+      static_cast<int32_t>(total_bytes_downloaded),
+      static_cast<int32_t>(download_overhead_percentage),
+      static_cast<int32_t>(total_duration.InMinutes()),
+      static_cast<int32_t>(reboot_count));
+}
+
+void MetricsReporterAndroid::ReportAbnormallyTerminatedUpdateAttemptMetrics() {
+  int attempt_result =
+      static_cast<int>(metrics::AttemptResult::kAbnormalTermination);
+  // TODO(xunchang) add statsd reporting
+  LOG(INFO) << "Abnormally terminated update attempt result " << attempt_result;
+}
+
+};  // namespace chromeos_update_engine
diff --git a/aosp/metrics_reporter_android.h b/aosp/metrics_reporter_android.h
new file mode 100644
index 0000000..4a173bf
--- /dev/null
+++ b/aosp/metrics_reporter_android.h
@@ -0,0 +1,101 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_METRICS_REPORTER_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_METRICS_REPORTER_ANDROID_H_
+
+#include <string>
+
+#include "update_engine/common/error_code.h"
+#include "update_engine/common/metrics_constants.h"
+#include "update_engine/common/metrics_reporter_interface.h"
+
+namespace chromeos_update_engine {
+
+class MetricsReporterAndroid : public MetricsReporterInterface {
+ public:
+  MetricsReporterAndroid() = default;
+
+  ~MetricsReporterAndroid() override = default;
+
+  void ReportRollbackMetrics(metrics::RollbackResult result) override {}
+
+  void ReportEnterpriseRollbackMetrics(
+      bool success, const std::string& rollback_version) override {}
+
+  void ReportDailyMetrics(base::TimeDelta os_age) override {}
+
+  void ReportUpdateCheckMetrics(
+      SystemState* system_state,
+      metrics::CheckResult result,
+      metrics::CheckReaction reaction,
+      metrics::DownloadErrorCode download_error_code) override {}
+
+  void ReportUpdateAttemptMetrics(SystemState* system_state,
+                                  int attempt_number,
+                                  PayloadType payload_type,
+                                  base::TimeDelta duration,
+                                  base::TimeDelta duration_uptime,
+                                  int64_t payload_size,
+                                  metrics::AttemptResult attempt_result,
+                                  ErrorCode internal_error_code) override;
+
+  void ReportUpdateAttemptDownloadMetrics(
+      int64_t payload_bytes_downloaded,
+      int64_t payload_download_speed_bps,
+      DownloadSource download_source,
+      metrics::DownloadErrorCode payload_download_error_code,
+      metrics::ConnectionType connection_type) override;
+
+  void ReportAbnormallyTerminatedUpdateAttemptMetrics() override;
+
+  void ReportSuccessfulUpdateMetrics(
+      int attempt_count,
+      int updates_abandoned_count,
+      PayloadType payload_type,
+      int64_t payload_size,
+      int64_t num_bytes_downloaded[kNumDownloadSources],
+      int download_overhead_percentage,
+      base::TimeDelta total_duration,
+      base::TimeDelta total_duration_uptime,
+      int reboot_count,
+      int url_switch_count) override;
+
+  void ReportCertificateCheckMetrics(ServerToCheck server_to_check,
+                                     CertificateCheckResult result) override {}
+
+  void ReportFailedUpdateCount(int target_attempt) override {}
+
+  void ReportTimeToReboot(int time_to_reboot_minutes) override {}
+
+  void ReportInstallDateProvisioningSource(int source, int max) override {}
+
+  void ReportInternalErrorCode(ErrorCode error_code) override {}
+
+  void ReportKeyVersionMetrics(int kernel_min_version,
+                               int kernel_max_rollforward_version,
+                               bool kernel_max_rollforward_success) override {}
+
+  void ReportEnterpriseUpdateSeenToDownloadDays(
+      bool has_time_restriction_policy, int time_to_update_days) override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MetricsReporterAndroid);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_AOSP_METRICS_REPORTER_ANDROID_H_
diff --git a/aosp/mock_dynamic_partition_control.h b/aosp/mock_dynamic_partition_control.h
new file mode 100644
index 0000000..df7208e
--- /dev/null
+++ b/aosp/mock_dynamic_partition_control.h
@@ -0,0 +1,129 @@
+//
+// Copyright (C) 2018 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 <stdint.h>
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include <libsnapshot/cow_writer.h>
+#include <libsnapshot/snapshot_writer.h>
+
+#include "update_engine/aosp/dynamic_partition_control_android.h"
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/dynamic_partition_control_interface.h"
+
+namespace chromeos_update_engine {
+
+class MockDynamicPartitionControlAndroid
+    : public DynamicPartitionControlAndroid {
+ public:
+  MOCK_METHOD(
+      bool,
+      MapPartitionOnDeviceMapper,
+      (const std::string&, const std::string&, uint32_t, bool, std::string*),
+      (override));
+  MOCK_METHOD(bool,
+              UnmapPartitionOnDeviceMapper,
+              (const std::string&),
+              (override));
+  MOCK_METHOD(void, Cleanup, (), (override));
+  MOCK_METHOD(bool, DeviceExists, (const std::string&), (override));
+  MOCK_METHOD(::android::dm::DmDeviceState,
+              GetState,
+              (const std::string&),
+              (override));
+  MOCK_METHOD(bool,
+              GetDmDevicePathByName,
+              (const std::string&, std::string*),
+              (override));
+  MOCK_METHOD(std::unique_ptr<::android::fs_mgr::MetadataBuilder>,
+              LoadMetadataBuilder,
+              (const std::string&, uint32_t),
+              (override));
+  MOCK_METHOD(std::unique_ptr<::android::fs_mgr::MetadataBuilder>,
+              LoadMetadataBuilder,
+              (const std::string&, uint32_t, uint32_t),
+              (override));
+  MOCK_METHOD(bool,
+              StoreMetadata,
+              (const std::string&, android::fs_mgr::MetadataBuilder*, uint32_t),
+              (override));
+  MOCK_METHOD(bool, GetDeviceDir, (std::string*), (override));
+  MOCK_METHOD(FeatureFlag, GetDynamicPartitionsFeatureFlag, (), (override));
+  MOCK_METHOD(std::string, GetSuperPartitionName, (uint32_t), (override));
+  MOCK_METHOD(FeatureFlag, GetVirtualAbFeatureFlag, (), (override));
+  MOCK_METHOD(bool, FinishUpdate, (bool), (override));
+  MOCK_METHOD(bool,
+              GetSystemOtherPath,
+              (uint32_t, uint32_t, const std::string&, std::string*, bool*),
+              (override));
+  MOCK_METHOD(bool,
+              EraseSystemOtherAvbFooter,
+              (uint32_t, uint32_t),
+              (override));
+  MOCK_METHOD(std::optional<bool>, IsAvbEnabledOnSystemOther, (), (override));
+  MOCK_METHOD(bool, IsRecovery, (), (override));
+  MOCK_METHOD(bool,
+              PrepareDynamicPartitionsForUpdate,
+              (uint32_t, uint32_t, const DeltaArchiveManifest&, bool),
+              (override));
+  MOCK_METHOD(std::unique_ptr<android::snapshot::ISnapshotWriter>,
+              OpenCowWriter,
+              (const std::string& unsuffixed_partition_name,
+               const std::optional<std::string>& source_path,
+               bool is_append),
+              (override));
+  MOCK_METHOD(bool, MapAllPartitions, (), (override));
+  MOCK_METHOD(bool, UnmapAllPartitions, (), (override));
+
+  void set_fake_mapped_devices(const std::set<std::string>& fake) override {
+    DynamicPartitionControlAndroid::set_fake_mapped_devices(fake);
+  }
+
+  bool RealGetSystemOtherPath(uint32_t source_slot,
+                              uint32_t target_slot,
+                              const std::string& partition_name_suffix,
+                              std::string* path,
+                              bool* should_unmap) {
+    return DynamicPartitionControlAndroid::GetSystemOtherPath(
+        source_slot, target_slot, partition_name_suffix, path, should_unmap);
+  }
+
+  bool RealEraseSystemOtherAvbFooter(uint32_t source_slot,
+                                     uint32_t target_slot) {
+    return DynamicPartitionControlAndroid::EraseSystemOtherAvbFooter(
+        source_slot, target_slot);
+  }
+
+  std::optional<bool> RealIsAvbEnabledInFstab(const std::string& path) {
+    return DynamicPartitionControlAndroid::IsAvbEnabledInFstab(path);
+  }
+
+  bool RealPrepareDynamicPartitionsForUpdate(
+      uint32_t source_slot,
+      uint32_t target_slot,
+      const DeltaArchiveManifest& manifest,
+      bool delete_source) {
+    return DynamicPartitionControlAndroid::PrepareDynamicPartitionsForUpdate(
+        source_slot, target_slot, manifest, delete_source);
+  }
+};
+
+}  // namespace chromeos_update_engine
diff --git a/aosp/network_selector_android.cc b/aosp/network_selector_android.cc
new file mode 100644
index 0000000..a7db415
--- /dev/null
+++ b/aosp/network_selector_android.cc
@@ -0,0 +1,46 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/network_selector_android.h"
+
+#include <memory>
+
+#include <android/multinetwork.h>
+#include <base/logging.h>
+
+namespace chromeos_update_engine {
+
+namespace network {
+
+// Factory defined in common/network_selector.h.
+std::unique_ptr<NetworkSelectorInterface> CreateNetworkSelector() {
+  return std::make_unique<NetworkSelectorAndroid>();
+}
+
+}  // namespace network
+
+// Defined in common/network_selector_interface.h.
+const NetworkId kDefaultNetworkId = NETWORK_UNSPECIFIED;
+
+bool NetworkSelectorAndroid::SetProcessNetwork(NetworkId network_id) {
+  if (android_setprocnetwork(network_id) < 0) {
+    PLOG(ERROR) << "Binding the network to " << network_id;
+    return false;
+  }
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/aosp/network_selector_android.h b/aosp/network_selector_android.h
new file mode 100644
index 0000000..b79d1b3
--- /dev/null
+++ b/aosp/network_selector_android.h
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_NETWORK_SELECTOR_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_NETWORK_SELECTOR_ANDROID_H_
+
+#include <base/macros.h>
+
+#include "update_engine/common/network_selector_interface.h"
+
+namespace chromeos_update_engine {
+
+class NetworkSelectorAndroid final : public NetworkSelectorInterface {
+ public:
+  NetworkSelectorAndroid() = default;
+  ~NetworkSelectorAndroid() override = default;
+
+  // NetworkSelectorInterface overrides.
+  bool SetProcessNetwork(NetworkId network_id) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NetworkSelectorAndroid);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_AOSP_NETWORK_SELECTOR_ANDROID_H_
diff --git a/aosp/platform_constants_android.cc b/aosp/platform_constants_android.cc
new file mode 100644
index 0000000..f468c3b
--- /dev/null
+++ b/aosp/platform_constants_android.cc
@@ -0,0 +1,38 @@
+//
+// 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 "update_engine/common/platform_constants.h"
+
+namespace chromeos_update_engine {
+namespace constants {
+
+const char kOmahaDefaultProductionURL[] =
+    "https://clients2.google.com/service/update2/brillo";
+const char kOmahaDefaultAUTestURL[] =
+    "https://clients2.google.com/service/update2/brillo";
+const char kOmahaUpdaterID[] = "Brillo";
+const char kOmahaPlatformName[] = "Brillo";
+const char kUpdatePayloadPublicKeyPath[] = "";
+const char kUpdateCertificatesPath[] = "/system/etc/security/otacerts.zip";
+const char kCACertificatesPath[] = "/system/etc/security/cacerts_google";
+// No deadline file API support on Android.
+const char kOmahaResponseDeadlineFile[] = "";
+const char kNonVolatileDirectory[] = "/data/misc/update_engine";
+const char kPostinstallMountOptions[] =
+    "context=u:object_r:postinstall_file:s0";
+
+}  // namespace constants
+}  // namespace chromeos_update_engine
diff --git a/aosp/service_delegate_android_interface.h b/aosp/service_delegate_android_interface.h
new file mode 100644
index 0000000..3c28794
--- /dev/null
+++ b/aosp/service_delegate_android_interface.h
@@ -0,0 +1,127 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_SERVICE_DELEGATE_ANDROID_INTERFACE_H_
+#define UPDATE_ENGINE_AOSP_SERVICE_DELEGATE_ANDROID_INTERFACE_H_
+
+#include <inttypes.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <brillo/errors/error.h>
+
+namespace chromeos_update_engine {
+
+// See ServiceDelegateAndroidInterface.CleanupSuccessfulUpdate
+// Wraps a IUpdateEngineCallback binder object used specifically for
+// CleanupSuccessfulUpdate.
+class CleanupSuccessfulUpdateCallbackInterface {
+ public:
+  virtual ~CleanupSuccessfulUpdateCallbackInterface() {}
+  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;
+};
+
+// This class defines the interface exposed by the Android version of the
+// daemon service. This interface only includes the method calls that such
+// daemon exposes. For asynchronous events initiated by a class implementing
+// this interface see the ServiceObserverInterface class.
+class ServiceDelegateAndroidInterface {
+ public:
+  virtual ~ServiceDelegateAndroidInterface() = default;
+
+  // Start an update attempt to download an apply the provided |payload_url| if
+  // no other update is running. The extra |key_value_pair_headers| will be
+  // included when fetching the payload. Returns whether the update was started
+  // successfully, which means that no other update was running and the passed
+  // parameters were correct, but not necessarily that the update finished
+  // correctly.
+  virtual bool ApplyPayload(
+      const std::string& payload_url,
+      int64_t payload_offset,
+      int64_t payload_size,
+      const std::vector<std::string>& key_value_pair_headers,
+      brillo::ErrorPtr* 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;
+
+  // 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;
+
+  // 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;
+
+  // 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;
+
+  // 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;
+
+  // 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;
+
+  // Allocates space for a payload.
+  // Returns 0 if space is successfully preallocated.
+  // Return non-zero if not enough space is not available; returned value is
+  // the total space required (in bytes) to be free on the device for this
+  // update to be applied, and |error| is unset.
+  // In case of error, returns 0, and sets |error| accordingly.
+  //
+  // This function may block for several minutes in the worst case.
+  virtual uint64_t AllocateSpaceForPayload(
+      const std::string& metadata_filename,
+      const std::vector<std::string>& key_value_pair_headers,
+      brillo::ErrorPtr* error) = 0;
+
+  // Wait for merge to complete, then clean up merge after an update has been
+  // successful.
+  //
+  // This function returns immediately. Progress updates are provided in
+  // |callback|.
+  virtual void CleanupSuccessfulUpdate(
+      std::unique_ptr<CleanupSuccessfulUpdateCallbackInterface> callback,
+      brillo::ErrorPtr* error) = 0;
+
+ protected:
+  ServiceDelegateAndroidInterface() = default;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_AOSP_SERVICE_DELEGATE_ANDROID_INTERFACE_H_
diff --git a/aosp/sideload_main.cc b/aosp/sideload_main.cc
new file mode 100644
index 0000000..3cbc0c7
--- /dev/null
+++ b/aosp/sideload_main.cc
@@ -0,0 +1,203 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <xz.h>
+
+#include <string>
+#include <vector>
+
+#include <base/command_line.h>
+#include <base/strings/string_split.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/asynchronous_signal_handler.h>
+#include <brillo/flag_helper.h>
+#include <brillo/message_loops/base_message_loop.h>
+#include <brillo/streams/file_stream.h>
+#include <brillo/streams/stream.h>
+
+#include "update_engine/aosp/update_attempter_android.h"
+#include "update_engine/common/boot_control.h"
+#include "update_engine/common/error_code_utils.h"
+#include "update_engine/common/hardware.h"
+#include "update_engine/common/logging.h"
+#include "update_engine/common/prefs.h"
+#include "update_engine/common/subprocess.h"
+#include "update_engine/common/terminator.h"
+#include "update_engine/common/utils.h"
+
+using std::string;
+using std::vector;
+using update_engine::UpdateEngineStatus;
+using update_engine::UpdateStatus;
+
+namespace chromeos_update_engine {
+namespace {
+
+class SideloadDaemonState : public DaemonStateInterface,
+                            public ServiceObserverInterface {
+ public:
+  explicit SideloadDaemonState(brillo::StreamPtr status_stream)
+      : status_stream_(std::move(status_stream)) {
+    // Add this class as the only observer.
+    observers_.insert(this);
+  }
+  ~SideloadDaemonState() override = default;
+
+  // DaemonStateInterface overrides.
+  bool StartUpdater() override { return true; }
+  void AddObserver(ServiceObserverInterface* observer) override {}
+  void RemoveObserver(ServiceObserverInterface* observer) override {}
+  const std::set<ServiceObserverInterface*>& service_observers() override {
+    return observers_;
+  }
+
+  // ServiceObserverInterface overrides.
+  void SendStatusUpdate(
+      const UpdateEngineStatus& update_engine_status) override {
+    UpdateStatus status = update_engine_status.status;
+    double progress = update_engine_status.progress;
+    if (status_ != status && (status == UpdateStatus::DOWNLOADING ||
+                              status == UpdateStatus::FINALIZING)) {
+      // Split the progress bar in two parts for the two stages DOWNLOADING and
+      // FINALIZING.
+      ReportStatus(base::StringPrintf(
+          "ui_print Step %d/2", status == UpdateStatus::DOWNLOADING ? 1 : 2));
+      ReportStatus(base::StringPrintf("progress 0.5 0"));
+    }
+    if (status_ != status || fabs(progress - progress_) > 0.005) {
+      ReportStatus(base::StringPrintf("set_progress %.lf", progress));
+    }
+    progress_ = progress;
+    status_ = status;
+  }
+
+  void SendPayloadApplicationComplete(ErrorCode error_code) override {
+    if (error_code != ErrorCode::kSuccess) {
+      ReportStatus(
+          base::StringPrintf("ui_print Error applying update: %d (%s)",
+                             error_code,
+                             utils::ErrorCodeToString(error_code).c_str()));
+    }
+    error_code_ = error_code;
+    brillo::MessageLoop::current()->BreakLoop();
+  }
+
+  // Getters.
+  UpdateStatus status() { return status_; }
+  ErrorCode error_code() { return error_code_; }
+
+ private:
+  // Report a status message in the status_stream_, if any. These messages
+  // should conform to the specification defined in the Android recovery.
+  void ReportStatus(const string& message) {
+    if (!status_stream_)
+      return;
+    string status_line = message + "\n";
+    status_stream_->WriteAllBlocking(
+        status_line.data(), status_line.size(), nullptr);
+  }
+
+  std::set<ServiceObserverInterface*> observers_;
+  brillo::StreamPtr status_stream_;
+
+  // The last status and error code reported.
+  UpdateStatus status_{UpdateStatus::IDLE};
+  ErrorCode error_code_{ErrorCode::kSuccess};
+  double progress_{-1.};
+};
+
+// Apply an update payload directly from the given payload URI.
+bool ApplyUpdatePayload(const string& payload,
+                        int64_t payload_offset,
+                        int64_t payload_size,
+                        const vector<string>& headers,
+                        int64_t status_fd) {
+  brillo::BaseMessageLoop loop;
+  loop.SetAsCurrent();
+
+  // Setup the subprocess handler.
+  brillo::AsynchronousSignalHandler handler;
+  handler.Init();
+  Subprocess subprocess;
+  subprocess.Init(&handler);
+
+  SideloadDaemonState sideload_daemon_state(
+      brillo::FileStream::FromFileDescriptor(status_fd, true, nullptr));
+
+  // During the sideload we don't access the prefs persisted on disk but instead
+  // use a temporary memory storage.
+  MemoryPrefs prefs;
+
+  std::unique_ptr<BootControlInterface> boot_control =
+      boot_control::CreateBootControl();
+  if (!boot_control) {
+    LOG(ERROR) << "Error initializing the BootControlInterface.";
+    return false;
+  }
+
+  std::unique_ptr<HardwareInterface> hardware = hardware::CreateHardware();
+  if (!hardware) {
+    LOG(ERROR) << "Error initializing the HardwareInterface.";
+    return false;
+  }
+
+  UpdateAttempterAndroid update_attempter(
+      &sideload_daemon_state, &prefs, boot_control.get(), hardware.get());
+  update_attempter.Init();
+
+  TEST_AND_RETURN_FALSE(update_attempter.ApplyPayload(
+      payload, payload_offset, payload_size, headers, nullptr));
+
+  loop.Run();
+  return sideload_daemon_state.status() == UpdateStatus::UPDATED_NEED_REBOOT;
+}
+
+}  // namespace
+}  // namespace chromeos_update_engine
+
+int main(int argc, char** argv) {
+  DEFINE_string(payload,
+                "file:///data/payload.bin",
+                "The URI to the update payload to use.");
+  DEFINE_int64(
+      offset, 0, "The offset in the payload where the CrAU update starts. ");
+  DEFINE_int64(size,
+               0,
+               "The size of the CrAU part of the payload. If 0 is passed, it "
+               "will be autodetected.");
+  DEFINE_string(headers,
+                "",
+                "A list of key-value pairs, one element of the list per line.");
+  DEFINE_int64(status_fd, -1, "A file descriptor to notify the update status.");
+
+  chromeos_update_engine::Terminator::Init();
+  chromeos_update_engine::SetupLogging(true /* stderr */, false /* file */);
+  brillo::FlagHelper::Init(argc, argv, "Update Engine Sideload");
+
+  LOG(INFO) << "Update Engine Sideloading starting";
+
+  // xz-embedded requires to initialize its CRC-32 table once on startup.
+  xz_crc32_init();
+
+  vector<string> headers = base::SplitString(
+      FLAGS_headers, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+
+  if (!chromeos_update_engine::ApplyUpdatePayload(
+          FLAGS_payload, FLAGS_offset, FLAGS_size, headers, FLAGS_status_fd))
+    return 1;
+
+  return 0;
+}
diff --git a/aosp/update_attempter_android.cc b/aosp/update_attempter_android.cc
new file mode 100644
index 0000000..7ed3249
--- /dev/null
+++ b/aosp/update_attempter_android.cc
@@ -0,0 +1,1054 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/update_attempter_android.h"
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <utility>
+
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+#include <base/bind.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <brillo/data_encoding.h>
+#include <brillo/message_loops/message_loop.h>
+#include <brillo/strings/string_utils.h>
+#include <log/log_safetynet.h>
+
+#include "update_engine/aosp/cleanup_previous_update_action.h"
+#include "update_engine/common/constants.h"
+#include "update_engine/common/daemon_state_interface.h"
+#include "update_engine/common/download_action.h"
+#include "update_engine/common/error_code_utils.h"
+#include "update_engine/common/file_fetcher.h"
+#include "update_engine/common/metrics_reporter_interface.h"
+#include "update_engine/common/network_selector.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/metrics_utils.h"
+#include "update_engine/payload_consumer/certificate_parser_interface.h"
+#include "update_engine/payload_consumer/delta_performer.h"
+#include "update_engine/payload_consumer/file_descriptor.h"
+#include "update_engine/payload_consumer/file_descriptor_utils.h"
+#include "update_engine/payload_consumer/filesystem_verifier_action.h"
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_consumer/payload_metadata.h"
+#include "update_engine/payload_consumer/payload_verifier.h"
+#include "update_engine/payload_consumer/postinstall_runner_action.h"
+#include "update_engine/update_boot_flags_action.h"
+#include "update_engine/update_status_utils.h"
+
+#ifndef _UE_SIDELOAD
+// Do not include support for external HTTP(s) urls when building
+// update_engine_sideload.
+#include "update_engine/libcurl_http_fetcher.h"
+#endif
+
+using android::base::unique_fd;
+using base::Bind;
+using base::Time;
+using base::TimeDelta;
+using base::TimeTicks;
+using std::shared_ptr;
+using std::string;
+using std::vector;
+using update_engine::UpdateEngineStatus;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+// Minimum threshold to broadcast an status update in progress and time.
+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 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;
+  return false;
+}
+
+bool GetHeaderAsBool(const string& header, bool default_value) {
+  int value = 0;
+  if (base::StringToInt(header, &value) && (value == 0 || value == 1))
+    return value == 1;
+  return default_value;
+}
+
+bool ParseKeyValuePairHeaders(const vector<string>& key_value_pair_headers,
+                              std::map<string, string>* headers,
+                              brillo::ErrorPtr* 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);
+    }
+    if (!headers->emplace(key, value).second)
+      return LogAndSetError(error, FROM_HERE, "Passed repeated key: " + key);
+  }
+  return true;
+}
+
+// Unique identifier for the payload. An empty string means that the payload
+// can't be resumed.
+string GetPayloadId(const std::map<string, string>& headers) {
+  return (headers.count(kPayloadPropertyFileHash)
+              ? headers.at(kPayloadPropertyFileHash)
+              : "") +
+         (headers.count(kPayloadPropertyMetadataHash)
+              ? headers.at(kPayloadPropertyMetadataHash)
+              : "");
+}
+
+}  // namespace
+
+UpdateAttempterAndroid::UpdateAttempterAndroid(
+    DaemonStateInterface* daemon_state,
+    PrefsInterface* prefs,
+    BootControlInterface* boot_control,
+    HardwareInterface* hardware)
+    : daemon_state_(daemon_state),
+      prefs_(prefs),
+      boot_control_(boot_control),
+      hardware_(hardware),
+      processor_(new ActionProcessor()),
+      clock_(new Clock()) {
+  metrics_reporter_ = metrics::CreateMetricsReporter();
+  network_selector_ = network::CreateNetworkSelector();
+}
+
+UpdateAttempterAndroid::~UpdateAttempterAndroid() {
+  // Release ourselves as the ActionProcessor's delegate to prevent
+  // re-scheduling the updates due to the processing stopped.
+  processor_->set_delegate(nullptr);
+}
+
+void UpdateAttempterAndroid::Init() {
+  // In case of update_engine restart without a reboot we need to restore the
+  // reboot needed state.
+  if (UpdateCompletedOnThisBoot()) {
+    SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT);
+  } else {
+    SetStatusAndNotify(UpdateStatus::IDLE);
+    UpdatePrefsAndReportUpdateMetricsOnReboot();
+#ifdef _UE_SIDELOAD
+    LOG(INFO) << "Skip ScheduleCleanupPreviousUpdate in sideload because "
+              << "ApplyPayload will call it later.";
+#else
+    ScheduleCleanupPreviousUpdate();
+#endif
+  }
+}
+
+bool UpdateAttempterAndroid::ApplyPayload(
+    const string& payload_url,
+    int64_t payload_offset,
+    int64_t payload_size,
+    const vector<string>& key_value_pair_headers,
+    brillo::ErrorPtr* error) {
+  if (status_ == UpdateStatus::UPDATED_NEED_REBOOT) {
+    return LogAndSetError(
+        error, FROM_HERE, "An update already applied, waiting for reboot");
+  }
+  if (processor_->IsRunning()) {
+    return LogAndSetError(
+        error, FROM_HERE, "Already processing an update, cancel it first.");
+  }
+  DCHECK(status_ == UpdateStatus::IDLE);
+
+  std::map<string, string> headers;
+  if (!ParseKeyValuePairHeaders(key_value_pair_headers, &headers, error)) {
+    return false;
+  }
+
+  string payload_id = GetPayloadId(headers);
+
+  // Setup the InstallPlan based on the request.
+  install_plan_ = InstallPlan();
+
+  install_plan_.download_url = payload_url;
+  install_plan_.version = "";
+  base_offset_ = payload_offset;
+  InstallPlan::Payload payload;
+  payload.size = payload_size;
+  if (!payload.size) {
+    if (!base::StringToUint64(headers[kPayloadPropertyFileSize],
+                              &payload.size)) {
+      payload.size = 0;
+    }
+  }
+  if (!brillo::data_encoding::Base64Decode(headers[kPayloadPropertyFileHash],
+                                           &payload.hash)) {
+    LOG(WARNING) << "Unable to decode base64 file hash: "
+                 << headers[kPayloadPropertyFileHash];
+  }
+  if (!base::StringToUint64(headers[kPayloadPropertyMetadataSize],
+                            &payload.metadata_size)) {
+    payload.metadata_size = 0;
+  }
+  // The |payload.type| is not used anymore since minor_version 3.
+  payload.type = InstallPayloadType::kUnknown;
+  install_plan_.payloads.push_back(payload);
+
+  // The |public_key_rsa| key would override the public key stored on disk.
+  install_plan_.public_key_rsa = "";
+
+  install_plan_.hash_checks_mandatory = hardware_->IsOfficialBuild();
+  install_plan_.is_resume = !payload_id.empty() &&
+                            DeltaPerformer::CanResumeUpdate(prefs_, payload_id);
+  if (!install_plan_.is_resume) {
+    // No need to reset dynamic_partititon_metadata_updated. If previous calls
+    // to AllocateSpaceForPayload uses the same payload_id, reuse preallocated
+    // space. Otherwise, DeltaPerformer re-allocates space when the payload is
+    // applied.
+    if (!DeltaPerformer::ResetUpdateProgress(
+            prefs_,
+            false /* quick */,
+            true /* skip_dynamic_partititon_metadata_updated */)) {
+      LOG(WARNING) << "Unable to reset the update progress.";
+    }
+    if (!prefs_->SetString(kPrefsUpdateCheckResponseHash, payload_id)) {
+      LOG(WARNING) << "Unable to save the update check response hash.";
+    }
+  }
+  install_plan_.source_slot = GetCurrentSlot();
+  install_plan_.target_slot = GetTargetSlot();
+
+  install_plan_.powerwash_required =
+      GetHeaderAsBool(headers[kPayloadPropertyPowerwash], false);
+
+  install_plan_.switch_slot_on_reboot =
+      GetHeaderAsBool(headers[kPayloadPropertySwitchSlotOnReboot], true);
+
+  install_plan_.run_post_install =
+      GetHeaderAsBool(headers[kPayloadPropertyRunPostInstall], true);
+
+  // Skip writing verity if we're resuming and verity has already been written.
+  install_plan_.write_verity = true;
+  if (install_plan_.is_resume && prefs_->Exists(kPrefsVerityWritten)) {
+    bool verity_written = false;
+    if (prefs_->GetBoolean(kPrefsVerityWritten, &verity_written) &&
+        verity_written) {
+      install_plan_.write_verity = false;
+    }
+  }
+
+  NetworkId network_id = kDefaultNetworkId;
+  if (!headers[kPayloadPropertyNetworkId].empty()) {
+    if (!base::StringToUint64(headers[kPayloadPropertyNetworkId],
+                              &network_id)) {
+      return LogAndSetError(
+          error,
+          FROM_HERE,
+          "Invalid network_id: " + headers[kPayloadPropertyNetworkId]);
+    }
+    if (!network_selector_->SetProcessNetwork(network_id)) {
+      return LogAndSetError(
+          error,
+          FROM_HERE,
+          "Unable to set network_id: " + headers[kPayloadPropertyNetworkId]);
+    }
+  }
+
+  LOG(INFO) << "Using this install plan:";
+  install_plan_.Dump();
+
+  HttpFetcher* fetcher = nullptr;
+  if (FileFetcher::SupportedUrl(payload_url)) {
+    DLOG(INFO) << "Using FileFetcher for file URL.";
+    fetcher = new FileFetcher();
+  } else {
+#ifdef _UE_SIDELOAD
+    LOG(FATAL) << "Unsupported sideload URI: " << payload_url;
+#else
+    LibcurlHttpFetcher* libcurl_fetcher =
+        new LibcurlHttpFetcher(&proxy_resolver_, hardware_);
+    libcurl_fetcher->set_server_to_check(ServerToCheck::kDownload);
+    fetcher = libcurl_fetcher;
+#endif  // _UE_SIDELOAD
+  }
+  // Setup extra headers.
+  if (!headers[kPayloadPropertyAuthorization].empty())
+    fetcher->SetHeader("Authorization", headers[kPayloadPropertyAuthorization]);
+  if (!headers[kPayloadPropertyUserAgent].empty())
+    fetcher->SetHeader("User-Agent", headers[kPayloadPropertyUserAgent]);
+
+  BuildUpdateActions(fetcher);
+
+  SetStatusAndNotify(UpdateStatus::UPDATE_AVAILABLE);
+
+  UpdatePrefsOnUpdateStart(install_plan_.is_resume);
+  // TODO(xunchang) report the metrics for unresumable updates
+
+  ScheduleProcessingStart();
+  return true;
+}
+
+bool UpdateAttempterAndroid::ApplyPayload(
+    int fd,
+    int64_t payload_offset,
+    int64_t payload_size,
+    const vector<string>& key_value_pair_headers,
+    brillo::ErrorPtr* error) {
+  payload_fd_.reset(dup(fd));
+  const string payload_url = "fd://" + std::to_string(payload_fd_.get());
+
+  return ApplyPayload(
+      payload_url, payload_offset, payload_size, key_value_pair_headers, error);
+}
+
+bool UpdateAttempterAndroid::SuspendUpdate(brillo::ErrorPtr* error) {
+  if (!processor_->IsRunning())
+    return LogAndSetError(error, FROM_HERE, "No ongoing update to suspend.");
+  processor_->SuspendProcessing();
+  return true;
+}
+
+bool UpdateAttempterAndroid::ResumeUpdate(brillo::ErrorPtr* error) {
+  if (!processor_->IsRunning())
+    return LogAndSetError(error, FROM_HERE, "No ongoing update to resume.");
+  processor_->ResumeProcessing();
+  return true;
+}
+
+bool UpdateAttempterAndroid::CancelUpdate(brillo::ErrorPtr* error) {
+  if (!processor_->IsRunning())
+    return LogAndSetError(error, FROM_HERE, "No ongoing update to cancel.");
+  processor_->StopProcessing();
+  return true;
+}
+
+bool UpdateAttempterAndroid::ResetStatus(brillo::ErrorPtr* error) {
+  LOG(INFO) << "Attempting to reset state from "
+            << UpdateStatusToString(status_) << " to UpdateStatus::IDLE";
+
+  switch (status_) {
+    case UpdateStatus::IDLE: {
+      if (!boot_control_->GetDynamicPartitionControl()->ResetUpdate(prefs_)) {
+        LOG(WARNING) << "Failed to reset snapshots. UpdateStatus is IDLE but"
+                     << "space might not be freed.";
+      }
+      return true;
+    }
+
+    case UpdateStatus::UPDATED_NEED_REBOOT: {
+      bool ret_value = true;
+
+      // Update the boot flags so the current slot has higher priority.
+      if (!boot_control_->SetActiveBootSlot(GetCurrentSlot()))
+        ret_value = false;
+
+      // 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) {})))
+        ret_value = false;
+
+      // Resets the warm reset property since we won't switch the slot.
+      hardware_->SetWarmReset(false);
+
+      // Remove update progress for DeltaPerformer and remove snapshots.
+      if (!boot_control_->GetDynamicPartitionControl()->ResetUpdate(prefs_))
+        ret_value = false;
+
+      // Remove the reboot marker so that if the machine is rebooted
+      // after resetting to idle state, it doesn't go back to
+      // UpdateStatus::UPDATED_NEED_REBOOT state.
+      if (!prefs_->Delete(kPrefsUpdateCompletedOnBootId))
+        ret_value = false;
+      ClearMetricsPrefs();
+
+      if (!ret_value) {
+        return LogAndSetError(
+            error, FROM_HERE, "Failed to reset the status to ");
+      }
+
+      SetStatusAndNotify(UpdateStatus::IDLE);
+      LOG(INFO) << "Reset status successful";
+      return true;
+    }
+
+    default:
+      return LogAndSetError(
+          error,
+          FROM_HERE,
+          "Reset not allowed in this state. Cancel the ongoing update first");
+  }
+}
+
+bool UpdateAttempterAndroid::VerifyPayloadParseManifest(
+    const std::string& metadata_filename,
+    DeltaArchiveManifest* manifest,
+    brillo::ErrorPtr* error) {
+  FileDescriptorPtr fd(new EintrSafeFileDescriptor);
+  if (!fd->Open(metadata_filename.c_str(), O_RDONLY)) {
+    return LogAndSetError(
+        error, FROM_HERE, "Failed to open " + metadata_filename);
+  }
+  brillo::Blob metadata(kMaxPayloadHeaderSize);
+  if (!fd->Read(metadata.data(), metadata.size())) {
+    return LogAndSetError(
+        error,
+        FROM_HERE,
+        "Failed to read payload header from " + metadata_filename);
+  }
+  ErrorCode errorcode;
+  PayloadMetadata payload_metadata;
+  if (payload_metadata.ParsePayloadHeader(metadata, &errorcode) !=
+      MetadataParseResult::kSuccess) {
+    return LogAndSetError(error,
+                          FROM_HERE,
+                          "Failed to parse payload header: " +
+                              utils::ErrorCodeToString(errorcode));
+  }
+  uint64_t metadata_size = payload_metadata.GetMetadataSize() +
+                           payload_metadata.GetMetadataSignatureSize();
+  if (metadata_size < kMaxPayloadHeaderSize ||
+      metadata_size >
+          static_cast<uint64_t>(utils::FileSize(metadata_filename))) {
+    return LogAndSetError(
+        error,
+        FROM_HERE,
+        "Invalid metadata size: " + std::to_string(metadata_size));
+  }
+  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);
+  }
+  fd->Close();
+
+  auto payload_verifier = PayloadVerifier::CreateInstanceFromZipPath(
+      constants::kUpdateCertificatesPath);
+  if (!payload_verifier) {
+    return LogAndSetError(error,
+                          FROM_HERE,
+                          "Failed to create the payload verifier from " +
+                              std::string(constants::kUpdateCertificatesPath));
+  }
+  errorcode = payload_metadata.ValidateMetadataSignature(
+      metadata, "", *payload_verifier);
+  if (errorcode != ErrorCode::kSuccess) {
+    return LogAndSetError(error,
+                          FROM_HERE,
+                          "Failed to validate metadata signature: " +
+                              utils::ErrorCodeToString(errorcode));
+  }
+  if (!payload_metadata.GetManifest(metadata, manifest)) {
+    return LogAndSetError(error, FROM_HERE, "Failed to parse manifest.");
+  }
+
+  return true;
+}
+
+bool UpdateAttempterAndroid::VerifyPayloadApplicable(
+    const std::string& metadata_filename, brillo::ErrorPtr* error) {
+  DeltaArchiveManifest manifest;
+  TEST_AND_RETURN_FALSE(
+      VerifyPayloadParseManifest(metadata_filename, &manifest, error));
+
+  FileDescriptorPtr fd(new EintrSafeFileDescriptor);
+  ErrorCode errorcode;
+
+  BootControlInterface::Slot current_slot = GetCurrentSlot();
+  for (const PartitionUpdate& partition : manifest.partitions()) {
+    if (!partition.has_old_partition_info())
+      continue;
+    string partition_path;
+    if (!boot_control_->GetPartitionDevice(
+            partition.partition_name(), current_slot, &partition_path)) {
+      return LogAndSetError(
+          error,
+          FROM_HERE,
+          "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);
+    }
+    for (const InstallOperation& operation : partition.operations()) {
+      if (!operation.has_src_sha256_hash())
+        continue;
+      brillo::Blob source_hash;
+      if (!fd_utils::ReadAndHashExtents(fd,
+                                        operation.src_extents(),
+                                        manifest.block_size(),
+                                        &source_hash)) {
+        return LogAndSetError(
+            error, FROM_HERE, "Failed to hash " + partition_path);
+      }
+      if (!PartitionWriter::ValidateSourceHash(
+              source_hash, operation, fd, &errorcode)) {
+        return false;
+      }
+    }
+    fd->Close();
+  }
+  return true;
+}
+
+void UpdateAttempterAndroid::ProcessingDone(const ActionProcessor* processor,
+                                            ErrorCode code) {
+  LOG(INFO) << "Processing Done.";
+
+  if (status_ == UpdateStatus::CLEANUP_PREVIOUS_UPDATE) {
+    TerminateUpdateAndNotify(code);
+    return;
+  }
+
+  switch (code) {
+    case ErrorCode::kSuccess:
+      // Update succeeded.
+      WriteUpdateCompletedMarker();
+      prefs_->SetInt64(kPrefsDeltaUpdateFailures, 0);
+
+      LOG(INFO) << "Update successfully applied, waiting to reboot.";
+      break;
+
+    case ErrorCode::kFilesystemCopierError:
+    case ErrorCode::kNewRootfsVerificationError:
+    case ErrorCode::kNewKernelVerificationError:
+    case ErrorCode::kFilesystemVerifierError:
+    case ErrorCode::kDownloadStateInitializationError:
+      // Reset the ongoing update for these errors so it starts from the
+      // beginning next time.
+      DeltaPerformer::ResetUpdateProgress(prefs_, false);
+      LOG(INFO) << "Resetting update progress.";
+      break;
+
+    case ErrorCode::kPayloadTimestampError:
+      // SafetyNet logging, b/36232423
+      android_errorWriteLog(0x534e4554, "36232423");
+      break;
+
+    default:
+      // Ignore all other error codes.
+      break;
+  }
+
+  TerminateUpdateAndNotify(code);
+}
+
+void UpdateAttempterAndroid::ProcessingStopped(
+    const ActionProcessor* processor) {
+  TerminateUpdateAndNotify(ErrorCode::kUserCanceled);
+}
+
+void UpdateAttempterAndroid::ActionCompleted(ActionProcessor* processor,
+                                             AbstractAction* action,
+                                             ErrorCode code) {
+  // Reset download progress regardless of whether or not the download
+  // action succeeded.
+  const string type = action->Type();
+  if (type == CleanupPreviousUpdateAction::StaticType() ||
+      (type == NoOpAction::StaticType() &&
+       status_ == UpdateStatus::CLEANUP_PREVIOUS_UPDATE)) {
+    cleanup_previous_update_code_ = code;
+    NotifyCleanupPreviousUpdateCallbacksAndClear();
+  }
+  // download_progress_ is actually used by other actions, such as
+  // filesystem_verify_action. Therefore we always clear it.
+  download_progress_ = 0;
+  if (type == PostinstallRunnerAction::StaticType()) {
+    bool succeeded =
+        code == ErrorCode::kSuccess || code == ErrorCode::kUpdatedButNotActive;
+    prefs_->SetBoolean(kPrefsPostInstallSucceeded, succeeded);
+  }
+  if (code != ErrorCode::kSuccess) {
+    // If an action failed, the ActionProcessor will cancel the whole thing.
+    return;
+  }
+  if (type == UpdateBootFlagsAction::StaticType()) {
+    SetStatusAndNotify(UpdateStatus::CLEANUP_PREVIOUS_UPDATE);
+  }
+  if (type == DownloadAction::StaticType()) {
+    auto download_action = static_cast<DownloadAction*>(action);
+    install_plan_ = *download_action->install_plan();
+    SetStatusAndNotify(UpdateStatus::VERIFYING);
+  } else if (type == FilesystemVerifierAction::StaticType()) {
+    SetStatusAndNotify(UpdateStatus::FINALIZING);
+    prefs_->SetBoolean(kPrefsVerityWritten, true);
+  }
+}
+
+void UpdateAttempterAndroid::BytesReceived(uint64_t bytes_progressed,
+                                           uint64_t bytes_received,
+                                           uint64_t total) {
+  double progress = 0;
+  if (total)
+    progress = static_cast<double>(bytes_received) / static_cast<double>(total);
+  if (status_ != UpdateStatus::DOWNLOADING || bytes_received == total) {
+    download_progress_ = progress;
+    SetStatusAndNotify(UpdateStatus::DOWNLOADING);
+  } else {
+    ProgressUpdate(progress);
+  }
+
+  // Update the bytes downloaded in prefs.
+  int64_t current_bytes_downloaded =
+      metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, prefs_);
+  int64_t total_bytes_downloaded =
+      metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, prefs_);
+  prefs_->SetInt64(kPrefsCurrentBytesDownloaded,
+                   current_bytes_downloaded + bytes_progressed);
+  prefs_->SetInt64(kPrefsTotalBytesDownloaded,
+                   total_bytes_downloaded + bytes_progressed);
+}
+
+bool UpdateAttempterAndroid::ShouldCancel(ErrorCode* cancel_reason) {
+  // TODO(deymo): Notify the DownloadAction that it should cancel the update
+  // download.
+  return false;
+}
+
+void UpdateAttempterAndroid::DownloadComplete() {
+  // Nothing needs to be done when the download completes.
+}
+
+void UpdateAttempterAndroid::ProgressUpdate(double progress) {
+  // Self throttle based on progress. Also send notifications if progress is
+  // too slow.
+  if (progress == 1.0 ||
+      progress - download_progress_ >= kBroadcastThresholdProgress ||
+      TimeTicks::Now() - last_notify_time_ >=
+          TimeDelta::FromSeconds(kBroadcastThresholdSeconds)) {
+    download_progress_ = progress;
+    SetStatusAndNotify(status_);
+  }
+}
+
+void UpdateAttempterAndroid::OnVerifyProgressUpdate(double progress) {
+  assert(status_ == UpdateStatus::VERIFYING);
+  ProgressUpdate(progress);
+}
+
+void UpdateAttempterAndroid::ScheduleProcessingStart() {
+  LOG(INFO) << "Scheduling an action processor start.";
+  brillo::MessageLoop::current()->PostTask(
+      FROM_HERE,
+      Bind([](ActionProcessor* processor) { processor->StartProcessing(); },
+           base::Unretained(processor_.get())));
+}
+
+void UpdateAttempterAndroid::TerminateUpdateAndNotify(ErrorCode error_code) {
+  if (status_ == UpdateStatus::IDLE) {
+    LOG(ERROR) << "No ongoing update, but TerminatedUpdate() called.";
+    return;
+  }
+
+  if (status_ == UpdateStatus::CLEANUP_PREVIOUS_UPDATE) {
+    LOG(INFO) << "Terminating cleanup previous update.";
+    SetStatusAndNotify(UpdateStatus::IDLE);
+    for (auto observer : daemon_state_->service_observers())
+      observer->SendPayloadApplicationComplete(error_code);
+    return;
+  }
+
+  boot_control_->GetDynamicPartitionControl()->Cleanup();
+
+  download_progress_ = 0;
+  UpdateStatus new_status =
+      (error_code == ErrorCode::kSuccess ? UpdateStatus::UPDATED_NEED_REBOOT
+                                         : UpdateStatus::IDLE);
+  SetStatusAndNotify(new_status);
+  payload_fd_.reset();
+
+  // The network id is only applicable to one download attempt and once it's
+  // done the network id should not be re-used anymore.
+  if (!network_selector_->SetProcessNetwork(kDefaultNetworkId)) {
+    LOG(WARNING) << "Unable to unbind network.";
+  }
+
+  for (auto observer : daemon_state_->service_observers())
+    observer->SendPayloadApplicationComplete(error_code);
+
+  CollectAndReportUpdateMetricsOnUpdateFinished(error_code);
+  ClearMetricsPrefs();
+  if (error_code == ErrorCode::kSuccess) {
+    // We should only reset the PayloadAttemptNumber if the update succeeds, or
+    // we switch to a different payload.
+    prefs_->Delete(kPrefsPayloadAttemptNumber);
+    metrics_utils::SetSystemUpdatedMarker(clock_.get(), prefs_);
+    // Clear the total bytes downloaded if and only if the update succeeds.
+    prefs_->SetInt64(kPrefsTotalBytesDownloaded, 0);
+  }
+}
+
+void UpdateAttempterAndroid::SetStatusAndNotify(UpdateStatus status) {
+  status_ = status;
+  size_t payload_size =
+      install_plan_.payloads.empty() ? 0 : install_plan_.payloads[0].size;
+  UpdateEngineStatus status_to_send = {.status = status_,
+                                       .progress = download_progress_,
+                                       .new_size_bytes = payload_size};
+
+  for (auto observer : daemon_state_->service_observers()) {
+    observer->SendStatusUpdate(status_to_send);
+  }
+  last_notify_time_ = TimeTicks::Now();
+}
+
+void UpdateAttempterAndroid::BuildUpdateActions(HttpFetcher* fetcher) {
+  CHECK(!processor_->IsRunning());
+  processor_->set_delegate(this);
+
+  // Actions:
+  auto update_boot_flags_action =
+      std::make_unique<UpdateBootFlagsAction>(boot_control_);
+  auto cleanup_previous_update_action =
+      boot_control_->GetDynamicPartitionControl()
+          ->GetCleanupPreviousUpdateAction(boot_control_, prefs_, this);
+  auto install_plan_action = std::make_unique<InstallPlanAction>(install_plan_);
+  auto download_action =
+      std::make_unique<DownloadAction>(prefs_,
+                                       boot_control_,
+                                       hardware_,
+                                       nullptr,  // system_state, not used.
+                                       fetcher,  // passes ownership
+                                       true /* interactive */);
+  download_action->set_delegate(this);
+  download_action->set_base_offset(base_offset_);
+  auto filesystem_verifier_action = std::make_unique<FilesystemVerifierAction>(
+      boot_control_->GetDynamicPartitionControl());
+  auto postinstall_runner_action =
+      std::make_unique<PostinstallRunnerAction>(boot_control_, hardware_);
+  filesystem_verifier_action->set_delegate(this);
+  postinstall_runner_action->set_delegate(this);
+
+  // Bond them together. We have to use the leaf-types when calling
+  // BondActions().
+  BondActions(install_plan_action.get(), download_action.get());
+  BondActions(download_action.get(), filesystem_verifier_action.get());
+  BondActions(filesystem_verifier_action.get(),
+              postinstall_runner_action.get());
+
+  processor_->EnqueueAction(std::move(update_boot_flags_action));
+  processor_->EnqueueAction(std::move(cleanup_previous_update_action));
+  processor_->EnqueueAction(std::move(install_plan_action));
+  processor_->EnqueueAction(std::move(download_action));
+  processor_->EnqueueAction(std::move(filesystem_verifier_action));
+  processor_->EnqueueAction(std::move(postinstall_runner_action));
+}
+
+bool UpdateAttempterAndroid::WriteUpdateCompletedMarker() {
+  string boot_id;
+  TEST_AND_RETURN_FALSE(utils::GetBootId(&boot_id));
+  prefs_->SetString(kPrefsUpdateCompletedOnBootId, boot_id);
+  return true;
+}
+
+bool UpdateAttempterAndroid::UpdateCompletedOnThisBoot() {
+  // In case of an update_engine restart without a reboot, we stored the boot_id
+  // when the update was completed by setting a pref, so we can check whether
+  // the last update was on this boot or a previous one.
+  string boot_id;
+  TEST_AND_RETURN_FALSE(utils::GetBootId(&boot_id));
+
+  string update_completed_on_boot_id;
+  return (prefs_->Exists(kPrefsUpdateCompletedOnBootId) &&
+          prefs_->GetString(kPrefsUpdateCompletedOnBootId,
+                            &update_completed_on_boot_id) &&
+          update_completed_on_boot_id == boot_id);
+}
+
+// Collect and report the android metrics when we terminate the update.
+void UpdateAttempterAndroid::CollectAndReportUpdateMetricsOnUpdateFinished(
+    ErrorCode error_code) {
+  int64_t attempt_number =
+      metrics_utils::GetPersistedValue(kPrefsPayloadAttemptNumber, prefs_);
+  PayloadType payload_type = kPayloadTypeFull;
+  int64_t payload_size = 0;
+  for (const auto& p : install_plan_.payloads) {
+    if (p.type == InstallPayloadType::kDelta)
+      payload_type = kPayloadTypeDelta;
+    payload_size += p.size;
+  }
+
+  metrics::AttemptResult attempt_result =
+      metrics_utils::GetAttemptResult(error_code);
+  Time boot_time_start = Time::FromInternalValue(
+      metrics_utils::GetPersistedValue(kPrefsUpdateBootTimestampStart, prefs_));
+  Time monotonic_time_start = Time::FromInternalValue(
+      metrics_utils::GetPersistedValue(kPrefsUpdateTimestampStart, prefs_));
+  TimeDelta duration = clock_->GetBootTime() - boot_time_start;
+  TimeDelta duration_uptime = clock_->GetMonotonicTime() - monotonic_time_start;
+
+  metrics_reporter_->ReportUpdateAttemptMetrics(
+      nullptr,  // system_state
+      static_cast<int>(attempt_number),
+      payload_type,
+      duration,
+      duration_uptime,
+      payload_size,
+      attempt_result,
+      error_code);
+
+  int64_t current_bytes_downloaded =
+      metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, prefs_);
+  metrics_reporter_->ReportUpdateAttemptDownloadMetrics(
+      current_bytes_downloaded,
+      0,
+      DownloadSource::kNumDownloadSources,
+      metrics::DownloadErrorCode::kUnset,
+      metrics::ConnectionType::kUnset);
+
+  if (error_code == ErrorCode::kSuccess) {
+    int64_t reboot_count =
+        metrics_utils::GetPersistedValue(kPrefsNumReboots, prefs_);
+    string build_version;
+    prefs_->GetString(kPrefsPreviousVersion, &build_version);
+
+    // For android metrics, we only care about the total bytes downloaded
+    // for all sources; for now we assume the only download source is
+    // HttpsServer.
+    int64_t total_bytes_downloaded =
+        metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, prefs_);
+    int64_t num_bytes_downloaded[kNumDownloadSources] = {};
+    num_bytes_downloaded[DownloadSource::kDownloadSourceHttpsServer] =
+        total_bytes_downloaded;
+
+    int download_overhead_percentage = 0;
+    if (total_bytes_downloaded >= payload_size) {
+      CHECK_GT(payload_size, 0);
+      download_overhead_percentage =
+          (total_bytes_downloaded - payload_size) * 100ull / payload_size;
+    } else {
+      LOG(WARNING) << "Downloaded bytes " << total_bytes_downloaded
+                   << " is smaller than the payload size " << payload_size;
+    }
+
+    metrics_reporter_->ReportSuccessfulUpdateMetrics(
+        static_cast<int>(attempt_number),
+        0,  // update abandoned count
+        payload_type,
+        payload_size,
+        num_bytes_downloaded,
+        download_overhead_percentage,
+        duration,
+        duration_uptime,
+        static_cast<int>(reboot_count),
+        0);  // url_switch_count
+  }
+}
+
+void UpdateAttempterAndroid::UpdatePrefsAndReportUpdateMetricsOnReboot() {
+  string current_boot_id;
+  TEST_AND_RETURN(utils::GetBootId(&current_boot_id));
+  // Example: [ro.build.version.incremental]: [4292972]
+  string current_version =
+      android::base::GetProperty("ro.build.version.incremental", "");
+  TEST_AND_RETURN(!current_version.empty());
+
+  // If there's no record of previous version (e.g. due to a data wipe), we
+  // save the info of current boot and skip the metrics report.
+  if (!prefs_->Exists(kPrefsPreviousVersion)) {
+    prefs_->SetString(kPrefsBootId, current_boot_id);
+    prefs_->SetString(kPrefsPreviousVersion, current_version);
+    ClearMetricsPrefs();
+    return;
+  }
+  string previous_version;
+  // update_engine restarted under the same build.
+  // TODO(xunchang) identify and report rollback by checking UpdateMarker.
+  if (prefs_->GetString(kPrefsPreviousVersion, &previous_version) &&
+      previous_version == current_version) {
+    string last_boot_id;
+    bool is_reboot = prefs_->Exists(kPrefsBootId) &&
+                     (prefs_->GetString(kPrefsBootId, &last_boot_id) &&
+                      last_boot_id != current_boot_id);
+    // Increment the reboot number if |kPrefsNumReboots| exists. That pref is
+    // set when we start a new update.
+    if (is_reboot && prefs_->Exists(kPrefsNumReboots)) {
+      prefs_->SetString(kPrefsBootId, current_boot_id);
+      int64_t reboot_count =
+          metrics_utils::GetPersistedValue(kPrefsNumReboots, prefs_);
+      metrics_utils::SetNumReboots(reboot_count + 1, prefs_);
+    }
+    return;
+  }
+
+  // Now that the build version changes, report the update metrics.
+  // TODO(xunchang) check the build version is larger than the previous one.
+  prefs_->SetString(kPrefsBootId, current_boot_id);
+  prefs_->SetString(kPrefsPreviousVersion, current_version);
+
+  bool previous_attempt_exists = prefs_->Exists(kPrefsPayloadAttemptNumber);
+  // |kPrefsPayloadAttemptNumber| should be cleared upon successful update.
+  if (previous_attempt_exists) {
+    metrics_reporter_->ReportAbnormallyTerminatedUpdateAttemptMetrics();
+  }
+
+  metrics_utils::LoadAndReportTimeToReboot(
+      metrics_reporter_.get(), prefs_, clock_.get());
+  ClearMetricsPrefs();
+
+  // Also reset the update progress if the build version has changed.
+  if (!DeltaPerformer::ResetUpdateProgress(prefs_, false)) {
+    LOG(WARNING) << "Unable to reset the update progress.";
+  }
+}
+
+// Save the update start time. Reset the reboot count and attempt number if the
+// update isn't a resume; otherwise increment the attempt number.
+void UpdateAttempterAndroid::UpdatePrefsOnUpdateStart(bool is_resume) {
+  if (!is_resume) {
+    metrics_utils::SetNumReboots(0, prefs_);
+    metrics_utils::SetPayloadAttemptNumber(1, prefs_);
+  } else {
+    int64_t attempt_number =
+        metrics_utils::GetPersistedValue(kPrefsPayloadAttemptNumber, prefs_);
+    metrics_utils::SetPayloadAttemptNumber(attempt_number + 1, prefs_);
+  }
+  metrics_utils::SetUpdateTimestampStart(clock_->GetMonotonicTime(), prefs_);
+  metrics_utils::SetUpdateBootTimestampStart(clock_->GetBootTime(), prefs_);
+}
+
+void UpdateAttempterAndroid::ClearMetricsPrefs() {
+  CHECK(prefs_);
+  prefs_->Delete(kPrefsCurrentBytesDownloaded);
+  prefs_->Delete(kPrefsNumReboots);
+  prefs_->Delete(kPrefsSystemUpdatedMarker);
+  prefs_->Delete(kPrefsUpdateTimestampStart);
+  prefs_->Delete(kPrefsUpdateBootTimestampStart);
+}
+
+BootControlInterface::Slot UpdateAttempterAndroid::GetCurrentSlot() const {
+  return boot_control_->GetCurrentSlot();
+}
+
+BootControlInterface::Slot UpdateAttempterAndroid::GetTargetSlot() const {
+  return GetCurrentSlot() == 0 ? 1 : 0;
+}
+
+uint64_t UpdateAttempterAndroid::AllocateSpaceForPayload(
+    const std::string& metadata_filename,
+    const vector<string>& key_value_pair_headers,
+    brillo::ErrorPtr* error) {
+  DeltaArchiveManifest manifest;
+  if (!VerifyPayloadParseManifest(metadata_filename, &manifest, error)) {
+    return 0;
+  }
+  std::map<string, string> headers;
+  if (!ParseKeyValuePairHeaders(key_value_pair_headers, &headers, error)) {
+    return 0;
+  }
+
+  string payload_id = GetPayloadId(headers);
+  uint64_t required_size = 0;
+  if (!DeltaPerformer::PreparePartitionsForUpdate(prefs_,
+                                                  boot_control_,
+                                                  GetTargetSlot(),
+                                                  manifest,
+                                                  payload_id,
+                                                  &required_size)) {
+    if (required_size == 0) {
+      LogAndSetError(error, FROM_HERE, "Failed to allocate space for payload.");
+      return 0;
+    } else {
+      LOG(ERROR) << "Insufficient space for payload: " << required_size
+                 << " bytes";
+      return required_size;
+    }
+  }
+
+  LOG(INFO) << "Successfully allocated space for payload.";
+  return 0;
+}
+
+void UpdateAttempterAndroid::CleanupSuccessfulUpdate(
+    std::unique_ptr<CleanupSuccessfulUpdateCallbackInterface> callback,
+    brillo::ErrorPtr* error) {
+  if (cleanup_previous_update_code_.has_value()) {
+    LOG(INFO) << "CleanupSuccessfulUpdate has previously completed with "
+              << utils::ErrorCodeToString(*cleanup_previous_update_code_);
+    if (callback) {
+      callback->OnCleanupComplete(
+          static_cast<int32_t>(*cleanup_previous_update_code_));
+    }
+    return;
+  }
+  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)));
+  }
+  ScheduleCleanupPreviousUpdate();
+}
+
+void UpdateAttempterAndroid::ScheduleCleanupPreviousUpdate() {
+  // If a previous CleanupSuccessfulUpdate call has not finished, or an update
+  // is in progress, skip enqueueing the action.
+  if (processor_->IsRunning()) {
+    LOG(INFO) << "Already processing an update. CleanupPreviousUpdate should "
+              << "be done when the current update finishes.";
+    return;
+  }
+  LOG(INFO) << "Scheduling CleanupPreviousUpdateAction.";
+  auto action =
+      boot_control_->GetDynamicPartitionControl()
+          ->GetCleanupPreviousUpdateAction(boot_control_, prefs_, this);
+  processor_->EnqueueAction(std::move(action));
+  processor_->set_delegate(this);
+  SetStatusAndNotify(UpdateStatus::CLEANUP_PREVIOUS_UPDATE);
+  processor_->StartProcessing();
+}
+
+void UpdateAttempterAndroid::OnCleanupProgressUpdate(double progress) {
+  for (auto&& callback : cleanup_previous_update_callbacks_) {
+    callback->OnCleanupProgressUpdate(progress);
+  }
+}
+
+void UpdateAttempterAndroid::NotifyCleanupPreviousUpdateCallbacksAndClear() {
+  CHECK(cleanup_previous_update_code_.has_value());
+  for (auto&& callback : cleanup_previous_update_callbacks_) {
+    callback->OnCleanupComplete(
+        static_cast<int32_t>(*cleanup_previous_update_code_));
+  }
+  cleanup_previous_update_callbacks_.clear();
+}
+
+void UpdateAttempterAndroid::RemoveCleanupPreviousUpdateCallback(
+    CleanupSuccessfulUpdateCallbackInterface* callback) {
+  auto end_it =
+      std::remove_if(cleanup_previous_update_callbacks_.begin(),
+                     cleanup_previous_update_callbacks_.end(),
+                     [&](const auto& e) { return e.get() == callback; });
+  cleanup_previous_update_callbacks_.erase(
+      end_it, cleanup_previous_update_callbacks_.end());
+}
+
+}  // namespace chromeos_update_engine
diff --git a/aosp/update_attempter_android.h b/aosp/update_attempter_android.h
new file mode 100644
index 0000000..499f8f6
--- /dev/null
+++ b/aosp/update_attempter_android.h
@@ -0,0 +1,249 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_UPDATE_ATTEMPTER_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_UPDATE_ATTEMPTER_ANDROID_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <base/time/time.h>
+
+#include "update_engine/aosp/service_delegate_android_interface.h"
+#include "update_engine/client_library/include/update_engine/update_status.h"
+#include "update_engine/common/action_processor.h"
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/clock.h"
+#include "update_engine/common/daemon_state_interface.h"
+#include "update_engine/common/download_action.h"
+#include "update_engine/common/hardware_interface.h"
+#include "update_engine/common/metrics_reporter_interface.h"
+#include "update_engine/common/network_selector_interface.h"
+#include "update_engine/common/prefs_interface.h"
+#include "update_engine/common/service_observer_interface.h"
+#include "update_engine/metrics_utils.h"
+#include "update_engine/payload_consumer/filesystem_verifier_action.h"
+#include "update_engine/payload_consumer/postinstall_runner_action.h"
+
+namespace chromeos_update_engine {
+
+class UpdateAttempterAndroid
+    : public ServiceDelegateAndroidInterface,
+      public ActionProcessorDelegate,
+      public DownloadActionDelegate,
+      public FilesystemVerifyDelegate,
+      public PostinstallRunnerAction::DelegateInterface,
+      public CleanupPreviousUpdateActionDelegateInterface {
+ public:
+  using UpdateStatus = update_engine::UpdateStatus;
+
+  UpdateAttempterAndroid(DaemonStateInterface* daemon_state,
+                         PrefsInterface* prefs,
+                         BootControlInterface* boot_control_,
+                         HardwareInterface* hardware_);
+  ~UpdateAttempterAndroid() override;
+
+  // Further initialization to be done post construction.
+  void Init();
+
+  // ServiceDelegateAndroidInterface overrides.
+  bool ApplyPayload(const std::string& payload_url,
+                    int64_t payload_offset,
+                    int64_t payload_size,
+                    const std::vector<std::string>& key_value_pair_headers,
+                    brillo::ErrorPtr* 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;
+  bool VerifyPayloadApplicable(const std::string& metadata_filename,
+                               brillo::ErrorPtr* error) override;
+  uint64_t AllocateSpaceForPayload(
+      const std::string& metadata_filename,
+      const std::vector<std::string>& key_value_pair_headers,
+      brillo::ErrorPtr* error) override;
+  void CleanupSuccessfulUpdate(
+      std::unique_ptr<CleanupSuccessfulUpdateCallbackInterface> callback,
+      brillo::ErrorPtr* error) override;
+
+  // ActionProcessorDelegate methods:
+  void ProcessingDone(const ActionProcessor* processor,
+                      ErrorCode code) override;
+  void ProcessingStopped(const ActionProcessor* processor) override;
+  void ActionCompleted(ActionProcessor* processor,
+                       AbstractAction* action,
+                       ErrorCode code) override;
+
+  // DownloadActionDelegate overrides.
+  void BytesReceived(uint64_t bytes_progressed,
+                     uint64_t bytes_received,
+                     uint64_t total) override;
+  bool ShouldCancel(ErrorCode* cancel_reason) override;
+  void DownloadComplete() override;
+
+  // FilesystemVerifyDelegate overrides
+  void OnVerifyProgressUpdate(double progress) override;
+
+  // PostinstallRunnerAction::DelegateInterface
+  void ProgressUpdate(double progress) override;
+
+  // CleanupPreviousUpdateActionDelegateInterface
+  void OnCleanupProgressUpdate(double progress) override;
+
+ private:
+  friend class UpdateAttempterAndroidTest;
+
+  // Schedules an event loop callback to start the action processor. This is
+  // scheduled asynchronously to unblock the event loop.
+  void ScheduleProcessingStart();
+
+  // Notifies an update request completed with the given error |code| to all
+  // observers.
+  void TerminateUpdateAndNotify(ErrorCode error_code);
+
+  // Sets the status to the given |status| and notifies a status update to
+  // all observers.
+  void SetStatusAndNotify(UpdateStatus status);
+
+  // Helper method to construct the sequence of actions to be performed for
+  // applying an update using a given HttpFetcher. The ownership of |fetcher| is
+  // passed to this function.
+  void BuildUpdateActions(HttpFetcher* fetcher);
+
+  // Writes to the processing completed marker. Does nothing if
+  // |update_completed_marker_| is empty.
+  bool WriteUpdateCompletedMarker();
+
+  // Returns whether an update was completed in the current boot.
+  bool UpdateCompletedOnThisBoot();
+
+  // Prefs to use for metrics report
+  // |kPrefsPayloadAttemptNumber|: number of update attempts for the current
+  // payload_id.
+  // |KprefsNumReboots|: number of reboots when applying the current update.
+  // |kPrefsSystemUpdatedMarker|: end timestamp of the last successful update.
+  // |kPrefsUpdateTimestampStart|: start timestamp in monotonic time of the
+  // current update.
+  // |kPrefsUpdateBootTimestampStart|: start timestamp in boot time of
+  // the current update.
+  // |kPrefsCurrentBytesDownloaded|: number of bytes downloaded for the current
+  // payload_id.
+  // |kPrefsTotalBytesDownloaded|: number of bytes downloaded in total since
+  // the last successful update.
+
+  // Metrics report function to call:
+  //   |ReportUpdateAttemptMetrics|
+  //   |ReportSuccessfulUpdateMetrics|
+  // Prefs to update:
+  //   |kPrefsSystemUpdatedMarker|
+  void CollectAndReportUpdateMetricsOnUpdateFinished(ErrorCode error_code);
+
+  // Metrics report function to call:
+  //   |ReportAbnormallyTerminatedUpdateAttemptMetrics|
+  //   |ReportTimeToRebootMetrics|
+  // Prefs to update:
+  //   |kPrefsBootId|, |kPrefsPreviousVersion|
+  void UpdatePrefsAndReportUpdateMetricsOnReboot();
+
+  // Prefs to update:
+  //   |kPrefsPayloadAttemptNumber|, |kPrefsUpdateTimestampStart|,
+  //   |kPrefsUpdateBootTimestampStart|
+  void UpdatePrefsOnUpdateStart(bool is_resume);
+
+  // Prefs to delete:
+  //   |kPrefsNumReboots|, |kPrefsCurrentBytesDownloaded|
+  //   |kPrefsSystemUpdatedMarker|, |kPrefsUpdateTimestampStart|,
+  //   |kPrefsUpdateBootTimestampStart|
+  void ClearMetricsPrefs();
+
+  // Return source and target slots for update.
+  BootControlInterface::Slot GetCurrentSlot() const;
+  BootControlInterface::Slot GetTargetSlot() const;
+
+  // Helper of public VerifyPayloadApplicable. Return the parsed manifest in
+  // |manifest|.
+  static bool VerifyPayloadParseManifest(const std::string& metadata_filename,
+                                         DeltaArchiveManifest* manifest,
+                                         brillo::ErrorPtr* error);
+
+  // Enqueue and run a CleanupPreviousUpdateAction.
+  void ScheduleCleanupPreviousUpdate();
+
+  // Notify and clear |cleanup_previous_update_callbacks_|.
+  void NotifyCleanupPreviousUpdateCallbacksAndClear();
+
+  // Remove |callback| from |cleanup_previous_update_callbacks_|.
+  void RemoveCleanupPreviousUpdateCallback(
+      CleanupSuccessfulUpdateCallbackInterface* callback);
+
+  DaemonStateInterface* daemon_state_;
+
+  // DaemonStateAndroid pointers.
+  PrefsInterface* prefs_;
+  BootControlInterface* boot_control_;
+  HardwareInterface* hardware_;
+
+  // Last status notification timestamp used for throttling. Use monotonic
+  // TimeTicks to ensure that notifications are sent even if the system clock is
+  // set back in the middle of an update.
+  base::TimeTicks last_notify_time_;
+
+  // Only direct proxy supported.
+  DirectProxyResolver proxy_resolver_;
+
+  // The processor for running Actions.
+  std::unique_ptr<ActionProcessor> processor_;
+
+  // The InstallPlan used during the ongoing update.
+  InstallPlan install_plan_;
+
+  // For status:
+  UpdateStatus status_{UpdateStatus::IDLE};
+  double download_progress_{0.0};
+
+  // The offset in the payload file where the CrAU part starts.
+  int64_t base_offset_{0};
+
+  // Helper class to select the network to use during the update.
+  std::unique_ptr<NetworkSelectorInterface> network_selector_;
+
+  std::unique_ptr<ClockInterface> clock_;
+
+  std::unique_ptr<MetricsReporterInterface> metrics_reporter_;
+
+  ::android::base::unique_fd payload_fd_;
+
+  std::vector<std::unique_ptr<CleanupSuccessfulUpdateCallbackInterface>>
+      cleanup_previous_update_callbacks_;
+  // Result of previous CleanupPreviousUpdateAction. Nullopt If
+  // CleanupPreviousUpdateAction has not been executed.
+  std::optional<ErrorCode> cleanup_previous_update_code_{std::nullopt};
+
+  DISALLOW_COPY_AND_ASSIGN(UpdateAttempterAndroid);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_AOSP_UPDATE_ATTEMPTER_ANDROID_H_
diff --git a/aosp/update_attempter_android_unittest.cc b/aosp/update_attempter_android_unittest.cc
new file mode 100644
index 0000000..bb44450
--- /dev/null
+++ b/aosp/update_attempter_android_unittest.cc
@@ -0,0 +1,228 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/update_attempter_android.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <android-base/properties.h>
+#include <base/time/time.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/aosp/daemon_state_android.h"
+#include "update_engine/common/fake_boot_control.h"
+#include "update_engine/common/fake_clock.h"
+#include "update_engine/common/fake_hardware.h"
+#include "update_engine/common/fake_prefs.h"
+#include "update_engine/common/mock_action_processor.h"
+#include "update_engine/common/mock_metrics_reporter.h"
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+
+using base::Time;
+using base::TimeDelta;
+using testing::_;
+using update_engine::UpdateStatus;
+
+namespace chromeos_update_engine {
+
+class UpdateAttempterAndroidTest : public ::testing::Test {
+ protected:
+  UpdateAttempterAndroidTest() = default;
+
+  void SetUp() override {
+    clock_ = new FakeClock();
+    metrics_reporter_ = new testing::NiceMock<MockMetricsReporter>();
+    update_attempter_android_.metrics_reporter_.reset(metrics_reporter_);
+    update_attempter_android_.clock_.reset(clock_);
+    update_attempter_android_.processor_.reset(
+        new testing::NiceMock<MockActionProcessor>());
+  }
+
+  void SetUpdateStatus(update_engine::UpdateStatus status) {
+    update_attempter_android_.status_ = status;
+  }
+
+  void AddPayload(InstallPlan::Payload&& payload) {
+    update_attempter_android_.install_plan_.payloads.push_back(
+        std::move(payload));
+  }
+
+  UpdateAttempterAndroid update_attempter_android_{
+      &daemon_state_, &prefs_, &boot_control_, &hardware_};
+
+  DaemonStateAndroid daemon_state_;
+  FakePrefs prefs_;
+  FakeBootControl boot_control_;
+  FakeHardware hardware_;
+
+  FakeClock* clock_;
+  testing::NiceMock<MockMetricsReporter>* metrics_reporter_;
+};
+
+TEST_F(UpdateAttempterAndroidTest, UpdatePrefsSameBuildVersionOnInit) {
+  std::string build_version =
+      android::base::GetProperty("ro.build.version.incremental", "");
+  prefs_.SetString(kPrefsPreviousVersion, build_version);
+  prefs_.SetString(kPrefsBootId, "oldboot");
+  prefs_.SetInt64(kPrefsNumReboots, 1);
+
+  EXPECT_CALL(*metrics_reporter_, ReportTimeToReboot(_)).Times(0);
+  update_attempter_android_.Init();
+
+  // Check that the boot_id and reboot_count are updated.
+  std::string boot_id;
+  utils::GetBootId(&boot_id);
+  EXPECT_TRUE(prefs_.Exists(kPrefsBootId));
+  std::string prefs_boot_id;
+  EXPECT_TRUE(prefs_.GetString(kPrefsBootId, &prefs_boot_id));
+  EXPECT_EQ(boot_id, prefs_boot_id);
+
+  EXPECT_TRUE(prefs_.Exists(kPrefsNumReboots));
+  int64_t reboot_count;
+  EXPECT_TRUE(prefs_.GetInt64(kPrefsNumReboots, &reboot_count));
+  EXPECT_EQ(2, reboot_count);
+}
+
+TEST_F(UpdateAttempterAndroidTest, UpdatePrefsBuildVersionChangeOnInit) {
+  prefs_.SetString(kPrefsPreviousVersion, "00001");  // Set the fake version
+  prefs_.SetInt64(kPrefsPayloadAttemptNumber, 1);
+  prefs_.SetInt64(kPrefsSystemUpdatedMarker, 23456);
+
+  EXPECT_CALL(*metrics_reporter_,
+              ReportAbnormallyTerminatedUpdateAttemptMetrics())
+      .Times(1);
+
+  Time now = Time::FromInternalValue(34456);
+  clock_->SetMonotonicTime(now);
+  TimeDelta duration = now - Time::FromInternalValue(23456);
+  EXPECT_CALL(*metrics_reporter_, ReportTimeToReboot(duration.InMinutes()))
+      .Times(1);
+
+  update_attempter_android_.Init();
+  // Check that we reset the metric prefs.
+  EXPECT_FALSE(prefs_.Exists(kPrefsNumReboots));
+  EXPECT_FALSE(prefs_.Exists(kPrefsUpdateTimestampStart));
+  EXPECT_FALSE(prefs_.Exists(kPrefsSystemUpdatedMarker));
+  // PayloadAttemptNumber should persist across reboots.
+  EXPECT_TRUE(prefs_.Exists(kPrefsPayloadAttemptNumber));
+}
+
+TEST_F(UpdateAttempterAndroidTest, ReportMetricsOnUpdateTerminated) {
+  prefs_.SetInt64(kPrefsNumReboots, 3);
+  prefs_.SetInt64(kPrefsPayloadAttemptNumber, 2);
+  prefs_.SetString(kPrefsPreviousVersion, "56789");
+  prefs_.SetInt64(kPrefsUpdateBootTimestampStart, 10000);
+  prefs_.SetInt64(kPrefsUpdateTimestampStart, 12345);
+
+  Time boot_time = Time::FromInternalValue(22345);
+  Time up_time = Time::FromInternalValue(21345);
+  clock_->SetBootTime(boot_time);
+  clock_->SetMonotonicTime(up_time);
+  TimeDelta duration = boot_time - Time::FromInternalValue(10000);
+  TimeDelta duration_uptime = up_time - Time::FromInternalValue(12345);
+  EXPECT_CALL(
+      *metrics_reporter_,
+      ReportUpdateAttemptMetrics(_,
+                                 2,
+                                 _,
+                                 duration,
+                                 duration_uptime,
+                                 _,
+                                 metrics::AttemptResult::kUpdateSucceeded,
+                                 ErrorCode::kSuccess))
+      .Times(1);
+  EXPECT_CALL(*metrics_reporter_,
+              ReportSuccessfulUpdateMetrics(
+                  2, 0, _, 50, _, _, duration, duration_uptime, 3, _))
+      .Times(1);
+
+  // Adds a payload of 50 bytes to the InstallPlan.
+  InstallPlan::Payload payload;
+  payload.size = 50;
+  AddPayload(std::move(payload));
+  SetUpdateStatus(UpdateStatus::UPDATE_AVAILABLE);
+  update_attempter_android_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+
+  EXPECT_FALSE(prefs_.Exists(kPrefsNumReboots));
+  EXPECT_FALSE(prefs_.Exists(kPrefsPayloadAttemptNumber));
+  EXPECT_FALSE(prefs_.Exists(kPrefsUpdateTimestampStart));
+  EXPECT_TRUE(prefs_.Exists(kPrefsSystemUpdatedMarker));
+}
+
+TEST_F(UpdateAttempterAndroidTest, ReportMetricsForBytesDownloaded) {
+  // Check both prefs are updated correctly.
+  update_attempter_android_.BytesReceived(20, 50, 200);
+  EXPECT_EQ(
+      20,
+      metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, &prefs_));
+  EXPECT_EQ(
+      20,
+      metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, &prefs_));
+
+  EXPECT_CALL(*metrics_reporter_,
+              ReportUpdateAttemptDownloadMetrics(50, _, _, _, _))
+      .Times(1);
+  EXPECT_CALL(*metrics_reporter_,
+              ReportUpdateAttemptDownloadMetrics(40, _, _, _, _))
+      .Times(1);
+
+  int64_t total_bytes[kNumDownloadSources] = {};
+  total_bytes[kDownloadSourceHttpsServer] = 90;
+  EXPECT_CALL(*metrics_reporter_,
+              ReportSuccessfulUpdateMetrics(
+                  _,
+                  _,
+                  _,
+                  50,
+                  test_utils::DownloadSourceMatcher(total_bytes),
+                  80,
+                  _,
+                  _,
+                  _,
+                  _))
+      .Times(1);
+
+  // Adds a payload of 50 bytes to the InstallPlan.
+  InstallPlan::Payload payload;
+  payload.size = 50;
+  AddPayload(std::move(payload));
+
+  // The first update fails after receiving 50 bytes in total.
+  update_attempter_android_.BytesReceived(30, 50, 200);
+  update_attempter_android_.ProcessingDone(nullptr, ErrorCode::kError);
+  EXPECT_EQ(
+      0,
+      metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, &prefs_));
+  EXPECT_EQ(
+      50,
+      metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, &prefs_));
+
+  // The second update succeeds after receiving 40 bytes, which leads to a
+  // overhead of (90 - 50) / 50 = 80%.
+  update_attempter_android_.BytesReceived(40, 40, 50);
+  update_attempter_android_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+  // Both prefs should be cleared.
+  EXPECT_EQ(
+      0,
+      metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, &prefs_));
+  EXPECT_EQ(
+      0, metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, &prefs_));
+}
+
+}  // namespace chromeos_update_engine
diff --git a/aosp/update_engine_client_android.cc b/aosp/update_engine_client_android.cc
new file mode 100644
index 0000000..1a68cf4
--- /dev/null
+++ b/aosp/update_engine_client_android.cc
@@ -0,0 +1,314 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/command_line.h>
+#include <base/logging.h>
+#include <base/strings/string_split.h>
+#include <binder/IServiceManager.h>
+#include <binderwrapper/binder_wrapper.h>
+#include <brillo/binder_watcher.h>
+#include <brillo/daemons/daemon.h>
+#include <brillo/flag_helper.h>
+#include <brillo/message_loops/message_loop.h>
+#include <brillo/syslog_logging.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+
+#include "android/os/BnUpdateEngineCallback.h"
+#include "android/os/IUpdateEngine.h"
+#include "update_engine/client_library/include/update_engine/update_status.h"
+#include "update_engine/common/error_code.h"
+#include "update_engine/common/error_code_utils.h"
+#include "update_engine/update_status_utils.h"
+
+using android::binder::Status;
+
+namespace chromeos_update_engine {
+namespace internal {
+
+class UpdateEngineClientAndroid : public brillo::Daemon {
+ public:
+  UpdateEngineClientAndroid(int argc, char** argv) : argc_(argc), argv_(argv) {}
+
+  int ExitWhenIdle(const Status& status);
+  int ExitWhenIdle(int return_code);
+
+ private:
+  class UECallback : public android::os::BnUpdateEngineCallback {
+   public:
+    explicit UECallback(UpdateEngineClientAndroid* client) : client_(client) {}
+
+    // android::os::BnUpdateEngineCallback overrides.
+    Status onStatusUpdate(int status_code, float progress) override;
+    Status onPayloadApplicationComplete(int error_code) override;
+
+   private:
+    UpdateEngineClientAndroid* client_;
+  };
+
+  int OnInit() override;
+
+  // Called whenever the UpdateEngine daemon dies.
+  void UpdateEngineServiceDied();
+
+  static std::vector<android::String16> ParseHeaders(const std::string& arg);
+
+  // Copy of argc and argv passed to main().
+  int argc_;
+  char** argv_;
+
+  android::sp<android::os::IUpdateEngine> service_;
+  android::sp<android::os::BnUpdateEngineCallback> callback_;
+  android::sp<android::os::BnUpdateEngineCallback> cleanup_callback_;
+
+  brillo::BinderWatcher binder_watcher_;
+};
+
+Status UpdateEngineClientAndroid::UECallback::onStatusUpdate(int status_code,
+                                                             float progress) {
+  update_engine::UpdateStatus status =
+      static_cast<update_engine::UpdateStatus>(status_code);
+  LOG(INFO) << "onStatusUpdate(" << UpdateStatusToString(status) << " ("
+            << status_code << "), " << progress << ")";
+  return Status::ok();
+}
+
+Status UpdateEngineClientAndroid::UECallback::onPayloadApplicationComplete(
+    int error_code) {
+  ErrorCode code = static_cast<ErrorCode>(error_code);
+  LOG(INFO) << "onPayloadApplicationComplete(" << utils::ErrorCodeToString(code)
+            << " (" << error_code << "))";
+  client_->ExitWhenIdle(
+      (code == ErrorCode::kSuccess || code == ErrorCode::kUpdatedButNotActive)
+          ? EX_OK
+          : 1);
+  return Status::ok();
+}
+
+int UpdateEngineClientAndroid::OnInit() {
+  int ret = Daemon::OnInit();
+  if (ret != EX_OK)
+    return ret;
+
+  DEFINE_bool(update, false, "Start a new update, if no update in progress.");
+  DEFINE_string(payload,
+                "http://127.0.0.1:8080/payload",
+                "The URI to the update payload to use.");
+  DEFINE_int64(offset,
+               0,
+               "The offset in the payload where the CrAU update starts. "
+               "Used when --update is passed.");
+  DEFINE_int64(size,
+               0,
+               "The size of the CrAU part of the payload. If 0 is passed, it "
+               "will be autodetected. Used when --update is passed.");
+  DEFINE_string(headers,
+                "",
+                "A list of key-value pairs, one element of the list per line. "
+                "Used when --update or --allocate is passed.");
+
+  DEFINE_bool(verify,
+              false,
+              "Given payload metadata, verify if the payload is applicable.");
+  DEFINE_bool(allocate, false, "Given payload metadata, allocate space.");
+  DEFINE_string(metadata,
+                "/data/ota_package/metadata",
+                "The path to the update payload metadata. "
+                "Used when --verify or --allocate is passed.");
+
+  DEFINE_bool(suspend, false, "Suspend an ongoing update and exit.");
+  DEFINE_bool(resume, false, "Resume a suspended update.");
+  DEFINE_bool(cancel, false, "Cancel the ongoing update and exit.");
+  DEFINE_bool(reset_status, false, "Reset an already applied update and exit.");
+  DEFINE_bool(follow,
+              false,
+              "Follow status update changes until a final state is reached. "
+              "Exit status is 0 if the update succeeded, and 1 otherwise.");
+  DEFINE_bool(merge,
+              false,
+              "Wait for previous update to merge. "
+              "Only available after rebooting to new slot.");
+  // Boilerplate init commands.
+  base::CommandLine::Init(argc_, argv_);
+  brillo::FlagHelper::Init(argc_, argv_, "Android Update Engine Client");
+  if (argc_ == 1) {
+    LOG(ERROR) << "Nothing to do. Run with --help for help.";
+    return 1;
+  }
+
+  // Ensure there are no positional arguments.
+  const std::vector<std::string> positional_args =
+      base::CommandLine::ForCurrentProcess()->GetArgs();
+  if (!positional_args.empty()) {
+    LOG(ERROR) << "Found a positional argument '" << positional_args.front()
+               << "'. If you want to pass a value to a flag, pass it as "
+                  "--flag=value.";
+    return 1;
+  }
+
+  bool keep_running = false;
+  brillo::InitLog(brillo::kLogToStderr);
+
+  // Initialize a binder watcher early in the process before any interaction
+  // with the binder driver.
+  binder_watcher_.Init();
+
+  android::status_t status = android::getService(
+      android::String16("android.os.UpdateEngineService"), &service_);
+  if (status != android::OK) {
+    LOG(ERROR) << "Failed to get IUpdateEngine binder from service manager: "
+               << Status::fromStatusT(status).toString8();
+    return ExitWhenIdle(1);
+  }
+
+  if (FLAGS_suspend) {
+    return ExitWhenIdle(service_->suspend());
+  }
+
+  if (FLAGS_resume) {
+    return ExitWhenIdle(service_->resume());
+  }
+
+  if (FLAGS_cancel) {
+    return ExitWhenIdle(service_->cancel());
+  }
+
+  if (FLAGS_reset_status) {
+    return ExitWhenIdle(service_->resetStatus());
+  }
+
+  if (FLAGS_verify) {
+    bool applicable = false;
+    Status status = service_->verifyPayloadApplicable(
+        android::String16{FLAGS_metadata.data(), FLAGS_metadata.size()},
+        &applicable);
+    LOG(INFO) << "Payload is " << (applicable ? "" : "not ") << "applicable.";
+    return ExitWhenIdle(status);
+  }
+
+  if (FLAGS_allocate) {
+    auto headers = ParseHeaders(FLAGS_headers);
+    int64_t ret = 0;
+    Status status = service_->allocateSpaceForPayload(
+        android::String16{FLAGS_metadata.data(), FLAGS_metadata.size()},
+        headers,
+        &ret);
+    if (status.isOk()) {
+      if (ret == 0) {
+        LOG(INFO) << "Successfully allocated space for payload.";
+      } else {
+        LOG(INFO) << "Insufficient space; required " << ret << " bytes.";
+      }
+    } else {
+      LOG(INFO) << "Allocation failed.";
+    }
+    return ExitWhenIdle(status);
+  }
+
+  if (FLAGS_merge) {
+    // Register a callback object with the service.
+    cleanup_callback_ = new UECallback(this);
+    Status status = service_->cleanupSuccessfulUpdate(cleanup_callback_);
+    if (!status.isOk()) {
+      LOG(ERROR) << "Failed to call cleanupSuccessfulUpdate.";
+      return ExitWhenIdle(status);
+    }
+    keep_running = true;
+  }
+
+  if (FLAGS_follow) {
+    // Register a callback object with the service.
+    callback_ = new UECallback(this);
+    bool bound;
+    if (!service_->bind(callback_, &bound).isOk() || !bound) {
+      LOG(ERROR) << "Failed to bind() the UpdateEngine daemon.";
+      return 1;
+    }
+    keep_running = true;
+  }
+
+  if (FLAGS_update) {
+    auto and_headers = ParseHeaders(FLAGS_headers);
+    Status status = service_->applyPayload(
+        android::String16{FLAGS_payload.data(), FLAGS_payload.size()},
+        FLAGS_offset,
+        FLAGS_size,
+        and_headers);
+    if (!status.isOk())
+      return ExitWhenIdle(status);
+  }
+
+  if (!keep_running)
+    return ExitWhenIdle(EX_OK);
+
+  // When following updates status changes, exit if the update_engine daemon
+  // dies.
+  android::BinderWrapper::Create();
+  android::BinderWrapper::Get()->RegisterForDeathNotifications(
+      android::os::IUpdateEngine::asBinder(service_),
+      base::Bind(&UpdateEngineClientAndroid::UpdateEngineServiceDied,
+                 base::Unretained(this)));
+
+  return EX_OK;
+}
+
+int UpdateEngineClientAndroid::ExitWhenIdle(const Status& status) {
+  if (status.isOk())
+    return ExitWhenIdle(EX_OK);
+  LOG(ERROR) << status.toString8();
+  return ExitWhenIdle(status.exceptionCode());
+}
+
+int UpdateEngineClientAndroid::ExitWhenIdle(int return_code) {
+  auto delayed_exit = base::Bind(
+      &Daemon::QuitWithExitCode, base::Unretained(this), return_code);
+  if (!brillo::MessageLoop::current()->PostTask(delayed_exit))
+    return 1;
+  return EX_OK;
+}
+
+void UpdateEngineClientAndroid::UpdateEngineServiceDied() {
+  LOG(ERROR) << "UpdateEngineService died.";
+  QuitWithExitCode(1);
+}
+
+std::vector<android::String16> UpdateEngineClientAndroid::ParseHeaders(
+    const std::string& arg) {
+  std::vector<std::string> headers = base::SplitString(
+      arg, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  std::vector<android::String16> and_headers;
+  for (const auto& header : headers) {
+    and_headers.push_back(android::String16{header.data(), header.size()});
+  }
+  return and_headers;
+}
+
+}  // namespace internal
+}  // namespace chromeos_update_engine
+
+int main(int argc, char** argv) {
+  chromeos_update_engine::internal::UpdateEngineClientAndroid client(argc,
+                                                                     argv);
+  return client.Run();
+}