Mark ab/6881855 as merged

Bug: 172690556
Change-Id: I5a2f345d46f744263cfd29385e63f03aaf737ebf
diff --git a/Android.bp b/Android.bp
index 8e9ec17..ed704ff 100644
--- a/Android.bp
+++ b/Android.bp
@@ -82,6 +82,28 @@
     },
 }
 
+// libcow_operation_convert (type: library)
+// ========================================================
+cc_library {
+    name: "libcow_operation_convert",
+    host_supported: true,
+    recovery_available: true,
+    defaults: [
+        "ue_defaults",
+        "update_metadata-protos_exports",
+    ],
+    srcs: [
+        "common/cow_operation_convert.cc",
+    ],
+    static_libs: [
+        "libsnapshot_cow",
+        "update_metadata-protos",
+        "libpayload_extent_ranges",
+        "libbrotli",
+        "libz",
+    ],
+}
+
 // update_metadata-protos (type: static_library)
 // ========================================================
 // Protobufs.
@@ -124,6 +146,11 @@
         "libfec_rs",
         "libpuffpatch",
         "libverity_tree",
+        "libsnapshot_cow",
+        "libbrotli",
+        "libz",
+        "libpayload_extent_ranges",
+        "libcow_operation_convert",
     ],
     shared_libs: [
         "libbase",
@@ -143,6 +170,7 @@
     recovery_available: true,
 
     srcs: [
+        "aosp/platform_constants_android.cc",
         "common/action_processor.cc",
         "common/boot_control_stub.cc",
         "common/clock.cc",
@@ -156,17 +184,16 @@
         "common/http_fetcher.cc",
         "common/hwid_override.cc",
         "common/multi_range_http_fetcher.cc",
-        "common/platform_constants_android.cc",
         "common/prefs.cc",
         "common/proxy_resolver.cc",
         "common/subprocess.cc",
         "common/terminator.cc",
         "common/utils.cc",
+        "download_action.cc",
         "payload_consumer/bzip_extent_writer.cc",
         "payload_consumer/cached_file_descriptor.cc",
         "payload_consumer/certificate_parser_android.cc",
         "payload_consumer/delta_performer.cc",
-        "payload_consumer/download_action.cc",
         "payload_consumer/extent_reader.cc",
         "payload_consumer/extent_writer.cc",
         "payload_consumer/file_descriptor.cc",
@@ -179,6 +206,9 @@
         "payload_consumer/payload_metadata.cc",
         "payload_consumer/payload_verifier.cc",
         "payload_consumer/partition_writer.cc",
+        "payload_consumer/partition_writer_factory_android.cc",
+        "payload_consumer/vabc_partition_writer.cc",
+        "payload_consumer/snapshot_extent_writer.cc",
         "payload_consumer/postinstall_runner_action.cc",
         "payload_consumer/verity_writer_android.cc",
         "payload_consumer/xz_extent_writer.cc",
@@ -201,6 +231,7 @@
         "libpayload_consumer",
         "libsnapshot",
         "libsnapshot_cow",
+        "libz",
         "update_metadata-protos",
     ],
     shared_libs: [
@@ -242,10 +273,10 @@
     recovery_available: true,
 
     srcs: [
-        "boot_control_android.cc",
-        "cleanup_previous_update_action.cc",
-        "dynamic_partition_control_android.cc",
-        "dynamic_partition_utils.cc",
+        "aosp/boot_control_android.cc",
+        "aosp/cleanup_previous_update_action.cc",
+        "aosp/dynamic_partition_control_android.cc",
+        "aosp/dynamic_partition_utils.cc",
     ],
 }
 
@@ -301,17 +332,17 @@
 
     srcs: [
         ":libupdate_engine_aidl",
-        "binder_service_android.cc",
-        "binder_service_stable_android.cc",
+        "aosp/binder_service_android.cc",
+        "aosp/binder_service_stable_android.cc",
+        "aosp/daemon_android.cc",
+        "aosp/daemon_state_android.cc",
+        "aosp/hardware_android.cc",
+        "aosp/logging_android.cc",
+        "aosp/network_selector_android.cc",
+        "aosp/update_attempter_android.cc",
         "certificate_checker.cc",
-        "daemon_android.cc",
-        "daemon_state_android.cc",
-        "hardware_android.cc",
         "libcurl_http_fetcher.cc",
-        "logging_android.cc",
         "metrics_utils.cc",
-        "network_selector_android.cc",
-        "update_attempter_android.cc",
         "update_boot_flags_action.cc",
         "update_status_utils.cc",
     ],
@@ -333,7 +364,7 @@
         "otacerts",
     ],
 
-    srcs: ["main.cc", "metrics_reporter_android.cc"],
+    srcs: ["main.cc", "aosp/metrics_reporter_android.cc"],
     init_rc: ["update_engine.rc"],
 }
 
@@ -358,13 +389,13 @@
     include_dirs: ["external/cros/system_api/dbus"],
 
     srcs: [
-        "hardware_android.cc",
-        "logging_android.cc",
-        "metrics_reporter_stub.cc",
+        "aosp/hardware_android.cc",
+        "aosp/logging_android.cc",
+        "aosp/sideload_main.cc",
+        "aosp/update_attempter_android.cc",
+        "common/metrics_reporter_stub.cc",
+        "common/network_selector_stub.cc",
         "metrics_utils.cc",
-        "network_selector_stub.cc",
-        "sideload_main.cc",
-        "update_attempter_android.cc",
         "update_boot_flags_action.cc",
         "update_status_utils.cc",
     ],
@@ -434,8 +465,8 @@
 
     srcs: [
         ":libupdate_engine_aidl",
+        "aosp/update_engine_client_android.cc",
         "common/error_code_utils.cc",
-        "update_engine_client_android.cc",
         "update_status_utils.cc",
     ],
 }
@@ -478,6 +509,7 @@
         "ue_defaults",
     ],
     host_supported: true,
+    recovery_available: true,
     srcs: [
         "payload_generator/extent_ranges.cc",
     ],
@@ -501,6 +533,7 @@
         "payload_generator/block_mapping.cc",
         "payload_generator/boot_img_filesystem.cc",
         "payload_generator/bzip.cc",
+        "payload_generator/cow_size_estimator.cc",
         "payload_generator/deflate_utils.cc",
         "payload_generator/delta_diff_generator.cc",
         "payload_generator/delta_diff_utils.cc",
@@ -667,16 +700,20 @@
     test_suites: ["device-tests"],
 
     srcs: [
+        "aosp/dynamic_partition_control_android_unittest.cc",
+        "aosp/update_attempter_android_unittest.cc",
         "certificate_checker_unittest.cc",
         "common/action_pipe_unittest.cc",
         "common/action_processor_unittest.cc",
         "common/action_unittest.cc",
+        "common/cow_operation_convert_unittest.cc",
         "common/cpu_limiter_unittest.cc",
         "common/fake_prefs.cc",
         "common/file_fetcher_unittest.cc",
         "common/hash_calculator_unittest.cc",
         "common/http_fetcher_unittest.cc",
         "common/hwid_override_unittest.cc",
+        "common/metrics_reporter_stub.cc",
         "common/mock_http_fetcher.cc",
         "common/prefs_unittest.cc",
         "common/proxy_resolver_unittest.cc",
@@ -684,7 +721,7 @@
         "common/terminator_unittest.cc",
         "common/test_utils.cc",
         "common/utils_unittest.cc",
-        "dynamic_partition_control_android_unittest.cc",
+        "download_action_android_unittest.cc",
         "libcurl_http_fetcher_unittest.cc",
         "payload_consumer/bzip_extent_writer_unittest.cc",
         "payload_consumer/cached_file_descriptor_unittest.cc",
@@ -692,9 +729,9 @@
         "payload_consumer/delta_performer_integration_test.cc",
         "payload_consumer/delta_performer_unittest.cc",
         "payload_consumer/partition_writer_unittest.cc",
-        "payload_consumer/download_action_android_unittest.cc",
         "payload_consumer/extent_reader_unittest.cc",
         "payload_consumer/extent_writer_unittest.cc",
+        "payload_consumer/snapshot_extent_writer_unittest.cc",
         "payload_consumer/fake_file_descriptor.cc",
         "payload_consumer/file_descriptor_utils_unittest.cc",
         "payload_consumer/file_writer_unittest.cc",
@@ -724,9 +761,7 @@
         "payload_generator/squashfs_filesystem_unittest.cc",
         "payload_generator/zip_unittest.cc",
         "testrunner.cc",
-        "update_attempter_android_unittest.cc",
         "update_status_utils_unittest.cc",
-        "metrics_reporter_stub.cc",
     ],
 }
 
@@ -754,6 +789,13 @@
 // update_engine header library
 cc_library_headers {
     name: "libupdate_engine_headers",
+
+    // This header library is available to core and product modules.
+    // Right now, vendor_available is the only way to specify this.
+    // vendor modules should NOT use this library.
+    // TODO(b/150902910): change this to product_available.
+    vendor_available: true,
+
     export_include_dirs: ["."],
     apex_available: [
         "com.android.gki.*",
diff --git a/BUILD.gn b/BUILD.gn
index b7de9fc..6e282f5 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -44,6 +44,7 @@
       ":test_subprocess",
       ":update_engine-test_images",
       ":update_engine-testkeys",
+      ":update_engine-testkeys-ec",
       ":update_engine_test_libs",
       ":update_engine_unittests",
     ]
@@ -60,6 +61,7 @@
 pkg_config("target_defaults") {
   cflags_cc = [
     "-fno-strict-aliasing",
+    "-std=gnu++17",
     "-Wnon-virtual-dtor",
   ]
   cflags = [
@@ -75,6 +77,7 @@
     "__CHROMEOS__",
     "_FILE_OFFSET_BITS=64",
     "_POSIX_C_SOURCE=199309L",
+    "USE_CFM=${use.cfm}",
     "USE_DBUS=${use.dbus}",
     "USE_FEC=0",
     "USE_HWID_OVERRIDE=${use.hwid_override}",
@@ -92,7 +95,7 @@
   # NOSORT
   pkg_deps = [
     "libbrillo",
-    "libchrome-${libbase_ver}",
+    "libchrome",
 
     # system_api depends on protobuf (or protobuf-lite). It must appear
     # before protobuf here or the linker flags won't be in the right
@@ -140,17 +143,17 @@
     "common/http_fetcher.cc",
     "common/hwid_override.cc",
     "common/multi_range_http_fetcher.cc",
-    "common/platform_constants_chromeos.cc",
     "common/prefs.cc",
     "common/proxy_resolver.cc",
     "common/subprocess.cc",
     "common/terminator.cc",
     "common/utils.cc",
+    "cros/platform_constants_chromeos.cc",
+    "download_action.cc",
     "payload_consumer/bzip_extent_writer.cc",
     "payload_consumer/cached_file_descriptor.cc",
     "payload_consumer/certificate_parser_stub.cc",
     "payload_consumer/delta_performer.cc",
-    "payload_consumer/download_action.cc",
     "payload_consumer/extent_reader.cc",
     "payload_consumer/extent_writer.cc",
     "payload_consumer/file_descriptor.cc",
@@ -160,6 +163,8 @@
     "payload_consumer/install_plan.cc",
     "payload_consumer/mount_history.cc",
     "payload_consumer/partition_update_generator_stub.cc",
+    "payload_consumer/partition_writer_factory_chromeos.cc",
+    "payload_consumer/partition_writer.cc",
     "payload_consumer/payload_constants.cc",
     "payload_consumer/payload_metadata.cc",
     "payload_consumer/payload_verifier.cc",
@@ -192,31 +197,32 @@
 # with Omaha and expose a DBus daemon.
 static_library("libupdate_engine") {
   sources = [
-    "boot_control_chromeos.cc",
     "certificate_checker.cc",
-    "common_service.cc",
-    "connection_manager.cc",
-    "connection_utils.cc",
-    "daemon_chromeos.cc",
-    "dbus_connection.cc",
-    "dbus_service.cc",
-    "hardware_chromeos.cc",
-    "image_properties_chromeos.cc",
+    "common/connection_utils.cc",
+    "cros/boot_control_chromeos.cc",
+    "cros/common_service.cc",
+    "cros/connection_manager.cc",
+    "cros/daemon_chromeos.cc",
+    "cros/dbus_connection.cc",
+    "cros/dbus_service.cc",
+    "cros/hardware_chromeos.cc",
+    "cros/image_properties_chromeos.cc",
+    "cros/logging.cc",
+    "cros/metrics_reporter_omaha.cc",
+    "cros/omaha_request_action.cc",
+    "cros/omaha_request_builder_xml.cc",
+    "cros/omaha_request_params.cc",
+    "cros/omaha_response_handler_action.cc",
+    "cros/omaha_utils.cc",
+    "cros/p2p_manager.cc",
+    "cros/payload_state.cc",
+    "cros/power_manager_chromeos.cc",
+    "cros/real_system_state.cc",
+    "cros/requisition_util.cc",
+    "cros/shill_proxy.cc",
+    "cros/update_attempter.cc",
     "libcurl_http_fetcher.cc",
-    "logging.cc",
-    "metrics_reporter_omaha.cc",
     "metrics_utils.cc",
-    "omaha_request_action.cc",
-    "omaha_request_builder_xml.cc",
-    "omaha_request_params.cc",
-    "omaha_response_handler_action.cc",
-    "omaha_utils.cc",
-    "p2p_manager.cc",
-    "payload_state.cc",
-    "power_manager_chromeos.cc",
-    "real_system_state.cc",
-    "shill_proxy.cc",
-    "update_attempter.cc",
     "update_boot_flags_action.cc",
     "update_manager/boxed_value.cc",
     "update_manager/chromeos_policy.cc",
@@ -256,7 +262,7 @@
     "expat",
     "libcurl",
     "libdebugd-client",
-    "libmetrics-${libbase_ver}",
+    "libmetrics",
     "libpower_manager-client",
     "libsession_manager-client",
     "libshill-client",
@@ -275,7 +281,7 @@
   }
 
   if (use.chrome_network_proxy) {
-    sources += [ "chrome_browser_proxy_resolver.cc" ]
+    sources += [ "cros/chrome_browser_proxy_resolver.cc" ]
   }
 
   if (use.chrome_kiosk_app) {
@@ -284,8 +290,8 @@
 
   if (use.dlc) {
     sources += [
-      "dlcservice_chromeos.cc",
-      "excluder_chromeos.cc",
+      "cros/dlcservice_chromeos.cc",
+      "cros/excluder_chromeos.cc",
     ]
   } else {
     sources += [
@@ -320,8 +326,8 @@
 executable("update_engine_client") {
   sources = [
     "common/error_code_utils.cc",
-    "omaha_utils.cc",
-    "update_engine_client.cc",
+    "cros/omaha_utils.cc",
+    "cros/update_engine_client.cc",
   ]
   configs += [ ":target_defaults" ]
   deps = [ ":libupdate_engine_client" ]
@@ -338,6 +344,7 @@
     "payload_generator/block_mapping.cc",
     "payload_generator/boot_img_filesystem_stub.cc",
     "payload_generator/bzip.cc",
+    "payload_generator/cow_size_estimator_stub.cc",
     "payload_generator/deflate_utils.cc",
     "payload_generator/delta_diff_generator.cc",
     "payload_generator/delta_diff_utils.cc",
@@ -390,8 +397,8 @@
       "common/fake_prefs.cc",
       "common/mock_http_fetcher.cc",
       "common/test_utils.cc",
-      "fake_shill_proxy.cc",
-      "fake_system_state.cc",
+      "cros/fake_shill_proxy.cc",
+      "cros/fake_system_state.cc",
       "payload_consumer/fake_file_descriptor.cc",
       "payload_generator/fake_filesystem.cc",
       "update_manager/umtest_utils.cc",
@@ -417,9 +424,17 @@
     sources = [
       "unittest_key.pem",
       "unittest_key2.pem",
+      "unittest_key_RSA4096.pem",
     ]
   }
 
+  genopenssl_key("update_engine-testkeys-ec") {
+    openssl_pem_in_dir = "."
+    openssl_pem_out_dir = "include/update_engine"
+    openssl_pem_algorithm = "ec"
+    sources = [ "unittest_key_EC.pem" ]
+  }
+
   # Unpacks sample images used for testing.
   tar_bunzip2("update_engine-test_images") {
     image_out_dir = "."
@@ -456,7 +471,6 @@
   # Main unittest file.
   executable("update_engine_unittests") {
     sources = [
-      "boot_control_chromeos_unittest.cc",
       "certificate_checker_unittest.cc",
       "common/action_pipe_unittest.cc",
       "common/action_processor_unittest.cc",
@@ -470,24 +484,28 @@
       "common/subprocess_unittest.cc",
       "common/terminator_unittest.cc",
       "common/utils_unittest.cc",
-      "common_service_unittest.cc",
-      "connection_manager_unittest.cc",
-      "hardware_chromeos_unittest.cc",
-      "image_properties_chromeos_unittest.cc",
+      "cros/boot_control_chromeos_unittest.cc",
+      "cros/common_service_unittest.cc",
+      "cros/connection_manager_unittest.cc",
+      "cros/hardware_chromeos_unittest.cc",
+      "cros/image_properties_chromeos_unittest.cc",
+      "cros/metrics_reporter_omaha_unittest.cc",
+      "cros/omaha_request_action_unittest.cc",
+      "cros/omaha_request_builder_xml_unittest.cc",
+      "cros/omaha_request_params_unittest.cc",
+      "cros/omaha_response_handler_action_unittest.cc",
+      "cros/omaha_utils_unittest.cc",
+      "cros/p2p_manager_unittest.cc",
+      "cros/payload_state_unittest.cc",
+      "cros/requisition_util_unittest.cc",
+      "cros/update_attempter_unittest.cc",
+      "download_action_unittest.cc",
       "libcurl_http_fetcher_unittest.cc",
-      "metrics_reporter_omaha_unittest.cc",
       "metrics_utils_unittest.cc",
-      "omaha_request_action_unittest.cc",
-      "omaha_request_builder_xml_unittest.cc",
-      "omaha_request_params_unittest.cc",
-      "omaha_response_handler_action_unittest.cc",
-      "omaha_utils_unittest.cc",
-      "p2p_manager_unittest.cc",
       "payload_consumer/bzip_extent_writer_unittest.cc",
       "payload_consumer/cached_file_descriptor_unittest.cc",
       "payload_consumer/delta_performer_integration_test.cc",
       "payload_consumer/delta_performer_unittest.cc",
-      "payload_consumer/download_action_unittest.cc",
       "payload_consumer/extent_reader_unittest.cc",
       "payload_consumer/extent_writer_unittest.cc",
       "payload_consumer/file_descriptor_utils_unittest.cc",
@@ -512,12 +530,11 @@
       "payload_generator/payload_signer_unittest.cc",
       "payload_generator/squashfs_filesystem_unittest.cc",
       "payload_generator/zip_unittest.cc",
-      "payload_state_unittest.cc",
       "testrunner.cc",
-      "update_attempter_unittest.cc",
       "update_boot_flags_action_unittest.cc",
       "update_manager/boxed_value_unittest.cc",
       "update_manager/chromeos_policy_unittest.cc",
+      "update_manager/enterprise_device_policy_impl_unittest.cc",
       "update_manager/evaluation_context_unittest.cc",
       "update_manager/generic_variables_unittest.cc",
       "update_manager/prng_unittest.cc",
@@ -535,7 +552,7 @@
       "update_status_utils_unittest.cc",
     ]
     if (use.dlc) {
-      sources += [ "excluder_chromeos_unittest.cc" ]
+      sources += [ "cros/excluder_chromeos_unittest.cc" ]
     }
 
     # //common-mk:test should be on the top.
@@ -546,7 +563,7 @@
     ]
     pkg_deps = [
       "libbrillo-test",
-      "libchrome-test-${libbase_ver}",
+      "libchrome-test",
       "libdebugd-client-test",
       "libpower_manager-client-test",
       "libsession_manager-client-test",
@@ -570,7 +587,7 @@
     ]
     pkg_deps = [
       "libbrillo-test",
-      "libchrome-test-${libbase_ver}",
+      "libchrome-test",
     ]
     deps = [
       ":libupdate_engine",
@@ -578,14 +595,14 @@
     ]
   }
   executable("update_engine_omaha_request_action_fuzzer") {
-    sources = [ "omaha_request_action_fuzzer.cc" ]
+    sources = [ "cros/omaha_request_action_fuzzer.cc" ]
     configs += [
       "//common-mk/common_fuzzer",
       ":target_defaults",
     ]
     pkg_deps = [
       "libbrillo-test",
-      "libchrome-test-${libbase_ver}",
+      "libchrome-test",
     ]
     deps = [
       ":libupdate_engine",
diff --git a/binder_service_android.cc b/aosp/binder_service_android.cc
similarity index 98%
rename from binder_service_android.cc
rename to aosp/binder_service_android.cc
index 0c8bc2f..ed76c4a 100644
--- a/binder_service_android.cc
+++ b/aosp/binder_service_android.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/binder_service_android.h"
+#include "update_engine/aosp/binder_service_android.h"
 
 #include <memory>
 
@@ -24,7 +24,7 @@
 #include <brillo/errors/error.h>
 #include <utils/String8.h>
 
-#include "update_engine/binder_service_android_common.h"
+#include "update_engine/aosp/binder_service_android_common.h"
 
 using android::binder::Status;
 using android::os::IUpdateEngineCallback;
diff --git a/binder_service_android.h b/aosp/binder_service_android.h
similarity index 92%
rename from binder_service_android.h
rename to aosp/binder_service_android.h
index 5f28225..f41fbdf 100644
--- a/binder_service_android.h
+++ b/aosp/binder_service_android.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_BINDER_SERVICE_ANDROID_H_
-#define UPDATE_ENGINE_BINDER_SERVICE_ANDROID_H_
+#ifndef UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_H_
 
 #include <stdint.h>
 
@@ -28,8 +28,8 @@
 
 #include "android/os/BnUpdateEngine.h"
 #include "android/os/IUpdateEngineCallback.h"
-#include "update_engine/service_delegate_android_interface.h"
-#include "update_engine/service_observer_interface.h"
+#include "update_engine/aosp/service_delegate_android_interface.h"
+#include "update_engine/common/service_observer_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -96,4 +96,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_BINDER_SERVICE_ANDROID_H_
+#endif  // UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_H_
diff --git a/binder_service_android_common.h b/aosp/binder_service_android_common.h
similarity index 86%
rename from binder_service_android_common.h
rename to aosp/binder_service_android_common.h
index fc621d9..223b32e 100644
--- a/binder_service_android_common.h
+++ b/aosp/binder_service_android_common.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_BINDER_SERVICE_ANDROID_COMMON_H_
-#define UPDATE_ENGINE_BINDER_SERVICE_ANDROID_COMMON_H_
+#ifndef UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_COMMON_H_
+#define UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_COMMON_H_
 
 #include <string>
 #include <vector>
@@ -42,4 +42,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_BINDER_SERVICE_ANDROID_COMMON_H_
+#endif  // UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_COMMON_H_
diff --git a/binder_service_stable_android.cc b/aosp/binder_service_stable_android.cc
similarity index 96%
rename from binder_service_stable_android.cc
rename to aosp/binder_service_stable_android.cc
index a12b349..17b35ee 100644
--- a/binder_service_stable_android.cc
+++ b/aosp/binder_service_stable_android.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/binder_service_stable_android.h"
+#include "update_engine/aosp/binder_service_stable_android.h"
 
 #include <memory>
 
@@ -24,7 +24,7 @@
 #include <brillo/errors/error.h>
 #include <utils/String8.h>
 
-#include "update_engine/binder_service_android_common.h"
+#include "update_engine/aosp/binder_service_android_common.h"
 
 using android::binder::Status;
 using android::os::IUpdateEngineStableCallback;
diff --git a/binder_service_stable_android.h b/aosp/binder_service_stable_android.h
similarity index 89%
rename from binder_service_stable_android.h
rename to aosp/binder_service_stable_android.h
index 1667798..212afaa 100644
--- a/binder_service_stable_android.h
+++ b/aosp/binder_service_stable_android.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_BINDER_SERVICE_STABLE_ANDROID_H_
-#define UPDATE_ENGINE_BINDER_SERVICE_STABLE_ANDROID_H_
+#ifndef UPDATE_ENGINE_AOSP_BINDER_SERVICE_STABLE_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_BINDER_SERVICE_STABLE_ANDROID_H_
 
 #include <stdint.h>
 
@@ -28,8 +28,8 @@
 
 #include "android/os/BnUpdateEngineStable.h"
 #include "android/os/IUpdateEngineStableCallback.h"
-#include "update_engine/service_delegate_android_interface.h"
-#include "update_engine/service_observer_interface.h"
+#include "update_engine/aosp/service_delegate_android_interface.h"
+#include "update_engine/common/service_observer_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -82,4 +82,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_BINDER_SERVICE_STABLE_ANDROID_H_
+#endif  // UPDATE_ENGINE_AOSP_BINDER_SERVICE_STABLE_ANDROID_H_
diff --git a/boot_control_android.cc b/aosp/boot_control_android.cc
similarity index 97%
rename from boot_control_android.cc
rename to aosp/boot_control_android.cc
index dee5fa8..bda65be 100644
--- a/boot_control_android.cc
+++ b/aosp/boot_control_android.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/boot_control_android.h"
+#include "update_engine/aosp/boot_control_android.h"
 
 #include <memory>
 #include <utility>
@@ -25,8 +25,8 @@
 #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"
-#include "update_engine/dynamic_partition_control_android.h"
 
 using std::string;
 
diff --git a/boot_control_android.h b/aosp/boot_control_android.h
similarity index 91%
rename from boot_control_android.h
rename to aosp/boot_control_android.h
index 5009dbd..e288723 100644
--- a/boot_control_android.h
+++ b/aosp/boot_control_android.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_BOOT_CONTROL_ANDROID_H_
-#define UPDATE_ENGINE_BOOT_CONTROL_ANDROID_H_
+#ifndef UPDATE_ENGINE_AOSP_BOOT_CONTROL_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_BOOT_CONTROL_ANDROID_H_
 
 #include <map>
 #include <memory>
@@ -24,9 +24,9 @@
 #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"
-#include "update_engine/dynamic_partition_control_android.h"
 
 namespace chromeos_update_engine {
 
@@ -70,4 +70,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_BOOT_CONTROL_ANDROID_H_
+#endif  // UPDATE_ENGINE_AOSP_BOOT_CONTROL_ANDROID_H_
diff --git a/cleanup_previous_update_action.cc b/aosp/cleanup_previous_update_action.cc
similarity index 99%
rename from cleanup_previous_update_action.cc
rename to aosp/cleanup_previous_update_action.cc
index 89ed6f8..16cb9fe 100644
--- a/cleanup_previous_update_action.cc
+++ b/aosp/cleanup_previous_update_action.cc
@@ -13,7 +13,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //
-#include "update_engine/cleanup_previous_update_action.h"
+#include "update_engine/aosp/cleanup_previous_update_action.h"
 
 #include <chrono>  // NOLINT(build/c++11) -- for merge times
 #include <functional>
diff --git a/cleanup_previous_update_action.h b/aosp/cleanup_previous_update_action.h
similarity index 94%
rename from cleanup_previous_update_action.h
rename to aosp/cleanup_previous_update_action.h
index fe65e60..b93c557 100644
--- a/cleanup_previous_update_action.h
+++ b/aosp/cleanup_previous_update_action.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CLEANUP_PREVIOUS_UPDATE_ACTION_H_
-#define UPDATE_ENGINE_CLEANUP_PREVIOUS_UPDATE_ACTION_H_
+#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>
@@ -100,4 +100,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CLEANUP_PREVIOUS_UPDATE_ACTION_H_
+#endif  // UPDATE_ENGINE_AOSP_CLEANUP_PREVIOUS_UPDATE_ACTION_H_
diff --git a/daemon_android.cc b/aosp/daemon_android.cc
similarity index 95%
rename from daemon_android.cc
rename to aosp/daemon_android.cc
index 313d7dd..c102e3b 100644
--- a/daemon_android.cc
+++ b/aosp/daemon_android.cc
@@ -14,13 +14,13 @@
 // limitations under the License.
 //
 
-#include "update_engine/daemon_android.h"
+#include "update_engine/aosp/daemon_android.h"
 
 #include <sysexits.h>
 
 #include <binderwrapper/binder_wrapper.h>
 
-#include "update_engine/daemon_state_android.h"
+#include "update_engine/aosp/daemon_state_android.h"
 
 using std::unique_ptr;
 
diff --git a/daemon_android.h b/aosp/daemon_android.h
similarity index 80%
rename from daemon_android.h
rename to aosp/daemon_android.h
index f0c028e..38a8689 100644
--- a/daemon_android.h
+++ b/aosp/daemon_android.h
@@ -14,18 +14,18 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_DAEMON_ANDROID_H_
-#define UPDATE_ENGINE_DAEMON_ANDROID_H_
+#ifndef UPDATE_ENGINE_AOSP_DAEMON_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_DAEMON_ANDROID_H_
 
 #include <memory>
 
 #include <brillo/binder_watcher.h>
 
-#include "update_engine/binder_service_android.h"
-#include "update_engine/binder_service_stable_android.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"
-#include "update_engine/daemon_base.h"
-#include "update_engine/daemon_state_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -55,4 +55,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_DAEMON_ANDROID_H_
+#endif  // UPDATE_ENGINE_AOSP_DAEMON_ANDROID_H_
diff --git a/daemon_state_android.cc b/aosp/daemon_state_android.cc
similarity index 95%
rename from daemon_state_android.cc
rename to aosp/daemon_state_android.cc
index 3376e64..9bdd175 100644
--- a/daemon_state_android.cc
+++ b/aosp/daemon_state_android.cc
@@ -14,15 +14,15 @@
 // limitations under the License.
 //
 
-#include "update_engine/daemon_state_android.h"
+#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"
-#include "update_engine/update_attempter_android.h"
 
 namespace chromeos_update_engine {
 
diff --git a/daemon_state_android.h b/aosp/daemon_state_android.h
similarity index 84%
rename from daemon_state_android.h
rename to aosp/daemon_state_android.h
index 928a14e..dea3a23 100644
--- a/daemon_state_android.h
+++ b/aosp/daemon_state_android.h
@@ -14,20 +14,20 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_DAEMON_STATE_ANDROID_H_
-#define UPDATE_ENGINE_DAEMON_STATE_ANDROID_H_
+#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/daemon_state_interface.h"
-#include "update_engine/service_delegate_android_interface.h"
-#include "update_engine/service_observer_interface.h"
-#include "update_engine/update_attempter_android.h"
+#include "update_engine/common/service_observer_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -73,4 +73,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_DAEMON_STATE_ANDROID_H_
+#endif  // UPDATE_ENGINE_AOSP_DAEMON_STATE_ANDROID_H_
diff --git a/dynamic_partition_control_android.cc b/aosp/dynamic_partition_control_android.cc
similarity index 94%
rename from dynamic_partition_control_android.cc
rename to aosp/dynamic_partition_control_android.cc
index c9888ab..3ced3e0 100644
--- a/dynamic_partition_control_android.cc
+++ b/aosp/dynamic_partition_control_android.cc
@@ -14,9 +14,10 @@
 // limitations under the License.
 //
 
-#include "update_engine/dynamic_partition_control_android.h"
+#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>
@@ -36,13 +37,15 @@
 #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/cleanup_previous_update_action.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/dynamic_partition_utils.h"
 #include "update_engine/payload_consumer/delta_performer.h"
 
 using android::base::GetBoolProperty;
@@ -86,12 +89,6 @@
 // needs to be mapped, this timeout is longer than |kMapTimeout|.
 constexpr std::chrono::milliseconds kMapSnapshotTimeout{5000};
 
-#ifdef __ANDROID_RECOVERY__
-constexpr bool kIsRecovery = true;
-#else
-constexpr bool kIsRecovery = false;
-#endif
-
 DynamicPartitionControlAndroid::~DynamicPartitionControlAndroid() {
   Cleanup();
 }
@@ -276,9 +273,9 @@
   return true;
 }
 
-void DynamicPartitionControlAndroid::UnmapAllPartitions() {
+bool DynamicPartitionControlAndroid::UnmapAllPartitions() {
   if (mapped_devices_.empty()) {
-    return;
+    return false;
   }
   // UnmapPartitionOnDeviceMapper removes objects from mapped_devices_, hence
   // a copy is needed for the loop.
@@ -287,6 +284,7 @@
   for (const auto& partition_name : mapped) {
     ignore_result(UnmapPartitionOnDeviceMapper(partition_name));
   }
+  return true;
 }
 
 void DynamicPartitionControlAndroid::Cleanup() {
@@ -1085,7 +1083,7 @@
 }
 
 bool DynamicPartitionControlAndroid::IsRecovery() {
-  return kIsRecovery;
+  return constants::kIsRecovery;
 }
 
 static bool IsIncrementalUpdate(const DeltaArchiveManifest& manifest) {
@@ -1236,4 +1234,64 @@
   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,
+      .timeout_ms = kMapSnapshotTimeout};
+  // TODO(zhangkelvin) Open an APPEND mode CowWriter once there's an API to do
+  // it.
+  return snapshot_->OpenSnapshotWriter(params, std::move(source_path));
+}  // namespace chromeos_update_engine
+
+FileDescriptorPtr DynamicPartitionControlAndroid::OpenCowReader(
+    const std::string& unsuffixed_partition_name,
+    const std::optional<std::string>& source_path,
+    bool is_append) {
+  auto cow_writer =
+      OpenCowWriter(unsuffixed_partition_name, source_path, is_append);
+  if (cow_writer == nullptr) {
+    return nullptr;
+  }
+  cow_writer->InitializeAppend(kEndOfInstallLabel);
+  return cow_writer->OpenReader();
+}
+
+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(kMapSnapshotTimeout);
+}
+
+bool DynamicPartitionControlAndroid::IsDynamicPartition(
+    const std::string& partition_name) {
+  if (dynamic_partition_list_.empty() &&
+      GetDynamicPartitionsFeatureFlag().IsEnabled()) {
+    CHECK(ListDynamicPartitionsForSlot(source_slot_, &dynamic_partition_list_));
+  }
+  return std::find(dynamic_partition_list_.begin(),
+                   dynamic_partition_list_.end(),
+                   partition_name) != dynamic_partition_list_.end();
+}
 }  // namespace chromeos_update_engine
diff --git a/dynamic_partition_control_android.h b/aosp/dynamic_partition_control_android.h
similarity index 91%
rename from dynamic_partition_control_android.h
rename to aosp/dynamic_partition_control_android.h
index f3805f0..4a2b114 100644
--- a/dynamic_partition_control_android.h
+++ b/aosp/dynamic_partition_control_android.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_DYNAMIC_PARTITION_CONTROL_ANDROID_H_
-#define UPDATE_ENGINE_DYNAMIC_PARTITION_CONTROL_ANDROID_H_
+#ifndef UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_CONTROL_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_CONTROL_ANDROID_H_
 
 #include <memory>
 #include <set>
@@ -25,6 +25,7 @@
 #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"
 
@@ -82,6 +83,20 @@
                           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;
+  FileDescriptorPtr OpenCowReader(const std::string& unsuffixed_partition_name,
+                                  const std::optional<std::string>&,
+                                  bool is_append = false) override;
+
+  bool UnmapAllPartitions() override;
+
+  bool IsDynamicPartition(const std::string& part_name) override;
+
  protected:
   // These functions are exposed for testing.
 
@@ -193,11 +208,14 @@
       const DeltaArchiveManifest& manifest,
       bool delete_source);
 
+  bool MapAllPartitions() override;
+
  private:
   friend class DynamicPartitionControlAndroidTest;
   friend class SnapshotPartitionTestP;
 
-  void UnmapAllPartitions();
+  std::optional<base::FilePath> GetSuperDevice();
+
   bool MapPartitionInternal(const std::string& super_device,
                             const std::string& target_partition_name,
                             uint32_t slot,
@@ -287,10 +305,11 @@
   bool is_target_dynamic_ = false;
   uint32_t source_slot_ = UINT32_MAX;
   uint32_t target_slot_ = UINT32_MAX;
+  std::vector<std::string> dynamic_partition_list_;
 
   DISALLOW_COPY_AND_ASSIGN(DynamicPartitionControlAndroid);
 };
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_DYNAMIC_PARTITION_CONTROL_ANDROID_H_
+#endif  // UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_CONTROL_ANDROID_H_
diff --git a/dynamic_partition_control_android_unittest.cc b/aosp/dynamic_partition_control_android_unittest.cc
similarity index 97%
rename from dynamic_partition_control_android_unittest.cc
rename to aosp/dynamic_partition_control_android_unittest.cc
index 223e177..7e751db 100644
--- a/dynamic_partition_control_android_unittest.cc
+++ b/aosp/dynamic_partition_control_android_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/dynamic_partition_control_android.h"
+#include "update_engine/aosp/dynamic_partition_control_android.h"
 
 #include <set>
 #include <vector>
@@ -26,15 +26,14 @@
 #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_android.h"
 #include "update_engine/common/mock_prefs.h"
 #include "update_engine/common/test_utils.h"
-#include "update_engine/dynamic_partition_test_utils.h"
-#include "update_engine/mock_dynamic_partition_control.h"
 
 using android::dm::DmDeviceState;
 using android::snapshot::MockSnapshotManager;
 using chromeos_update_engine::test_utils::ScopedLoopbackDeviceBinder;
-using chromeos_update_engine::test_utils::ScopedTempFile;
 using std::string;
 using testing::_;
 using testing::AnyNumber;
@@ -793,11 +792,11 @@
 }
 
 TEST_F(DynamicPartitionControlAndroidTest, IsAvbNotEnabledInFstab) {
-  // clang-format off
   std::string fstab_content =
-      "system /postinstall ext4 ro,nosuid,nodev,noexec slotselect_other,logical\n"  // NOLINT(whitespace/line_length)
-      "/dev/block/by-name/system /postinstall ext4 ro,nosuid,nodev,noexec slotselect_other\n";  // NOLINT(whitespace/line_length)
-  // clang-format on
+      "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()),
@@ -805,10 +804,9 @@
 }
 
 TEST_F(DynamicPartitionControlAndroidTest, IsAvbEnabledInFstab) {
-  // clang-format off
   std::string fstab_content =
-      "system /postinstall ext4 ro,nosuid,nodev,noexec slotselect_other,logical,avb_keys=/foo\n";  // NOLINT(whitespace/line_length)
-  // clang-format on
+      "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()),
diff --git a/dynamic_partition_test_utils.h b/aosp/dynamic_partition_test_utils.h
similarity index 97%
rename from dynamic_partition_test_utils.h
rename to aosp/dynamic_partition_test_utils.h
index d701dce..c7be1cb 100644
--- a/dynamic_partition_test_utils.h
+++ b/aosp/dynamic_partition_test_utils.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_DYNAMIC_PARTITION_TEST_UTILS_H_
-#define UPDATE_ENGINE_DYNAMIC_PARTITION_TEST_UTILS_H_
+#ifndef UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_TEST_UTILS_H_
+#define UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_TEST_UTILS_H_
 
 #include <stdint.h>
 
@@ -285,4 +285,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_DYNAMIC_PARTITION_TEST_UTILS_H_
+#endif  // UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_TEST_UTILS_H_
diff --git a/dynamic_partition_utils.cc b/aosp/dynamic_partition_utils.cc
similarity index 95%
rename from dynamic_partition_utils.cc
rename to aosp/dynamic_partition_utils.cc
index f9bd886..6b77a45 100644
--- a/dynamic_partition_utils.cc
+++ b/aosp/dynamic_partition_utils.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/dynamic_partition_utils.h"
+#include "update_engine/aosp/dynamic_partition_utils.h"
 
 #include <vector>
 
diff --git a/dynamic_partition_utils.h b/aosp/dynamic_partition_utils.h
similarity index 85%
rename from dynamic_partition_utils.h
rename to aosp/dynamic_partition_utils.h
index 09fce00..5a51d5e 100644
--- a/dynamic_partition_utils.h
+++ b/aosp/dynamic_partition_utils.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_DYNAMIC_PARTITION_UTILS_H_
-#define UPDATE_ENGINE_DYNAMIC_PARTITION_UTILS_H_
+#ifndef UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_UTILS_H_
+#define UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_UTILS_H_
 
 #include <string>
 
@@ -30,4 +30,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_DYNAMIC_PARTITION_UTILS_H_
+#endif  // UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_UTILS_H_
diff --git a/hardware_android.cc b/aosp/hardware_android.cc
similarity index 94%
rename from hardware_android.cc
rename to aosp/hardware_android.cc
index a659bf6..6f884d4 100644
--- a/hardware_android.cc
+++ b/aosp/hardware_android.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/hardware_android.h"
+#include "update_engine/aosp/hardware_android.h"
 
 #include <sys/types.h>
 
@@ -45,8 +45,6 @@
 // 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 kPropBootBootloader[] = "ro.boot.bootloader";
-const char kPropBootBaseband[] = "ro.boot.baseband";
 const char kPropProductManufacturer[] = "ro.product.manufacturer";
 const char kPropBootHardwareSKU[] = "ro.boot.hardware.sku";
 const char kPropBootRevision[] = "ro.boot.revision";
@@ -137,14 +135,6 @@
   return manufacturer + ":" + sku + ":" + revision;
 }
 
-string HardwareAndroid::GetFirmwareVersion() const {
-  return GetProperty(kPropBootBootloader, "");
-}
-
-string HardwareAndroid::GetECVersion() const {
-  return GetProperty(kPropBootBaseband, "");
-}
-
 string HardwareAndroid::GetDeviceRequisition() const {
   LOG(WARNING) << "STUB: Getting requisition is not supported.";
   return "";
@@ -241,9 +231,11 @@
 }
 
 void HardwareAndroid::SetWarmReset(bool warm_reset) {
-  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;
+  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;
+    }
   }
 }
 
diff --git a/hardware_android.h b/aosp/hardware_android.h
similarity index 91%
rename from hardware_android.h
rename to aosp/hardware_android.h
index d8fbbbe..5ffd7c5 100644
--- a/hardware_android.h
+++ b/aosp/hardware_android.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_HARDWARE_ANDROID_H_
-#define UPDATE_ENGINE_HARDWARE_ANDROID_H_
+#ifndef UPDATE_ENGINE_AOSP_HARDWARE_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_HARDWARE_ANDROID_H_
 
 #include <string>
 #include <string_view>
@@ -42,8 +42,6 @@
   bool IsOOBEEnabled() const override;
   bool IsOOBEComplete(base::Time* out_time_of_oobe) const override;
   std::string GetHardwareClass() const override;
-  std::string GetFirmwareVersion() const override;
-  std::string GetECVersion() const override;
   std::string GetDeviceRequisition() const override;
   int GetMinKernelKeyVersion() const override;
   int GetMinFirmwareKeyVersion() const override;
@@ -72,4 +70,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_HARDWARE_ANDROID_H_
+#endif  // UPDATE_ENGINE_AOSP_HARDWARE_ANDROID_H_
diff --git a/logging_android.cc b/aosp/logging_android.cc
similarity index 100%
rename from logging_android.cc
rename to aosp/logging_android.cc
diff --git a/metrics_reporter_android.cc b/aosp/metrics_reporter_android.cc
similarity index 98%
rename from metrics_reporter_android.cc
rename to aosp/metrics_reporter_android.cc
index d8fa6e5..ea3bb6d 100644
--- a/metrics_reporter_android.cc
+++ b/aosp/metrics_reporter_android.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/metrics_reporter_android.h"
+#include "update_engine/aosp/metrics_reporter_android.h"
 
 #include <stdint.h>
 
diff --git a/metrics_reporter_android.h b/aosp/metrics_reporter_android.h
similarity index 92%
rename from metrics_reporter_android.h
rename to aosp/metrics_reporter_android.h
index 7770619..4a173bf 100644
--- a/metrics_reporter_android.h
+++ b/aosp/metrics_reporter_android.h
@@ -14,14 +14,14 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_METRICS_REPORTER_ANDROID_H_
-#define UPDATE_ENGINE_METRICS_REPORTER_ANDROID_H_
+#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/metrics_constants.h"
-#include "update_engine/metrics_reporter_interface.h"
+#include "update_engine/common/metrics_constants.h"
+#include "update_engine/common/metrics_reporter_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -98,4 +98,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_METRICS_REPORTER_ANDROID_H_
+#endif  // UPDATE_ENGINE_AOSP_METRICS_REPORTER_ANDROID_H_
diff --git a/mock_dynamic_partition_control.h b/aosp/mock_dynamic_partition_control_android.h
similarity index 82%
rename from mock_dynamic_partition_control.h
rename to aosp/mock_dynamic_partition_control_android.h
index e85df32..8d8ddb3 100644
--- a/mock_dynamic_partition_control.h
+++ b/aosp/mock_dynamic_partition_control_android.h
@@ -22,9 +22,13 @@
 
 #include <gmock/gmock.h>
 
+#include <libsnapshot/cow_writer.h>
+#include <libsnapshot/snapshot_writer.h>
+
+#include "payload_consumer/file_descriptor.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"
-#include "update_engine/dynamic_partition_control_android.h"
 
 namespace chromeos_update_engine {
 
@@ -81,6 +85,21 @@
               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(FileDescriptorPtr,
+              OpenCowReader,
+              (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));
+  MOCK_METHOD(bool, IsDynamicPartition, (const std::string&), (override));
 
   void set_fake_mapped_devices(const std::set<std::string>& fake) override {
     DynamicPartitionControlAndroid::set_fake_mapped_devices(fake);
diff --git a/network_selector_android.cc b/aosp/network_selector_android.cc
similarity index 88%
rename from network_selector_android.cc
rename to aosp/network_selector_android.cc
index 55ba799..a7db415 100644
--- a/network_selector_android.cc
+++ b/aosp/network_selector_android.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/network_selector_android.h"
+#include "update_engine/aosp/network_selector_android.h"
 
 #include <memory>
 
@@ -25,14 +25,14 @@
 
 namespace network {
 
-// Factory defined in network_selector.h.
+// Factory defined in common/network_selector.h.
 std::unique_ptr<NetworkSelectorInterface> CreateNetworkSelector() {
   return std::make_unique<NetworkSelectorAndroid>();
 }
 
 }  // namespace network
 
-// Defined in network_selector_interface.h.
+// Defined in common/network_selector_interface.h.
 const NetworkId kDefaultNetworkId = NETWORK_UNSPECIFIED;
 
 bool NetworkSelectorAndroid::SetProcessNetwork(NetworkId network_id) {
diff --git a/network_selector_android.h b/aosp/network_selector_android.h
similarity index 81%
rename from network_selector_android.h
rename to aosp/network_selector_android.h
index 135536c..b79d1b3 100644
--- a/network_selector_android.h
+++ b/aosp/network_selector_android.h
@@ -14,12 +14,12 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_NETWORK_SELECTOR_ANDROID_H_
-#define UPDATE_ENGINE_NETWORK_SELECTOR_ANDROID_H_
+#ifndef UPDATE_ENGINE_AOSP_NETWORK_SELECTOR_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_NETWORK_SELECTOR_ANDROID_H_
 
 #include <base/macros.h>
 
-#include "update_engine/network_selector_interface.h"
+#include "update_engine/common/network_selector_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -37,4 +37,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_NETWORK_SELECTOR_ANDROID_H_
+#endif  // UPDATE_ENGINE_AOSP_NETWORK_SELECTOR_ANDROID_H_
diff --git a/common/platform_constants_android.cc b/aosp/platform_constants_android.cc
similarity index 100%
rename from common/platform_constants_android.cc
rename to aosp/platform_constants_android.cc
diff --git a/service_delegate_android_interface.h b/aosp/service_delegate_android_interface.h
similarity index 95%
rename from service_delegate_android_interface.h
rename to aosp/service_delegate_android_interface.h
index 34a9712..3c28794 100644
--- a/service_delegate_android_interface.h
+++ b/aosp/service_delegate_android_interface.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_SERVICE_DELEGATE_ANDROID_INTERFACE_H_
-#define UPDATE_ENGINE_SERVICE_DELEGATE_ANDROID_INTERFACE_H_
+#ifndef UPDATE_ENGINE_AOSP_SERVICE_DELEGATE_ANDROID_INTERFACE_H_
+#define UPDATE_ENGINE_AOSP_SERVICE_DELEGATE_ANDROID_INTERFACE_H_
 
 #include <inttypes.h>
 
@@ -124,4 +124,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_SERVICE_DELEGATE_ANDROID_INTERFACE_H_
+#endif  // UPDATE_ENGINE_AOSP_SERVICE_DELEGATE_ANDROID_INTERFACE_H_
diff --git a/sideload_main.cc b/aosp/sideload_main.cc
similarity index 98%
rename from sideload_main.cc
rename to aosp/sideload_main.cc
index 27967cd..3cbc0c7 100644
--- a/sideload_main.cc
+++ b/aosp/sideload_main.cc
@@ -28,15 +28,15 @@
 #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"
-#include "update_engine/logging.h"
-#include "update_engine/update_attempter_android.h"
 
 using std::string;
 using std::vector;
diff --git a/update_attempter_android.cc b/aosp/update_attempter_android.cc
similarity index 98%
rename from update_attempter_android.cc
rename to aosp/update_attempter_android.cc
index 3578d95..7ed3249 100644
--- a/update_attempter_android.cc
+++ b/aosp/update_attempter_android.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/update_attempter_android.h"
+#include "update_engine/aosp/update_attempter_android.h"
 
 #include <algorithm>
 #include <map>
@@ -31,18 +31,18 @@
 #include <brillo/strings/string_utils.h>
 #include <log/log_safetynet.h>
 
-#include "update_engine/cleanup_previous_update_action.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/daemon_state_interface.h"
-#include "update_engine/metrics_reporter_interface.h"
 #include "update_engine/metrics_utils.h"
-#include "update_engine/network_selector.h"
 #include "update_engine/payload_consumer/certificate_parser_interface.h"
 #include "update_engine/payload_consumer/delta_performer.h"
-#include "update_engine/payload_consumer/download_action.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"
diff --git a/update_attempter_android.h b/aosp/update_attempter_android.h
similarity index 94%
rename from update_attempter_android.h
rename to aosp/update_attempter_android.h
index 55003a0..499f8f6 100644
--- a/update_attempter_android.h
+++ b/aosp/update_attempter_android.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_UPDATE_ATTEMPTER_ANDROID_H_
-#define UPDATE_ENGINE_UPDATE_ATTEMPTER_ANDROID_H_
+#ifndef UPDATE_ENGINE_AOSP_UPDATE_ATTEMPTER_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_UPDATE_ATTEMPTER_ANDROID_H_
 
 #include <stdint.h>
 
@@ -26,21 +26,21 @@
 #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/daemon_state_interface.h"
-#include "update_engine/metrics_reporter_interface.h"
+#include "update_engine/common/service_observer_interface.h"
 #include "update_engine/metrics_utils.h"
-#include "update_engine/network_selector_interface.h"
-#include "update_engine/payload_consumer/download_action.h"
 #include "update_engine/payload_consumer/filesystem_verifier_action.h"
 #include "update_engine/payload_consumer/postinstall_runner_action.h"
-#include "update_engine/service_delegate_android_interface.h"
-#include "update_engine/service_observer_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -246,4 +246,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_UPDATE_ATTEMPTER_ANDROID_H_
+#endif  // UPDATE_ENGINE_AOSP_UPDATE_ATTEMPTER_ANDROID_H_
diff --git a/update_attempter_android_unittest.cc b/aosp/update_attempter_android_unittest.cc
similarity index 97%
rename from update_attempter_android_unittest.cc
rename to aosp/update_attempter_android_unittest.cc
index 721b735..bb44450 100644
--- a/update_attempter_android_unittest.cc
+++ b/aosp/update_attempter_android_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/update_attempter_android.h"
+#include "update_engine/aosp/update_attempter_android.h"
 
 #include <memory>
 #include <string>
@@ -24,15 +24,15 @@
 #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"
-#include "update_engine/daemon_state_android.h"
-#include "update_engine/mock_metrics_reporter.h"
 
 using base::Time;
 using base::TimeDelta;
diff --git a/update_engine_client_android.cc b/aosp/update_engine_client_android.cc
similarity index 100%
rename from update_engine_client_android.cc
rename to aosp/update_engine_client_android.cc
diff --git a/client_library/client_dbus.cc b/client_library/client_dbus.cc
index 8e9a7fd..30ad78c 100644
--- a/client_library/client_dbus.cc
+++ b/client_library/client_dbus.cc
@@ -16,7 +16,7 @@
 
 #include "update_engine/client_library/client_dbus.h"
 
-#include <base/message_loop/message_loop.h>
+#include <base/message_loop/message_loop_current.h>
 
 #include <memory>
 
diff --git a/common/cleanup_previous_update_action_delegate.h b/common/cleanup_previous_update_action_delegate.h
index 7dad9c5..8daf860 100644
--- a/common/cleanup_previous_update_action_delegate.h
+++ b/common/cleanup_previous_update_action_delegate.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CLEANUP_PREVIOUS_UPDATE_ACTION_DELEGETE_H_
-#define UPDATE_ENGINE_CLEANUP_PREVIOUS_UPDATE_ACTION_DELEGETE_H_
+#ifndef UPDATE_ENGINE_COMMON_CLEANUP_PREVIOUS_UPDATE_ACTION_DELEGETE_H_
+#define UPDATE_ENGINE_COMMON_CLEANUP_PREVIOUS_UPDATE_ACTION_DELEGETE_H_
 
 namespace chromeos_update_engine {
 
@@ -29,4 +29,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CLEANUP_PREVIOUS_UPDATE_ACTION_DELEGETE_H_
+#endif  // UPDATE_ENGINE_COMMON_CLEANUP_PREVIOUS_UPDATE_ACTION_DELEGETE_H_
diff --git a/connection_utils.cc b/common/connection_utils.cc
similarity index 97%
rename from connection_utils.cc
rename to common/connection_utils.cc
index 5af7341..44e5128 100644
--- a/connection_utils.cc
+++ b/common/connection_utils.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/connection_utils.h"
+#include "update_engine/common/connection_utils.h"
 
 #include <shill/dbus-constants.h>
 
diff --git a/connection_utils.h b/common/connection_utils.h
similarity index 89%
rename from connection_utils.h
rename to common/connection_utils.h
index 4e71fcf..5d63fb2 100644
--- a/connection_utils.h
+++ b/common/connection_utils.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CONNECTION_UTILS_H_
-#define UPDATE_ENGINE_CONNECTION_UTILS_H_
+#ifndef UPDATE_ENGINE_COMMON_CONNECTION_UTILS_H_
+#define UPDATE_ENGINE_COMMON_CONNECTION_UTILS_H_
 
 #include <string>
 
@@ -47,4 +47,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CONNECTION_UTILS_H_
+#endif  // UPDATE_ENGINE_COMMON_CONNECTION_UTILS_H_
diff --git a/common/constants.cc b/common/constants.cc
index c85ba54..8883668 100644
--- a/common/constants.cc
+++ b/common/constants.cc
@@ -64,6 +64,8 @@
 const char kPrefsP2PFirstAttemptTimestamp[] = "p2p-first-attempt-timestamp";
 const char kPrefsP2PNumAttempts[] = "p2p-num-attempts";
 const char kPrefsPayloadAttemptNumber[] = "payload-attempt-number";
+const char kPrefsTestUpdateCheckIntervalTimeout[] =
+    "test-update-check-interval-timeout";
 // Keep |kPrefsPingActive| in sync with |kDlcMetadataFilePingActive| in
 // dlcservice.
 const char kPrefsPingActive[] = "active";
diff --git a/common/constants.h b/common/constants.h
index 7170201..f468b55 100644
--- a/common/constants.h
+++ b/common/constants.h
@@ -17,6 +17,8 @@
 #ifndef UPDATE_ENGINE_COMMON_CONSTANTS_H_
 #define UPDATE_ENGINE_COMMON_CONSTANTS_H_
 
+#include <cstdint>
+
 namespace chromeos_update_engine {
 
 // The root path of all exclusion prefs.
@@ -67,6 +69,7 @@
 extern const char kPrefsP2PFirstAttemptTimestamp[];
 extern const char kPrefsP2PNumAttempts[];
 extern const char kPrefsPayloadAttemptNumber[];
+extern const char kPrefsTestUpdateCheckIntervalTimeout[];
 extern const char kPrefsPingActive[];
 extern const char kPrefsPingLastActive[];
 extern const char kPrefsPingLastRollcall[];
@@ -152,30 +155,30 @@
 } PayloadType;
 
 // Maximum number of times we'll allow using p2p for the same update payload.
-const int kMaxP2PAttempts = 10;
+constexpr int kMaxP2PAttempts = 10;
 
 // Maximum wallclock time we allow attempting to update using p2p for
 // the same update payload - five days.
-const int kMaxP2PAttemptTimeSeconds = 5 * 24 * 60 * 60;
+constexpr int kMaxP2PAttemptTimeSeconds = 5 * 24 * 60 * 60;
 
 // The maximum amount of time to spend waiting for p2p-client(1) to
 // return while waiting in line to use the LAN - six hours.
-const int kMaxP2PNetworkWaitTimeSeconds = 6 * 60 * 60;
+constexpr int kMaxP2PNetworkWaitTimeSeconds = 6 * 60 * 60;
 
 // The maximum number of payload files to keep in /var/cache/p2p.
-const int kMaxP2PFilesToKeep = 3;
+constexpr int kMaxP2PFilesToKeep = 3;
 
 // The maximum number of days to keep a p2p file;
-const int kMaxP2PFileAgeDays = 5;
+constexpr int kMaxP2PFileAgeDays = 5;
 
 // The default number of UMA buckets for metrics.
-const int kNumDefaultUmaBuckets = 50;
+constexpr int kNumDefaultUmaBuckets = 50;
 
-// General constants
-const int kNumBytesInOneMiB = 1024 * 1024;
+// General constexprants
+constexpr int kNumBytesInOneMiB = 1024 * 1024;
 
 // Number of redirects allowed when downloading.
-const int kDownloadMaxRedirects = 10;
+constexpr int kDownloadMaxRedirects = 10;
 
 // The minimum average speed that downloads must sustain...
 //
@@ -183,8 +186,8 @@
 // connectivity and we want to make as much forward progress as
 // possible. For p2p this is high (25 kB/second) since we can assume
 // high bandwidth (same LAN) and we want to fail fast.
-const int kDownloadLowSpeedLimitBps = 1;
-const int kDownloadP2PLowSpeedLimitBps = 25 * 1000;
+constexpr int kDownloadLowSpeedLimitBps = 1;
+constexpr int kDownloadP2PLowSpeedLimitBps = 25 * 1000;
 
 // ... measured over this period.
 //
@@ -193,18 +196,18 @@
 // for the workstation to generate the payload. For normal operation
 // and p2p, make this relatively low since we want to fail fast in
 // those cases.
-const int kDownloadLowSpeedTimeSeconds = 30;
-const int kDownloadDevModeLowSpeedTimeSeconds = 180;
-const int kDownloadP2PLowSpeedTimeSeconds = 60;
+constexpr int kDownloadLowSpeedTimeSeconds = 30;
+constexpr int kDownloadDevModeLowSpeedTimeSeconds = 180;
+constexpr int kDownloadP2PLowSpeedTimeSeconds = 60;
 
 // The maximum amount of HTTP server reconnect attempts.
 //
 // This is set high in order to maximize the attempt's chance of
 // succeeding. When using p2p, this is low in order to fail fast.
-const int kDownloadMaxRetryCount = 20;
-const int kDownloadMaxRetryCountOobeNotComplete = 3;
-const int kDownloadMaxRetryCountInteractive = 3;
-const int kDownloadP2PMaxRetryCount = 5;
+constexpr int kDownloadMaxRetryCount = 20;
+constexpr int kDownloadMaxRetryCountOobeNotComplete = 3;
+constexpr int kDownloadMaxRetryCountInteractive = 3;
+constexpr int kDownloadP2PMaxRetryCount = 5;
 
 // The connect timeout, in seconds.
 //
@@ -212,11 +215,19 @@
 // connectivity and we may be using HTTPS which involves complicated
 // multi-roundtrip setup. For p2p, this is set low because we can
 // the server is on the same LAN and we want to fail fast.
-const int kDownloadConnectTimeoutSeconds = 30;
-const int kDownloadP2PConnectTimeoutSeconds = 5;
+constexpr int kDownloadConnectTimeoutSeconds = 30;
+constexpr int kDownloadP2PConnectTimeoutSeconds = 5;
 
 // Size in bytes of SHA256 hash.
-const int kSHA256Size = 32;
+constexpr int kSHA256Size = 32;
+
+// A hardcoded label to mark end of all InstallOps
+// This number must be greater than number of install ops.
+// Number of install ops is bounded by number of blocks on any partition.
+// Currently, the block size is 4096. Using |kEndOfInstallLabel| of 2^48 will
+// allow partitions with 2^48 * 4096 = 2^60 bytes. That's 1024PB? Partitions on
+// android aren't getting that big any time soon.
+constexpr uint64_t kEndOfInstallLabel = (1ULL << 48);
 
 }  // namespace chromeos_update_engine
 
diff --git a/common/cow_operation_convert.cc b/common/cow_operation_convert.cc
new file mode 100644
index 0000000..6b64a9c
--- /dev/null
+++ b/common/cow_operation_convert.cc
@@ -0,0 +1,93 @@
+//
+// 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/common/cow_operation_convert.h"
+
+#include <base/logging.h>
+
+#include "update_engine/payload_generator/extent_ranges.h"
+#include "update_engine/payload_generator/extent_utils.h"
+
+namespace chromeos_update_engine {
+
+std::vector<CowOperation> ConvertToCowOperations(
+    const ::google::protobuf::RepeatedPtrField<
+        ::chromeos_update_engine::InstallOperation>& operations,
+    const ::google::protobuf::RepeatedPtrField<CowMergeOperation>&
+        merge_operations) {
+  ExtentRanges merge_extents;
+  std::vector<CowOperation> converted;
+  ExtentRanges modified_extents;
+
+  // We want all CowCopy ops to be done first, before any COW_REPLACE happen.
+  // Therefore we add these ops in 2 separate loops. This is because during
+  // merge, a CowReplace might modify a block needed by CowCopy, so we always
+  // perform CowCopy first.
+
+  // This loop handles CowCopy blocks within SOURCE_COPY, and the next loop
+  // converts the leftover blocks to CowReplace?
+  for (const auto& merge_op : merge_operations) {
+    merge_extents.AddExtent(merge_op.dst_extent());
+    const auto& src_extent = merge_op.src_extent();
+    const auto& dst_extent = merge_op.dst_extent();
+    // Add blocks in reverse order to avoid merge conflicts on self-overlapping
+    // Ops.
+    // For example: SOURCE_COPY [20 - 30] -> [25 - 35] If blocks are added in
+    // forward order, then 20->25 is performed first, destroying block 25, which
+    // is neede by a later operation.
+    if (src_extent.start_block() < dst_extent.start_block()) {
+      for (uint64_t i = src_extent.num_blocks(); i > 0; i--) {
+        auto src_block = src_extent.start_block() + i - 1;
+        auto dst_block = dst_extent.start_block() + i - 1;
+        CHECK(!modified_extents.ContainsBlock(src_block))
+            << "block " << src_block << " is modified by previous CowCopy";
+        converted.push_back({CowOperation::CowCopy, src_block, dst_block});
+        modified_extents.AddBlock(dst_block);
+      }
+    } else {
+      for (uint64_t i = 0; i < src_extent.num_blocks(); i++) {
+        auto src_block = src_extent.start_block() + i;
+        auto dst_block = dst_extent.start_block() + i;
+        CHECK(!modified_extents.ContainsBlock(src_block))
+            << "block " << src_block << " is modified by previous CowCopy";
+        converted.push_back({CowOperation::CowCopy, src_block, dst_block});
+        modified_extents.AddBlock(dst_block);
+      }
+    }
+  }
+  // COW_REPLACE are added after COW_COPY, because replace might modify blocks
+  // needed by COW_COPY. Please don't merge this loop with the previous one.
+  for (const auto& operation : operations) {
+    if (operation.type() != InstallOperation::SOURCE_COPY) {
+      continue;
+    }
+    const auto& src_extents = operation.src_extents();
+    const auto& dst_extents = operation.dst_extents();
+    BlockIterator it1{src_extents};
+    BlockIterator it2{dst_extents};
+    while (!it1.is_end() && !it2.is_end()) {
+      auto src_block = *it1;
+      auto dst_block = *it2;
+      if (!merge_extents.ContainsBlock(dst_block)) {
+        converted.push_back({CowOperation::CowReplace, src_block, dst_block});
+      }
+      ++it1;
+      ++it2;
+    }
+  }
+  return converted;
+}
+}  // namespace chromeos_update_engine
diff --git a/common/cow_operation_convert.h b/common/cow_operation_convert.h
new file mode 100644
index 0000000..c0543f7
--- /dev/null
+++ b/common/cow_operation_convert.h
@@ -0,0 +1,56 @@
+//
+// 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 __COW_OPERATION_CONVERT_H
+#define __COW_OPERATION_CONVERT_H
+
+#include <vector>
+
+#include <libsnapshot/cow_format.h>
+
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+struct CowOperation {
+  enum Type {
+    CowCopy = android::snapshot::kCowCopyOp,
+    CowReplace = android::snapshot::kCowReplaceOp,
+  };
+  Type op;
+  uint64_t src_block;
+  uint64_t dst_block;
+};
+
+// Convert SOURCE_COPY operations in `operations` list to a list of
+// CowOperations according to the merge sequence. This function only converts
+// SOURCE_COPY, other operations are ignored. If there's a merge conflict in
+// SOURCE_COPY operations, some blocks may be converted to COW_REPLACE instead
+// of COW_COPY.
+
+// The list returned does not necessarily preserve the order of
+// SOURCE_COPY in `operations`. The only guarantee about ordering in the
+// returned list is that if operations are applied in such order, there would be
+// no merge conflicts.
+
+// This funnction is intended to be used by delta_performer to perform
+// SOURCE_COPY operations on Virtual AB Compression devices.
+std::vector<CowOperation> ConvertToCowOperations(
+    const ::google::protobuf::RepeatedPtrField<
+        ::chromeos_update_engine::InstallOperation>& operations,
+    const ::google::protobuf::RepeatedPtrField<CowMergeOperation>&
+        merge_operations);
+}  // namespace chromeos_update_engine
+#endif
diff --git a/common/cow_operation_convert_unittest.cc b/common/cow_operation_convert_unittest.cc
new file mode 100644
index 0000000..93173fe
--- /dev/null
+++ b/common/cow_operation_convert_unittest.cc
@@ -0,0 +1,236 @@
+//
+// 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 <algorithm>
+#include <array>
+#include <initializer_list>
+
+#include <gtest/gtest.h>
+
+#include "update_engine/common/cow_operation_convert.h"
+#include "update_engine/payload_generator/extent_ranges.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+using OperationList = ::google::protobuf::RepeatedPtrField<
+    ::chromeos_update_engine::InstallOperation>;
+using MergeOplist = ::google::protobuf::RepeatedPtrField<
+    ::chromeos_update_engine::CowMergeOperation>;
+
+std::ostream& operator<<(std::ostream& out, CowOperation::Type op) {
+  switch (op) {
+    case CowOperation::Type::CowCopy:
+      out << "CowCopy";
+      break;
+    case CowOperation::Type::CowReplace:
+      out << "CowReplace";
+      break;
+    default:
+      out << op;
+      break;
+  }
+  return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const CowOperation& c) {
+  out << "{" << c.op << ", " << c.src_block << ", " << c.dst_block << "}";
+  return out;
+}
+
+class CowOperationConvertTest : public testing::Test {
+ public:
+  void VerifyCowMergeOp(const std::vector<CowOperation>& cow_ops) {
+    // Build a set of all extents covered by InstallOps.
+    ExtentRanges src_extent_set;
+    ExtentRanges dst_extent_set;
+    for (auto&& op : operations_) {
+      src_extent_set.AddRepeatedExtents(op.src_extents());
+      dst_extent_set.AddRepeatedExtents(op.dst_extents());
+    }
+    ExtentRanges modified_extents;
+    for (auto&& cow_op : cow_ops) {
+      if (cow_op.op == CowOperation::CowCopy) {
+        EXPECT_TRUE(src_extent_set.ContainsBlock(cow_op.src_block));
+        // converted operations should be conflict free.
+        EXPECT_FALSE(modified_extents.ContainsBlock(cow_op.src_block))
+            << "SOURCE_COPY operation " << cow_op
+            << " read from a modified block";
+      }
+      EXPECT_TRUE(dst_extent_set.ContainsBlock(cow_op.dst_block));
+      dst_extent_set.SubtractExtent(ExtentForRange(cow_op.dst_block, 1));
+      modified_extents.AddBlock(cow_op.dst_block);
+    }
+    // The generated CowOps should cover all extents in InstallOps.
+    EXPECT_EQ(dst_extent_set.blocks(), 0UL);
+    // It's possible that src_extent_set is non-empty, because some operations
+    // will be converted to CowReplace, and we don't count the source extent for
+    // those.
+  }
+  OperationList operations_;
+  MergeOplist merge_operations_;
+};
+
+void AddOperation(OperationList* operations,
+                  ::chromeos_update_engine::InstallOperation_Type op_type,
+                  std::initializer_list<std::array<int, 2>> src_extents,
+                  std::initializer_list<std::array<int, 2>> dst_extents) {
+  auto&& op = operations->Add();
+  op->set_type(op_type);
+  for (const auto& extent : src_extents) {
+    *op->add_src_extents() = ExtentForRange(extent[0], extent[1]);
+  }
+  for (const auto& extent : dst_extents) {
+    *op->add_dst_extents() = ExtentForRange(extent[0], extent[1]);
+  }
+}
+
+void AddMergeOperation(MergeOplist* operations,
+                       ::chromeos_update_engine::CowMergeOperation_Type op_type,
+                       std::array<int, 2> src_extent,
+                       std::array<int, 2> dst_extent) {
+  auto&& op = operations->Add();
+  op->set_type(op_type);
+  *op->mutable_src_extent() = ExtentForRange(src_extent[0], src_extent[1]);
+  *op->mutable_dst_extent() = ExtentForRange(dst_extent[0], dst_extent[1]);
+}
+
+TEST_F(CowOperationConvertTest, NoConflict) {
+  AddOperation(
+      &operations_, InstallOperation::SOURCE_COPY, {{20, 1}}, {{30, 1}});
+  AddOperation(
+      &operations_, InstallOperation::SOURCE_COPY, {{10, 1}}, {{20, 1}});
+  AddOperation(
+      &operations_, InstallOperation::SOURCE_COPY, {{0, 1}}, {{10, 1}});
+
+  AddMergeOperation(
+      &merge_operations_, CowMergeOperation::COW_COPY, {20, 1}, {30, 1});
+  AddMergeOperation(
+      &merge_operations_, CowMergeOperation::COW_COPY, {10, 1}, {20, 1});
+  AddMergeOperation(
+      &merge_operations_, CowMergeOperation::COW_COPY, {0, 1}, {10, 1});
+
+  auto cow_ops = ConvertToCowOperations(operations_, merge_operations_);
+  ASSERT_EQ(cow_ops.size(), 3UL);
+  ASSERT_TRUE(std::all_of(cow_ops.begin(), cow_ops.end(), [](auto&& cow_op) {
+    return cow_op.op == CowOperation::CowCopy;
+  }));
+  VerifyCowMergeOp(cow_ops);
+}
+
+TEST_F(CowOperationConvertTest, CowReplace) {
+  AddOperation(
+      &operations_, InstallOperation::SOURCE_COPY, {{30, 1}}, {{0, 1}});
+  AddOperation(
+      &operations_, InstallOperation::SOURCE_COPY, {{20, 1}}, {{30, 1}});
+  AddOperation(
+      &operations_, InstallOperation::SOURCE_COPY, {{10, 1}}, {{20, 1}});
+  AddOperation(
+      &operations_, InstallOperation::SOURCE_COPY, {{0, 1}}, {{10, 1}});
+
+  AddMergeOperation(
+      &merge_operations_, CowMergeOperation::COW_COPY, {20, 1}, {30, 1});
+  AddMergeOperation(
+      &merge_operations_, CowMergeOperation::COW_COPY, {10, 1}, {20, 1});
+  AddMergeOperation(
+      &merge_operations_, CowMergeOperation::COW_COPY, {0, 1}, {10, 1});
+
+  auto cow_ops = ConvertToCowOperations(operations_, merge_operations_);
+  ASSERT_EQ(cow_ops.size(), 4UL);
+  // Expect 3 COW_COPY and 1 COW_REPLACE
+  ASSERT_EQ(std::count_if(cow_ops.begin(),
+                          cow_ops.end(),
+                          [](auto&& cow_op) {
+                            return cow_op.op == CowOperation::CowCopy;
+                          }),
+            3);
+  ASSERT_EQ(std::count_if(cow_ops.begin(),
+                          cow_ops.end(),
+                          [](auto&& cow_op) {
+                            return cow_op.op == CowOperation::CowReplace;
+                          }),
+            1);
+  VerifyCowMergeOp(cow_ops);
+}
+
+TEST_F(CowOperationConvertTest, ReOrderSourceCopy) {
+  AddOperation(
+      &operations_, InstallOperation::SOURCE_COPY, {{30, 1}}, {{20, 1}});
+  AddOperation(
+      &operations_, InstallOperation::SOURCE_COPY, {{20, 1}}, {{10, 1}});
+  AddOperation(
+      &operations_, InstallOperation::SOURCE_COPY, {{10, 1}}, {{0, 1}});
+
+  AddMergeOperation(
+      &merge_operations_, CowMergeOperation::COW_COPY, {10, 1}, {0, 1});
+  AddMergeOperation(
+      &merge_operations_, CowMergeOperation::COW_COPY, {20, 1}, {10, 1});
+  AddMergeOperation(
+      &merge_operations_, CowMergeOperation::COW_COPY, {30, 1}, {20, 1});
+
+  auto cow_ops = ConvertToCowOperations(operations_, merge_operations_);
+  ASSERT_EQ(cow_ops.size(), 3UL);
+  // Expect 3 COW_COPY
+  ASSERT_TRUE(std::all_of(cow_ops.begin(), cow_ops.end(), [](auto&& cow_op) {
+    return cow_op.op == CowOperation::CowCopy;
+  }));
+  VerifyCowMergeOp(cow_ops);
+}
+
+TEST_F(CowOperationConvertTest, InterleavingSrcExtent) {
+  AddOperation(&operations_,
+               InstallOperation::SOURCE_COPY,
+               {{30, 5}, {35, 5}},
+               {{20, 10}});
+  AddOperation(
+      &operations_, InstallOperation::SOURCE_COPY, {{20, 1}}, {{10, 1}});
+  AddOperation(
+      &operations_, InstallOperation::SOURCE_COPY, {{10, 1}}, {{0, 1}});
+
+  AddMergeOperation(
+      &merge_operations_, CowMergeOperation::COW_COPY, {10, 1}, {0, 1});
+  AddMergeOperation(
+      &merge_operations_, CowMergeOperation::COW_COPY, {20, 1}, {10, 1});
+  AddMergeOperation(
+      &merge_operations_, CowMergeOperation::COW_COPY, {30, 5}, {20, 5});
+  AddMergeOperation(
+      &merge_operations_, CowMergeOperation::COW_COPY, {35, 5}, {25, 5});
+
+  auto cow_ops = ConvertToCowOperations(operations_, merge_operations_);
+  // Expect 4 COW_COPY
+  ASSERT_EQ(cow_ops.size(), 12UL);
+  ASSERT_TRUE(std::all_of(cow_ops.begin(), cow_ops.end(), [](auto&& cow_op) {
+    return cow_op.op == CowOperation::CowCopy;
+  }));
+  VerifyCowMergeOp(cow_ops);
+}
+
+TEST_F(CowOperationConvertTest, SelfOverlappingOperation) {
+  AddOperation(
+      &operations_, InstallOperation::SOURCE_COPY, {{20, 10}}, {{25, 10}});
+
+  AddMergeOperation(
+      &merge_operations_, CowMergeOperation::COW_COPY, {20, 10}, {25, 10});
+
+  auto cow_ops = ConvertToCowOperations(operations_, merge_operations_);
+  // Expect 10 COW_COPY
+  ASSERT_EQ(cow_ops.size(), 10UL);
+  ASSERT_TRUE(std::all_of(cow_ops.begin(), cow_ops.end(), [](auto&& cow_op) {
+    return cow_op.op == CowOperation::CowCopy;
+  }));
+  VerifyCowMergeOp(cow_ops);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/daemon_base.h b/common/daemon_base.h
similarity index 87%
rename from daemon_base.h
rename to common/daemon_base.h
index 742a0ba..4bc5ef7 100644
--- a/daemon_base.h
+++ b/common/daemon_base.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_DAEMON_BASE_H_
-#define UPDATE_ENGINE_DAEMON_BASE_H_
+#ifndef UPDATE_ENGINE_COMMON_DAEMON_BASE_H_
+#define UPDATE_ENGINE_COMMON_DAEMON_BASE_H_
 
 #include <memory>
 
@@ -37,4 +37,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_DAEMON_BASE_H_
+#endif  // UPDATE_ENGINE_COMMON_DAEMON_BASE_H_
diff --git a/daemon_state_interface.h b/common/daemon_state_interface.h
similarity index 85%
rename from daemon_state_interface.h
rename to common/daemon_state_interface.h
index 2356816..9509fa2 100644
--- a/daemon_state_interface.h
+++ b/common/daemon_state_interface.h
@@ -14,10 +14,10 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_DAEMON_STATE_INTERFACE_H_
-#define UPDATE_ENGINE_DAEMON_STATE_INTERFACE_H_
+#ifndef UPDATE_ENGINE_COMMON_DAEMON_STATE_INTERFACE_H_
+#define UPDATE_ENGINE_COMMON_DAEMON_STATE_INTERFACE_H_
 
-#include "update_engine/service_observer_interface.h"
+#include "update_engine/common/service_observer_interface.h"
 
 #include <memory>
 #include <set>
@@ -46,4 +46,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_DAEMON_STATE_INTERFACE_H_
+#endif  // UPDATE_ENGINE_COMMON_DAEMON_STATE_INTERFACE_H_
diff --git a/payload_consumer/download_action.h b/common/download_action.h
similarity index 96%
rename from payload_consumer/download_action.h
rename to common/download_action.h
index 6928443..c167c2d 100644
--- a/payload_consumer/download_action.h
+++ b/common/download_action.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_PAYLOAD_CONSUMER_DOWNLOAD_ACTION_H_
-#define UPDATE_ENGINE_PAYLOAD_CONSUMER_DOWNLOAD_ACTION_H_
+#ifndef UPDATE_ENGINE_COMMON_DOWNLOAD_ACTION_H_
+#define UPDATE_ENGINE_COMMON_DOWNLOAD_ACTION_H_
 
 #include <fcntl.h>
 #include <sys/stat.h>
@@ -28,9 +28,9 @@
 #include "update_engine/common/boot_control_interface.h"
 #include "update_engine/common/http_fetcher.h"
 #include "update_engine/common/multi_range_http_fetcher.h"
+#include "update_engine/common/system_state.h"
 #include "update_engine/payload_consumer/delta_performer.h"
 #include "update_engine/payload_consumer/install_plan.h"
-#include "update_engine/system_state.h"
 
 // The Download Action downloads a specified url to disk. The url should point
 // to an update in a delta payload format. The payload will be piped into a
@@ -200,4 +200,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_DOWNLOAD_ACTION_H_
+#endif  // UPDATE_ENGINE_COMMON_DOWNLOAD_ACTION_H_
diff --git a/common/dynamic_partition_control_interface.h b/common/dynamic_partition_control_interface.h
index 22f6db8..1362c19 100644
--- a/common/dynamic_partition_control_interface.h
+++ b/common/dynamic_partition_control_interface.h
@@ -26,8 +26,14 @@
 #include "update_engine/common/action.h"
 #include "update_engine/common/cleanup_previous_update_action_delegate.h"
 #include "update_engine/common/error_code.h"
+#include "update_engine/payload_consumer/file_descriptor.h"
 #include "update_engine/update_metadata.pb.h"
 
+// Forware declare for libsnapshot/snapshot_writer.h
+namespace android::snapshot {
+class ISnapshotWriter;
+}
+
 namespace chromeos_update_engine {
 
 struct FeatureFlag {
@@ -139,6 +145,26 @@
       uint32_t source_slot,
       uint32_t target_slot,
       const std::vector<std::string>& partitions) = 0;
+  // Partition name is expected to be unsuffixed. e.g. system, vendor
+  // Return an interface to write to a snapshoted partition.
+  // If `is_append` is false, then existing COW data will be overwritten.
+  // Otherwise the cow writer will be opened on APPEND mode, existing COW data
+  // is preserved.
+  virtual std::unique_ptr<android::snapshot::ISnapshotWriter> OpenCowWriter(
+      const std::string& unsuffixed_partition_name,
+      const std::optional<std::string>&,
+      bool is_append = false) = 0;
+  virtual FileDescriptorPtr OpenCowReader(
+      const std::string& unsuffixed_partition_name,
+      const std::optional<std::string>&,
+      bool is_append = false) = 0;
+
+  virtual bool IsDynamicPartition(const std::string& part_name) = 0;
+
+  // Create virtual block devices for all partitions.
+  virtual bool MapAllPartitions() = 0;
+  // Unmap virtual block devices for all partitions.
+  virtual bool UnmapAllPartitions() = 0;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/common/dynamic_partition_control_stub.cc b/common/dynamic_partition_control_stub.cc
index c63a8ff..2c6bb1b 100644
--- a/common/dynamic_partition_control_stub.cc
+++ b/common/dynamic_partition_control_stub.cc
@@ -20,6 +20,7 @@
 #include <string>
 
 #include <base/logging.h>
+#include <libsnapshot/cow_writer.h>
 
 #include "update_engine/common/dynamic_partition_control_stub.h"
 
@@ -87,4 +88,32 @@
   return true;
 }
 
+std::unique_ptr<android::snapshot::ISnapshotWriter>
+DynamicPartitionControlStub::OpenCowWriter(
+    const std::string& /*unsuffixed_partition_name*/,
+    const std::optional<std::string>& /*source_path*/,
+    bool /*is_append*/) {
+  return nullptr;
+}
+
+FileDescriptorPtr DynamicPartitionControlStub::OpenCowReader(
+    const std::string& unsuffixed_partition_name,
+    const std::optional<std::string>&,
+    bool /*is_append */) {
+  return nullptr;
+}
+
+bool DynamicPartitionControlStub::MapAllPartitions() {
+  return false;
+}
+
+bool DynamicPartitionControlStub::UnmapAllPartitions() {
+  return false;
+}
+
+bool DynamicPartitionControlStub::IsDynamicPartition(
+    const std::string& part_name) {
+  return false;
+}
+
 }  // namespace chromeos_update_engine
diff --git a/common/dynamic_partition_control_stub.h b/common/dynamic_partition_control_stub.h
index 8bff474..0f428ab 100644
--- a/common/dynamic_partition_control_stub.h
+++ b/common/dynamic_partition_control_stub.h
@@ -57,8 +57,20 @@
       uint32_t source_slot,
       uint32_t target_slot,
       const std::vector<std::string>& partitions) override;
-};
 
+  std::unique_ptr<android::snapshot::ISnapshotWriter> OpenCowWriter(
+      const std::string& unsuffixed_partition_name,
+      const std::optional<std::string>&,
+      bool is_append) override;
+  FileDescriptorPtr OpenCowReader(const std::string& unsuffixed_partition_name,
+                                  const std::optional<std::string>&,
+                                  bool is_append = false) override;
+
+  bool MapAllPartitions() override;
+  bool UnmapAllPartitions() override;
+
+  bool IsDynamicPartition(const std::string& part_name) override;
+};
 }  // namespace chromeos_update_engine
 
 #endif  // UPDATE_ENGINE_COMMON_DYNAMIC_PARTITION_CONTROL_STUB_H_
diff --git a/common/error_code.h b/common/error_code.h
index e473a05..a889888 100644
--- a/common/error_code.h
+++ b/common/error_code.h
@@ -85,6 +85,8 @@
   kUnresolvedHostRecovered = 59,
   kNotEnoughSpace = 60,
   kDeviceCorrupted = 61,
+  kPackageExcludedFromUpdate = 62,
+  kPostInstallMountError = 63,
 
   // VERY IMPORTANT! When adding new error codes:
   //
diff --git a/common/error_code_utils.cc b/common/error_code_utils.cc
index 64df24a..421544a 100644
--- a/common/error_code_utils.cc
+++ b/common/error_code_utils.cc
@@ -171,6 +171,10 @@
       return "ErrorCode::kNotEnoughSpace";
     case ErrorCode::kDeviceCorrupted:
       return "ErrorCode::kDeviceCorrupted";
+    case ErrorCode::kPackageExcludedFromUpdate:
+      return "ErrorCode::kPackageExcludedFromUpdate";
+    case ErrorCode::kPostInstallMountError:
+      return "ErrorCode::kPostInstallMountError";
       // Don't add a default case to let the compiler warn about newly added
       // error codes which should be added here.
   }
diff --git a/common/fake_boot_control.h b/common/fake_boot_control.h
index 5d8823a..98b93e6 100644
--- a/common/fake_boot_control.h
+++ b/common/fake_boot_control.h
@@ -116,7 +116,7 @@
     is_bootable_[slot] = bootable;
   }
 
-  DynamicPartitionControlInterface* GetDynamicPartitionControl() {
+  DynamicPartitionControlInterface* GetDynamicPartitionControl() override {
     return dynamic_partition_control_.get();
   }
 
diff --git a/common/fake_hardware.h b/common/fake_hardware.h
index 82382ff..00a212e 100644
--- a/common/fake_hardware.h
+++ b/common/fake_hardware.h
@@ -76,10 +76,6 @@
 
   std::string GetHardwareClass() const override { return hardware_class_; }
 
-  std::string GetFirmwareVersion() const override { return firmware_version_; }
-
-  std::string GetECVersion() const override { return ec_version_; }
-
   std::string GetDeviceRequisition() const override {
     return device_requisition_;
   }
@@ -176,12 +172,6 @@
     hardware_class_ = hardware_class;
   }
 
-  void SetFirmwareVersion(const std::string& firmware_version) {
-    firmware_version_ = firmware_version;
-  }
-
-  void SetECVersion(const std::string& ec_version) { ec_version_ = ec_version; }
-
   void SetDeviceRequisition(const std::string& requisition) {
     device_requisition_ = requisition;
   }
@@ -233,8 +223,6 @@
   // Jan 20, 2007
   base::Time oobe_timestamp_{base::Time::FromTimeT(1169280000)};
   std::string hardware_class_{"Fake HWID BLAH-1234"};
-  std::string firmware_version_{"Fake Firmware v1.0.1"};
-  std::string ec_version_{"Fake EC v1.0a"};
   std::string device_requisition_{"fake_requisition"};
   int min_kernel_key_version_{kMinKernelKeyVersion};
   int min_firmware_key_version_{kMinFirmwareKeyVersion};
diff --git a/common/hardware_interface.h b/common/hardware_interface.h
index b37b007..cad32fc 100644
--- a/common/hardware_interface.h
+++ b/common/hardware_interface.h
@@ -64,14 +64,6 @@
   // Returns the HWID or an empty string on error.
   virtual std::string GetHardwareClass() const = 0;
 
-  // Returns the firmware version or an empty string if the system is
-  // not running chrome os firmware.
-  virtual std::string GetFirmwareVersion() const = 0;
-
-  // Returns the ec version or an empty string if the system is not
-  // running a custom chrome os ec.
-  virtual std::string GetECVersion() const = 0;
-
   // Returns the OEM device requisition or an empty string if the system does
   // not have a requisition, or if not running Chrome OS.
   virtual std::string GetDeviceRequisition() const = 0;
diff --git a/common/hash_calculator_unittest.cc b/common/hash_calculator_unittest.cc
index e8f73d5..fe7d543 100644
--- a/common/hash_calculator_unittest.cc
+++ b/common/hash_calculator_unittest.cc
@@ -104,7 +104,7 @@
 }
 
 TEST_F(HashCalculatorTest, UpdateFileSimpleTest) {
-  test_utils::ScopedTempFile data_file("data.XXXXXX");
+  ScopedTempFile data_file("data.XXXXXX");
   ASSERT_TRUE(test_utils::WriteFileString(data_file.path(), "hi"));
 
   for (const int length : {-1, 2, 10}) {
@@ -126,7 +126,7 @@
 }
 
 TEST_F(HashCalculatorTest, RawHashOfFileSimpleTest) {
-  test_utils::ScopedTempFile data_file("data.XXXXXX");
+  ScopedTempFile data_file("data.XXXXXX");
   ASSERT_TRUE(test_utils::WriteFileString(data_file.path(), "hi"));
 
   for (const int length : {-1, 2, 10}) {
diff --git a/common/http_fetcher.h b/common/http_fetcher.h
index f74a0f0..7fa5f09 100644
--- a/common/http_fetcher.h
+++ b/common/http_fetcher.h
@@ -28,8 +28,8 @@
 #include <brillo/message_loops/message_loop.h>
 
 #include "update_engine/common/http_common.h"
+#include "update_engine/common/metrics_constants.h"
 #include "update_engine/common/proxy_resolver.h"
-#include "update_engine/metrics_constants.h"
 
 // This class is a simple wrapper around an HTTP library (libcurl). We can
 // easily mock out this interface for testing.
diff --git a/common/http_fetcher_unittest.cc b/common/http_fetcher_unittest.cc
index 9338087..99ea99b 100644
--- a/common/http_fetcher_unittest.cc
+++ b/common/http_fetcher_unittest.cc
@@ -28,11 +28,16 @@
 #include <base/bind.h>
 #include <base/location.h>
 #include <base/logging.h>
+#if BASE_VER < 780000  // Android
 #include <base/message_loop/message_loop.h>
+#endif  // BASE_VER < 780000
 #include <base/stl_util.h>
 #include <base/strings/string_number_conversions.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
+#if BASE_VER >= 780000  // CrOS
+#include <base/task/single_thread_task_executor.h>
+#endif  // BASE_VER >= 780000
 #include <base/time/time.h>
 #include <brillo/message_loops/base_message_loop.h>
 #include <brillo/message_loops/message_loop.h>
@@ -364,7 +369,7 @@
   HttpServer* CreateServer() override { return new NullHttpServer; }
 
  private:
-  test_utils::ScopedTempFile temp_file_{"ue_file_fetcher.XXXXXX"};
+  ScopedTempFile temp_file_{"ue_file_fetcher.XXXXXX"};
 };
 
 class MultiRangeHttpFetcherOverFileFetcherTest : public FileFetcherTest {
@@ -403,8 +408,13 @@
 template <typename T>
 class HttpFetcherTest : public ::testing::Test {
  public:
+#if BASE_VER < 780000  // Android
   base::MessageLoopForIO base_loop_;
   brillo::BaseMessageLoop loop_{&base_loop_};
+#else   // Chrome OS
+  base::SingleThreadTaskExecutor base_loop_{base::MessagePumpType::IO};
+  brillo::BaseMessageLoop loop_{base_loop_.task_runner()};
+#endif  // BASE_VER < 780000
 
   T test_;
 
diff --git a/logging.h b/common/logging.h
similarity index 100%
rename from logging.h
rename to common/logging.h
diff --git a/metrics_constants.h b/common/metrics_constants.h
similarity index 95%
rename from metrics_constants.h
rename to common/metrics_constants.h
index db21d90..b7633b9 100644
--- a/metrics_constants.h
+++ b/common/metrics_constants.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_METRICS_CONSTANTS_H_
-#define UPDATE_ENGINE_METRICS_CONSTANTS_H_
+#ifndef UPDATE_ENGINE_COMMON_METRICS_CONSTANTS_H_
+#define UPDATE_ENGINE_COMMON_METRICS_CONSTANTS_H_
 
 namespace chromeos_update_engine {
 
@@ -106,7 +106,7 @@
   kUpdateCanceled,              // Update canceled by the user.
   kUpdateSucceededNotActive,    // Update succeeded but the new slot is not
                                 // active.
-
+  kUpdateSkipped,               // Current update skipped.
   kNumConstants,
 
   kUnset = -1
@@ -144,4 +144,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_METRICS_CONSTANTS_H_
+#endif  // UPDATE_ENGINE_COMMON_METRICS_CONSTANTS_H_
diff --git a/metrics_reporter_interface.h b/common/metrics_reporter_interface.h
similarity index 96%
rename from metrics_reporter_interface.h
rename to common/metrics_reporter_interface.h
index 180a680..d7c5347 100644
--- a/metrics_reporter_interface.h
+++ b/common/metrics_reporter_interface.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_METRICS_REPORTER_INTERFACE_H_
-#define UPDATE_ENGINE_METRICS_REPORTER_INTERFACE_H_
+#ifndef UPDATE_ENGINE_COMMON_METRICS_REPORTER_INTERFACE_H_
+#define UPDATE_ENGINE_COMMON_METRICS_REPORTER_INTERFACE_H_
 
 #include <memory>
 #include <string>
@@ -24,8 +24,8 @@
 
 #include "update_engine/common/constants.h"
 #include "update_engine/common/error_code.h"
-#include "update_engine/metrics_constants.h"
-#include "update_engine/system_state.h"
+#include "update_engine/common/metrics_constants.h"
+#include "update_engine/common/system_state.h"
 
 namespace chromeos_update_engine {
 
@@ -244,4 +244,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_METRICS_REPORTER_INTERFACE_H_
+#endif  // UPDATE_ENGINE_COMMON_METRICS_REPORTER_INTERFACE_H_
diff --git a/metrics_reporter_stub.cc b/common/metrics_reporter_stub.cc
similarity index 93%
rename from metrics_reporter_stub.cc
rename to common/metrics_reporter_stub.cc
index 81664a5..dcb4e8c 100644
--- a/metrics_reporter_stub.cc
+++ b/common/metrics_reporter_stub.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/metrics_reporter_stub.h"
+#include "update_engine/common/metrics_reporter_stub.h"
 
 #include <memory>
 
diff --git a/metrics_reporter_stub.h b/common/metrics_reporter_stub.h
similarity index 92%
rename from metrics_reporter_stub.h
rename to common/metrics_reporter_stub.h
index 0cfeea0..1470aaa 100644
--- a/metrics_reporter_stub.h
+++ b/common/metrics_reporter_stub.h
@@ -14,14 +14,14 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_METRICS_REPORTER_STUB_H_
-#define UPDATE_ENGINE_METRICS_REPORTER_STUB_H_
+#ifndef UPDATE_ENGINE_COMMON_METRICS_REPORTER_STUB_H_
+#define UPDATE_ENGINE_COMMON_METRICS_REPORTER_STUB_H_
 
 #include <string>
 
 #include "update_engine/common/error_code.h"
-#include "update_engine/metrics_constants.h"
-#include "update_engine/metrics_reporter_interface.h"
+#include "update_engine/common/metrics_constants.h"
+#include "update_engine/common/metrics_reporter_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -98,4 +98,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_METRICS_REPORTER_STUB_H_
+#endif  // UPDATE_ENGINE_COMMON_METRICS_REPORTER_STUB_H_
diff --git a/payload_consumer/mock_download_action.h b/common/mock_download_action.h
similarity index 81%
rename from payload_consumer/mock_download_action.h
rename to common/mock_download_action.h
index 3abb809..ecda9a3 100644
--- a/payload_consumer/mock_download_action.h
+++ b/common/mock_download_action.h
@@ -14,15 +14,15 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_PAYLOAD_CONSUMER_MOCK_DOWNLOAD_ACTION_H_
-#define UPDATE_ENGINE_PAYLOAD_CONSUMER_MOCK_DOWNLOAD_ACTION_H_
+#ifndef UPDATE_ENGINE_COMMON_MOCK_DOWNLOAD_ACTION_H_
+#define UPDATE_ENGINE_COMMON_MOCK_DOWNLOAD_ACTION_H_
 
 #include <stdint.h>
 
 #include <gmock/gmock.h>
 
+#include "update_engine/common/download_action.h"
 #include "update_engine/common/error_code.h"
-#include "update_engine/payload_consumer/download_action.h"
 
 namespace chromeos_update_engine {
 
@@ -38,4 +38,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_MOCK_DOWNLOAD_ACTION_H_
+#endif  // UPDATE_ENGINE_COMMON_MOCK_DOWNLOAD_ACTION_H_
diff --git a/common/mock_excluder.h b/common/mock_excluder.h
index bc54772..560ba0d 100644
--- a/common/mock_excluder.h
+++ b/common/mock_excluder.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_MOCK_APP_EXCLUDER_H_
-#define UPDATE_ENGINE_MOCK_APP_EXCLUDER_H_
+#ifndef UPDATE_ENGINE_COMMON_MOCK_APP_EXCLUDER_H_
+#define UPDATE_ENGINE_COMMON_MOCK_APP_EXCLUDER_H_
 
 #include "update_engine/common/excluder_interface.h"
 
@@ -34,4 +34,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_MOCK_APP_EXCLUDER_H_
+#endif  // UPDATE_ENGINE_COMMON_MOCK_APP_EXCLUDER_H_
diff --git a/common/mock_hardware.h b/common/mock_hardware.h
index 84c0c5b..071906b 100644
--- a/common/mock_hardware.h
+++ b/common/mock_hardware.h
@@ -45,11 +45,6 @@
     ON_CALL(*this, GetHardwareClass())
         .WillByDefault(
             testing::Invoke(&fake_, &FakeHardware::GetHardwareClass));
-    ON_CALL(*this, GetFirmwareVersion())
-        .WillByDefault(
-            testing::Invoke(&fake_, &FakeHardware::GetFirmwareVersion));
-    ON_CALL(*this, GetECVersion())
-        .WillByDefault(testing::Invoke(&fake_, &FakeHardware::GetECVersion));
     ON_CALL(*this, GetMinKernelKeyVersion())
         .WillByDefault(
             testing::Invoke(&fake_, &FakeHardware::GetMinKernelKeyVersion));
@@ -90,8 +85,6 @@
   MOCK_CONST_METHOD0(IsOOBEEnabled, bool());
   MOCK_CONST_METHOD1(IsOOBEComplete, bool(base::Time* out_time_of_oobe));
   MOCK_CONST_METHOD0(GetHardwareClass, std::string());
-  MOCK_CONST_METHOD0(GetFirmwareVersion, std::string());
-  MOCK_CONST_METHOD0(GetECVersion, std::string());
   MOCK_CONST_METHOD0(GetMinKernelKeyVersion, int());
   MOCK_CONST_METHOD0(GetMinFirmwareKeyVersion, int());
   MOCK_CONST_METHOD0(GetMaxFirmwareKeyRollforward, int());
diff --git a/mock_metrics_reporter.h b/common/mock_metrics_reporter.h
similarity index 93%
rename from mock_metrics_reporter.h
rename to common/mock_metrics_reporter.h
index baf3a78..922d1ee 100644
--- a/mock_metrics_reporter.h
+++ b/common/mock_metrics_reporter.h
@@ -14,14 +14,14 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_MOCK_METRICS_REPORTER_H_
-#define UPDATE_ENGINE_MOCK_METRICS_REPORTER_H_
+#ifndef UPDATE_ENGINE_COMMON_MOCK_METRICS_REPORTER_H_
+#define UPDATE_ENGINE_COMMON_MOCK_METRICS_REPORTER_H_
 
 #include <string>
 
 #include <gmock/gmock.h>
 
-#include "update_engine/metrics_reporter_interface.h"
+#include "update_engine/common/metrics_reporter_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -96,4 +96,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_MOCK_METRICS_REPORTER_H_
+#endif  // UPDATE_ENGINE_COMMON_MOCK_METRICS_REPORTER_H_
diff --git a/mock_service_observer.h b/common/mock_service_observer.h
similarity index 81%
rename from mock_service_observer.h
rename to common/mock_service_observer.h
index e434eab..2c895f9 100644
--- a/mock_service_observer.h
+++ b/common/mock_service_observer.h
@@ -14,11 +14,11 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_MOCK_SERVICE_OBSERVER_H_
-#define UPDATE_ENGINE_MOCK_SERVICE_OBSERVER_H_
+#ifndef UPDATE_ENGINE_COMMON_MOCK_SERVICE_OBSERVER_H_
+#define UPDATE_ENGINE_COMMON_MOCK_SERVICE_OBSERVER_H_
 
 #include <gmock/gmock.h>
-#include "update_engine/service_observer_interface.h"
+#include "update_engine/common/service_observer_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -32,4 +32,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_MOCK_SERVICE_OBSERVER_H_
+#endif  // UPDATE_ENGINE_COMMON_MOCK_SERVICE_OBSERVER_H_
diff --git a/network_selector.h b/common/network_selector.h
similarity index 80%
rename from network_selector.h
rename to common/network_selector.h
index 22aed8e..bfc09c5 100644
--- a/network_selector.h
+++ b/common/network_selector.h
@@ -14,12 +14,12 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_NETWORK_SELECTOR_H_
-#define UPDATE_ENGINE_NETWORK_SELECTOR_H_
+#ifndef UPDATE_ENGINE_COMMON_NETWORK_SELECTOR_H_
+#define UPDATE_ENGINE_COMMON_NETWORK_SELECTOR_H_
 
 #include <memory>
 
-#include "update_engine/network_selector_interface.h"
+#include "update_engine/common/network_selector_interface.h"
 
 namespace chromeos_update_engine {
 namespace network {
@@ -30,4 +30,4 @@
 }  // namespace network
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_NETWORK_SELECTOR_H_
+#endif  // UPDATE_ENGINE_COMMON_NETWORK_SELECTOR_H_
diff --git a/network_selector_interface.h b/common/network_selector_interface.h
similarity index 88%
rename from network_selector_interface.h
rename to common/network_selector_interface.h
index bd0948f..42ce32e 100644
--- a/network_selector_interface.h
+++ b/common/network_selector_interface.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_NETWORK_SELECTOR_INTERFACE_H_
-#define UPDATE_ENGINE_NETWORK_SELECTOR_INTERFACE_H_
+#ifndef UPDATE_ENGINE_COMMON_NETWORK_SELECTOR_INTERFACE_H_
+#define UPDATE_ENGINE_COMMON_NETWORK_SELECTOR_INTERFACE_H_
 
 #include <cstdint>
 
@@ -45,4 +45,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_NETWORK_SELECTOR_INTERFACE_H_
+#endif  // UPDATE_ENGINE_COMMON_NETWORK_SELECTOR_INTERFACE_H_
diff --git a/network_selector_stub.cc b/common/network_selector_stub.cc
similarity index 87%
rename from network_selector_stub.cc
rename to common/network_selector_stub.cc
index 67925f4..24c0e25 100644
--- a/network_selector_stub.cc
+++ b/common/network_selector_stub.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/network_selector_stub.h"
+#include "update_engine/common/network_selector_stub.h"
 
 #include <memory>
 
@@ -24,14 +24,14 @@
 
 namespace network {
 
-// Factory defined in network_selector.h.
+// Factory defined in common/network_selector.h.
 std::unique_ptr<NetworkSelectorInterface> CreateNetworkSelector() {
   return std::make_unique<NetworkSelectorStub>();
 }
 
 }  // namespace network
 
-// Defined in network_selector_interface.h.
+// Defined in common/network_selector_interface.h.
 const NetworkId kDefaultNetworkId = 0;
 
 bool NetworkSelectorStub::SetProcessNetwork(NetworkId network_id) {
diff --git a/network_selector_stub.h b/common/network_selector_stub.h
similarity index 81%
rename from network_selector_stub.h
rename to common/network_selector_stub.h
index b3f7b48..b32df91 100644
--- a/network_selector_stub.h
+++ b/common/network_selector_stub.h
@@ -14,12 +14,12 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_NETWORK_SELECTOR_STUB_H_
-#define UPDATE_ENGINE_NETWORK_SELECTOR_STUB_H_
+#ifndef UPDATE_ENGINE_COMMON_NETWORK_SELECTOR_STUB_H_
+#define UPDATE_ENGINE_COMMON_NETWORK_SELECTOR_STUB_H_
 
 #include <base/macros.h>
 
-#include "update_engine/network_selector_interface.h"
+#include "update_engine/common/network_selector_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -37,4 +37,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_NETWORK_SELECTOR_STUB_H_
+#endif  // UPDATE_ENGINE_COMMON_NETWORK_SELECTOR_STUB_H_
diff --git a/common/platform_constants.h b/common/platform_constants.h
index 243af69..c060133 100644
--- a/common/platform_constants.h
+++ b/common/platform_constants.h
@@ -58,6 +58,12 @@
 // postinstall.
 extern const char kPostinstallMountOptions[];
 
+#ifdef __ANDROID_RECOVERY__
+constexpr bool kIsRecovery = true;
+#else
+constexpr bool kIsRecovery = false;
+#endif
+
 }  // namespace constants
 }  // namespace chromeos_update_engine
 
diff --git a/service_observer_interface.h b/common/service_observer_interface.h
similarity index 88%
rename from service_observer_interface.h
rename to common/service_observer_interface.h
index 4edb0ac..c471231 100644
--- a/service_observer_interface.h
+++ b/common/service_observer_interface.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_SERVICE_OBSERVER_INTERFACE_H_
-#define UPDATE_ENGINE_SERVICE_OBSERVER_INTERFACE_H_
+#ifndef UPDATE_ENGINE_COMMON_SERVICE_OBSERVER_INTERFACE_H_
+#define UPDATE_ENGINE_COMMON_SERVICE_OBSERVER_INTERFACE_H_
 
 #include <memory>
 #include <string>
@@ -43,4 +43,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_SERVICE_OBSERVER_INTERFACE_H_
+#endif  // UPDATE_ENGINE_COMMON_SERVICE_OBSERVER_INTERFACE_H_
diff --git a/common/subprocess.h b/common/subprocess.h
index 179a5c5..2ed8b81 100644
--- a/common/subprocess.h
+++ b/common/subprocess.h
@@ -37,7 +37,7 @@
 #include <brillo/process.h>
 #include <brillo/process_reaper.h>
 #endif  // __CHROMEOS__
-#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+#include <gtest/gtest_prod.h>
 
 // The Subprocess class is a singleton. It's used to spawn off a subprocess
 // and get notified when the subprocess exits. The result of Exec() can
diff --git a/common/subprocess_unittest.cc b/common/subprocess_unittest.cc
index b4d068f..ff4158e 100644
--- a/common/subprocess_unittest.cc
+++ b/common/subprocess_unittest.cc
@@ -28,9 +28,14 @@
 #include <base/bind.h>
 #include <base/files/scoped_temp_dir.h>
 #include <base/location.h>
+#if BASE_VER < 780000  // Android
 #include <base/message_loop/message_loop.h>
+#endif  // BASE_VER < 780000
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
+#if BASE_VER >= 780000  // Chrome OS
+#include <base/task/single_thread_task_executor.h>
+#endif  // BASE_VER >= 780000
 #include <base/time/time.h>
 #include <brillo/message_loops/base_message_loop.h>
 #include <brillo/message_loops/message_loop.h>
@@ -70,8 +75,13 @@
     subprocess_.Init(&async_signal_handler_);
   }
 
+#if BASE_VER < 780000  // Android
   base::MessageLoopForIO base_loop_;
   brillo::BaseMessageLoop loop_{&base_loop_};
+#else   // Chrome OS
+  base::SingleThreadTaskExecutor base_loop_{base::MessagePumpType::IO};
+  brillo::BaseMessageLoop loop_{base_loop_.task_runner()};
+#endif  // BASE_VER < 780000
   brillo::AsynchronousSignalHandler async_signal_handler_;
   Subprocess subprocess_;
   unique_ptr<base::FileDescriptorWatcher::Controller> watcher_;
diff --git a/system_state.h b/common/system_state.h
similarity index 96%
rename from system_state.h
rename to common/system_state.h
index f46cbcf..7a67046 100644
--- a/system_state.h
+++ b/common/system_state.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_SYSTEM_STATE_H_
-#define UPDATE_ENGINE_SYSTEM_STATE_H_
+#ifndef UPDATE_ENGINE_COMMON_SYSTEM_STATE_H_
+#define UPDATE_ENGINE_COMMON_SYSTEM_STATE_H_
 
 namespace chromeos_update_manager {
 
@@ -117,4 +117,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_SYSTEM_STATE_H_
+#endif  // UPDATE_ENGINE_COMMON_SYSTEM_STATE_H_
diff --git a/common/test_utils.h b/common/test_utils.h
index 63ea749..bb5a678 100644
--- a/common/test_utils.h
+++ b/common/test_utils.h
@@ -138,22 +138,6 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedLoopbackDeviceBinder);
 };
 
-class ScopedTempFile {
- public:
-  ScopedTempFile() : ScopedTempFile("update_engine_test_temp_file.XXXXXX") {}
-
-  explicit ScopedTempFile(const std::string& pattern) {
-    EXPECT_TRUE(utils::MakeTempFile(pattern, &path_, nullptr));
-    unlinker_.reset(new ScopedPathUnlinker(path_));
-  }
-
-  const std::string& path() const { return path_; }
-
- private:
-  std::string path_;
-  std::unique_ptr<ScopedPathUnlinker> unlinker_;
-};
-
 class ScopedLoopMounter {
  public:
   explicit ScopedLoopMounter(const std::string& file_path,
diff --git a/common/utils.cc b/common/utils.cc
index 5d76f3f..1ac42dc 100644
--- a/common/utils.cc
+++ b/common/utils.cc
@@ -112,27 +112,6 @@
 
 namespace utils {
 
-string ParseECVersion(string input_line) {
-  base::TrimWhitespaceASCII(input_line, base::TRIM_ALL, &input_line);
-
-  // At this point we want to convert the format key=value pair from mosys to
-  // a vector of key value pairs.
-  vector<pair<string, string>> kv_pairs;
-  if (base::SplitStringIntoKeyValuePairs(input_line, '=', ' ', &kv_pairs)) {
-    for (const pair<string, string>& kv_pair : kv_pairs) {
-      // Finally match against the fw_verion which may have quotes.
-      if (kv_pair.first == "fw_version") {
-        string output;
-        // Trim any quotes.
-        base::TrimString(kv_pair.second, "\"", &output);
-        return output;
-      }
-    }
-  }
-  LOG(ERROR) << "Unable to parse fwid from ec info.";
-  return "";
-}
-
 bool WriteFile(const char* path, const void* data, size_t data_len) {
   int fd = HANDLE_EINTR(open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600));
   TEST_AND_RETURN_FALSE_ERRNO(fd >= 0);
@@ -213,10 +192,10 @@
   return true;
 }
 
-bool PWriteAll(const FileDescriptorPtr& fd,
-               const void* buf,
-               size_t count,
-               off_t offset) {
+bool WriteAll(const FileDescriptorPtr& fd,
+              const void* buf,
+              size_t count,
+              off_t offset) {
   TEST_AND_RETURN_FALSE_ERRNO(fd->Seek(offset, SEEK_SET) !=
                               static_cast<off_t>(-1));
   return WriteAll(fd, buf, count);
@@ -239,11 +218,11 @@
   return true;
 }
 
-bool PReadAll(const FileDescriptorPtr& fd,
-              void* buf,
-              size_t count,
-              off_t offset,
-              ssize_t* out_bytes_read) {
+bool ReadAll(const FileDescriptorPtr& fd,
+             void* buf,
+             size_t count,
+             off_t offset,
+             ssize_t* out_bytes_read) {
   TEST_AND_RETURN_FALSE_ERRNO(fd->Seek(offset, SEEK_SET) !=
                               static_cast<off_t>(-1));
   char* c_buf = static_cast<char*>(buf);
@@ -260,6 +239,31 @@
   return true;
 }
 
+bool PReadAll(const FileDescriptorPtr& fd,
+              void* buf,
+              size_t count,
+              off_t offset,
+              ssize_t* out_bytes_read) {
+  auto old_off = fd->Seek(0, SEEK_CUR);
+  TEST_AND_RETURN_FALSE_ERRNO(old_off >= 0);
+
+  auto success = ReadAll(fd, buf, count, offset, out_bytes_read);
+  TEST_AND_RETURN_FALSE_ERRNO(fd->Seek(old_off, SEEK_SET) == old_off);
+  return success;
+}
+
+bool PWriteAll(const FileDescriptorPtr& fd,
+               const void* buf,
+               size_t count,
+               off_t offset) {
+  auto old_off = fd->Seek(0, SEEK_CUR);
+  TEST_AND_RETURN_FALSE_ERRNO(old_off >= 0);
+
+  auto success = WriteAll(fd, buf, count, offset);
+  TEST_AND_RETURN_FALSE_ERRNO(fd->Seek(old_off, SEEK_SET) == old_off);
+  return success;
+}
+
 // Append |nbytes| of content from |buf| to the vector pointed to by either
 // |vec_p| or |str_p|.
 static void AppendBytes(const uint8_t* buf,
@@ -911,6 +915,25 @@
   return true;
 }
 
+bool GetVpdValue(string key, string* result) {
+  int exit_code = 0;
+  string value, error;
+  vector<string> cmd = {"vpd_get_value", key};
+  if (!chromeos_update_engine::Subprocess::SynchronousExec(
+          cmd, &exit_code, &value, &error) ||
+      exit_code) {
+    LOG(ERROR) << "Failed to get vpd key for " << value
+               << " with exit code: " << exit_code << " and error: " << error;
+    return false;
+  } else if (!error.empty()) {
+    LOG(INFO) << "vpd_get_value succeeded but with following errors: " << error;
+  }
+
+  base::TrimWhitespaceASCII(value, base::TRIM_ALL, &value);
+  *result = value;
+  return true;
+}
+
 bool GetBootId(string* boot_id) {
   TEST_AND_RETURN_FALSE(
       base::ReadFileToString(base::FilePath(kBootIdPath), boot_id));
diff --git a/common/utils.h b/common/utils.h
index 0a1dc0c..616de06 100644
--- a/common/utils.h
+++ b/common/utils.h
@@ -18,6 +18,7 @@
 #define UPDATE_ENGINE_COMMON_UTILS_H_
 
 #include <errno.h>
+#include <sys/types.h>
 #include <time.h>
 #include <unistd.h>
 
@@ -53,10 +54,6 @@
 std::string CalculateP2PFileId(const brillo::Blob& payload_hash,
                                size_t payload_size);
 
-// Parse the firmware version from one line of output from the
-// "mosys" command.
-std::string ParseECVersion(std::string input_line);
-
 // Writes the data passed to path. The file at path will be overwritten if it
 // exists. Returns true on success, false otherwise.
 bool WriteFile(const char* path, const void* data, size_t data_len);
@@ -67,6 +64,15 @@
 bool PWriteAll(int fd, const void* buf, size_t count, off_t offset);
 
 bool WriteAll(const FileDescriptorPtr& fd, const void* buf, size_t count);
+// WriteAll writes data at specified offset, but it modifies file position.
+bool WriteAll(const FileDescriptorPtr& fd,
+              const void* buf,
+              size_t count,
+              off_t off);
+
+// https://man7.org/linux/man-pages/man2/pread.2.html
+// PWriteAll writes data at specified offset, but it DOES NOT modify file
+// position. Behaves similar to linux' pwrite syscall.
 bool PWriteAll(const FileDescriptorPtr& fd,
                const void* buf,
                size_t count,
@@ -85,6 +91,16 @@
 bool PReadAll(
     int fd, void* buf, size_t count, off_t offset, ssize_t* out_bytes_read);
 
+// Reads data at specified offset, this function does change file position.
+bool ReadAll(const FileDescriptorPtr& fd,
+             void* buf,
+             size_t count,
+             off_t offset,
+             ssize_t* out_bytes_read);
+
+// https://man7.org/linux/man-pages/man2/pread.2.html
+// Reads data at specified offset, this function DOES NOT change file position.
+// Behavior is similar to linux's pread syscall.
 bool PReadAll(const FileDescriptorPtr& fd,
               void* buf,
               size_t count,
@@ -292,6 +308,10 @@
 // reboot. Returns whether it succeeded getting the boot_id.
 bool GetBootId(std::string* boot_id);
 
+// Gets a string value from the vpd for a given key using the `vpd_get_value`
+// shell command. Returns true on success.
+bool GetVpdValue(std::string key, std::string* result);
+
 // This function gets the file path of the file pointed to by FileDiscriptor.
 std::string GetFilePath(int fd);
 
@@ -370,6 +390,42 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedPathUnlinker);
 };
 
+class ScopedTempFile {
+ public:
+  ScopedTempFile() : ScopedTempFile("update_engine_temp.XXXXXX") {}
+
+  // If |open_fd| is true, a writable file descriptor will be opened for this
+  // file.
+  explicit ScopedTempFile(const std::string& pattern, bool open_fd = false) {
+    CHECK(utils::MakeTempFile(pattern, &path_, open_fd ? &fd_ : nullptr));
+    unlinker_.reset(new ScopedPathUnlinker(path_));
+    if (open_fd) {
+      CHECK_GE(fd_, 0);
+      fd_closer_.reset(new ScopedFdCloser(&fd_));
+    }
+  }
+  virtual ~ScopedTempFile() = default;
+
+  const std::string& path() const { return path_; }
+  int fd() const {
+    CHECK(fd_closer_);
+    return fd_;
+  }
+  void CloseFd() {
+    CHECK(fd_closer_);
+    fd_closer_.reset();
+  }
+
+ private:
+  std::string path_;
+  std::unique_ptr<ScopedPathUnlinker> unlinker_;
+
+  int fd_{-1};
+  std::unique_ptr<ScopedFdCloser> fd_closer_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedTempFile);
+};
+
 // A little object to call ActionComplete on the ActionProcessor when
 // it's destructed.
 class ScopedActionCompleter {
diff --git a/common/utils_unittest.cc b/common/utils_unittest.cc
index d73b3da..20c6b84 100644
--- a/common/utils_unittest.cc
+++ b/common/utils_unittest.cc
@@ -41,25 +41,12 @@
 
 class UtilsTest : public ::testing::Test {};
 
-TEST(UtilsTest, CanParseECVersion) {
-  // Should be able to parse and valid key value line.
-  EXPECT_EQ("12345", utils::ParseECVersion("fw_version=12345"));
-  EXPECT_EQ("123456",
-            utils::ParseECVersion("b=1231a fw_version=123456 a=fasd2"));
-  EXPECT_EQ("12345", utils::ParseECVersion("fw_version=12345"));
-  EXPECT_EQ("00VFA616",
-            utils::ParseECVersion("vendor=\"sam\" fw_version=\"00VFA616\""));
-
-  // For invalid entries, should return the empty string.
-  EXPECT_EQ("", utils::ParseECVersion("b=1231a fw_version a=fasd2"));
-}
-
 TEST(UtilsTest, WriteFileOpenFailure) {
   EXPECT_FALSE(utils::WriteFile("/this/doesn't/exist", "hello", 5));
 }
 
 TEST(UtilsTest, WriteFileReadFile) {
-  test_utils::ScopedTempFile file;
+  ScopedTempFile file;
   EXPECT_TRUE(utils::WriteFile(file.path().c_str(), "hello", 5));
 
   brillo::Blob readback;
@@ -73,7 +60,7 @@
 }
 
 TEST(UtilsTest, ReadFileChunk) {
-  test_utils::ScopedTempFile file;
+  ScopedTempFile file;
   brillo::Blob data;
   const size_t kSize = 1024 * 1024;
   for (size_t i = 0; i < kSize; i++) {
@@ -162,7 +149,7 @@
 namespace {
 void GetFileFormatTester(const string& expected,
                          const vector<uint8_t>& contents) {
-  test_utils::ScopedTempFile file;
+  ScopedTempFile file;
   ASSERT_TRUE(utils::WriteFile(file.path().c_str(),
                                reinterpret_cast<const char*>(contents.data()),
                                contents.size()));
@@ -391,7 +378,7 @@
 }
 
 TEST(UtilsTest, RunAsRootUnmountFilesystemBusyFailureTest) {
-  test_utils::ScopedTempFile tmp_image("img.XXXXXX");
+  ScopedTempFile tmp_image("img.XXXXXX");
 
   EXPECT_TRUE(base::CopyFile(
       test_utils::GetBuildArtifactsPath().Append("gen/disk_ext2_4k.img"),
@@ -431,7 +418,7 @@
   EXPECT_TRUE(mnt_dir.CreateUniqueTempDir());
   EXPECT_FALSE(utils::IsMountpoint(mnt_dir.GetPath().value()));
 
-  test_utils::ScopedTempFile file;
+  ScopedTempFile file;
   EXPECT_FALSE(utils::IsMountpoint(file.path()));
 }
 
@@ -473,7 +460,7 @@
 }
 
 TEST(UtilsTest, GetFilePathTest) {
-  test_utils::ScopedTempFile file;
+  ScopedTempFile file;
   int fd = HANDLE_EINTR(open(file.path().c_str(), O_RDONLY));
   EXPECT_GE(fd, 0);
   EXPECT_EQ(file.path(), utils::GetFilePath(fd));
diff --git a/connection_manager_android.cc b/connection_manager_android.cc
deleted file mode 100644
index 9d0c57b..0000000
--- a/connection_manager_android.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-// 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/connection_manager_android.h"
-
-#include <memory>
-
-namespace chromeos_update_engine {
-
-namespace connection_manager {
-std::unique_ptr<ConnectionManagerInterface> CreateConnectionManager(
-    SystemState* system_state) {
-  return std::unique_ptr<ConnectionManagerInterface>(
-      new ConnectionManagerAndroid());
-}
-}  // namespace connection_manager
-
-bool ConnectionManagerAndroid::GetConnectionProperties(
-    ConnectionType* out_type, ConnectionTethering* out_tethering) {
-  return false;
-}
-bool ConnectionManagerAndroid::IsUpdateAllowedOver(
-    ConnectionType type, ConnectionTethering tethering) const {
-  return true;
-}
-bool ConnectionManagerAndroid::IsAllowedConnectionTypesForUpdateSet() const {
-  return false;
-}
-
-}  // namespace chromeos_update_engine
diff --git a/connection_manager_android.h b/connection_manager_android.h
deleted file mode 100644
index 006f4ea..0000000
--- a/connection_manager_android.h
+++ /dev/null
@@ -1,44 +0,0 @@
-//
-// 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_CONNECTION_MANAGER_ANDROID_H_
-#define UPDATE_ENGINE_CONNECTION_MANAGER_ANDROID_H_
-
-#include <base/macros.h>
-
-#include "update_engine/connection_manager_interface.h"
-
-namespace chromeos_update_engine {
-
-// TODO(senj): Remove this class and use ShillProvider from the UpdateManager.
-class ConnectionManagerAndroid : public ConnectionManagerInterface {
- public:
-  ConnectionManagerAndroid() = default;
-  ~ConnectionManagerAndroid() override = default;
-
-  // ConnectionManagerInterface overrides.
-  bool GetConnectionProperties(ConnectionType* out_type,
-                               ConnectionTethering* out_tethering) override;
-  bool IsUpdateAllowedOver(ConnectionType type,
-                           ConnectionTethering tethering) const override;
-  bool IsAllowedConnectionTypesForUpdateSet() const override;
-
-  DISALLOW_COPY_AND_ASSIGN(ConnectionManagerAndroid);
-};
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_CONNECTION_MANAGER_ANDROID_H_
diff --git a/boot_control_chromeos.cc b/cros/boot_control_chromeos.cc
similarity index 97%
rename from boot_control_chromeos.cc
rename to cros/boot_control_chromeos.cc
index 95456f0..17659ae 100644
--- a/boot_control_chromeos.cc
+++ b/cros/boot_control_chromeos.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/boot_control_chromeos.h"
+#include "update_engine/cros/boot_control_chromeos.h"
 
 #include <memory>
 #include <string>
@@ -128,8 +128,9 @@
   }
   if (current_slot_ >= num_slots_) {
     LOG(ERROR) << "Couldn't find the slot number corresponding to the "
-               << "partition " << boot_device << ", number of slots: "
-               << num_slots_ << ". This device is not updateable.";
+               << "partition " << boot_device
+               << ", number of slots: " << num_slots_
+               << ". This device is not updateable.";
     num_slots_ = 1;
     current_slot_ = BootControlInterface::kInvalidSlot;
     return false;
diff --git a/boot_control_chromeos.h b/cros/boot_control_chromeos.h
similarity index 96%
rename from boot_control_chromeos.h
rename to cros/boot_control_chromeos.h
index 4271672..0dff2c0 100644
--- a/boot_control_chromeos.h
+++ b/cros/boot_control_chromeos.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_BOOT_CONTROL_CHROMEOS_H_
-#define UPDATE_ENGINE_BOOT_CONTROL_CHROMEOS_H_
+#ifndef UPDATE_ENGINE_CROS_BOOT_CONTROL_CHROMEOS_H_
+#define UPDATE_ENGINE_CROS_BOOT_CONTROL_CHROMEOS_H_
 
 #include <memory>
 #include <string>
@@ -101,4 +101,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_BOOT_CONTROL_CHROMEOS_H_
+#endif  // UPDATE_ENGINE_CROS_BOOT_CONTROL_CHROMEOS_H_
diff --git a/boot_control_chromeos_unittest.cc b/cros/boot_control_chromeos_unittest.cc
similarity index 97%
rename from boot_control_chromeos_unittest.cc
rename to cros/boot_control_chromeos_unittest.cc
index 1c40dce..fc1dd1e 100644
--- a/boot_control_chromeos_unittest.cc
+++ b/cros/boot_control_chromeos_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/boot_control_chromeos.h"
+#include "update_engine/cros/boot_control_chromeos.h"
 
 #include <gtest/gtest.h>
 
diff --git a/chrome_browser_proxy_resolver.cc b/cros/chrome_browser_proxy_resolver.cc
similarity index 94%
rename from chrome_browser_proxy_resolver.cc
rename to cros/chrome_browser_proxy_resolver.cc
index bfb58f7..3ea8a9b 100644
--- a/chrome_browser_proxy_resolver.cc
+++ b/cros/chrome_browser_proxy_resolver.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/chrome_browser_proxy_resolver.h"
+#include "update_engine/cros/chrome_browser_proxy_resolver.h"
 
 #include <utility>
 
@@ -23,7 +23,7 @@
 #include <base/strings/string_util.h>
 #include <brillo/http/http_proxy.h>
 
-#include "update_engine/dbus_connection.h"
+#include "update_engine/cros/dbus_connection.h"
 
 namespace chromeos_update_engine {
 
diff --git a/chrome_browser_proxy_resolver.h b/cros/chrome_browser_proxy_resolver.h
similarity index 91%
rename from chrome_browser_proxy_resolver.h
rename to cros/chrome_browser_proxy_resolver.h
index 10a55fb..76848ef 100644
--- a/chrome_browser_proxy_resolver.h
+++ b/cros/chrome_browser_proxy_resolver.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CHROME_BROWSER_PROXY_RESOLVER_H_
-#define UPDATE_ENGINE_CHROME_BROWSER_PROXY_RESOLVER_H_
+#ifndef UPDATE_ENGINE_CROS_CHROME_BROWSER_PROXY_RESOLVER_H_
+#define UPDATE_ENGINE_CROS_CHROME_BROWSER_PROXY_RESOLVER_H_
 
 #include <deque>
 #include <map>
@@ -63,4 +63,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CHROME_BROWSER_PROXY_RESOLVER_H_
+#endif  // UPDATE_ENGINE_CROS_CHROME_BROWSER_PROXY_RESOLVER_H_
diff --git a/common_service.cc b/cros/common_service.cc
similarity index 97%
rename from common_service.cc
rename to cros/common_service.cc
index 85fb9e4..aecad8b 100644
--- a/common_service.cc
+++ b/cros/common_service.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/common_service.h"
+#include "update_engine/cros/common_service.h"
 
 #include <string>
 
@@ -30,12 +30,12 @@
 #include "update_engine/common/hardware_interface.h"
 #include "update_engine/common/prefs.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/connection_manager_interface.h"
-#include "update_engine/omaha_request_params.h"
-#include "update_engine/omaha_utils.h"
-#include "update_engine/p2p_manager.h"
-#include "update_engine/payload_state_interface.h"
-#include "update_engine/update_attempter.h"
+#include "update_engine/cros/connection_manager_interface.h"
+#include "update_engine/cros/omaha_request_params.h"
+#include "update_engine/cros/omaha_utils.h"
+#include "update_engine/cros/p2p_manager.h"
+#include "update_engine/cros/payload_state_interface.h"
+#include "update_engine/cros/update_attempter.h"
 
 using base::StringPrintf;
 using brillo::ErrorPtr;
diff --git a/common_service.h b/cros/common_service.h
similarity index 97%
rename from common_service.h
rename to cros/common_service.h
index cfcece5..6169d9c 100644
--- a/common_service.h
+++ b/cros/common_service.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_COMMON_SERVICE_H_
-#define UPDATE_ENGINE_COMMON_SERVICE_H_
+#ifndef UPDATE_ENGINE_CROS_SERVICE_H_
+#define UPDATE_ENGINE_CROS_SERVICE_H_
 
 #include <inttypes.h>
 
@@ -26,7 +26,7 @@
 #include <brillo/errors/error.h>
 
 #include "update_engine/client_library/include/update_engine/update_status.h"
-#include "update_engine/system_state.h"
+#include "update_engine/common/system_state.h"
 
 namespace chromeos_update_engine {
 
@@ -167,4 +167,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_COMMON_SERVICE_H_
+#endif  // UPDATE_ENGINE_CROS_SERVICE_H_
diff --git a/common_service_unittest.cc b/cros/common_service_unittest.cc
similarity index 97%
rename from common_service_unittest.cc
rename to cros/common_service_unittest.cc
index 3dc8a22..733ec0a 100644
--- a/common_service_unittest.cc
+++ b/cros/common_service_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/common_service.h"
+#include "update_engine/cros/common_service.h"
 
 #include <gtest/gtest.h>
 #include <string>
@@ -25,8 +25,8 @@
 #include <policy/mock_device_policy.h>
 
 #include "update_engine/common/fake_prefs.h"
-#include "update_engine/fake_system_state.h"
-#include "update_engine/omaha_utils.h"
+#include "update_engine/cros/fake_system_state.h"
+#include "update_engine/cros/omaha_utils.h"
 
 using std::string;
 using std::vector;
diff --git a/connection_manager.cc b/cros/connection_manager.cc
similarity index 96%
rename from connection_manager.cc
rename to cros/connection_manager.cc
index fe43f37..331f76b 100644
--- a/connection_manager.cc
+++ b/cros/connection_manager.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/connection_manager.h"
+#include "update_engine/cros/connection_manager.h"
 
 #include <memory>
 #include <set>
@@ -26,12 +26,12 @@
 #include <shill/dbus-constants.h>
 #include <shill/dbus-proxies.h>
 
+#include "update_engine/common/connection_utils.h"
 #include "update_engine/common/prefs.h"
+#include "update_engine/common/system_state.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/connection_utils.h"
-#include "update_engine/shill_proxy.h"
-#include "update_engine/system_state.h"
-#include "update_engine/update_attempter.h"
+#include "update_engine/cros/shill_proxy.h"
+#include "update_engine/cros/update_attempter.h"
 
 using org::chromium::flimflam::ManagerProxyInterface;
 using org::chromium::flimflam::ServiceProxyInterface;
diff --git a/connection_manager.h b/cros/connection_manager.h
similarity index 89%
rename from connection_manager.h
rename to cros/connection_manager.h
index d8527a3..b1fb961 100644
--- a/connection_manager.h
+++ b/cros/connection_manager.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CONNECTION_MANAGER_H_
-#define UPDATE_ENGINE_CONNECTION_MANAGER_H_
+#ifndef UPDATE_ENGINE_CROS_CONNECTION_MANAGER_H_
+#define UPDATE_ENGINE_CROS_CONNECTION_MANAGER_H_
 
 #include <memory>
 #include <string>
@@ -23,8 +23,8 @@
 #include <base/macros.h>
 #include <dbus/object_path.h>
 
-#include "update_engine/connection_manager_interface.h"
-#include "update_engine/shill_proxy_interface.h"
+#include "update_engine/cros/connection_manager_interface.h"
+#include "update_engine/cros/shill_proxy_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -66,4 +66,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CONNECTION_MANAGER_H_
+#endif  // UPDATE_ENGINE_CROS_CONNECTION_MANAGER_H_
diff --git a/connection_manager_interface.h b/cros/connection_manager_interface.h
similarity index 90%
rename from connection_manager_interface.h
rename to cros/connection_manager_interface.h
index 9f77989..6dd9fbd 100644
--- a/connection_manager_interface.h
+++ b/cros/connection_manager_interface.h
@@ -14,14 +14,14 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CONNECTION_MANAGER_INTERFACE_H_
-#define UPDATE_ENGINE_CONNECTION_MANAGER_INTERFACE_H_
+#ifndef UPDATE_ENGINE_CROS_CONNECTION_MANAGER_INTERFACE_H_
+#define UPDATE_ENGINE_CROS_CONNECTION_MANAGER_INTERFACE_H_
 
 #include <memory>
 
 #include <base/macros.h>
 
-#include "update_engine/connection_utils.h"
+#include "update_engine/common/connection_utils.h"
 
 namespace chromeos_update_engine {
 
@@ -65,4 +65,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CONNECTION_MANAGER_INTERFACE_H_
+#endif  // UPDATE_ENGINE_CROS_CONNECTION_MANAGER_INTERFACE_H_
diff --git a/connection_manager_unittest.cc b/cros/connection_manager_unittest.cc
similarity index 98%
rename from connection_manager_unittest.cc
rename to cros/connection_manager_unittest.cc
index 97436c9..3f1ee5a 100644
--- a/connection_manager_unittest.cc
+++ b/cros/connection_manager_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/connection_manager.h"
+#include "update_engine/cros/connection_manager.h"
 
 #include <memory>
 #include <set>
@@ -32,8 +32,8 @@
 #include <shill/dbus-proxy-mocks.h>
 
 #include "update_engine/common/test_utils.h"
-#include "update_engine/fake_shill_proxy.h"
-#include "update_engine/fake_system_state.h"
+#include "update_engine/cros/fake_shill_proxy.h"
+#include "update_engine/cros/fake_system_state.h"
 
 using chromeos_update_engine::connection_utils::StringForConnectionType;
 using org::chromium::flimflam::ManagerProxyMock;
diff --git a/daemon_chromeos.cc b/cros/daemon_chromeos.cc
similarity index 95%
rename from daemon_chromeos.cc
rename to cros/daemon_chromeos.cc
index 21740d8..a7cad8c 100644
--- a/daemon_chromeos.cc
+++ b/cros/daemon_chromeos.cc
@@ -14,14 +14,14 @@
 // limitations under the License.
 //
 
-#include "update_engine/daemon_chromeos.h"
+#include "update_engine/cros/daemon_chromeos.h"
 
 #include <sysexits.h>
 
 #include <base/bind.h>
 #include <base/location.h>
 
-#include "update_engine/real_system_state.h"
+#include "update_engine/cros/real_system_state.h"
 
 using brillo::Daemon;
 using std::unique_ptr;
diff --git a/daemon_chromeos.h b/cros/daemon_chromeos.h
similarity index 84%
rename from daemon_chromeos.h
rename to cros/daemon_chromeos.h
index 657e797..5d568c7 100644
--- a/daemon_chromeos.h
+++ b/cros/daemon_chromeos.h
@@ -14,15 +14,15 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_DAEMON_CHROMEOS_H_
-#define UPDATE_ENGINE_DAEMON_CHROMEOS_H_
+#ifndef UPDATE_ENGINE_CROS_DAEMON_CHROMEOS_H_
+#define UPDATE_ENGINE_CROS_DAEMON_CHROMEOS_H_
 
 #include <memory>
 
+#include "update_engine/common/daemon_base.h"
+#include "update_engine/common/daemon_state_interface.h"
 #include "update_engine/common/subprocess.h"
-#include "update_engine/daemon_base.h"
-#include "update_engine/daemon_state_interface.h"
-#include "update_engine/dbus_service.h"
+#include "update_engine/cros/dbus_service.h"
 
 namespace chromeos_update_engine {
 
@@ -56,4 +56,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_DAEMON_CHROMEOS_H_
+#endif  // UPDATE_ENGINE_CROS_DAEMON_CHROMEOS_H_
diff --git a/dbus_connection.cc b/cros/dbus_connection.cc
similarity index 96%
rename from dbus_connection.cc
rename to cros/dbus_connection.cc
index cf17ec9..6808bae 100644
--- a/dbus_connection.cc
+++ b/cros/dbus_connection.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/dbus_connection.h"
+#include "update_engine/cros/dbus_connection.h"
 
 #include <base/time/time.h>
 
diff --git a/dbus_connection.h b/cros/dbus_connection.h
similarity index 87%
rename from dbus_connection.h
rename to cros/dbus_connection.h
index c3205ba..8f0d6f1 100644
--- a/dbus_connection.h
+++ b/cros/dbus_connection.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_DBUS_CONNECTION_H_
-#define UPDATE_ENGINE_DBUS_CONNECTION_H_
+#ifndef UPDATE_ENGINE_CROS_DBUS_CONNECTION_H_
+#define UPDATE_ENGINE_CROS_DBUS_CONNECTION_H_
 
 #include <base/memory/ref_counted.h>
 #include <brillo/dbus/dbus_connection.h>
@@ -41,4 +41,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_DBUS_CONNECTION_H_
+#endif  // UPDATE_ENGINE_CROS_DBUS_CONNECTION_H_
diff --git a/dbus_service.cc b/cros/dbus_service.cc
similarity index 98%
rename from dbus_service.cc
rename to cros/dbus_service.cc
index a282d1e..d115195 100644
--- a/dbus_service.cc
+++ b/cros/dbus_service.cc
@@ -14,14 +14,14 @@
 // limitations under the License.
 //
 
-#include "update_engine/dbus_service.h"
+#include "update_engine/cros/dbus_service.h"
 
 #include <string>
 #include <vector>
 
 #include <update_engine/dbus-constants.h>
 
-#include "update_engine/dbus_connection.h"
+#include "update_engine/cros/dbus_connection.h"
 #include "update_engine/proto_bindings/update_engine.pb.h"
 #include "update_engine/update_status_utils.h"
 
diff --git a/dbus_service.h b/cros/dbus_service.h
similarity index 96%
rename from dbus_service.h
rename to cros/dbus_service.h
index 873909e..9e4457f 100644
--- a/dbus_service.h
+++ b/cros/dbus_service.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_DBUS_SERVICE_H_
-#define UPDATE_ENGINE_DBUS_SERVICE_H_
+#ifndef UPDATE_ENGINE_CROS_DBUS_SERVICE_H_
+#define UPDATE_ENGINE_CROS_DBUS_SERVICE_H_
 
 #include <inttypes.h>
 
@@ -27,9 +27,9 @@
 #include <brillo/errors/error.h>
 #include <update_engine/proto_bindings/update_engine.pb.h>
 
-#include "update_engine/common_service.h"
-#include "update_engine/service_observer_interface.h"
-#include "update_engine/update_attempter.h"
+#include "update_engine/common/service_observer_interface.h"
+#include "update_engine/cros/common_service.h"
+#include "update_engine/cros/update_attempter.h"
 
 #include "dbus_bindings/org.chromium.UpdateEngineInterface.h"
 
@@ -191,4 +191,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_DBUS_SERVICE_H_
+#endif  // UPDATE_ENGINE_CROS_DBUS_SERVICE_H_
diff --git a/dbus_test_utils.h b/cros/dbus_test_utils.h
similarity index 95%
rename from dbus_test_utils.h
rename to cros/dbus_test_utils.h
index 72fd4e0..1116c52 100644
--- a/dbus_test_utils.h
+++ b/cros/dbus_test_utils.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_DBUS_TEST_UTILS_H_
-#define UPDATE_ENGINE_DBUS_TEST_UTILS_H_
+#ifndef UPDATE_ENGINE_CROS_DBUS_TEST_UTILS_H_
+#define UPDATE_ENGINE_CROS_DBUS_TEST_UTILS_H_
 
 #include <memory>
 #include <set>
@@ -88,4 +88,4 @@
 }  // namespace dbus_test_utils
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_DBUS_TEST_UTILS_H_
+#endif  // UPDATE_ENGINE_CROS_DBUS_TEST_UTILS_H_
diff --git a/dlcservice_chromeos.cc b/cros/dlcservice_chromeos.cc
similarity index 95%
rename from dlcservice_chromeos.cc
rename to cros/dlcservice_chromeos.cc
index 08482ee..e510c1d 100644
--- a/dlcservice_chromeos.cc
+++ b/cros/dlcservice_chromeos.cc
@@ -14,14 +14,14 @@
 // limitations under the License.
 //
 
-#include "update_engine/dlcservice_chromeos.h"
+#include "update_engine/cros/dlcservice_chromeos.h"
 
 #include <brillo/errors/error.h>
 #include <dlcservice/proto_bindings/dlcservice.pb.h>
 // NOLINTNEXTLINE(build/include_alpha) "dbus-proxies.h" needs "dlcservice.pb.h"
 #include <dlcservice/dbus-proxies.h>
 
-#include "update_engine/dbus_connection.h"
+#include "update_engine/cros/dbus_connection.h"
 
 using std::string;
 using std::vector;
diff --git a/dlcservice_chromeos.h b/cros/dlcservice_chromeos.h
similarity index 91%
rename from dlcservice_chromeos.h
rename to cros/dlcservice_chromeos.h
index 8828e1a..3f11b12 100644
--- a/dlcservice_chromeos.h
+++ b/cros/dlcservice_chromeos.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_DLCSERVICE_CHROMEOS_H_
-#define UPDATE_ENGINE_DLCSERVICE_CHROMEOS_H_
+#ifndef UPDATE_ENGINE_CROS_DLCSERVICE_CHROMEOS_H_
+#define UPDATE_ENGINE_CROS_DLCSERVICE_CHROMEOS_H_
 
 #include <memory>
 #include <string>
@@ -52,4 +52,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_DLCSERVICE_CHROMEOS_H_
+#endif  // UPDATE_ENGINE_CROS_DLCSERVICE_CHROMEOS_H_
diff --git a/excluder_chromeos.cc b/cros/excluder_chromeos.cc
similarity index 94%
rename from excluder_chromeos.cc
rename to cros/excluder_chromeos.cc
index bfd6f04..4796525 100644
--- a/excluder_chromeos.cc
+++ b/cros/excluder_chromeos.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/excluder_chromeos.h"
+#include "update_engine/cros/excluder_chromeos.h"
 
 #include <memory>
 #include <vector>
@@ -26,7 +26,7 @@
 
 #include "update_engine/common/constants.h"
 #include "update_engine/common/prefs.h"
-#include "update_engine/system_state.h"
+#include "update_engine/common/system_state.h"
 
 using std::string;
 using std::vector;
diff --git a/excluder_chromeos.h b/cros/excluder_chromeos.h
similarity index 90%
rename from excluder_chromeos.h
rename to cros/excluder_chromeos.h
index e4c1a52..2480066 100644
--- a/excluder_chromeos.h
+++ b/cros/excluder_chromeos.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_EXCLUDER_CHROMEOS_H_
-#define UPDATE_ENGINE_EXCLUDER_CHROMEOS_H_
+#ifndef UPDATE_ENGINE_CROS_EXCLUDER_CHROMEOS_H_
+#define UPDATE_ENGINE_CROS_EXCLUDER_CHROMEOS_H_
 
 #include <string>
 
@@ -49,4 +49,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_EXCLUDER_CHROMEOS_H_
+#endif  // UPDATE_ENGINE_CROS_EXCLUDER_CHROMEOS_H_
diff --git a/excluder_chromeos_unittest.cc b/cros/excluder_chromeos_unittest.cc
similarity index 97%
rename from excluder_chromeos_unittest.cc
rename to cros/excluder_chromeos_unittest.cc
index dba77e4..3602e56 100644
--- a/excluder_chromeos_unittest.cc
+++ b/cros/excluder_chromeos_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/excluder_chromeos.h"
+#include "update_engine/cros/excluder_chromeos.h"
 
 #include <memory>
 
diff --git a/fake_p2p_manager.h b/cros/fake_p2p_manager.h
similarity index 94%
rename from fake_p2p_manager.h
rename to cros/fake_p2p_manager.h
index 1f8ae95..1011b7e 100644
--- a/fake_p2p_manager.h
+++ b/cros/fake_p2p_manager.h
@@ -14,12 +14,12 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_FAKE_P2P_MANAGER_H_
-#define UPDATE_ENGINE_FAKE_P2P_MANAGER_H_
+#ifndef UPDATE_ENGINE_CROS_FAKE_P2P_MANAGER_H_
+#define UPDATE_ENGINE_CROS_FAKE_P2P_MANAGER_H_
 
 #include <string>
 
-#include "update_engine/p2p_manager.h"
+#include "update_engine/cros/p2p_manager.h"
 
 namespace chromeos_update_engine {
 
@@ -109,4 +109,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_FAKE_P2P_MANAGER_H_
+#endif  // UPDATE_ENGINE_CROS_FAKE_P2P_MANAGER_H_
diff --git a/fake_p2p_manager_configuration.h b/cros/fake_p2p_manager_configuration.h
similarity index 93%
rename from fake_p2p_manager_configuration.h
rename to cros/fake_p2p_manager_configuration.h
index f5b0e80..8d50ac8 100644
--- a/fake_p2p_manager_configuration.h
+++ b/cros/fake_p2p_manager_configuration.h
@@ -14,10 +14,10 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_FAKE_P2P_MANAGER_CONFIGURATION_H_
-#define UPDATE_ENGINE_FAKE_P2P_MANAGER_CONFIGURATION_H_
+#ifndef UPDATE_ENGINE_CROS_FAKE_P2P_MANAGER_CONFIGURATION_H_
+#define UPDATE_ENGINE_CROS_FAKE_P2P_MANAGER_CONFIGURATION_H_
 
-#include "update_engine/p2p_manager.h"
+#include "update_engine/cros/p2p_manager.h"
 
 #include <string>
 #include <vector>
@@ -99,4 +99,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_FAKE_P2P_MANAGER_CONFIGURATION_H_
+#endif  // UPDATE_ENGINE_CROS_FAKE_P2P_MANAGER_CONFIGURATION_H_
diff --git a/fake_shill_proxy.cc b/cros/fake_shill_proxy.cc
similarity index 96%
rename from fake_shill_proxy.cc
rename to cros/fake_shill_proxy.cc
index de96511..2d05a6b 100644
--- a/fake_shill_proxy.cc
+++ b/cros/fake_shill_proxy.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/fake_shill_proxy.h"
+#include "update_engine/cros/fake_shill_proxy.h"
 
 #include <utility>
 
diff --git a/fake_shill_proxy.h b/cros/fake_shill_proxy.h
similarity index 90%
rename from fake_shill_proxy.h
rename to cros/fake_shill_proxy.h
index ae17eaa..8c15a9d 100644
--- a/fake_shill_proxy.h
+++ b/cros/fake_shill_proxy.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_FAKE_SHILL_PROXY_H_
-#define UPDATE_ENGINE_FAKE_SHILL_PROXY_H_
+#ifndef UPDATE_ENGINE_CROS_FAKE_SHILL_PROXY_H_
+#define UPDATE_ENGINE_CROS_FAKE_SHILL_PROXY_H_
 
 #include <map>
 #include <memory>
@@ -25,7 +25,7 @@
 #include <shill/dbus-proxies.h>
 #include <shill/dbus-proxy-mocks.h>
 
-#include "update_engine/shill_proxy_interface.h"
+#include "update_engine/cros/shill_proxy_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -63,4 +63,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_FAKE_SHILL_PROXY_H_
+#endif  // UPDATE_ENGINE_CROS_FAKE_SHILL_PROXY_H_
diff --git a/fake_system_state.cc b/cros/fake_system_state.cc
similarity index 96%
rename from fake_system_state.cc
rename to cros/fake_system_state.cc
index 1bfcafa..9dfdc5b 100644
--- a/fake_system_state.cc
+++ b/cros/fake_system_state.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/fake_system_state.h"
+#include "update_engine/cros/fake_system_state.h"
 
 namespace chromeos_update_engine {
 
diff --git a/fake_system_state.h b/cros/fake_system_state.h
similarity index 93%
rename from fake_system_state.h
rename to cros/fake_system_state.h
index 24b1eec..2f92b7c 100644
--- a/fake_system_state.h
+++ b/cros/fake_system_state.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_FAKE_SYSTEM_STATE_H_
-#define UPDATE_ENGINE_FAKE_SYSTEM_STATE_H_
+#ifndef UPDATE_ENGINE_CROS_FAKE_SYSTEM_STATE_H_
+#define UPDATE_ENGINE_CROS_FAKE_SYSTEM_STATE_H_
 
 #include <base/logging.h>
 #include <gmock/gmock.h>
@@ -25,15 +25,15 @@
 #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/mock_metrics_reporter.h"
 #include "update_engine/common/mock_prefs.h"
-#include "update_engine/mock_connection_manager.h"
-#include "update_engine/mock_metrics_reporter.h"
-#include "update_engine/mock_omaha_request_params.h"
-#include "update_engine/mock_p2p_manager.h"
-#include "update_engine/mock_payload_state.h"
-#include "update_engine/mock_power_manager.h"
-#include "update_engine/mock_update_attempter.h"
-#include "update_engine/system_state.h"
+#include "update_engine/common/system_state.h"
+#include "update_engine/cros/mock_connection_manager.h"
+#include "update_engine/cros/mock_omaha_request_params.h"
+#include "update_engine/cros/mock_p2p_manager.h"
+#include "update_engine/cros/mock_payload_state.h"
+#include "update_engine/cros/mock_power_manager.h"
+#include "update_engine/cros/mock_update_attempter.h"
 #include "update_engine/update_manager/fake_update_manager.h"
 
 namespace chromeos_update_engine {
@@ -278,4 +278,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_FAKE_SYSTEM_STATE_H_
+#endif  // UPDATE_ENGINE_CROS_FAKE_SYSTEM_STATE_H_
diff --git a/hardware_chromeos.cc b/cros/hardware_chromeos.cc
similarity index 87%
rename from hardware_chromeos.cc
rename to cros/hardware_chromeos.cc
index 807e086..b9018ff 100644
--- a/hardware_chromeos.cc
+++ b/cros/hardware_chromeos.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/hardware_chromeos.h"
+#include "update_engine/cros/hardware_chromeos.h"
 
 #include <utility>
 
@@ -37,7 +37,10 @@
 #include "update_engine/common/platform_constants.h"
 #include "update_engine/common/subprocess.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/dbus_connection.h"
+#include "update_engine/cros/dbus_connection.h"
+#if USE_CFM
+#include "update_engine/cros/requisition_util.h"
+#endif
 
 using std::string;
 using std::vector;
@@ -81,29 +84,6 @@
 
 const char* kActivePingKey = "first_active_omaha_ping_sent";
 
-const char* kOemRequisitionKey = "oem_device_requisition";
-
-// Gets a string value from the vpd for a given key using the `vpd_get_value`
-// shell command. Returns true on success.
-int GetVpdValue(string key, string* result) {
-  int exit_code = 0;
-  string value, error;
-  vector<string> cmd = {"vpd_get_value", key};
-  if (!chromeos_update_engine::Subprocess::SynchronousExec(
-          cmd, &exit_code, &value, &error) ||
-      exit_code) {
-    LOG(ERROR) << "Failed to get vpd key for " << value
-               << " with exit code: " << exit_code << " and error: " << error;
-    return false;
-  } else if (!error.empty()) {
-    LOG(INFO) << "vpd_get_value succeeded but with following errors: " << error;
-  }
-
-  base::TrimWhitespaceASCII(value, base::TRIM_ALL, &value);
-  *result = value;
-  return true;
-}
-
 }  // namespace
 
 namespace chromeos_update_engine {
@@ -195,28 +175,13 @@
   return ReadValueFromCrosSystem("hwid");
 }
 
-string HardwareChromeOS::GetFirmwareVersion() const {
-  return ReadValueFromCrosSystem("fwid");
-}
-
-string HardwareChromeOS::GetECVersion() const {
-  string input_line, error;
-  int exit_code = 0;
-  vector<string> cmd = {"/usr/sbin/mosys", "-k", "ec", "info"};
-
-  if (!Subprocess::SynchronousExec(cmd, &exit_code, &input_line, &error) ||
-      exit_code != 0) {
-    LOG(ERROR) << "Unable to read EC info from mosys with exit code: "
-               << exit_code << " and error: " << error;
-    return "";
-  }
-
-  return utils::ParseECVersion(input_line);
-}
-
 string HardwareChromeOS::GetDeviceRequisition() const {
-  string requisition;
-  return GetVpdValue(kOemRequisitionKey, &requisition) ? requisition : "";
+#if USE_CFM
+  const char* kLocalStatePath = "/home/chronos/Local State";
+  return ReadDeviceRequisition(base::FilePath(kLocalStatePath));
+#else
+  return "";
+#endif
 }
 
 int HardwareChromeOS::GetMinKernelKeyVersion() const {
@@ -341,7 +306,7 @@
 
 bool HardwareChromeOS::GetFirstActiveOmahaPingSent() const {
   string active_ping_str;
-  if (!GetVpdValue(kActivePingKey, &active_ping_str)) {
+  if (!utils::GetVpdValue(kActivePingKey, &active_ping_str)) {
     return false;
   }
 
diff --git a/hardware_chromeos.h b/cros/hardware_chromeos.h
similarity index 92%
rename from hardware_chromeos.h
rename to cros/hardware_chromeos.h
index bbfe273..de84d78 100644
--- a/hardware_chromeos.h
+++ b/cros/hardware_chromeos.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_HARDWARE_CHROMEOS_H_
-#define UPDATE_ENGINE_HARDWARE_CHROMEOS_H_
+#ifndef UPDATE_ENGINE_CROS_HARDWARE_CHROMEOS_H_
+#define UPDATE_ENGINE_CROS_HARDWARE_CHROMEOS_H_
 
 #include <memory>
 #include <string>
@@ -46,8 +46,6 @@
   bool IsOOBEEnabled() const override;
   bool IsOOBEComplete(base::Time* out_time_of_oobe) const override;
   std::string GetHardwareClass() const override;
-  std::string GetFirmwareVersion() const override;
-  std::string GetECVersion() const override;
   std::string GetDeviceRequisition() const override;
   int GetMinKernelKeyVersion() const override;
   int GetMinFirmwareKeyVersion() const override;
@@ -87,4 +85,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_HARDWARE_CHROMEOS_H_
+#endif  // UPDATE_ENGINE_CROS_HARDWARE_CHROMEOS_H_
diff --git a/hardware_chromeos_unittest.cc b/cros/hardware_chromeos_unittest.cc
similarity index 97%
rename from hardware_chromeos_unittest.cc
rename to cros/hardware_chromeos_unittest.cc
index 162dec4..50bced6 100644
--- a/hardware_chromeos_unittest.cc
+++ b/cros/hardware_chromeos_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/hardware_chromeos.h"
+#include "update_engine/cros/hardware_chromeos.h"
 
 #include <memory>
 
diff --git a/image_properties.h b/cros/image_properties.h
similarity index 91%
rename from image_properties.h
rename to cros/image_properties.h
index 49fe82f..4957d12 100644
--- a/image_properties.h
+++ b/cros/image_properties.h
@@ -18,8 +18,8 @@
 // properties are meant to be constant during the life of this daemon, but can
 // be modified in dev-move or non-official builds.
 
-#ifndef UPDATE_ENGINE_IMAGE_PROPERTIES_H_
-#define UPDATE_ENGINE_IMAGE_PROPERTIES_H_
+#ifndef UPDATE_ENGINE_CROS_IMAGE_PROPERTIES_H_
+#define UPDATE_ENGINE_CROS_IMAGE_PROPERTIES_H_
 
 #include <string>
 
@@ -33,13 +33,9 @@
   std::string product_id;
   // The canary-channel product id.
   std::string canary_product_id;
-  // The system id for the Android Things SoM, empty for Chrome OS.
-  std::string system_id;
 
   // The product version of this image.
   std::string version;
-  // The system version of this image.
-  std::string system_version;
 
   // The version of all product components in key values pairs.
   std::string product_components;
@@ -103,4 +99,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_IMAGE_PROPERTIES_H_
+#endif  // UPDATE_ENGINE_CROS_IMAGE_PROPERTIES_H_
diff --git a/image_properties_chromeos.cc b/cros/image_properties_chromeos.cc
similarity index 98%
rename from image_properties_chromeos.cc
rename to cros/image_properties_chromeos.cc
index 5ab8f05..c22da7c 100644
--- a/image_properties_chromeos.cc
+++ b/cros/image_properties_chromeos.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/image_properties.h"
+#include "update_engine/cros/image_properties.h"
 
 #include <string>
 #include <vector>
@@ -26,8 +26,8 @@
 #include "update_engine/common/constants.h"
 #include "update_engine/common/hardware_interface.h"
 #include "update_engine/common/platform_constants.h"
+#include "update_engine/common/system_state.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/system_state.h"
 
 namespace {
 
diff --git a/image_properties_chromeos_unittest.cc b/cros/image_properties_chromeos_unittest.cc
similarity index 98%
rename from image_properties_chromeos_unittest.cc
rename to cros/image_properties_chromeos_unittest.cc
index d9ed688..4822995 100644
--- a/image_properties_chromeos_unittest.cc
+++ b/cros/image_properties_chromeos_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/image_properties.h"
+#include "update_engine/cros/image_properties.h"
 
 #include <string>
 
@@ -24,7 +24,7 @@
 
 #include "update_engine/common/constants.h"
 #include "update_engine/common/test_utils.h"
-#include "update_engine/fake_system_state.h"
+#include "update_engine/cros/fake_system_state.h"
 
 using chromeos_update_engine::test_utils::WriteFileString;
 using std::string;
diff --git a/logging.cc b/cros/logging.cc
similarity index 95%
rename from logging.cc
rename to cros/logging.cc
index 6320e36..e09166c 100644
--- a/logging.cc
+++ b/cros/logging.cc
@@ -25,8 +25,8 @@
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
 
+#include "update_engine/common/logging.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/logging.h"
 
 using std::string;
 
@@ -79,7 +79,11 @@
   if (log_to_file) {
     log_file = SetupLogFile(kSystemLogsRoot);
     log_settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE;
+#if BASE_VER < 780000
     log_settings.log_file = log_file.c_str();
+#else
+    log_settings.log_file_path = log_file.c_str();
+#endif
   }
   logging::InitLogging(log_settings);
 }
diff --git a/metrics_reporter_omaha.cc b/cros/metrics_reporter_omaha.cc
similarity index 82%
rename from metrics_reporter_omaha.cc
rename to cros/metrics_reporter_omaha.cc
index fb4e4ce..2cc0de5 100644
--- a/metrics_reporter_omaha.cc
+++ b/cros/metrics_reporter_omaha.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/metrics_reporter_omaha.h"
+#include "update_engine/cros/metrics_reporter_omaha.h"
 
 #include <memory>
 
@@ -25,15 +25,16 @@
 #include "update_engine/common/clock_interface.h"
 #include "update_engine/common/constants.h"
 #include "update_engine/common/prefs_interface.h"
+#include "update_engine/common/system_state.h"
 #include "update_engine/common/utils.h"
+#include "update_engine/cros/omaha_request_params.h"
 #include "update_engine/metrics_utils.h"
-#include "update_engine/omaha_request_params.h"
-#include "update_engine/system_state.h"
 
+using base::Time;
+using base::TimeDelta;
 using std::string;
 
 namespace chromeos_update_engine {
-
 namespace metrics {
 
 // UpdateEngine.Daily.* metrics.
@@ -146,8 +147,6 @@
 
 void MetricsReporterOmaha::ReportDailyMetrics(base::TimeDelta os_age) {
   string metric = metrics::kMetricDailyOSAgeDays;
-  LOG(INFO) << "Uploading " << utils::FormatTimeDelta(os_age) << " for metric "
-            << metric;
   metrics_lib_->SendToUMA(metric,
                           static_cast<int>(os_age.InDays()),
                           0,       // min: 0 days
@@ -168,31 +167,25 @@
     metric = metrics::kMetricCheckResult;
     value = static_cast<int>(result);
     max_value = static_cast<int>(metrics::CheckResult::kNumConstants) - 1;
-    LOG(INFO) << "Sending " << value << " for metric " << metric << " (enum)";
     metrics_lib_->SendEnumToUMA(metric, value, max_value);
   }
   if (reaction != metrics::CheckReaction::kUnset) {
     metric = metrics::kMetricCheckReaction;
     value = static_cast<int>(reaction);
     max_value = static_cast<int>(metrics::CheckReaction::kNumConstants) - 1;
-    LOG(INFO) << "Sending " << value << " for metric " << metric << " (enum)";
     metrics_lib_->SendEnumToUMA(metric, value, max_value);
   }
   if (download_error_code != metrics::DownloadErrorCode::kUnset) {
     metric = metrics::kMetricCheckDownloadErrorCode;
     value = static_cast<int>(download_error_code);
-    LOG(INFO) << "Sending " << value << " for metric " << metric << " (sparse)";
     metrics_lib_->SendSparseToUMA(metric, value);
   }
 
   base::TimeDelta time_since_last;
-  if (metrics_utils::WallclockDurationHelper(
-          system_state,
-          kPrefsMetricsCheckLastReportingTime,
-          &time_since_last)) {
+  if (WallclockDurationHelper(system_state,
+                              kPrefsMetricsCheckLastReportingTime,
+                              &time_since_last)) {
     metric = metrics::kMetricCheckTimeSinceLastCheckMinutes;
-    LOG(INFO) << "Sending " << utils::FormatTimeDelta(time_since_last)
-              << " for metric " << metric;
     metrics_lib_->SendToUMA(metric,
                             time_since_last.InMinutes(),
                             0,             // min: 0 min
@@ -202,11 +195,9 @@
 
   base::TimeDelta uptime_since_last;
   static int64_t uptime_since_last_storage = 0;
-  if (metrics_utils::MonotonicDurationHelper(
+  if (MonotonicDurationHelper(
           system_state, &uptime_since_last_storage, &uptime_since_last)) {
     metric = metrics::kMetricCheckTimeSinceLastCheckUptimeMinutes;
-    LOG(INFO) << "Sending " << utils::FormatTimeDelta(uptime_since_last)
-              << " for metric " << metric;
     metrics_lib_->SendToUMA(metric,
                             uptime_since_last.InMinutes(),
                             0,             // min: 0 min
@@ -221,13 +212,9 @@
     value = utils::VersionPrefix(target_version);
     if (value != 0) {
       metric = metrics::kMetricCheckTargetVersion;
-      LOG(INFO) << "Sending " << value << " for metric " << metric
-                << " (sparse)";
       metrics_lib_->SendSparseToUMA(metric, value);
       if (system_state->request_params()->rollback_allowed()) {
         metric = metrics::kMetricCheckRollbackTargetVersion;
-        LOG(INFO) << "Sending " << value << " for metric " << metric
-                  << " (sparse)";
         metrics_lib_->SendSparseToUMA(metric, value);
       }
     }
@@ -239,8 +226,6 @@
   metrics::AttemptResult attempt_result =
       metrics::AttemptResult::kAbnormalTermination;
 
-  LOG(INFO) << "Uploading " << static_cast<int>(attempt_result)
-            << " for metric " << metric;
   metrics_lib_->SendEnumToUMA(
       metric,
       static_cast<int>(attempt_result),
@@ -257,7 +242,6 @@
     metrics::AttemptResult attempt_result,
     ErrorCode internal_error_code) {
   string metric = metrics::kMetricAttemptNumber;
-  LOG(INFO) << "Uploading " << attempt_number << " for metric " << metric;
   metrics_lib_->SendToUMA(metric,
                           attempt_number,
                           0,    // min: 0 attempts
@@ -265,13 +249,9 @@
                           50);  // num_buckets
 
   metric = metrics::kMetricAttemptPayloadType;
-  LOG(INFO) << "Uploading " << utils::ToString(payload_type) << " for metric "
-            << metric;
   metrics_lib_->SendEnumToUMA(metric, payload_type, kNumPayloadTypes);
 
   metric = metrics::kMetricAttemptDurationMinutes;
-  LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration)
-            << " for metric " << metric;
   metrics_lib_->SendToUMA(metric,
                           duration.InMinutes(),
                           0,             // min: 0 min
@@ -279,8 +259,6 @@
                           50);           // num_buckets
 
   metric = metrics::kMetricAttemptDurationUptimeMinutes;
-  LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration_uptime)
-            << " for metric " << metric;
   metrics_lib_->SendToUMA(metric,
                           duration_uptime.InMinutes(),
                           0,             // min: 0 min
@@ -289,7 +267,6 @@
 
   metric = metrics::kMetricAttemptPayloadSizeMiB;
   int64_t payload_size_mib = payload_size / kNumBytesInOneMiB;
-  LOG(INFO) << "Uploading " << payload_size_mib << " for metric " << metric;
   metrics_lib_->SendToUMA(metric,
                           payload_size_mib,
                           0,     // min: 0 MiB
@@ -297,8 +274,6 @@
                           50);   // num_buckets
 
   metric = metrics::kMetricAttemptResult;
-  LOG(INFO) << "Uploading " << static_cast<int>(attempt_result)
-            << " for metric " << metric;
   metrics_lib_->SendEnumToUMA(
       metric,
       static_cast<int>(attempt_result),
@@ -309,13 +284,10 @@
   }
 
   base::TimeDelta time_since_last;
-  if (metrics_utils::WallclockDurationHelper(
-          system_state,
-          kPrefsMetricsAttemptLastReportingTime,
-          &time_since_last)) {
+  if (WallclockDurationHelper(system_state,
+                              kPrefsMetricsAttemptLastReportingTime,
+                              &time_since_last)) {
     metric = metrics::kMetricAttemptTimeSinceLastAttemptMinutes;
-    LOG(INFO) << "Sending " << utils::FormatTimeDelta(time_since_last)
-              << " for metric " << metric;
     metrics_lib_->SendToUMA(metric,
                             time_since_last.InMinutes(),
                             0,             // min: 0 min
@@ -325,11 +297,9 @@
 
   static int64_t uptime_since_last_storage = 0;
   base::TimeDelta uptime_since_last;
-  if (metrics_utils::MonotonicDurationHelper(
+  if (MonotonicDurationHelper(
           system_state, &uptime_since_last_storage, &uptime_since_last)) {
     metric = metrics::kMetricAttemptTimeSinceLastAttemptUptimeMinutes;
-    LOG(INFO) << "Sending " << utils::FormatTimeDelta(uptime_since_last)
-              << " for metric " << metric;
     metrics_lib_->SendToUMA(metric,
                             uptime_since_last.InMinutes(),
                             0,             // min: 0 min
@@ -347,8 +317,6 @@
   string metric = metrics::kMetricAttemptPayloadBytesDownloadedMiB;
   int64_t payload_bytes_downloaded_mib =
       payload_bytes_downloaded / kNumBytesInOneMiB;
-  LOG(INFO) << "Uploading " << payload_bytes_downloaded_mib << " for metric "
-            << metric;
   metrics_lib_->SendToUMA(metric,
                           payload_bytes_downloaded_mib,
                           0,     // min: 0 MiB
@@ -357,8 +325,6 @@
 
   metric = metrics::kMetricAttemptPayloadDownloadSpeedKBps;
   int64_t payload_download_speed_kbps = payload_download_speed_bps / 1000;
-  LOG(INFO) << "Uploading " << payload_download_speed_kbps << " for metric "
-            << metric;
   metrics_lib_->SendToUMA(metric,
                           payload_download_speed_kbps,
                           0,          // min: 0 kB/s
@@ -366,20 +332,15 @@
                           50);        // num_buckets
 
   metric = metrics::kMetricAttemptDownloadSource;
-  LOG(INFO) << "Uploading " << download_source << " for metric " << metric;
   metrics_lib_->SendEnumToUMA(metric, download_source, kNumDownloadSources);
 
   if (payload_download_error_code != metrics::DownloadErrorCode::kUnset) {
     metric = metrics::kMetricAttemptDownloadErrorCode;
-    LOG(INFO) << "Uploading " << static_cast<int>(payload_download_error_code)
-              << " for metric " << metric << " (sparse)";
     metrics_lib_->SendSparseToUMA(
         metric, static_cast<int>(payload_download_error_code));
   }
 
   metric = metrics::kMetricAttemptConnectionType;
-  LOG(INFO) << "Uploading " << static_cast<int>(connection_type)
-            << " for metric " << metric;
   metrics_lib_->SendEnumToUMA(
       metric,
       static_cast<int>(connection_type),
@@ -399,7 +360,6 @@
     int url_switch_count) {
   string metric = metrics::kMetricSuccessfulUpdatePayloadSizeMiB;
   int64_t mbs = payload_size / kNumBytesInOneMiB;
-  LOG(INFO) << "Uploading " << mbs << " (MiBs) for metric " << metric;
   metrics_lib_->SendToUMA(metric,
                           mbs,
                           0,     // min: 0 MiB
@@ -429,7 +389,6 @@
     }
 
     if (mbs > 0) {
-      LOG(INFO) << "Uploading " << mbs << " (MiBs) for metric " << metric;
       metrics_lib_->SendToUMA(metric,
                               mbs,
                               0,     // min: 0 MiB
@@ -439,8 +398,6 @@
   }
 
   metric = metrics::kMetricSuccessfulUpdateDownloadSourcesUsed;
-  LOG(INFO) << "Uploading 0x" << std::hex << download_sources_used
-            << " (bit flags) for metric " << metric;
   metrics_lib_->SendToUMA(metric,
                           download_sources_used,
                           0,                               // min
@@ -448,8 +405,6 @@
                           1 << kNumDownloadSources);       // num_buckets
 
   metric = metrics::kMetricSuccessfulUpdateDownloadOverheadPercentage;
-  LOG(INFO) << "Uploading " << download_overhead_percentage << "% for metric "
-            << metric;
   metrics_lib_->SendToUMA(metric,
                           download_overhead_percentage,
                           0,     // min: 0% overhead
@@ -457,8 +412,6 @@
                           50);   // num_buckets
 
   metric = metrics::kMetricSuccessfulUpdateUrlSwitchCount;
-  LOG(INFO) << "Uploading " << url_switch_count << " (count) for metric "
-            << metric;
   metrics_lib_->SendToUMA(metric,
                           url_switch_count,
                           0,    // min: 0 URL switches
@@ -466,8 +419,6 @@
                           50);  // num_buckets
 
   metric = metrics::kMetricSuccessfulUpdateTotalDurationMinutes;
-  LOG(INFO) << "Uploading " << utils::FormatTimeDelta(total_duration)
-            << " for metric " << metric;
   metrics_lib_->SendToUMA(metric,
                           static_cast<int>(total_duration.InMinutes()),
                           0,              // min: 0 min
@@ -475,8 +426,6 @@
                           50);            // num_buckets
 
   metric = metrics::kMetricSuccessfulUpdateTotalDurationUptimeMinutes;
-  LOG(INFO) << "Uploading " << utils::FormatTimeDelta(total_duration_uptime)
-            << " for metric " << metric;
   metrics_lib_->SendToUMA(metric,
                           static_cast<int>(total_duration_uptime.InMinutes()),
                           0,             // min: 0 min
@@ -484,8 +433,6 @@
                           50);           // num_buckets
 
   metric = metrics::kMetricSuccessfulUpdateRebootCount;
-  LOG(INFO) << "Uploading reboot count of " << reboot_count << " for metric "
-            << metric;
   metrics_lib_->SendToUMA(metric,
                           reboot_count,
                           0,    // min: 0 reboots
@@ -494,8 +441,6 @@
 
   metric = metrics::kMetricSuccessfulUpdatePayloadType;
   metrics_lib_->SendEnumToUMA(metric, payload_type, kNumPayloadTypes);
-  LOG(INFO) << "Uploading " << utils::ToString(payload_type) << " for metric "
-            << metric;
 
   metric = metrics::kMetricSuccessfulUpdateAttemptCount;
   metrics_lib_->SendToUMA(metric,
@@ -503,11 +448,8 @@
                           1,    // min: 1 attempt
                           50,   // max: 50 attempts
                           50);  // num_buckets
-  LOG(INFO) << "Uploading " << attempt_count << " for metric " << metric;
 
   metric = metrics::kMetricSuccessfulUpdateUpdatesAbandonedCount;
-  LOG(INFO) << "Uploading " << updates_abandoned_count << " (count) for metric "
-            << metric;
   metrics_lib_->SendToUMA(metric,
                           updates_abandoned_count,
                           0,    // min: 0 counts
@@ -519,7 +461,6 @@
     metrics::RollbackResult result) {
   string metric = metrics::kMetricRollbackResult;
   int value = static_cast<int>(result);
-  LOG(INFO) << "Sending " << value << " for metric " << metric << " (enum)";
   metrics_lib_->SendEnumToUMA(
       metric, value, static_cast<int>(metrics::RollbackResult::kNumConstants));
 }
@@ -530,7 +471,6 @@
   string metric = metrics::kMetricEnterpriseRollbackSuccess;
   if (!success)
     metric = metrics::kMetricEnterpriseRollbackFailure;
-  LOG(INFO) << "Sending " << value << " for metric " << metric;
   metrics_lib_->SendSparseToUMA(metric, value);
 }
 
@@ -547,8 +487,6 @@
     case ServerToCheck::kNone:
       return;
   }
-  LOG(INFO) << "Uploading " << static_cast<int>(result) << " for metric "
-            << metric;
   metrics_lib_->SendEnumToUMA(
       metric,
       static_cast<int>(result),
@@ -562,9 +500,6 @@
                           1,   // min value
                           50,  // max value
                           kNumDefaultUmaBuckets);
-
-  LOG(INFO) << "Uploading " << target_attempt << " (count) for metric "
-            << metric;
 }
 
 void MetricsReporterOmaha::ReportTimeToReboot(int time_to_reboot_minutes) {
@@ -574,9 +509,6 @@
                           0,             // min: 0 minute
                           30 * 24 * 60,  // max: 1 month (approx)
                           kNumDefaultUmaBuckets);
-
-  LOG(INFO) << "Uploading " << time_to_reboot_minutes << " for metric "
-            << metric;
 }
 
 void MetricsReporterOmaha::ReportInstallDateProvisioningSource(int source,
@@ -588,7 +520,6 @@
 
 void MetricsReporterOmaha::ReportInternalErrorCode(ErrorCode error_code) {
   auto metric = metrics::kMetricAttemptInternalErrorCode;
-  LOG(INFO) << "Uploading " << error_code << " for metric " << metric;
   metrics_lib_->SendEnumToUMA(metric,
                               static_cast<int>(error_code),
                               static_cast<int>(ErrorCode::kUmaReportedMax));
@@ -600,18 +531,14 @@
     bool kernel_max_rollforward_success) {
   int value = kernel_min_version;
   string metric = metrics::kMetricKernelMinVersion;
-  LOG(INFO) << "Sending " << value << " for metric " << metric;
   metrics_lib_->SendSparseToUMA(metric, value);
 
   value = kernel_max_rollforward_version;
   metric = metrics::kMetricKernelMaxRollforwardVersion;
-  LOG(INFO) << "Sending " << value << " for metric " << metric;
   metrics_lib_->SendSparseToUMA(metric, value);
 
   bool bool_value = kernel_max_rollforward_success;
   metric = metrics::kMetricKernelMaxRollforwardSetSuccess;
-  LOG(INFO) << "Sending " << bool_value << " for metric " << metric
-            << " (bool)";
   metrics_lib_->SendBoolToUMA(metric, bool_value);
 }
 
@@ -621,7 +548,6 @@
       has_time_restriction_policy
           ? metrics::kMetricSuccessfulUpdateDurationFromSeenTimeRestrictedDays
           : metrics::kMetricSuccessfulUpdateDurationFromSeenDays;
-  LOG(INFO) << "Sending " << time_to_update_days << " for metric " << metric;
 
   metrics_lib_->SendToUMA(metric,
                           time_to_update_days,
@@ -630,4 +556,45 @@
                           50);     // num_buckets
 }
 
+bool MetricsReporterOmaha::WallclockDurationHelper(
+    SystemState* system_state,
+    const std::string& state_variable_key,
+    TimeDelta* out_duration) {
+  bool ret = false;
+  Time now = system_state->clock()->GetWallclockTime();
+  int64_t stored_value;
+  if (system_state->prefs()->GetInt64(state_variable_key, &stored_value)) {
+    Time stored_time = Time::FromInternalValue(stored_value);
+    if (stored_time > now) {
+      LOG(ERROR) << "Stored time-stamp used for " << state_variable_key
+                 << " is in the future.";
+    } else {
+      *out_duration = now - stored_time;
+      ret = true;
+    }
+  }
+
+  if (!system_state->prefs()->SetInt64(state_variable_key,
+                                       now.ToInternalValue())) {
+    LOG(ERROR) << "Error storing time-stamp in " << state_variable_key;
+  }
+
+  return ret;
+}
+
+bool MetricsReporterOmaha::MonotonicDurationHelper(SystemState* system_state,
+                                                   int64_t* storage,
+                                                   TimeDelta* out_duration) {
+  bool ret = false;
+  Time now = system_state->clock()->GetMonotonicTime();
+  if (*storage != 0) {
+    Time stored_time = Time::FromInternalValue(*storage);
+    *out_duration = now - stored_time;
+    ret = true;
+  }
+  *storage = now.ToInternalValue();
+
+  return ret;
+}
+
 }  // namespace chromeos_update_engine
diff --git a/metrics_reporter_omaha.h b/cros/metrics_reporter_omaha.h
similarity index 79%
rename from metrics_reporter_omaha.h
rename to cros/metrics_reporter_omaha.h
index c84ac1e..5b3fdb1 100644
--- a/metrics_reporter_omaha.h
+++ b/cros/metrics_reporter_omaha.h
@@ -14,21 +14,22 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_METRICS_REPORTER_OMAHA_H_
-#define UPDATE_ENGINE_METRICS_REPORTER_OMAHA_H_
+#ifndef UPDATE_ENGINE_CROS_METRICS_REPORTER_OMAHA_H_
+#define UPDATE_ENGINE_CROS_METRICS_REPORTER_OMAHA_H_
 
 #include <memory>
 #include <string>
 
 #include <base/time/time.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
 #include <metrics/metrics_library.h>
 
 #include "update_engine/certificate_checker.h"
 #include "update_engine/common/constants.h"
 #include "update_engine/common/error_code.h"
-#include "update_engine/metrics_constants.h"
-#include "update_engine/metrics_reporter_interface.h"
-#include "update_engine/system_state.h"
+#include "update_engine/common/metrics_constants.h"
+#include "update_engine/common/metrics_reporter_interface.h"
+#include "update_engine/common/system_state.h"
 
 namespace chromeos_update_engine {
 
@@ -171,6 +172,31 @@
 
  private:
   friend class MetricsReporterOmahaTest;
+  FRIEND_TEST(MetricsReporterOmahaTest, WallclockDurationHelper);
+  FRIEND_TEST(MetricsReporterOmahaTest, MonotonicDurationHelper);
+
+  // This function returns the duration on the wallclock since the last
+  // time it was called for the same |state_variable_key| value.
+  //
+  // If the function returns |true|, the duration (always non-negative)
+  // is returned in |out_duration|. If the function returns |false|
+  // something went wrong or there was no previous measurement.
+  bool WallclockDurationHelper(SystemState* system_state,
+                               const std::string& state_variable_key,
+                               base::TimeDelta* out_duration);
+
+  // This function returns the duration on the monotonic clock since the
+  // last time it was called for the same |storage| pointer.
+  //
+  // You should pass a pointer to a 64-bit integer in |storage| which
+  // should be initialized to 0.
+  //
+  // If the function returns |true|, the duration (always non-negative)
+  // is returned in |out_duration|. If the function returns |false|
+  // something went wrong or there was no previous measurement.
+  bool MonotonicDurationHelper(SystemState* system_state,
+                               int64_t* storage,
+                               base::TimeDelta* out_duration);
 
   std::unique_ptr<MetricsLibraryInterface> metrics_lib_;
 
@@ -179,4 +205,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_METRICS_REPORTER_OMAHA_H_
+#endif  // UPDATE_ENGINE_CROS_METRICS_REPORTER_OMAHA_H_
diff --git a/metrics_reporter_omaha_unittest.cc b/cros/metrics_reporter_omaha_unittest.cc
similarity index 80%
rename from metrics_reporter_omaha_unittest.cc
rename to cros/metrics_reporter_omaha_unittest.cc
index 545d02f..a25472a 100644
--- a/metrics_reporter_omaha_unittest.cc
+++ b/cros/metrics_reporter_omaha_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/metrics_reporter_omaha.h"
+#include "update_engine/cros/metrics_reporter_omaha.h"
 
 #include <memory>
 #include <string>
@@ -26,7 +26,7 @@
 
 #include "update_engine/common/fake_clock.h"
 #include "update_engine/common/fake_prefs.h"
-#include "update_engine/fake_system_state.h"
+#include "update_engine/cros/fake_system_state.h"
 
 using base::TimeDelta;
 using testing::_;
@@ -538,4 +538,115 @@
       true /* has_time_restriction_policy */, kDaysToUpdate);
 }
 
+TEST_F(MetricsReporterOmahaTest, WallclockDurationHelper) {
+  FakeSystemState fake_system_state;
+  FakeClock fake_clock;
+  base::TimeDelta duration;
+  const std::string state_variable_key = "test-prefs";
+  FakePrefs fake_prefs;
+
+  fake_system_state.set_clock(&fake_clock);
+  fake_system_state.set_prefs(&fake_prefs);
+
+  // Initialize wallclock to 1 sec.
+  fake_clock.SetWallclockTime(base::Time::FromInternalValue(1000000));
+
+  // First time called so no previous measurement available.
+  EXPECT_FALSE(reporter_.WallclockDurationHelper(
+      &fake_system_state, state_variable_key, &duration));
+
+  // Next time, we should get zero since the clock didn't advance.
+  EXPECT_TRUE(reporter_.WallclockDurationHelper(
+      &fake_system_state, state_variable_key, &duration));
+  EXPECT_EQ(duration.InSeconds(), 0);
+
+  // We can also call it as many times as we want with it being
+  // considered a failure.
+  EXPECT_TRUE(reporter_.WallclockDurationHelper(
+      &fake_system_state, state_variable_key, &duration));
+  EXPECT_EQ(duration.InSeconds(), 0);
+  EXPECT_TRUE(reporter_.WallclockDurationHelper(
+      &fake_system_state, state_variable_key, &duration));
+  EXPECT_EQ(duration.InSeconds(), 0);
+
+  // Advance the clock one second, then we should get 1 sec on the
+  // next call and 0 sec on the subsequent call.
+  fake_clock.SetWallclockTime(base::Time::FromInternalValue(2000000));
+  EXPECT_TRUE(reporter_.WallclockDurationHelper(
+      &fake_system_state, state_variable_key, &duration));
+  EXPECT_EQ(duration.InSeconds(), 1);
+  EXPECT_TRUE(reporter_.WallclockDurationHelper(
+      &fake_system_state, state_variable_key, &duration));
+  EXPECT_EQ(duration.InSeconds(), 0);
+
+  // Advance clock two seconds and we should get 2 sec and then 0 sec.
+  fake_clock.SetWallclockTime(base::Time::FromInternalValue(4000000));
+  EXPECT_TRUE(reporter_.WallclockDurationHelper(
+      &fake_system_state, state_variable_key, &duration));
+  EXPECT_EQ(duration.InSeconds(), 2);
+  EXPECT_TRUE(reporter_.WallclockDurationHelper(
+      &fake_system_state, state_variable_key, &duration));
+  EXPECT_EQ(duration.InSeconds(), 0);
+
+  // There's a possibility that the wallclock can go backwards (NTP
+  // adjustments, for example) so check that we properly handle this
+  // case.
+  fake_clock.SetWallclockTime(base::Time::FromInternalValue(3000000));
+  EXPECT_FALSE(reporter_.WallclockDurationHelper(
+      &fake_system_state, state_variable_key, &duration));
+  fake_clock.SetWallclockTime(base::Time::FromInternalValue(4000000));
+  EXPECT_TRUE(reporter_.WallclockDurationHelper(
+      &fake_system_state, state_variable_key, &duration));
+  EXPECT_EQ(duration.InSeconds(), 1);
+}
+
+TEST_F(MetricsReporterOmahaTest, MonotonicDurationHelper) {
+  int64_t storage = 0;
+  FakeSystemState fake_system_state;
+  FakeClock fake_clock;
+  base::TimeDelta duration;
+
+  fake_system_state.set_clock(&fake_clock);
+
+  // Initialize monotonic clock to 1 sec.
+  fake_clock.SetMonotonicTime(base::Time::FromInternalValue(1000000));
+
+  // First time called so no previous measurement available.
+  EXPECT_FALSE(reporter_.MonotonicDurationHelper(
+      &fake_system_state, &storage, &duration));
+
+  // Next time, we should get zero since the clock didn't advance.
+  EXPECT_TRUE(reporter_.MonotonicDurationHelper(
+      &fake_system_state, &storage, &duration));
+  EXPECT_EQ(duration.InSeconds(), 0);
+
+  // We can also call it as many times as we want with it being
+  // considered a failure.
+  EXPECT_TRUE(reporter_.MonotonicDurationHelper(
+      &fake_system_state, &storage, &duration));
+  EXPECT_EQ(duration.InSeconds(), 0);
+  EXPECT_TRUE(reporter_.MonotonicDurationHelper(
+      &fake_system_state, &storage, &duration));
+  EXPECT_EQ(duration.InSeconds(), 0);
+
+  // Advance the clock one second, then we should get 1 sec on the
+  // next call and 0 sec on the subsequent call.
+  fake_clock.SetMonotonicTime(base::Time::FromInternalValue(2000000));
+  EXPECT_TRUE(reporter_.MonotonicDurationHelper(
+      &fake_system_state, &storage, &duration));
+  EXPECT_EQ(duration.InSeconds(), 1);
+  EXPECT_TRUE(reporter_.MonotonicDurationHelper(
+      &fake_system_state, &storage, &duration));
+  EXPECT_EQ(duration.InSeconds(), 0);
+
+  // Advance clock two seconds and we should get 2 sec and then 0 sec.
+  fake_clock.SetMonotonicTime(base::Time::FromInternalValue(4000000));
+  EXPECT_TRUE(reporter_.MonotonicDurationHelper(
+      &fake_system_state, &storage, &duration));
+  EXPECT_EQ(duration.InSeconds(), 2);
+  EXPECT_TRUE(reporter_.MonotonicDurationHelper(
+      &fake_system_state, &storage, &duration));
+  EXPECT_EQ(duration.InSeconds(), 0);
+}
+
 }  // namespace chromeos_update_engine
diff --git a/mock_connection_manager.h b/cros/mock_connection_manager.h
similarity index 85%
rename from mock_connection_manager.h
rename to cros/mock_connection_manager.h
index 2fff68c..899a49b 100644
--- a/mock_connection_manager.h
+++ b/cros/mock_connection_manager.h
@@ -14,12 +14,12 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_MOCK_CONNECTION_MANAGER_H_
-#define UPDATE_ENGINE_MOCK_CONNECTION_MANAGER_H_
+#ifndef UPDATE_ENGINE_CROS_MOCK_CONNECTION_MANAGER_H_
+#define UPDATE_ENGINE_CROS_MOCK_CONNECTION_MANAGER_H_
 
 #include <gmock/gmock.h>
 
-#include "update_engine/connection_manager_interface.h"
+#include "update_engine/cros/connection_manager_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -41,4 +41,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_MOCK_CONNECTION_MANAGER_H_
+#endif  // UPDATE_ENGINE_CROS_MOCK_CONNECTION_MANAGER_H_
diff --git a/mock_omaha_request_params.h b/cros/mock_omaha_request_params.h
similarity index 92%
rename from mock_omaha_request_params.h
rename to cros/mock_omaha_request_params.h
index 41bdc19..6072d22 100644
--- a/mock_omaha_request_params.h
+++ b/cros/mock_omaha_request_params.h
@@ -14,14 +14,14 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_MOCK_OMAHA_REQUEST_PARAMS_H_
-#define UPDATE_ENGINE_MOCK_OMAHA_REQUEST_PARAMS_H_
+#ifndef UPDATE_ENGINE_CROS_MOCK_OMAHA_REQUEST_PARAMS_H_
+#define UPDATE_ENGINE_CROS_MOCK_OMAHA_REQUEST_PARAMS_H_
 
 #include <string>
 
 #include <gmock/gmock.h>
 
-#include "update_engine/omaha_request_params.h"
+#include "update_engine/cros/omaha_request_params.h"
 
 namespace chromeos_update_engine {
 
@@ -79,4 +79,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_MOCK_OMAHA_REQUEST_PARAMS_H_
+#endif  // UPDATE_ENGINE_CROS_MOCK_OMAHA_REQUEST_PARAMS_H_
diff --git a/mock_p2p_manager.h b/cros/mock_p2p_manager.h
similarity index 94%
rename from mock_p2p_manager.h
rename to cros/mock_p2p_manager.h
index fd67034..273f7f9 100644
--- a/mock_p2p_manager.h
+++ b/cros/mock_p2p_manager.h
@@ -14,12 +14,12 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_MOCK_P2P_MANAGER_H_
-#define UPDATE_ENGINE_MOCK_P2P_MANAGER_H_
+#ifndef UPDATE_ENGINE_CROS_MOCK_P2P_MANAGER_H_
+#define UPDATE_ENGINE_CROS_MOCK_P2P_MANAGER_H_
 
 #include <string>
 
-#include "update_engine/fake_p2p_manager.h"
+#include "update_engine/cros/fake_p2p_manager.h"
 
 #include <gmock/gmock.h>
 
@@ -99,4 +99,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_MOCK_P2P_MANAGER_H_
+#endif  // UPDATE_ENGINE_CROS_MOCK_P2P_MANAGER_H_
diff --git a/mock_payload_state.h b/cros/mock_payload_state.h
similarity index 92%
rename from mock_payload_state.h
rename to cros/mock_payload_state.h
index ad22de5..56094e6 100644
--- a/mock_payload_state.h
+++ b/cros/mock_payload_state.h
@@ -14,15 +14,15 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_MOCK_PAYLOAD_STATE_H_
-#define UPDATE_ENGINE_MOCK_PAYLOAD_STATE_H_
+#ifndef UPDATE_ENGINE_CROS_MOCK_PAYLOAD_STATE_H_
+#define UPDATE_ENGINE_CROS_MOCK_PAYLOAD_STATE_H_
 
 #include <string>
 
 #include <gmock/gmock.h>
 
-#include "update_engine/omaha_request_action.h"
-#include "update_engine/payload_state_interface.h"
+#include "update_engine/common/system_state.h"
+#include "update_engine/cros/payload_state_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -81,4 +81,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_MOCK_PAYLOAD_STATE_H_
+#endif  // UPDATE_ENGINE_CROS_MOCK_PAYLOAD_STATE_H_
diff --git a/mock_power_manager.h b/cros/mock_power_manager.h
similarity index 80%
rename from mock_power_manager.h
rename to cros/mock_power_manager.h
index 8363171..d4a8682 100644
--- a/mock_power_manager.h
+++ b/cros/mock_power_manager.h
@@ -14,12 +14,12 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_MOCK_POWER_MANAGER_H_
-#define UPDATE_ENGINE_MOCK_POWER_MANAGER_H_
+#ifndef UPDATE_ENGINE_CROS_MOCK_POWER_MANAGER_H_
+#define UPDATE_ENGINE_CROS_MOCK_POWER_MANAGER_H_
 
 #include <gmock/gmock.h>
 
-#include "update_engine/power_manager_interface.h"
+#include "update_engine/cros/power_manager_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -32,4 +32,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_MOCK_POWER_MANAGER_H_
+#endif  // UPDATE_ENGINE_CROS_MOCK_POWER_MANAGER_H_
diff --git a/mock_update_attempter.h b/cros/mock_update_attempter.h
similarity index 72%
rename from mock_update_attempter.h
rename to cros/mock_update_attempter.h
index ad34802..be8cfcc 100644
--- a/mock_update_attempter.h
+++ b/cros/mock_update_attempter.h
@@ -14,13 +14,13 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_MOCK_UPDATE_ATTEMPTER_H_
-#define UPDATE_ENGINE_MOCK_UPDATE_ATTEMPTER_H_
+#ifndef UPDATE_ENGINE_CROS_MOCK_UPDATE_ATTEMPTER_H_
+#define UPDATE_ENGINE_CROS_MOCK_UPDATE_ATTEMPTER_H_
 
 #include <string>
 #include <vector>
 
-#include "update_engine/update_attempter.h"
+#include "update_engine/cros/update_attempter.h"
 
 #include <gmock/gmock.h>
 
@@ -30,16 +30,10 @@
  public:
   using UpdateAttempter::UpdateAttempter;
 
-  MOCK_METHOD9(Update,
-               void(const std::string& app_version,
-                    const std::string& omaha_url,
-                    const std::string& target_channel,
-                    const std::string& target_version_prefix,
-                    bool rollback_allowed,
-                    bool rollback_data_save_requested,
-                    int rollback_allowed_milestones,
-                    bool obey_proxies,
-                    bool interactive));
+  MOCK_METHOD(void,
+              Update,
+              (const chromeos_update_manager::UpdateCheckParams& params),
+              (override));
 
   MOCK_METHOD1(GetStatus, bool(update_engine::UpdateEngineStatus* out_status));
 
@@ -71,4 +65,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_MOCK_UPDATE_ATTEMPTER_H_
+#endif  // UPDATE_ENGINE_CROS_MOCK_UPDATE_ATTEMPTER_H_
diff --git a/omaha_request_action.cc b/cros/omaha_request_action.cc
similarity index 98%
rename from omaha_request_action.cc
rename to cros/omaha_request_action.cc
index 95e1250..0916f9d 100644
--- a/omaha_request_action.cc
+++ b/cros/omaha_request_action.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/omaha_request_action.h"
+#include "update_engine/cros/omaha_request_action.h"
 
 #include <inttypes.h>
 
@@ -44,18 +44,18 @@
 #include "update_engine/common/constants.h"
 #include "update_engine/common/hardware_interface.h"
 #include "update_engine/common/hash_calculator.h"
+#include "update_engine/common/metrics_reporter_interface.h"
 #include "update_engine/common/platform_constants.h"
 #include "update_engine/common/prefs.h"
 #include "update_engine/common/prefs_interface.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/connection_manager_interface.h"
-#include "update_engine/metrics_reporter_interface.h"
+#include "update_engine/cros/connection_manager_interface.h"
+#include "update_engine/cros/omaha_request_builder_xml.h"
+#include "update_engine/cros/omaha_request_params.h"
+#include "update_engine/cros/p2p_manager.h"
+#include "update_engine/cros/payload_state_interface.h"
+#include "update_engine/cros/update_attempter.h"
 #include "update_engine/metrics_utils.h"
-#include "update_engine/omaha_request_builder_xml.h"
-#include "update_engine/omaha_request_params.h"
-#include "update_engine/p2p_manager.h"
-#include "update_engine/payload_state_interface.h"
-#include "update_engine/update_attempter.h"
 
 using base::Optional;
 using base::Time;
@@ -582,6 +582,7 @@
     LOG(INFO) << "Found package " << package.name;
 
     OmahaResponse::Package out_package;
+    out_package.app_id = app->id;
     out_package.can_exclude = can_exclude;
     for (const string& codebase : app->url_codebase) {
       if (codebase.empty()) {
@@ -631,6 +632,7 @@
 // Removes the candidate URLs which are excluded within packages, if all the
 // candidate URLs are excluded within a package, the package will be excluded.
 void ProcessExclusions(OmahaResponse* output_object,
+                       OmahaRequestParams* params,
                        ExcluderInterface* excluder) {
   for (auto package_it = output_object->packages.begin();
        package_it != output_object->packages.end();
@@ -657,6 +659,9 @@
     // If there are no candidate payload URLs, remove the package.
     if (package_it->payload_urls.empty()) {
       LOG(INFO) << "Excluding payload hash=" << package_it->hash;
+      // Need to set DLC as not updated so correct metrics can be sent when an
+      // update is completed.
+      params->SetDlcNoUpdate(package_it->app_id);
       package_it = output_object->packages.erase(package_it);
       continue;
     }
@@ -860,11 +865,6 @@
     if (app.id == params_->GetAppId()) {
       // this is the app (potentially the only app)
       output_object->version = app.manifest_version;
-    } else if (!params_->system_app_id().empty() &&
-               app.id == params_->system_app_id()) {
-      // this is the system app (this check is intentionally skipped if there is
-      // no system_app_id set)
-      output_object->system_version = app.manifest_version;
     } else if (params_->is_install() &&
                app.manifest_version != params_->app_version()) {
       LOG(WARNING) << "An app has a different version (" << app.manifest_version
@@ -1023,6 +1023,7 @@
   if (!ParseResponse(&parser_data, &output_object, &completer))
     return;
   ProcessExclusions(&output_object,
+                    system_state_->request_params(),
                     system_state_->update_attempter()->GetExcluder());
   output_object.update_exists = true;
   SetOutputObject(output_object);
diff --git a/omaha_request_action.h b/cros/omaha_request_action.h
similarity index 97%
rename from omaha_request_action.h
rename to cros/omaha_request_action.h
index 30b3d22..1a3a912 100644
--- a/omaha_request_action.h
+++ b/cros/omaha_request_action.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_OMAHA_REQUEST_ACTION_H_
-#define UPDATE_ENGINE_OMAHA_REQUEST_ACTION_H_
+#ifndef UPDATE_ENGINE_CROS_OMAHA_REQUEST_ACTION_H_
+#define UPDATE_ENGINE_CROS_OMAHA_REQUEST_ACTION_H_
 
 #include <fcntl.h>
 #include <sys/stat.h>
@@ -33,9 +33,9 @@
 
 #include "update_engine/common/action.h"
 #include "update_engine/common/http_fetcher.h"
-#include "update_engine/omaha_request_builder_xml.h"
-#include "update_engine/omaha_response.h"
-#include "update_engine/system_state.h"
+#include "update_engine/common/system_state.h"
+#include "update_engine/cros/omaha_request_builder_xml.h"
+#include "update_engine/cros/omaha_response.h"
 
 // The Omaha Request action makes a request to Omaha and can output
 // the response on the output ActionPipe.
@@ -317,4 +317,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_OMAHA_REQUEST_ACTION_H_
+#endif  // UPDATE_ENGINE_CROS_OMAHA_REQUEST_ACTION_H_
diff --git a/omaha_request_action_fuzzer.cc b/cros/omaha_request_action_fuzzer.cc
similarity index 94%
rename from omaha_request_action_fuzzer.cc
rename to cros/omaha_request_action_fuzzer.cc
index 6c41b12..dd02467 100644
--- a/omaha_request_action_fuzzer.cc
+++ b/cros/omaha_request_action_fuzzer.cc
@@ -18,8 +18,8 @@
 
 #include "update_engine/common/mock_http_fetcher.h"
 #include "update_engine/common/test_utils.h"
-#include "update_engine/fake_system_state.h"
-#include "update_engine/omaha_request_action.h"
+#include "update_engine/cros/fake_system_state.h"
+#include "update_engine/cros/omaha_request_action.h"
 
 class Environment {
  public:
diff --git a/omaha_request_action_unittest.cc b/cros/omaha_request_action_unittest.cc
similarity index 97%
rename from omaha_request_action_unittest.cc
rename to cros/omaha_request_action_unittest.cc
index 6a0c213..c3842b8 100644
--- a/omaha_request_action_unittest.cc
+++ b/cros/omaha_request_action_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/omaha_request_action.h"
+#include "update_engine/cros/omaha_request_action.h"
 
 #include <stdint.h>
 
@@ -44,18 +44,18 @@
 #include "update_engine/common/constants.h"
 #include "update_engine/common/fake_prefs.h"
 #include "update_engine/common/hash_calculator.h"
+#include "update_engine/common/metrics_reporter_interface.h"
 #include "update_engine/common/mock_excluder.h"
 #include "update_engine/common/mock_http_fetcher.h"
 #include "update_engine/common/platform_constants.h"
 #include "update_engine/common/prefs.h"
 #include "update_engine/common/test_utils.h"
-#include "update_engine/fake_system_state.h"
-#include "update_engine/metrics_reporter_interface.h"
-#include "update_engine/mock_connection_manager.h"
-#include "update_engine/mock_payload_state.h"
-#include "update_engine/omaha_request_builder_xml.h"
-#include "update_engine/omaha_request_params.h"
-#include "update_engine/omaha_utils.h"
+#include "update_engine/cros/fake_system_state.h"
+#include "update_engine/cros/mock_connection_manager.h"
+#include "update_engine/cros/mock_payload_state.h"
+#include "update_engine/cros/omaha_request_builder_xml.h"
+#include "update_engine/cros/omaha_request_params.h"
+#include "update_engine/cros/omaha_utils.h"
 #include "update_engine/update_manager/rollback_prefs.h"
 
 using base::Time;
@@ -365,8 +365,6 @@
     request_params_.set_current_channel("unittest");
     request_params_.set_target_channel("unittest");
     request_params_.set_hwid("OEM MODEL 09235 7471");
-    request_params_.set_fw_version("ChromeOSFirmware.1.0");
-    request_params_.set_ec_version("0X0A1");
     request_params_.set_delta_okay(true);
     request_params_.set_interactive(false);
     request_params_.set_update_url("http://url");
@@ -650,7 +648,6 @@
 
   EXPECT_TRUE(response.update_exists);
   EXPECT_EQ(fake_update_response_.version, response.version);
-  EXPECT_EQ("", response.system_version);
   EXPECT_EQ(fake_update_response_.GetPayloadUrl(),
             response.packages[0].payload_urls[0]);
   EXPECT_EQ(fake_update_response_.more_info_url, response.more_info_url);
@@ -713,32 +710,6 @@
   EXPECT_EQ(false, response.packages[1].is_delta);
 }
 
-TEST_F(OmahaRequestActionTest, MultiAppAndSystemUpdateTest) {
-  fake_update_response_.multi_app = true;
-  // Trigger the lining up of the app and system versions.
-  request_params_.set_system_app_id(fake_update_response_.app_id2);
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_TRUE(response.update_exists);
-  EXPECT_EQ(fake_update_response_.version, response.version);
-  EXPECT_EQ(fake_update_response_.version2, response.system_version);
-  EXPECT_EQ(fake_update_response_.GetPayloadUrl(),
-            response.packages[0].payload_urls[0]);
-  EXPECT_EQ(fake_update_response_.codebase2 + "package3",
-            response.packages[1].payload_urls[0]);
-  EXPECT_EQ(fake_update_response_.hash, response.packages[0].hash);
-  EXPECT_EQ(fake_update_response_.size, response.packages[0].size);
-  EXPECT_EQ(11u, response.packages[0].metadata_size);
-  EXPECT_EQ(true, response.packages[0].is_delta);
-  ASSERT_EQ(2u, response.packages.size());
-  EXPECT_EQ(string("hash3"), response.packages[1].hash);
-  EXPECT_EQ(333u, response.packages[1].size);
-  EXPECT_EQ(33u, response.packages[1].metadata_size);
-  EXPECT_EQ(false, response.packages[1].is_delta);
-}
-
 TEST_F(OmahaRequestActionTest, MultiAppPartialUpdateTest) {
   fake_update_response_.multi_app = true;
   fake_update_response_.multi_app_self_update = true;
@@ -748,7 +719,6 @@
 
   EXPECT_TRUE(response.update_exists);
   EXPECT_EQ(fake_update_response_.version, response.version);
-  EXPECT_EQ("", response.system_version);
   EXPECT_EQ(fake_update_response_.GetPayloadUrl(),
             response.packages[0].payload_urls[0]);
   EXPECT_EQ(fake_update_response_.hash, response.packages[0].hash);
@@ -770,7 +740,6 @@
 
   EXPECT_TRUE(response.update_exists);
   EXPECT_EQ(fake_update_response_.version, response.version);
-  EXPECT_EQ("", response.system_version);
   EXPECT_EQ(fake_update_response_.GetPayloadUrl(),
             response.packages[0].payload_urls[0]);
   EXPECT_EQ(fake_update_response_.codebase + "package2",
@@ -1528,6 +1497,7 @@
   request_params_.set_os_board("x86 generic<id");
   request_params_.set_current_channel("unittest_track&lt;");
   request_params_.set_target_channel("unittest_track&lt;");
+  request_params_.set_lts_tag("unittest_hint&lt;");
   request_params_.set_hwid("<OEM MODEL>");
   fake_prefs_.SetString(kPrefsOmahaCohort, "evil\nstring");
   fake_prefs_.SetString(kPrefsOmahaCohortHint, "evil&string\\");
@@ -1547,6 +1517,8 @@
   EXPECT_EQ(string::npos, post_str.find("x86 generic<id"));
   EXPECT_NE(string::npos, post_str.find("unittest_track&amp;lt;"));
   EXPECT_EQ(string::npos, post_str.find("unittest_track&lt;"));
+  EXPECT_NE(string::npos, post_str.find("unittest_hint&amp;lt;"));
+  EXPECT_EQ(string::npos, post_str.find("unittest_hint&lt;"));
   EXPECT_NE(string::npos, post_str.find("&lt;OEM MODEL&gt;"));
   EXPECT_EQ(string::npos, post_str.find("<OEM MODEL>"));
   EXPECT_NE(string::npos, post_str.find("cohort=\"evil\nstring\""));
@@ -1601,8 +1573,6 @@
       string::npos);
   EXPECT_NE(post_str.find("hardware_class=\"OEM MODEL 09235 7471\""),
             string::npos);
-  EXPECT_NE(post_str.find("fw_version=\"ChromeOSFirmware.1.0\""), string::npos);
-  EXPECT_NE(post_str.find("ec_version=\"0X0A1\""), string::npos);
   // No <event> tag should be sent if we didn't reboot to an update.
   EXPECT_EQ(post_str.find("<event"), string::npos);
 }
@@ -1801,6 +1771,17 @@
   EXPECT_EQ(string::npos, post_str.find(omaha_cohort_hint));
 }
 
+TEST_F(OmahaRequestActionTest, TargetChannelHintTest) {
+  tuc_params_.http_response = fake_update_response_.GetNoUpdateResponse();
+  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
+  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
+  request_params_.set_lts_tag("hint>");
+
+  ASSERT_TRUE(TestUpdateCheck());
+
+  EXPECT_NE(string::npos, post_str.find("ltstag=\"hint&gt;\""));
+}
+
 void OmahaRequestActionTest::PingTest(bool ping_only) {
   NiceMock<MockPrefs> prefs;
   fake_system_state_.set_prefs(&prefs);
@@ -2776,8 +2757,8 @@
 }
 
 TEST_F(OmahaRequestActionTest, UpdateWithPartiallyExcludedDlcTest) {
-  request_params_.set_dlc_apps_params(
-      {{request_params_.GetDlcAppId(kDlcId1), {.name = kDlcId1}}});
+  const string kDlcAppId = request_params_.GetDlcAppId(kDlcId1);
+  request_params_.set_dlc_apps_params({{kDlcAppId, {.name = kDlcId1}}});
   fake_update_response_.dlc_app_update = true;
   tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
   // The first DLC candidate URL is excluded.
@@ -2790,11 +2771,12 @@
   // One candidate URL.
   EXPECT_EQ(response.packages[1].payload_urls.size(), 1u);
   EXPECT_TRUE(response.update_exists);
+  EXPECT_TRUE(request_params_.dlc_apps_params().at(kDlcAppId).updated);
 }
 
 TEST_F(OmahaRequestActionTest, UpdateWithExcludedDlcTest) {
-  request_params_.set_dlc_apps_params(
-      {{request_params_.GetDlcAppId(kDlcId1), {.name = kDlcId1}}});
+  const string kDlcAppId = request_params_.GetDlcAppId(kDlcId1);
+  request_params_.set_dlc_apps_params({{kDlcAppId, {.name = kDlcId1}}});
   fake_update_response_.dlc_app_update = true;
   tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
   // Both DLC candidate URLs are excluded.
@@ -2805,6 +2787,7 @@
 
   EXPECT_EQ(response.packages.size(), 1u);
   EXPECT_TRUE(response.update_exists);
+  EXPECT_FALSE(request_params_.dlc_apps_params().at(kDlcAppId).updated);
 }
 
 TEST_F(OmahaRequestActionTest, UpdateWithDeprecatedDlcTest) {
diff --git a/omaha_request_builder_xml.cc b/cros/omaha_request_builder_xml.cc
similarity index 94%
rename from omaha_request_builder_xml.cc
rename to cros/omaha_request_builder_xml.cc
index e2857f1..43ee548 100644
--- a/omaha_request_builder_xml.cc
+++ b/cros/omaha_request_builder_xml.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/omaha_request_builder_xml.h"
+#include "update_engine/cros/omaha_request_builder_xml.h"
 
 #include <inttypes.h>
 
@@ -30,7 +30,7 @@
 #include "update_engine/common/constants.h"
 #include "update_engine/common/prefs_interface.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/omaha_request_params.h"
+#include "update_engine/cros/omaha_request_params.h"
 
 using std::string;
 
@@ -154,6 +154,11 @@
             app_body += " rollback_allowed=\"true\"";
           }
         }
+        if (!params_->lts_tag().empty()) {
+          app_body += base::StringPrintf(
+              " ltstag=\"%s\"",
+              XmlEncodeWithDefault(params_->lts_tag()).c_str());
+        }
         app_body += "></updatecheck>\n";
       }
 
@@ -184,17 +189,26 @@
       }
     }
   } else {
+    int event_result = event_->result;
     // The error code is an optional attribute so append it only if the result
     // is not success.
     string error_code;
-    if (event_->result != OmahaEvent::kResultSuccess) {
+    if (event_result != OmahaEvent::kResultSuccess) {
       error_code = base::StringPrintf(" errorcode=\"%d\"",
                                       static_cast<int>(event_->error_code));
+    } else if (app_data.is_dlc && !app_data.app_params.updated) {
+      // On a |OmahaEvent::kResultSuccess|, if the event is for an update
+      // completion and the App is a DLC, send error for excluded DLCs as they
+      // did not update.
+      event_result = OmahaEvent::Result::kResultError;
+      error_code = base::StringPrintf(
+          " errorcode=\"%d\"",
+          static_cast<int>(ErrorCode::kPackageExcludedFromUpdate));
     }
     app_body = base::StringPrintf(
         "        <event eventtype=\"%d\" eventresult=\"%d\"%s></event>\n",
         event_->type,
-        event_->result,
+        event_result,
         error_code.c_str());
   }
 
@@ -354,8 +368,6 @@
       // DLC excluded for installs and updates.
       (app_data.is_dlc ? "" :
       "lang=\"" + XmlEncodeWithDefault(params_->app_lang(), "en-US") + "\" " +
-      "fw_version=\"" + XmlEncodeWithDefault(params_->fw_version()) + "\" " +
-      "ec_version=\"" + XmlEncodeWithDefault(params_->ec_version()) + "\" " +
       requisition_arg) +
 
       ">\n" +
@@ -410,16 +422,6 @@
       .app_params = {.active_counting_type = OmahaRequestParams::kDayBased,
                      .send_ping = include_ping_}};
   app_xml += GetApp(product_app);
-  if (!params_->system_app_id().empty()) {
-    OmahaAppData system_app = {
-        .id = params_->system_app_id(),
-        .version = params_->system_version(),
-        .skip_update = false,
-        .is_dlc = false,
-        .app_params = {.active_counting_type = OmahaRequestParams::kDayBased,
-                       .send_ping = include_ping_}};
-    app_xml += GetApp(system_app);
-  }
   for (const auto& it : params_->dlc_apps_params()) {
     OmahaAppData dlc_app_data = {
         .id = it.first,
diff --git a/omaha_request_builder_xml.h b/cros/omaha_request_builder_xml.h
similarity index 94%
rename from omaha_request_builder_xml.h
rename to cros/omaha_request_builder_xml.h
index 50c708d..4f860dd 100644
--- a/omaha_request_builder_xml.h
+++ b/cros/omaha_request_builder_xml.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_OMAHA_REQUEST_BUILDER_XML_H_
-#define UPDATE_ENGINE_OMAHA_REQUEST_BUILDER_XML_H_
+#ifndef UPDATE_ENGINE_CROS_OMAHA_REQUEST_BUILDER_XML_H_
+#define UPDATE_ENGINE_CROS_OMAHA_REQUEST_BUILDER_XML_H_
 
 #include <fcntl.h>
 #include <sys/stat.h>
@@ -33,9 +33,9 @@
 
 #include "update_engine/common/action.h"
 #include "update_engine/common/http_fetcher.h"
-#include "update_engine/omaha_request_params.h"
-#include "update_engine/omaha_response.h"
-#include "update_engine/system_state.h"
+#include "update_engine/common/system_state.h"
+#include "update_engine/cros/omaha_request_params.h"
+#include "update_engine/cros/omaha_response.h"
 
 namespace chromeos_update_engine {
 
@@ -196,4 +196,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_OMAHA_REQUEST_BUILDER_XML_H_
+#endif  // UPDATE_ENGINE_CROS_OMAHA_REQUEST_BUILDER_XML_H_
diff --git a/omaha_request_builder_xml_unittest.cc b/cros/omaha_request_builder_xml_unittest.cc
similarity index 71%
rename from omaha_request_builder_xml_unittest.cc
rename to cros/omaha_request_builder_xml_unittest.cc
index 017acec..11d808b 100644
--- a/omaha_request_builder_xml_unittest.cc
+++ b/cros/omaha_request_builder_xml_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/omaha_request_builder_xml.h"
+#include "update_engine/cros/omaha_request_builder_xml.h"
 
 #include <string>
 #include <utility>
@@ -23,7 +23,7 @@
 #include <base/guid.h>
 #include <gtest/gtest.h>
 
-#include "update_engine/fake_system_state.h"
+#include "update_engine/cros/fake_system_state.h"
 
 using std::pair;
 using std::string;
@@ -108,8 +108,6 @@
   // in fact present in the <app ...></app>.
   const string app = omaha_request.GetApp(dlc_app_data);
   EXPECT_NE(string::npos, app.find("lang="));
-  EXPECT_NE(string::npos, app.find("fw_version="));
-  EXPECT_NE(string::npos, app.find("ec_version="));
   EXPECT_NE(string::npos, app.find("requisition="));
 }
 
@@ -132,8 +130,6 @@
   // fact not present in the <app ...></app>.
   const string app = omaha_request.GetApp(dlc_app_data);
   EXPECT_EQ(string::npos, app.find("lang="));
-  EXPECT_EQ(string::npos, app.find("fw_version="));
-  EXPECT_EQ(string::npos, app.find("ec_version="));
   EXPECT_EQ(string::npos, app.find("requisition="));
 }
 
@@ -148,10 +144,10 @@
                                        0,
                                        fake_system_state_.prefs(),
                                        ""};
-  const string request_xml = omaha_request.GetRequest();
+  const string kRequestXml = omaha_request.GetRequest();
   const string key = "requestid";
   const string request_id =
-      FindAttributeKeyValueInXml(request_xml, key, kGuidSize);
+      FindAttributeKeyValueInXml(kRequestXml, key, kGuidSize);
   // A valid |request_id| is either a GUID version 4 or empty string.
   if (!request_id.empty())
     EXPECT_TRUE(base::IsValidGUID(request_id));
@@ -169,10 +165,10 @@
                                        0,
                                        fake_system_state_.prefs(),
                                        gen_session_id};
-  const string request_xml = omaha_request.GetRequest();
+  const string kRequestXml = omaha_request.GetRequest();
   const string key = "sessionid";
   const string session_id =
-      FindAttributeKeyValueInXml(request_xml, key, kGuidSize);
+      FindAttributeKeyValueInXml(kRequestXml, key, kGuidSize);
   // A valid |session_id| is either a GUID version 4 or empty string.
   if (!session_id.empty()) {
     EXPECT_TRUE(base::IsValidGUID(session_id));
@@ -191,9 +187,9 @@
                                        0,
                                        fake_system_state_.prefs(),
                                        ""};
-  const string request_xml = omaha_request.GetRequest();
-  EXPECT_EQ(1, CountSubstringInString(request_xml, "<updatecheck"))
-      << request_xml;
+  const string kRequestXml = omaha_request.GetRequest();
+  EXPECT_EQ(1, CountSubstringInString(kRequestXml, "<updatecheck"))
+      << kRequestXml;
 }
 
 TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlPlatformUpdateWithDlcsTest) {
@@ -210,9 +206,9 @@
                                        0,
                                        fake_system_state_.prefs(),
                                        ""};
-  const string request_xml = omaha_request.GetRequest();
-  EXPECT_EQ(3, CountSubstringInString(request_xml, "<updatecheck"))
-      << request_xml;
+  const string kRequestXml = omaha_request.GetRequest();
+  EXPECT_EQ(3, CountSubstringInString(kRequestXml, "<updatecheck"))
+      << kRequestXml;
 }
 
 TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlDlcInstallationTest) {
@@ -231,25 +227,25 @@
                                        0,
                                        fake_system_state_.prefs(),
                                        ""};
-  const string request_xml = omaha_request.GetRequest();
-  EXPECT_EQ(2, CountSubstringInString(request_xml, "<updatecheck"))
-      << request_xml;
+  const string kRequestXml = omaha_request.GetRequest();
+  EXPECT_EQ(2, CountSubstringInString(kRequestXml, "<updatecheck"))
+      << kRequestXml;
 
-  auto FindAppId = [request_xml](size_t pos) -> size_t {
-    return request_xml.find("<app appid", pos);
+  auto FindAppId = [kRequestXml](size_t pos) -> size_t {
+    return kRequestXml.find("<app appid", pos);
   };
   // Skip over the Platform AppID, which is always first.
   size_t pos = FindAppId(0);
   for (auto&& _ : dlcs) {
     (void)_;
-    EXPECT_NE(string::npos, (pos = FindAppId(pos + 1))) << request_xml;
+    EXPECT_NE(string::npos, (pos = FindAppId(pos + 1))) << kRequestXml;
     const string dlc_app_id_version = FindAttributeKeyValueInXml(
-        request_xml.substr(pos), "version", string(kNoVersion).size());
+        kRequestXml.substr(pos), "version", string(kNoVersion).size());
     EXPECT_EQ(kNoVersion, dlc_app_id_version);
 
     const string false_str = "false";
     const string dlc_app_id_delta_okay = FindAttributeKeyValueInXml(
-        request_xml.substr(pos), "delta_okay", false_str.length());
+        kRequestXml.substr(pos), "delta_okay", false_str.length());
     EXPECT_EQ(false_str, dlc_app_id_delta_okay);
   }
 }
@@ -267,8 +263,8 @@
                                        0,
                                        fake_system_state_.prefs(),
                                        ""};
-  const string request_xml = omaha_request.GetRequest();
-  EXPECT_EQ(0, CountSubstringInString(request_xml, "<ping")) << request_xml;
+  const string kRequestXml = omaha_request.GetRequest();
+  EXPECT_EQ(0, CountSubstringInString(kRequestXml, "<ping")) << kRequestXml;
 }
 
 TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlDlcPingRollCallNoActive) {
@@ -289,9 +285,9 @@
                                        0,
                                        fake_system_state_.prefs(),
                                        ""};
-  const string request_xml = omaha_request.GetRequest();
-  EXPECT_EQ(1, CountSubstringInString(request_xml, "<ping rd=\"36\""))
-      << request_xml;
+  const string kRequestXml = omaha_request.GetRequest();
+  EXPECT_EQ(1, CountSubstringInString(kRequestXml, "<ping rd=\"36\""))
+      << kRequestXml;
 }
 
 TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlDlcPingRollCallAndActive) {
@@ -313,10 +309,93 @@
                                        0,
                                        fake_system_state_.prefs(),
                                        ""};
-  const string request_xml = omaha_request.GetRequest();
+  const string kRequestXml = omaha_request.GetRequest();
   EXPECT_EQ(1,
-            CountSubstringInString(request_xml,
+            CountSubstringInString(kRequestXml,
                                    "<ping active=\"1\" ad=\"25\" rd=\"36\""))
-      << request_xml;
+      << kRequestXml;
+}
+
+TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlUpdateCompleteEvent) {
+  OmahaRequestParams omaha_request_params{&fake_system_state_};
+  OmahaEvent event(OmahaEvent::kTypeUpdateComplete);
+  OmahaRequestBuilderXml omaha_request{&event,
+                                       &omaha_request_params,
+                                       false,
+                                       false,
+                                       0,
+                                       0,
+                                       0,
+                                       fake_system_state_.prefs(),
+                                       ""};
+  const string kRequestXml = omaha_request.GetRequest();
+  LOG(INFO) << kRequestXml;
+  EXPECT_EQ(
+      1,
+      CountSubstringInString(
+          kRequestXml, "<event eventtype=\"3\" eventresult=\"1\"></event>"))
+      << kRequestXml;
+}
+
+TEST_F(OmahaRequestBuilderXmlTest,
+       GetRequestXmlUpdateCompleteEventSomeDlcsExcluded) {
+  OmahaRequestParams omaha_request_params{&fake_system_state_};
+  omaha_request_params.set_dlc_apps_params({
+      {omaha_request_params.GetDlcAppId("dlc_1"), {.updated = true}},
+      {omaha_request_params.GetDlcAppId("dlc_2"), {.updated = false}},
+  });
+  OmahaEvent event(OmahaEvent::kTypeUpdateComplete);
+  OmahaRequestBuilderXml omaha_request{&event,
+                                       &omaha_request_params,
+                                       false,
+                                       false,
+                                       0,
+                                       0,
+                                       0,
+                                       fake_system_state_.prefs(),
+                                       ""};
+  const string kRequestXml = omaha_request.GetRequest();
+  EXPECT_EQ(
+      2,
+      CountSubstringInString(
+          kRequestXml, "<event eventtype=\"3\" eventresult=\"1\"></event>"))
+      << kRequestXml;
+  EXPECT_EQ(
+      1,
+      CountSubstringInString(
+          kRequestXml,
+          "<event eventtype=\"3\" eventresult=\"0\" errorcode=\"62\"></event>"))
+      << kRequestXml;
+}
+
+TEST_F(OmahaRequestBuilderXmlTest,
+       GetRequestXmlUpdateCompleteEventAllDlcsExcluded) {
+  OmahaRequestParams omaha_request_params{&fake_system_state_};
+  omaha_request_params.set_dlc_apps_params({
+      {omaha_request_params.GetDlcAppId("dlc_1"), {.updated = false}},
+      {omaha_request_params.GetDlcAppId("dlc_2"), {.updated = false}},
+  });
+  OmahaEvent event(OmahaEvent::kTypeUpdateComplete);
+  OmahaRequestBuilderXml omaha_request{&event,
+                                       &omaha_request_params,
+                                       false,
+                                       false,
+                                       0,
+                                       0,
+                                       0,
+                                       fake_system_state_.prefs(),
+                                       ""};
+  const string kRequestXml = omaha_request.GetRequest();
+  EXPECT_EQ(
+      1,
+      CountSubstringInString(
+          kRequestXml, "<event eventtype=\"3\" eventresult=\"1\"></event>"))
+      << kRequestXml;
+  EXPECT_EQ(
+      2,
+      CountSubstringInString(
+          kRequestXml,
+          "<event eventtype=\"3\" eventresult=\"0\" errorcode=\"62\"></event>"))
+      << kRequestXml;
 }
 }  // namespace chromeos_update_engine
diff --git a/omaha_request_params.cc b/cros/omaha_request_params.cc
similarity index 81%
rename from omaha_request_params.cc
rename to cros/omaha_request_params.cc
index 8a2e3dc..c814e00 100644
--- a/omaha_request_params.cc
+++ b/cros/omaha_request_params.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/omaha_request_params.h"
+#include "update_engine/cros/omaha_request_params.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -35,11 +35,13 @@
 #include "update_engine/common/constants.h"
 #include "update_engine/common/hardware_interface.h"
 #include "update_engine/common/platform_constants.h"
+#include "update_engine/common/system_state.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/system_state.h"
+#include "update_engine/update_manager/policy.h"
 
 #define CALL_MEMBER_FN(object, member) ((object).*(member))
 
+using chromeos_update_manager::UpdateCheckParams;
 using std::string;
 
 namespace chromeos_update_engine {
@@ -59,9 +61,9 @@
     test::SetImagePropertiesRootPrefix(nullptr);
 }
 
-bool OmahaRequestParams::Init(const string& in_app_version,
-                              const string& in_update_url,
-                              bool in_interactive) {
+bool OmahaRequestParams::Init(const string& app_version,
+                              const string& update_url,
+                              const UpdateCheckParams& params) {
   LOG(INFO) << "Initializing parameters for this update attempt";
   image_props_ = LoadImageProperties(system_state_);
   mutable_image_props_ = LoadMutableImageProperties(system_state_);
@@ -76,24 +78,13 @@
   LOG(INFO) << "Running from channel " << image_props_.current_channel;
 
   os_platform_ = constants::kOmahaPlatformName;
-  if (!image_props_.system_version.empty()) {
-    if (in_app_version == "ForcedUpdate") {
-      image_props_.system_version = in_app_version;
-    }
-    os_version_ = image_props_.system_version;
-  } else {
-    os_version_ = OmahaRequestParams::kOsVersion;
-  }
-  if (!in_app_version.empty())
-    image_props_.version = in_app_version;
+  os_version_ = OmahaRequestParams::kOsVersion;
+  if (!app_version.empty())
+    image_props_.version = app_version;
 
   os_sp_ = image_props_.version + "_" + GetMachineType();
   app_lang_ = "en-US";
   hwid_ = system_state_->hardware()->GetHardwareClass();
-  if (CollectECFWVersions()) {
-    fw_version_ = system_state_->hardware()->GetFirmwareVersion();
-    ec_version_ = system_state_->hardware()->GetECVersion();
-  }
   device_requisition_ = system_state_->hardware()->GetDeviceRequisition();
 
   if (image_props_.current_channel == mutable_image_props_.target_channel) {
@@ -115,17 +106,51 @@
     delta_okay_ = false;
   }
 
-  if (in_update_url.empty())
+  if (update_url.empty())
     update_url_ = image_props_.omaha_url;
   else
-    update_url_ = in_update_url;
+    update_url_ = update_url;
 
   // Set the interactive flag accordingly.
-  interactive_ = in_interactive;
+  interactive_ = params.interactive;
 
   dlc_apps_params_.clear();
   // Set false so it will do update by default.
   is_install_ = false;
+
+  target_version_prefix_ = params.target_version_prefix;
+
+  lts_tag_ = params.lts_tag;
+
+  rollback_allowed_ = params.rollback_allowed;
+
+  // Set whether saving data over rollback is requested.
+  rollback_data_save_requested_ = params.rollback_data_save_requested;
+
+  // Set how many milestones of rollback are allowed.
+  rollback_allowed_milestones_ = params.rollback_allowed_milestones;
+
+  // Set the target channel, if one was provided.
+  if (params.target_channel.empty()) {
+    LOG(INFO) << "No target channel mandated by policy.";
+  } else {
+    LOG(INFO) << "Setting target channel as mandated: "
+              << params.target_channel;
+    string error_message;
+    if (!SetTargetChannel(params.target_channel,
+                          params.rollback_on_channel_downgrade,
+                          &error_message)) {
+      LOG(ERROR) << "Setting the channel failed: " << error_message;
+    }
+
+    // Since this is the beginning of a new attempt, update the download
+    // channel. The download channel won't be updated until the next attempt,
+    // even if target channel changes meanwhile, so that how we'll know if we
+    // should cancel the current download attempt if there's such a change in
+    // target channel.
+    UpdateDownloadChannel();
+  }
+
   return true;
 }
 
@@ -134,14 +159,6 @@
           update_url_ == image_props_.omaha_url);
 }
 
-bool OmahaRequestParams::CollectECFWVersions() const {
-  return base::StartsWith(
-             hwid_, string("PARROT"), base::CompareCase::SENSITIVE) ||
-         base::StartsWith(
-             hwid_, string("SPRING"), base::CompareCase::SENSITIVE) ||
-         base::StartsWith(hwid_, string("SNOW"), base::CompareCase::SENSITIVE);
-}
-
 bool OmahaRequestParams::SetTargetChannel(const string& new_target_channel,
                                           bool is_powerwash_allowed,
                                           string* error_message) {
diff --git a/omaha_request_params.h b/cros/omaha_request_params.h
similarity index 91%
rename from omaha_request_params.h
rename to cros/omaha_request_params.h
index 76fc806..26ea1c9 100644
--- a/omaha_request_params.h
+++ b/cros/omaha_request_params.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_OMAHA_REQUEST_PARAMS_H_
-#define UPDATE_ENGINE_OMAHA_REQUEST_PARAMS_H_
+#ifndef UPDATE_ENGINE_CROS_OMAHA_REQUEST_PARAMS_H_
+#define UPDATE_ENGINE_CROS_OMAHA_REQUEST_PARAMS_H_
 
 #include <stdint.h>
 
@@ -29,7 +29,8 @@
 
 #include "update_engine/common/constants.h"
 #include "update_engine/common/platform_constants.h"
-#include "update_engine/image_properties.h"
+#include "update_engine/cros/image_properties.h"
+#include "update_engine/update_manager/policy.h"
 
 // This gathers local system information and prepares info used by the
 // Omaha request action.
@@ -93,27 +94,18 @@
   inline std::string canary_app_id() const {
     return image_props_.canary_product_id;
   }
-  inline std::string system_app_id() const { return image_props_.system_id; }
-  inline void set_system_app_id(const std::string& system_app_id) {
-    image_props_.system_id = system_app_id;
-  }
   inline void set_app_id(const std::string& app_id) {
     image_props_.product_id = app_id;
     image_props_.canary_product_id = app_id;
   }
   inline std::string app_lang() const { return app_lang_; }
   inline std::string hwid() const { return hwid_; }
-  inline std::string fw_version() const { return fw_version_; }
-  inline std::string ec_version() const { return ec_version_; }
   inline std::string device_requisition() const { return device_requisition_; }
 
   inline void set_app_version(const std::string& version) {
     image_props_.version = version;
   }
   inline std::string app_version() const { return image_props_.version; }
-  inline std::string system_version() const {
-    return image_props_.system_version;
-  }
   inline std::string product_components() const {
     return image_props_.product_components;
   }
@@ -148,6 +140,10 @@
     return target_version_prefix_;
   }
 
+  inline std::string lts_tag() const { return lts_tag_; }
+
+  inline void set_lts_tag(const std::string& hint) { lts_tag_ = hint; }
+
   inline void set_rollback_allowed(bool rollback_allowed) {
     rollback_allowed_ = rollback_allowed;
   }
@@ -245,7 +241,7 @@
   // of the parameter. Returns true on success, false otherwise.
   bool Init(const std::string& in_app_version,
             const std::string& in_update_url,
-            bool in_interactive);
+            const chromeos_update_manager::UpdateCheckParams& params);
 
   // Permanently changes the release channel to |channel|. Performs a
   // powerwash, if required and allowed.
@@ -289,22 +285,19 @@
   }
   void set_app_lang(const std::string& app_lang) { app_lang_ = app_lang; }
   void set_hwid(const std::string& hwid) { hwid_ = hwid; }
-  void set_fw_version(const std::string& fw_version) {
-    fw_version_ = fw_version;
-  }
-  void set_ec_version(const std::string& ec_version) {
-    ec_version_ = ec_version;
-  }
   void set_is_powerwash_allowed(bool powerwash_allowed) {
     mutable_image_props_.is_powerwash_allowed = powerwash_allowed;
   }
+  bool is_powerwash_allowed() {
+    return mutable_image_props_.is_powerwash_allowed;
+  }
+
   void set_device_requisition(const std::string& requisition) {
     device_requisition_ = requisition;
   }
 
  private:
   FRIEND_TEST(OmahaRequestParamsTest, ChannelIndexTest);
-  FRIEND_TEST(OmahaRequestParamsTest, CollectECFWVersionsTest);
   FRIEND_TEST(OmahaRequestParamsTest, IsValidChannelTest);
   FRIEND_TEST(OmahaRequestParamsTest, SetIsPowerwashAllowedTest);
   FRIEND_TEST(OmahaRequestParamsTest, SetTargetChannelInvalidTest);
@@ -327,10 +320,6 @@
   // i.e. index(target_channel) > index(current_channel).
   bool ToMoreStableChannel() const;
 
-  // Returns True if we should store the fw/ec versions based on our hwid_.
-  // Compares hwid to a set of prefixes in the allowlist.
-  bool CollectECFWVersions() const;
-
   // Gets the machine type (e.g. "i686").
   std::string GetMachineType() const;
 
@@ -367,14 +356,15 @@
   //   changed and cancel the current download attempt.
   std::string download_channel_;
 
-  std::string hwid_;        // Hardware Qualification ID of the client
-  std::string fw_version_;  // Chrome OS Firmware Version.
-  std::string ec_version_;  // Chrome OS EC Version.
+  // The value defining the parameters of the LTS (Long Term Support).
+  std::string lts_tag_;
+
+  std::string hwid_;  // Hardware Qualification ID of the client
   // TODO(b:133324571) tracks removal of this field once it is no longer
   // needed in AU requests. Remove by October 1st 2019.
   std::string device_requisition_;  // Chrome OS Requisition type.
-  bool delta_okay_;         // If this client can accept a delta
-  bool interactive_;        // Whether this is a user-initiated update check
+  bool delta_okay_;                 // If this client can accept a delta
+  bool interactive_;  // Whether this is a user-initiated update check
 
   // The URL to send the Omaha request to.
   std::string update_url_;
@@ -427,4 +417,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_OMAHA_REQUEST_PARAMS_H_
+#endif  // UPDATE_ENGINE_CROS_OMAHA_REQUEST_PARAMS_H_
diff --git a/omaha_request_params_unittest.cc b/cros/omaha_request_params_unittest.cc
similarity index 89%
rename from omaha_request_params_unittest.cc
rename to cros/omaha_request_params_unittest.cc
index bfcbc32..71f3d4c 100644
--- a/omaha_request_params_unittest.cc
+++ b/cros/omaha_request_params_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/omaha_request_params.h"
+#include "update_engine/cros/omaha_request_params.h"
 
 #include <stdio.h>
 
@@ -29,7 +29,7 @@
 #include "update_engine/common/platform_constants.h"
 #include "update_engine/common/test_utils.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/fake_system_state.h"
+#include "update_engine/cros/fake_system_state.h"
 
 using chromeos_update_engine::test_utils::WriteFileString;
 using std::string;
@@ -75,36 +75,36 @@
 }  // namespace
 
 TEST_F(OmahaRequestParamsTest, MissingChannelTest) {
-  EXPECT_TRUE(params_.Init("", "", false));
+  EXPECT_TRUE(params_.Init("", "", {}));
   // By default, if no channel is set, we should track the stable-channel.
   EXPECT_EQ("stable-channel", params_.target_channel());
 }
 
 TEST_F(OmahaRequestParamsTest, ForceVersionTest) {
-  EXPECT_TRUE(params_.Init("ForcedVersion", "", false));
+  EXPECT_TRUE(params_.Init("ForcedVersion", "", {}));
   EXPECT_EQ(string("ForcedVersion_") + GetMachineType(), params_.os_sp());
   EXPECT_EQ("ForcedVersion", params_.app_version());
 }
 
 TEST_F(OmahaRequestParamsTest, ForcedURLTest) {
-  EXPECT_TRUE(params_.Init("", "http://forced.google.com", false));
+  EXPECT_TRUE(params_.Init("", "http://forced.google.com", {}));
   EXPECT_EQ("http://forced.google.com", params_.update_url());
 }
 
 TEST_F(OmahaRequestParamsTest, MissingURLTest) {
-  EXPECT_TRUE(params_.Init("", "", false));
+  EXPECT_TRUE(params_.Init("", "", {}));
   EXPECT_EQ(constants::kOmahaDefaultProductionURL, params_.update_url());
 }
 
 TEST_F(OmahaRequestParamsTest, DeltaOKTest) {
-  EXPECT_TRUE(params_.Init("", "", false));
+  EXPECT_TRUE(params_.Init("", "", {}));
   EXPECT_TRUE(params_.delta_okay());
 }
 
 TEST_F(OmahaRequestParamsTest, NoDeltasTest) {
   ASSERT_TRUE(
       WriteFileString(tempdir_.GetPath().Append(".nodelta").value(), ""));
-  EXPECT_TRUE(params_.Init("", "", false));
+  EXPECT_TRUE(params_.Init("", "", {}));
   EXPECT_FALSE(params_.delta_okay());
 }
 
@@ -112,12 +112,12 @@
   {
     OmahaRequestParams params(&fake_system_state_);
     params.set_root(tempdir_.GetPath().value());
-    EXPECT_TRUE(params.Init("", "", false));
+    EXPECT_TRUE(params.Init("", "", {}));
     EXPECT_TRUE(params.SetTargetChannel("canary-channel", false, nullptr));
     EXPECT_FALSE(params.mutable_image_props_.is_powerwash_allowed);
   }
   params_.set_root(tempdir_.GetPath().value());
-  EXPECT_TRUE(params_.Init("", "", false));
+  EXPECT_TRUE(params_.Init("", "", {}));
   EXPECT_EQ("canary-channel", params_.target_channel());
   EXPECT_FALSE(params_.mutable_image_props_.is_powerwash_allowed);
 }
@@ -126,12 +126,12 @@
   {
     OmahaRequestParams params(&fake_system_state_);
     params.set_root(tempdir_.GetPath().value());
-    EXPECT_TRUE(params.Init("", "", false));
+    EXPECT_TRUE(params.Init("", "", {}));
     EXPECT_TRUE(params.SetTargetChannel("canary-channel", true, nullptr));
     EXPECT_TRUE(params.mutable_image_props_.is_powerwash_allowed);
   }
   params_.set_root(tempdir_.GetPath().value());
-  EXPECT_TRUE(params_.Init("", "", false));
+  EXPECT_TRUE(params_.Init("", "", {}));
   EXPECT_EQ("canary-channel", params_.target_channel());
   EXPECT_TRUE(params_.mutable_image_props_.is_powerwash_allowed);
 }
@@ -141,7 +141,7 @@
     OmahaRequestParams params(&fake_system_state_);
     params.set_root(tempdir_.GetPath().value());
     SetLockDown(true);
-    EXPECT_TRUE(params.Init("", "", false));
+    EXPECT_TRUE(params.Init("", "", {}));
     params.image_props_.allow_arbitrary_channels = false;
     string error_message;
     EXPECT_FALSE(
@@ -151,7 +151,7 @@
     EXPECT_FALSE(params.mutable_image_props_.is_powerwash_allowed);
   }
   params_.set_root(tempdir_.GetPath().value());
-  EXPECT_TRUE(params_.Init("", "", false));
+  EXPECT_TRUE(params_.Init("", "", {}));
   EXPECT_EQ("stable-channel", params_.target_channel());
   EXPECT_FALSE(params_.mutable_image_props_.is_powerwash_allowed);
 }
@@ -197,7 +197,7 @@
 
   // When set to a valid value while a change is already pending, it should
   // succeed.
-  params_.Init("", "", false);
+  params_.Init("", "", {});
   EXPECT_TRUE(params_.SetTargetChannel("beta-channel", true, nullptr));
   // The target channel should reflect the change, but the download channel
   // should continue to retain the old value ...
@@ -236,6 +236,13 @@
   EXPECT_FALSE(params_.ToMoreStableChannel());
 }
 
+TEST_F(OmahaRequestParamsTest, TargetChannelHintTest) {
+  EXPECT_TRUE(params_.Init("", "", {}));
+  const string kHint("foo-hint");
+  params_.set_lts_tag(kHint);
+  EXPECT_EQ(kHint, params_.lts_tag());
+}
+
 TEST_F(OmahaRequestParamsTest, ShouldPowerwashTest) {
   params_.mutable_image_props_.is_powerwash_allowed = false;
   EXPECT_FALSE(params_.ShouldPowerwash());
@@ -250,16 +257,8 @@
   EXPECT_TRUE(params_.ShouldPowerwash());
 }
 
-TEST_F(OmahaRequestParamsTest, CollectECFWVersionsTest) {
-  params_.hwid_ = string("STUMPY ALEX 12345");
-  EXPECT_FALSE(params_.CollectECFWVersions());
-
-  params_.hwid_ = string("SNOW 12345");
-  EXPECT_TRUE(params_.CollectECFWVersions());
-}
-
 TEST_F(OmahaRequestParamsTest, RequisitionIsSetTest) {
-  EXPECT_TRUE(params_.Init("", "", false));
+  EXPECT_TRUE(params_.Init("", "", {}));
   EXPECT_EQ("fake_requisition", params_.device_requisition());
 }
 }  // namespace chromeos_update_engine
diff --git a/omaha_response.h b/cros/omaha_response.h
similarity index 94%
rename from omaha_response.h
rename to cros/omaha_response.h
index 2b86fe7..43783d6 100644
--- a/omaha_response.h
+++ b/cros/omaha_response.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_OMAHA_RESPONSE_H_
-#define UPDATE_ENGINE_OMAHA_RESPONSE_H_
+#ifndef UPDATE_ENGINE_CROS_OMAHA_RESPONSE_H_
+#define UPDATE_ENGINE_CROS_OMAHA_RESPONSE_H_
 
 #include <fcntl.h>
 #include <sys/stat.h>
@@ -38,7 +38,6 @@
 
   // These are only valid if update_exists is true:
   std::string version;
-  std::string system_version;
 
   struct Package {
     // The ordered list of URLs in the Omaha response. Each item is a complete
@@ -54,6 +53,8 @@
     // True if the payload can be excluded from updating if consistently faulty.
     // False if the payload is critical to update.
     bool can_exclude = false;
+    // The App ID associated with the package.
+    std::string app_id;
   };
   std::vector<Package> packages;
 
@@ -117,4 +118,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_OMAHA_RESPONSE_H_
+#endif  // UPDATE_ENGINE_CROS_OMAHA_RESPONSE_H_
diff --git a/omaha_response_handler_action.cc b/cros/omaha_response_handler_action.cc
similarity index 94%
rename from omaha_response_handler_action.cc
rename to cros/omaha_response_handler_action.cc
index 040f8e7..b6c223f 100644
--- a/omaha_response_handler_action.cc
+++ b/cros/omaha_response_handler_action.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/omaha_response_handler_action.h"
+#include "update_engine/cros/omaha_response_handler_action.h"
 
 #include <limits>
 #include <string>
@@ -28,10 +28,10 @@
 #include "update_engine/common/hardware_interface.h"
 #include "update_engine/common/prefs_interface.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/connection_manager_interface.h"
-#include "update_engine/omaha_request_params.h"
+#include "update_engine/cros/connection_manager_interface.h"
+#include "update_engine/cros/omaha_request_params.h"
+#include "update_engine/cros/payload_state_interface.h"
 #include "update_engine/payload_consumer/delta_performer.h"
-#include "update_engine/payload_state_interface.h"
 #include "update_engine/update_manager/policy.h"
 #include "update_engine/update_manager/update_manager.h"
 
@@ -75,7 +75,6 @@
   // |OmahaRequestAction| and keep the enforcement of exclusions for updates.
   install_plan_.download_url = current_url;
   install_plan_.version = response.version;
-  install_plan_.system_version = response.system_version;
 
   OmahaRequestParams* const params = system_state_->request_params();
   PayloadStateInterface* const payload_state = system_state_->payload_state();
@@ -188,10 +187,12 @@
   }
 
   // Powerwash if either the response requires it or the parameters indicated
-  // powerwash and we are downgrading the version.
+  // powerwash (usually because there was a channel downgrade) and we are
+  // downgrading the version. Enterprise rollback, indicated by
+  // |response.is_rollback| is dealt with separately above.
   if (response.powerwash_required) {
     install_plan_.powerwash_required = true;
-  } else if (params->ShouldPowerwash()) {
+  } else if (params->ShouldPowerwash() && !response.is_rollback) {
     base::Version new_version(response.version);
     base::Version current_version(params->app_version());
 
@@ -205,6 +206,10 @@
                    << " Current version number: " << params->app_version();
     } else if (new_version < current_version) {
       install_plan_.powerwash_required = true;
+      // Always try to preserve enrollment and wifi data for enrolled devices.
+      install_plan_.rollback_data_save_requested =
+          system_state_ && system_state_->device_policy() &&
+          system_state_->device_policy()->IsEnterpriseEnrolled();
     }
   }
 
diff --git a/omaha_response_handler_action.h b/cros/omaha_response_handler_action.h
similarity index 91%
rename from omaha_response_handler_action.h
rename to cros/omaha_response_handler_action.h
index d2e6db8..f3b821e 100644
--- a/omaha_response_handler_action.h
+++ b/cros/omaha_response_handler_action.h
@@ -14,17 +14,17 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_OMAHA_RESPONSE_HANDLER_ACTION_H_
-#define UPDATE_ENGINE_OMAHA_RESPONSE_HANDLER_ACTION_H_
+#ifndef UPDATE_ENGINE_CROS_OMAHA_RESPONSE_HANDLER_ACTION_H_
+#define UPDATE_ENGINE_CROS_OMAHA_RESPONSE_HANDLER_ACTION_H_
 
 #include <string>
 
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
 #include "update_engine/common/action.h"
-#include "update_engine/omaha_request_action.h"
+#include "update_engine/common/system_state.h"
+#include "update_engine/cros/omaha_request_action.h"
 #include "update_engine/payload_consumer/install_plan.h"
-#include "update_engine/system_state.h"
 
 // This class reads in an Omaha response and converts what it sees into
 // an install plan which is passed out.
@@ -90,4 +90,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_OMAHA_RESPONSE_HANDLER_ACTION_H_
+#endif  // UPDATE_ENGINE_CROS_OMAHA_RESPONSE_HANDLER_ACTION_H_
diff --git a/omaha_response_handler_action_unittest.cc b/cros/omaha_response_handler_action_unittest.cc
similarity index 86%
rename from omaha_response_handler_action_unittest.cc
rename to cros/omaha_response_handler_action_unittest.cc
index 04cfa73..8da3205 100644
--- a/omaha_response_handler_action_unittest.cc
+++ b/cros/omaha_response_handler_action_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/omaha_response_handler_action.h"
+#include "update_engine/cros/omaha_response_handler_action.h"
 
 #include <memory>
 #include <string>
@@ -29,8 +29,8 @@
 #include "update_engine/common/platform_constants.h"
 #include "update_engine/common/test_utils.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/fake_system_state.h"
-#include "update_engine/mock_payload_state.h"
+#include "update_engine/cros/fake_system_state.h"
+#include "update_engine/cros/mock_payload_state.h"
 #include "update_engine/payload_consumer/payload_constants.h"
 #include "update_engine/update_manager/mock_policy.h"
 
@@ -176,7 +176,7 @@
 }
 
 TEST_F(OmahaResponseHandlerActionTest, SimpleTest) {
-  test_utils::ScopedTempFile test_deadline_file(
+  ScopedTempFile test_deadline_file(
       "omaha_response_handler_action_unittest-XXXXXX");
   {
     OmahaResponse in;
@@ -532,6 +532,132 @@
 }
 
 TEST_F(OmahaResponseHandlerActionTest,
+       ChangeToMoreStableChannelButSameVersionTest) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.version = "12345.0.0.0";
+  in.packages.push_back({.payload_urls = {"https://ChannelDownVersionUp"},
+                         .size = 1,
+                         .hash = kPayloadHashHex});
+  in.more_info_url = "http://more/info";
+
+  // Create a uniquely named test directory.
+  base::ScopedTempDir tempdir;
+  ASSERT_TRUE(tempdir.CreateUniqueTempDir());
+
+  OmahaRequestParams params(&fake_system_state_);
+  fake_system_state_.fake_hardware()->SetIsOfficialBuild(false);
+  params.set_root(tempdir.GetPath().value());
+  params.set_current_channel("beta-channel");
+  EXPECT_TRUE(params.SetTargetChannel("stable-channel", true, nullptr));
+  params.UpdateDownloadChannel();
+  params.set_app_version("12345.0.0.0");
+
+  fake_system_state_.set_request_params(&params);
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_FALSE(install_plan.powerwash_required);
+  EXPECT_FALSE(install_plan.rollback_data_save_requested);
+}
+
+// On an enrolled device, the rollback data restore should be attempted when
+// doing a powerwash and channel downgrade.
+TEST_F(OmahaResponseHandlerActionTest,
+       ChangeToMoreStableChannelEnrolledDataRestore) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.version = "12345.96.0.0";
+  in.packages.push_back({.payload_urls = {"https://ChannelDownEnrolled"},
+                         .size = 1,
+                         .hash = kPayloadHashHex});
+  in.more_info_url = "http://more/info";
+
+  // Create a uniquely named test directory.
+  base::ScopedTempDir tempdir;
+  ASSERT_TRUE(tempdir.CreateUniqueTempDir());
+
+  OmahaRequestParams params(&fake_system_state_);
+  fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
+  params.set_root(tempdir.GetPath().value());
+  params.set_current_channel("beta-channel");
+  EXPECT_TRUE(params.SetTargetChannel("stable-channel", true, nullptr));
+  params.UpdateDownloadChannel();
+  params.set_app_version("12347.48.0.0");
+
+  testing::NiceMock<policy::MockDevicePolicy> mock_device_policy;
+  EXPECT_CALL(mock_device_policy, IsEnterpriseEnrolled())
+      .WillOnce(Return(true));
+  fake_system_state_.set_device_policy(&mock_device_policy);
+
+  fake_system_state_.set_request_params(&params);
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_TRUE(install_plan.rollback_data_save_requested);
+}
+
+// Never attempt rollback data restore if the device is not enrolled.
+TEST_F(OmahaResponseHandlerActionTest,
+       ChangeToMoreStableChannelUnenrolledNoDataRestore) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.version = "12345.96.0.0";
+  in.packages.push_back({.payload_urls = {"https://ChannelDownEnrolled"},
+                         .size = 1,
+                         .hash = kPayloadHashHex});
+  in.more_info_url = "http://more/info";
+
+  // Create a uniquely named test directory.
+  base::ScopedTempDir tempdir;
+  ASSERT_TRUE(tempdir.CreateUniqueTempDir());
+
+  OmahaRequestParams params(&fake_system_state_);
+  fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
+  params.set_root(tempdir.GetPath().value());
+  params.set_current_channel("beta-channel");
+  EXPECT_TRUE(params.SetTargetChannel("stable-channel", true, nullptr));
+  params.UpdateDownloadChannel();
+  params.set_app_version("12347.48.0.0");
+
+  testing::NiceMock<policy::MockDevicePolicy> mock_device_policy;
+  EXPECT_CALL(mock_device_policy, IsEnterpriseEnrolled())
+      .WillOnce(Return(false));
+  fake_system_state_.set_device_policy(&mock_device_policy);
+
+  fake_system_state_.set_request_params(&params);
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_FALSE(install_plan.rollback_data_save_requested);
+}
+
+// Never attempt rollback data restore if powerwash is not allowed.
+TEST_F(OmahaResponseHandlerActionTest,
+       ChangeToMoreStableChannelNoPowerwashNoDataRestore) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.version = "12345.96.0.0";
+  in.packages.push_back(
+      {.payload_urls = {"https://URL"}, .size = 1, .hash = kPayloadHashHex});
+  in.more_info_url = "http://more/info";
+
+  // Create a uniquely named test directory.
+  base::ScopedTempDir tempdir;
+  ASSERT_TRUE(tempdir.CreateUniqueTempDir());
+
+  OmahaRequestParams params(&fake_system_state_);
+  fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
+  params.set_root(tempdir.GetPath().value());
+  params.set_current_channel("beta-channel");
+  EXPECT_TRUE(params.SetTargetChannel("stable-channel", false, nullptr));
+  params.UpdateDownloadChannel();
+  params.set_app_version("12347.48.0.0");
+
+  fake_system_state_.set_request_params(&params);
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_FALSE(install_plan.rollback_data_save_requested);
+}
+
+TEST_F(OmahaResponseHandlerActionTest,
        ChangeToLessStableVersionAndChannelTest) {
   OmahaResponse in;
   in.update_exists = true;
@@ -793,7 +919,6 @@
   OmahaResponse in;
   in.update_exists = true;
   in.version = "a.b.c.d";
-  in.system_version = "b.c.d.e";
   in.packages.push_back({.payload_urls = {"http://package/1"},
                          .size = 1,
                          .hash = kPayloadHashHex});
@@ -810,7 +935,6 @@
   EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
   EXPECT_EQ(expected_hash_, install_plan.payloads[1].hash);
   EXPECT_EQ(in.version, install_plan.version);
-  EXPECT_EQ(in.system_version, install_plan.system_version);
 }
 
 TEST_F(OmahaResponseHandlerActionTest, TestDeferredByPolicy) {
diff --git a/omaha_utils.cc b/cros/omaha_utils.cc
similarity index 95%
rename from omaha_utils.cc
rename to cros/omaha_utils.cc
index c7f9921..fc05cb9 100644
--- a/omaha_utils.cc
+++ b/cros/omaha_utils.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/omaha_utils.h"
+#include "update_engine/cros/omaha_utils.h"
 
 #include <base/logging.h>
 #include <base/strings/string_number_conversions.h>
diff --git a/omaha_utils.h b/cros/omaha_utils.h
similarity index 89%
rename from omaha_utils.h
rename to cros/omaha_utils.h
index 458bf9e..6741635 100644
--- a/omaha_utils.h
+++ b/cros/omaha_utils.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_OMAHA_UTILS_H_
-#define UPDATE_ENGINE_OMAHA_UTILS_H_
+#ifndef UPDATE_ENGINE_CROS_OMAHA_UTILS_H_
+#define UPDATE_ENGINE_CROS_OMAHA_UTILS_H_
 
 #include <string>
 
@@ -36,4 +36,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_OMAHA_UTILS_H_
+#endif  // UPDATE_ENGINE_CROS_OMAHA_UTILS_H_
diff --git a/omaha_utils_unittest.cc b/cros/omaha_utils_unittest.cc
similarity index 96%
rename from omaha_utils_unittest.cc
rename to cros/omaha_utils_unittest.cc
index 849905a..f89f690 100644
--- a/omaha_utils_unittest.cc
+++ b/cros/omaha_utils_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/omaha_utils.h"
+#include "update_engine/cros/omaha_utils.h"
 
 #include <gtest/gtest.h>
 #include <vector>
diff --git a/p2p_manager.cc b/cros/p2p_manager.cc
similarity index 99%
rename from p2p_manager.cc
rename to cros/p2p_manager.cc
index 00ff8ce..dc12b35 100644
--- a/p2p_manager.cc
+++ b/cros/p2p_manager.cc
@@ -23,7 +23,7 @@
 #define _BSD_SOURCE
 #endif
 
-#include "update_engine/p2p_manager.h"
+#include "update_engine/cros/p2p_manager.h"
 
 #include <errno.h>
 #include <fcntl.h>
diff --git a/p2p_manager.h b/cros/p2p_manager.h
similarity index 97%
rename from p2p_manager.h
rename to cros/p2p_manager.h
index ef62f0d..bd359fa 100644
--- a/p2p_manager.h
+++ b/cros/p2p_manager.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_P2P_MANAGER_H_
-#define UPDATE_ENGINE_P2P_MANAGER_H_
+#ifndef UPDATE_ENGINE_CROS_P2P_MANAGER_H_
+#define UPDATE_ENGINE_CROS_P2P_MANAGER_H_
 
 #include <string>
 #include <vector>
@@ -183,4 +183,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_P2P_MANAGER_H_
+#endif  // UPDATE_ENGINE_CROS_P2P_MANAGER_H_
diff --git a/p2p_manager_unittest.cc b/cros/p2p_manager_unittest.cc
similarity index 97%
rename from p2p_manager_unittest.cc
rename to cros/p2p_manager_unittest.cc
index 5771ec1..8b6d741 100644
--- a/p2p_manager_unittest.cc
+++ b/cros/p2p_manager_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/p2p_manager.h"
+#include "update_engine/cros/p2p_manager.h"
 
 #include <dirent.h>
 #include <fcntl.h>
@@ -30,8 +30,13 @@
 #include <base/bind.h>
 #include <base/callback.h>
 #include <base/files/file_util.h>
+#if BASE_VER < 780000  // Android
 #include <base/message_loop/message_loop.h>
+#endif  // BASE_VER < 780000
 #include <base/strings/stringprintf.h>
+#if BASE_VER >= 780000  // CrOS
+#include <base/task/single_thread_task_executor.h>
+#endif  // BASE_VER >= 780000
 #include <brillo/asynchronous_signal_handler.h>
 #include <brillo/message_loops/base_message_loop.h>
 #include <brillo/message_loops/message_loop.h>
@@ -46,7 +51,7 @@
 #include "update_engine/common/subprocess.h"
 #include "update_engine/common/test_utils.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/fake_p2p_manager_configuration.h"
+#include "update_engine/cros/fake_p2p_manager_configuration.h"
 #include "update_engine/update_manager/fake_update_manager.h"
 #include "update_engine/update_manager/mock_policy.h"
 
@@ -92,8 +97,13 @@
                                          TimeDelta::FromDays(5)));
   }
 
+#if BASE_VER < 780000  // Android
   base::MessageLoopForIO base_loop_;
   brillo::BaseMessageLoop loop_{&base_loop_};
+#else   // CrOS
+  base::SingleThreadTaskExecutor base_loop_{base::MessagePumpType::IO};
+  brillo::BaseMessageLoop loop_{base_loop_.task_runner()};
+#endif  // BASE_VER < 780000
   brillo::AsynchronousSignalHandler async_signal_handler_;
   Subprocess subprocess_;
 
diff --git a/payload_state.cc b/cros/payload_state.cc
similarity index 98%
rename from payload_state.cc
rename to cros/payload_state.cc
index 4945fe7..d2e6851 100644
--- a/payload_state.cc
+++ b/cros/payload_state.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/payload_state.h"
+#include "update_engine/cros/payload_state.h"
 
 #include <algorithm>
 #include <string>
@@ -29,15 +29,15 @@
 #include "update_engine/common/constants.h"
 #include "update_engine/common/error_code_utils.h"
 #include "update_engine/common/hardware_interface.h"
+#include "update_engine/common/metrics_reporter_interface.h"
 #include "update_engine/common/prefs.h"
+#include "update_engine/common/system_state.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/connection_manager_interface.h"
-#include "update_engine/metrics_reporter_interface.h"
+#include "update_engine/cros/connection_manager_interface.h"
+#include "update_engine/cros/omaha_request_params.h"
+#include "update_engine/cros/update_attempter.h"
 #include "update_engine/metrics_utils.h"
-#include "update_engine/omaha_request_params.h"
 #include "update_engine/payload_consumer/install_plan.h"
-#include "update_engine/system_state.h"
-#include "update_engine/update_attempter.h"
 
 using base::Time;
 using base::TimeDelta;
@@ -375,6 +375,7 @@
     case ErrorCode::kUnresolvedHostRecovered:
     case ErrorCode::kNotEnoughSpace:
     case ErrorCode::kDeviceCorrupted:
+    case ErrorCode::kPackageExcludedFromUpdate:
       LOG(INFO) << "Not incrementing URL index or failure count for this error";
       break;
 
@@ -463,6 +464,7 @@
 }
 
 void PayloadState::IncrementFullPayloadAttemptNumber() {
+  DCHECK(payload_index_ < response_.packages.size());
   // Update the payload attempt number for full payloads and the backoff time.
   if (response_.packages[payload_index_].is_delta) {
     LOG(INFO) << "Not incrementing payload attempt number for delta payloads";
@@ -475,6 +477,7 @@
 }
 
 void PayloadState::IncrementUrlIndex() {
+  DCHECK(payload_index_ < candidate_urls_.size());
   size_t next_url_index = url_index_ + 1;
   size_t max_url_size = candidate_urls_[payload_index_].size();
   if (next_url_index < max_url_size) {
@@ -511,6 +514,10 @@
 }
 
 void PayloadState::ExcludeCurrentPayload() {
+  if (payload_index_ >= response_.packages.size()) {
+    LOG(INFO) << "Skipping exclusion of the current payload.";
+    return;
+  }
   const auto& package = response_.packages[payload_index_];
   if (!package.can_exclude) {
     LOG(INFO) << "Not excluding as marked non-excludable for package hash="
@@ -613,10 +620,6 @@
   return kPayloadTypeForcedFull;
 }
 
-// TODO(zeuthen): Currently we don't report the UpdateEngine.Attempt.*
-// metrics if the attempt ends abnormally, e.g. if the update_engine
-// process crashes or the device is rebooted. See
-// http://crbug.com/357676
 void PayloadState::CollectAndReportAttemptMetrics(ErrorCode code) {
   int attempt_number = GetPayloadAttemptNumber();
 
@@ -671,6 +674,7 @@
     case metrics::AttemptResult::kAbnormalTermination:
     case metrics::AttemptResult::kUpdateCanceled:
     case metrics::AttemptResult::kUpdateSucceededNotActive:
+    case metrics::AttemptResult::kUpdateSkipped:
     case metrics::AttemptResult::kNumConstants:
     case metrics::AttemptResult::kUnset:
       break;
@@ -924,10 +928,12 @@
 }
 
 bool PayloadState::NextPayload() {
-  if (payload_index_ + 1 >= candidate_urls_.size())
+  if (payload_index_ >= candidate_urls_.size())
+    return false;
+  SetPayloadIndex(payload_index_ + 1);
+  if (payload_index_ >= candidate_urls_.size())
     return false;
   SetUrlIndex(0);
-  SetPayloadIndex(payload_index_ + 1);
   return true;
 }
 
diff --git a/payload_state.h b/cros/payload_state.h
similarity index 97%
rename from payload_state.h
rename to cros/payload_state.h
index 427836b..0827273 100644
--- a/payload_state.h
+++ b/cros/payload_state.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_PAYLOAD_STATE_H_
-#define UPDATE_ENGINE_PAYLOAD_STATE_H_
+#ifndef UPDATE_ENGINE_CROS_PAYLOAD_STATE_H_
+#define UPDATE_ENGINE_CROS_PAYLOAD_STATE_H_
 
 #include <algorithm>
 #include <string>
@@ -25,9 +25,9 @@
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
 #include "update_engine/common/excluder_interface.h"
+#include "update_engine/common/metrics_constants.h"
 #include "update_engine/common/prefs_interface.h"
-#include "update_engine/metrics_constants.h"
-#include "update_engine/payload_state_interface.h"
+#include "update_engine/cros/payload_state_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -161,6 +161,8 @@
   FRIEND_TEST(PayloadStateTest, ExcludeNoopForNonExcludables);
   FRIEND_TEST(PayloadStateTest, ExcludeOnlyCanExcludables);
   FRIEND_TEST(PayloadStateTest, IncrementFailureExclusionTest);
+  FRIEND_TEST(PayloadStateTest, HaltExclusionPostPayloadExhaustion);
+  FRIEND_TEST(PayloadStateTest, NonInfinitePayloadIndexIncrement);
 
   // Helper called when an attempt has begun, is called by
   // UpdateResumed(), UpdateRestarted() and Rollback().
@@ -597,4 +599,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_PAYLOAD_STATE_H_
+#endif  // UPDATE_ENGINE_CROS_PAYLOAD_STATE_H_
diff --git a/payload_state_interface.h b/cros/payload_state_interface.h
similarity index 97%
rename from payload_state_interface.h
rename to cros/payload_state_interface.h
index d384a0e..9ead650 100644
--- a/payload_state_interface.h
+++ b/cros/payload_state_interface.h
@@ -14,14 +14,14 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_PAYLOAD_STATE_INTERFACE_H_
-#define UPDATE_ENGINE_PAYLOAD_STATE_INTERFACE_H_
+#ifndef UPDATE_ENGINE_CROS_PAYLOAD_STATE_INTERFACE_H_
+#define UPDATE_ENGINE_CROS_PAYLOAD_STATE_INTERFACE_H_
 
 #include <string>
 
 #include "update_engine/common/action_processor.h"
 #include "update_engine/common/constants.h"
-#include "update_engine/omaha_response.h"
+#include "update_engine/cros/omaha_response.h"
 
 namespace chromeos_update_engine {
 
@@ -212,4 +212,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_PAYLOAD_STATE_INTERFACE_H_
+#endif  // UPDATE_ENGINE_CROS_PAYLOAD_STATE_INTERFACE_H_
diff --git a/payload_state_unittest.cc b/cros/payload_state_unittest.cc
similarity index 97%
rename from payload_state_unittest.cc
rename to cros/payload_state_unittest.cc
index c33bda4..b48cff4 100644
--- a/payload_state_unittest.cc
+++ b/cros/payload_state_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/payload_state.h"
+#include "update_engine/cros/payload_state.h"
 
 #include <base/files/file_path.h>
 #include <base/files/file_util.h>
@@ -27,14 +27,14 @@
 #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/metrics_reporter_interface.h"
 #include "update_engine/common/mock_excluder.h"
 #include "update_engine/common/mock_prefs.h"
 #include "update_engine/common/prefs.h"
 #include "update_engine/common/test_utils.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/fake_system_state.h"
-#include "update_engine/metrics_reporter_interface.h"
-#include "update_engine/omaha_request_action.h"
+#include "update_engine/cros/fake_system_state.h"
+#include "update_engine/cros/omaha_request_action.h"
 
 using base::Time;
 using base::TimeDelta;
@@ -630,7 +630,7 @@
   PayloadState payload_state;
   FakeSystemState fake_system_state;
   OmahaRequestParams params(&fake_system_state);
-  params.Init("", "", true);  // interactive = True.
+  params.Init("", "", {.interactive = true});
   fake_system_state.set_request_params(&params);
 
   EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
@@ -653,7 +653,7 @@
   PayloadState payload_state;
   FakeSystemState fake_system_state;
   OmahaRequestParams params(&fake_system_state);
-  params.Init("", "", false);  // interactive = False.
+  params.Init("", "", {});
   fake_system_state.set_request_params(&params);
 
   EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
@@ -1019,7 +1019,7 @@
   // Mock out the os version and make sure it's excluded correctly.
   string rollback_version = "2345.0.0";
   OmahaRequestParams params(&fake_system_state);
-  params.Init(rollback_version, "", false);
+  params.Init(rollback_version, "", {});
   fake_system_state.set_request_params(&params);
 
   EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
@@ -1778,4 +1778,49 @@
   payload_state.IncrementFailureCount();
 }
 
+TEST(PayloadStateTest, HaltExclusionPostPayloadExhaustion) {
+  PayloadState payload_state;
+  FakeSystemState fake_system_state;
+  StrictMock<MockExcluder> mock_excluder;
+  EXPECT_CALL(*fake_system_state.mock_update_attempter(), GetExcluder())
+      .WillOnce(Return(&mock_excluder));
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+
+  OmahaResponse response;
+  // Non-critical package.
+  response.packages.push_back(
+      {.payload_urls = {"http://test1a", "http://test2a"},
+       .size = 123456789,
+       .metadata_size = 58123,
+       .metadata_signature = "msign",
+       .hash = "hash",
+       .can_exclude = true});
+  payload_state.SetResponse(response);
+
+  // Exclusion should be called when excluded.
+  EXPECT_CALL(mock_excluder, Exclude(utils::GetExclusionName("http://test1a")))
+      .WillOnce(Return(true));
+  payload_state.ExcludeCurrentPayload();
+
+  // No more paylods to go through.
+  EXPECT_FALSE(payload_state.NextPayload());
+
+  // Exclusion should not be called as all |Payload|s are exhausted.
+  payload_state.ExcludeCurrentPayload();
+}
+
+TEST(PayloadStateTest, NonInfinitePayloadIndexIncrement) {
+  PayloadState payload_state;
+  FakeSystemState fake_system_state;
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+
+  payload_state.SetResponse({});
+
+  EXPECT_FALSE(payload_state.NextPayload());
+  int payload_index = payload_state.payload_index_;
+
+  EXPECT_FALSE(payload_state.NextPayload());
+  EXPECT_EQ(payload_index, payload_state.payload_index_);
+}
+
 }  // namespace chromeos_update_engine
diff --git a/common/platform_constants_chromeos.cc b/cros/platform_constants_chromeos.cc
similarity index 100%
rename from common/platform_constants_chromeos.cc
rename to cros/platform_constants_chromeos.cc
diff --git a/power_manager_chromeos.cc b/cros/power_manager_chromeos.cc
similarity index 92%
rename from power_manager_chromeos.cc
rename to cros/power_manager_chromeos.cc
index 531d367..c1a2859 100644
--- a/power_manager_chromeos.cc
+++ b/cros/power_manager_chromeos.cc
@@ -14,14 +14,14 @@
 // limitations under the License.
 //
 
-#include "update_engine/power_manager_chromeos.h"
+#include "update_engine/cros/power_manager_chromeos.h"
 
 #include <memory>
 
 #include <power_manager/dbus-constants.h>
 #include <power_manager/dbus-proxies.h>
 
-#include "update_engine/dbus_connection.h"
+#include "update_engine/cros/dbus_connection.h"
 
 namespace chromeos_update_engine {
 
diff --git a/power_manager_chromeos.h b/cros/power_manager_chromeos.h
similarity index 83%
rename from power_manager_chromeos.h
rename to cros/power_manager_chromeos.h
index eeb14d8..8930508 100644
--- a/power_manager_chromeos.h
+++ b/cros/power_manager_chromeos.h
@@ -14,13 +14,13 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_POWER_MANAGER_CHROMEOS_H_
-#define UPDATE_ENGINE_POWER_MANAGER_CHROMEOS_H_
+#ifndef UPDATE_ENGINE_CROS_POWER_MANAGER_CHROMEOS_H_
+#define UPDATE_ENGINE_CROS_POWER_MANAGER_CHROMEOS_H_
 
 #include <base/macros.h>
 #include <power_manager/dbus-proxies.h>
 
-#include "update_engine/power_manager_interface.h"
+#include "update_engine/cros/power_manager_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -41,4 +41,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_POWER_MANAGER_CHROMEOS_H_
+#endif  // UPDATE_ENGINE_CROS_POWER_MANAGER_CHROMEOS_H_
diff --git a/power_manager_interface.h b/cros/power_manager_interface.h
similarity index 87%
rename from power_manager_interface.h
rename to cros/power_manager_interface.h
index 8f77650..1f712d2 100644
--- a/power_manager_interface.h
+++ b/cros/power_manager_interface.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_POWER_MANAGER_INTERFACE_H_
-#define UPDATE_ENGINE_POWER_MANAGER_INTERFACE_H_
+#ifndef UPDATE_ENGINE_CROS_POWER_MANAGER_INTERFACE_H_
+#define UPDATE_ENGINE_CROS_POWER_MANAGER_INTERFACE_H_
 
 #include <memory>
 
@@ -44,4 +44,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_POWER_MANAGER_INTERFACE_H_
+#endif  // UPDATE_ENGINE_CROS_POWER_MANAGER_INTERFACE_H_
diff --git a/real_system_state.cc b/cros/real_system_state.cc
similarity index 97%
rename from real_system_state.cc
rename to cros/real_system_state.cc
index 74a37f3..4f57246 100644
--- a/real_system_state.cc
+++ b/cros/real_system_state.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/real_system_state.h"
+#include "update_engine/cros/real_system_state.h"
 
 #include <memory>
 #include <string>
@@ -35,11 +35,11 @@
 #include "update_engine/common/dlcservice_interface.h"
 #include "update_engine/common/hardware.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/metrics_reporter_omaha.h"
-#include "update_engine/update_boot_flags_action.h"
+#include "update_engine/cros/metrics_reporter_omaha.h"
 #if USE_DBUS
-#include "update_engine/dbus_connection.h"
+#include "update_engine/cros/dbus_connection.h"
 #endif  // USE_DBUS
+#include "update_engine/update_boot_flags_action.h"
 #include "update_engine/update_manager/state_factory.h"
 
 using brillo::MessageLoop;
@@ -138,7 +138,7 @@
   // will be re-initialized before every request using the actual request
   // options. This initialization here pre-loads current channel and version, so
   // the DBus service can access it.
-  if (!request_params_.Init("", "", false)) {
+  if (!request_params_.Init("", "", {})) {
     LOG(WARNING) << "Ignoring OmahaRequestParams initialization error. Some "
                     "features might not work properly.";
   }
diff --git a/real_system_state.h b/cros/real_system_state.h
similarity index 90%
rename from real_system_state.h
rename to cros/real_system_state.h
index 807a205..798fca0 100644
--- a/real_system_state.h
+++ b/cros/real_system_state.h
@@ -14,10 +14,10 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_REAL_SYSTEM_STATE_H_
-#define UPDATE_ENGINE_REAL_SYSTEM_STATE_H_
+#ifndef UPDATE_ENGINE_CROS_REAL_SYSTEM_STATE_H_
+#define UPDATE_ENGINE_CROS_REAL_SYSTEM_STATE_H_
 
-#include "update_engine/system_state.h"
+#include "update_engine/common/system_state.h"
 
 #include <memory>
 #include <set>
@@ -31,17 +31,17 @@
 #include "update_engine/certificate_checker.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/dlcservice_interface.h"
 #include "update_engine/common/hardware_interface.h"
+#include "update_engine/common/metrics_reporter_interface.h"
 #include "update_engine/common/prefs.h"
-#include "update_engine/connection_manager_interface.h"
-#include "update_engine/daemon_state_interface.h"
-#include "update_engine/metrics_reporter_interface.h"
-#include "update_engine/metrics_reporter_omaha.h"
-#include "update_engine/p2p_manager.h"
-#include "update_engine/payload_state.h"
-#include "update_engine/power_manager_interface.h"
-#include "update_engine/update_attempter.h"
+#include "update_engine/cros/connection_manager_interface.h"
+#include "update_engine/cros/metrics_reporter_omaha.h"
+#include "update_engine/cros/p2p_manager.h"
+#include "update_engine/cros/payload_state.h"
+#include "update_engine/cros/power_manager_interface.h"
+#include "update_engine/cros/update_attempter.h"
 #include "update_engine/update_manager/update_manager.h"
 
 namespace chromeos_update_engine {
@@ -199,4 +199,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_REAL_SYSTEM_STATE_H_
+#endif  // UPDATE_ENGINE_CROS_REAL_SYSTEM_STATE_H_
diff --git a/cros/requisition_util.cc b/cros/requisition_util.cc
new file mode 100644
index 0000000..6296d0b
--- /dev/null
+++ b/cros/requisition_util.cc
@@ -0,0 +1,69 @@
+//
+// 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/cros/requisition_util.h"
+
+#include <memory>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/json/json_file_value_serializer.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+
+#include "update_engine/common/subprocess.h"
+#include "update_engine/common/utils.h"
+
+using std::string;
+using std::vector;
+
+namespace {
+
+constexpr char kOemRequisitionKey[] = "oem_device_requisition";
+
+}  // namespace
+
+namespace chromeos_update_engine {
+
+string ReadDeviceRequisition(const base::FilePath& local_state) {
+  string requisition;
+  bool vpd_retval = utils::GetVpdValue(kOemRequisitionKey, &requisition);
+
+  // Some users manually convert non-CfM hardware at enrollment time, so VPD
+  // value may be missing. So check the Local State JSON as well.
+  if ((requisition.empty() || !vpd_retval) && base::PathExists(local_state)) {
+    int error_code;
+    std::string error_msg;
+    JSONFileValueDeserializer deserializer(local_state);
+    std::unique_ptr<base::Value> root =
+        deserializer.Deserialize(&error_code, &error_msg);
+    if (!root) {
+      if (error_code != 0) {
+        LOG(ERROR) << "Unable to deserialize Local State with exit code: "
+                   << error_code << " and error: " << error_msg;
+      }
+      return "";
+    }
+    auto* path = root->FindPath({"enrollment", "device_requisition"});
+    if (!path || !path->is_string()) {
+      return "";
+    }
+    path->GetAsString(&requisition);
+  }
+  return requisition;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/cros/requisition_util.h b/cros/requisition_util.h
new file mode 100644
index 0000000..6ec4783
--- /dev/null
+++ b/cros/requisition_util.h
@@ -0,0 +1,32 @@
+//
+// 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_CROS_REQUISITION_UTIL_H_
+#define UPDATE_ENGINE_CROS_REQUISITION_UTIL_H_
+
+#include <string>
+
+#include <base/files/file_path.h>
+
+namespace chromeos_update_engine {
+
+// Checks the VPD and Local State for the device's requisition and returns it,
+// or an empty string if the device has no requisition.
+std::string ReadDeviceRequisition(const base::FilePath& local_state);
+
+}  // namespace chromeos_update_engine
+
+#endif  //  UPDATE_ENGINE_CROS_REQUISITION_UTIL_H_
diff --git a/cros/requisition_util_unittest.cc b/cros/requisition_util_unittest.cc
new file mode 100644
index 0000000..269585e
--- /dev/null
+++ b/cros/requisition_util_unittest.cc
@@ -0,0 +1,94 @@
+//
+// 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/cros/requisition_util.h"
+
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/test_utils.h"
+
+using chromeos_update_engine::test_utils::WriteFileString;
+using std::string;
+
+namespace {
+
+const char kRemoraJSON[] =
+    "{\n"
+    "   \"the_list\": [ \"val1\", \"val2\" ],\n"
+    "   \"enrollment\": {\n"
+    "      \"autostart\": true,\n"
+    "      \"can_exit\": false,\n"
+    "      \"device_requisition\": \"remora\"\n"
+    "   },\n"
+    "   \"some_String\": \"1337\",\n"
+    "   \"some_int\": 42\n"
+    "}\n";
+
+const char kNoEnrollmentJSON[] =
+    "{\n"
+    "   \"the_list\": [ \"val1\", \"val2\" ],\n"
+    "   \"enrollment\": {\n"
+    "      \"autostart\": true,\n"
+    "      \"can_exit\": false,\n"
+    "      \"device_requisition\": \"\"\n"
+    "   },\n"
+    "   \"some_String\": \"1337\",\n"
+    "   \"some_int\": 42\n"
+    "}\n";
+}  // namespace
+
+namespace chromeos_update_engine {
+
+class RequisitionUtilTest : public ::testing::Test {
+ protected:
+  void SetUp() override { ASSERT_TRUE(root_dir_.CreateUniqueTempDir()); }
+
+  void WriteJsonToFile(const string& json) {
+    path_ =
+        base::FilePath(root_dir_.GetPath().value() + "/chronos/Local State");
+    ASSERT_TRUE(base::CreateDirectory(path_.DirName()));
+    ASSERT_TRUE(WriteFileString(path_.value(), json));
+  }
+
+  base::ScopedTempDir root_dir_;
+  base::FilePath path_;
+};
+
+TEST_F(RequisitionUtilTest, BadJsonReturnsEmpty) {
+  WriteJsonToFile("this isn't JSON");
+  EXPECT_EQ("", ReadDeviceRequisition(path_));
+}
+
+TEST_F(RequisitionUtilTest, NoFileReturnsEmpty) {
+  EXPECT_EQ("", ReadDeviceRequisition(path_));
+}
+
+TEST_F(RequisitionUtilTest, EnrollmentRequisition) {
+  WriteJsonToFile(kRemoraJSON);
+  EXPECT_EQ("remora", ReadDeviceRequisition(path_));
+}
+
+TEST_F(RequisitionUtilTest, BlankEnrollment) {
+  WriteJsonToFile(kNoEnrollmentJSON);
+  EXPECT_EQ("", ReadDeviceRequisition(path_));
+}
+
+}  // namespace chromeos_update_engine
diff --git a/shill_proxy.cc b/cros/shill_proxy.cc
similarity index 93%
rename from shill_proxy.cc
rename to cros/shill_proxy.cc
index d398bba..a3c8543 100644
--- a/shill_proxy.cc
+++ b/cros/shill_proxy.cc
@@ -14,9 +14,9 @@
 // limitations under the License.
 //
 
-#include "update_engine/shill_proxy.h"
+#include "update_engine/cros/shill_proxy.h"
 
-#include "update_engine/dbus_connection.h"
+#include "update_engine/cros/dbus_connection.h"
 
 using org::chromium::flimflam::ManagerProxy;
 using org::chromium::flimflam::ManagerProxyInterface;
diff --git a/shill_proxy.h b/cros/shill_proxy.h
similarity index 88%
rename from shill_proxy.h
rename to cros/shill_proxy.h
index 4b466c9..aff428a 100644
--- a/shill_proxy.h
+++ b/cros/shill_proxy.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_SHILL_PROXY_H_
-#define UPDATE_ENGINE_SHILL_PROXY_H_
+#ifndef UPDATE_ENGINE_CROS_SHILL_PROXY_H_
+#define UPDATE_ENGINE_CROS_SHILL_PROXY_H_
 
 #include <memory>
 #include <string>
@@ -25,7 +25,7 @@
 #include <dbus/object_path.h>
 #include <shill/dbus-proxies.h>
 
-#include "update_engine/shill_proxy_interface.h"
+#include "update_engine/cros/shill_proxy_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -51,4 +51,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_SHILL_PROXY_H_
+#endif  // UPDATE_ENGINE_CROS_SHILL_PROXY_H_
diff --git a/shill_proxy_interface.h b/cros/shill_proxy_interface.h
similarity index 91%
rename from shill_proxy_interface.h
rename to cros/shill_proxy_interface.h
index 5f6b44e..19e81f3 100644
--- a/shill_proxy_interface.h
+++ b/cros/shill_proxy_interface.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_SHILL_PROXY_INTERFACE_H_
-#define UPDATE_ENGINE_SHILL_PROXY_INTERFACE_H_
+#ifndef UPDATE_ENGINE_CROS_SHILL_PROXY_INTERFACE_H_
+#define UPDATE_ENGINE_CROS_SHILL_PROXY_INTERFACE_H_
 
 #include <memory>
 #include <string>
@@ -53,4 +53,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_SHILL_PROXY_INTERFACE_H_
+#endif  // UPDATE_ENGINE_CROS_SHILL_PROXY_INTERFACE_H_
diff --git a/update_attempter.cc b/cros/update_attempter.cc
similarity index 93%
rename from update_attempter.cc
rename to cros/update_attempter.cc
index c4fe348..e8cb291 100644
--- a/update_attempter.cc
+++ b/cros/update_attempter.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/update_attempter.h"
+#include "update_engine/cros/update_attempter.h"
 
 #include <stdint.h>
 
@@ -47,26 +47,26 @@
 #include "update_engine/common/clock_interface.h"
 #include "update_engine/common/constants.h"
 #include "update_engine/common/dlcservice_interface.h"
+#include "update_engine/common/download_action.h"
 #include "update_engine/common/excluder_interface.h"
 #include "update_engine/common/hardware_interface.h"
+#include "update_engine/common/metrics_reporter_interface.h"
 #include "update_engine/common/platform_constants.h"
 #include "update_engine/common/prefs.h"
 #include "update_engine/common/prefs_interface.h"
 #include "update_engine/common/subprocess.h"
+#include "update_engine/common/system_state.h"
 #include "update_engine/common/utils.h"
+#include "update_engine/cros/omaha_request_action.h"
+#include "update_engine/cros/omaha_request_params.h"
+#include "update_engine/cros/omaha_response_handler_action.h"
+#include "update_engine/cros/omaha_utils.h"
+#include "update_engine/cros/p2p_manager.h"
+#include "update_engine/cros/payload_state_interface.h"
+#include "update_engine/cros/power_manager_interface.h"
 #include "update_engine/libcurl_http_fetcher.h"
-#include "update_engine/metrics_reporter_interface.h"
-#include "update_engine/omaha_request_action.h"
-#include "update_engine/omaha_request_params.h"
-#include "update_engine/omaha_response_handler_action.h"
-#include "update_engine/omaha_utils.h"
-#include "update_engine/p2p_manager.h"
-#include "update_engine/payload_consumer/download_action.h"
 #include "update_engine/payload_consumer/filesystem_verifier_action.h"
 #include "update_engine/payload_consumer/postinstall_runner_action.h"
-#include "update_engine/payload_state_interface.h"
-#include "update_engine/power_manager_interface.h"
-#include "update_engine/system_state.h"
 #include "update_engine/update_boot_flags_action.h"
 #include "update_engine/update_manager/policy.h"
 #include "update_engine/update_manager/policy_utils.h"
@@ -244,15 +244,7 @@
   system_state_->metrics_reporter()->ReportDailyMetrics(age);
 }
 
-void UpdateAttempter::Update(const string& app_version,
-                             const string& omaha_url,
-                             const string& target_channel,
-                             const string& target_version_prefix,
-                             bool rollback_allowed,
-                             bool rollback_data_save_requested,
-                             int rollback_allowed_milestones,
-                             bool obey_proxies,
-                             bool interactive) {
+void UpdateAttempter::Update(const UpdateCheckParams& params) {
   // This is normally called frequently enough so it's appropriate to use as a
   // hook for reporting daily metrics.
   // TODO(garnold) This should be hooked to a separate (reliable and consistent)
@@ -281,19 +273,11 @@
     return;
   }
 
-  if (!CalculateUpdateParams(app_version,
-                             omaha_url,
-                             target_channel,
-                             target_version_prefix,
-                             rollback_allowed,
-                             rollback_data_save_requested,
-                             rollback_allowed_milestones,
-                             obey_proxies,
-                             interactive)) {
+  if (!CalculateUpdateParams(params)) {
     return;
   }
 
-  BuildUpdateActions(interactive);
+  BuildUpdateActions(params.interactive);
 
   SetStatusAndNotify(UpdateStatus::CHECKING_FOR_UPDATE);
 
@@ -356,15 +340,7 @@
   payload_state->SetUsingP2PForSharing(use_p2p_for_sharing);
 }
 
-bool UpdateAttempter::CalculateUpdateParams(const string& app_version,
-                                            const string& omaha_url,
-                                            const string& target_channel,
-                                            const string& target_version_prefix,
-                                            bool rollback_allowed,
-                                            bool rollback_data_save_requested,
-                                            int rollback_allowed_milestones,
-                                            bool obey_proxies,
-                                            bool interactive) {
+bool UpdateAttempter::CalculateUpdateParams(const UpdateCheckParams& params) {
   http_response_code_ = 0;
   PayloadStateInterface* const payload_state = system_state_->payload_state();
 
@@ -375,27 +351,13 @@
   // policy is available again.
   UpdateRollbackHappened();
 
-  // Update the target version prefix.
-  omaha_request_params_->set_target_version_prefix(target_version_prefix);
-
-  // Set whether rollback is allowed.
-  omaha_request_params_->set_rollback_allowed(rollback_allowed);
-
-  // Set whether saving data over rollback is requested.
-  omaha_request_params_->set_rollback_data_save_requested(
-      rollback_data_save_requested);
-
-  CalculateStagingParams(interactive);
+  CalculateStagingParams(params.interactive);
   // If staging_wait_time_ wasn't set, staging is off, use scattering instead.
   if (staging_wait_time_.InSeconds() == 0) {
-    CalculateScatteringParams(interactive);
+    CalculateScatteringParams(params.interactive);
   }
 
-  // Set how many milestones of rollback are allowed.
-  omaha_request_params_->set_rollback_allowed_milestones(
-      rollback_allowed_milestones);
-
-  CalculateP2PParams(interactive);
+  CalculateP2PParams(params.interactive);
   if (payload_state->GetUsingP2PForDownloading() ||
       payload_state->GetUsingP2PForSharing()) {
     // OK, p2p is to be used - start it and perform housekeeping.
@@ -408,32 +370,12 @@
     }
   }
 
-  if (!omaha_request_params_->Init(app_version, omaha_url, interactive)) {
+  if (!omaha_request_params_->Init(
+          forced_app_version_, forced_omaha_url_, params)) {
     LOG(ERROR) << "Unable to initialize Omaha request params.";
     return false;
   }
 
-  // Set the target channel, if one was provided.
-  if (target_channel.empty()) {
-    LOG(INFO) << "No target channel mandated by policy.";
-  } else {
-    LOG(INFO) << "Setting target channel as mandated: " << target_channel;
-    // Pass in false for powerwash_allowed until we add it to the policy
-    // protobuf.
-    string error_message;
-    if (!omaha_request_params_->SetTargetChannel(
-            target_channel, false, &error_message)) {
-      LOG(ERROR) << "Setting the channel failed: " << error_message;
-    }
-
-    // Since this is the beginning of a new attempt, update the download
-    // channel. The download channel won't be updated until the next attempt,
-    // even if target channel changes meanwhile, so that how we'll know if we
-    // should cancel the current download attempt if there's such a change in
-    // target channel.
-    omaha_request_params_->UpdateDownloadChannel();
-  }
-
   // The function |CalculateDlcParams| makes use of the function |GetAppId| from
   // |OmahaRequestParams|, so to ensure that the return from |GetAppId|
   // doesn't change, no changes to the values |download_channel_|,
@@ -441,8 +383,6 @@
   // |omaha_request_params_| shall be made below this line.
   CalculateDlcParams();
 
-  omaha_request_params_->set_is_install(is_install_);
-
   // Set Quick Fix Build token if policy is set and the device is enterprise
   // enrolled.
   string token;
@@ -473,7 +413,7 @@
             << payload_state->GetUsingP2PForSharing();
 
   obeying_proxies_ = true;
-  if (obey_proxies || proxy_manual_checks_ == 0) {
+  if (proxy_manual_checks_ == 0) {
     LOG(INFO) << "forced to obey proxies";
     // If forced to obey proxies, every 20th request will not use proxies
     proxy_manual_checks_++;
@@ -760,6 +700,7 @@
     dlc_apps_params[omaha_request_params_->GetDlcAppId(dlc_id)] = dlc_params;
   }
   omaha_request_params_->set_dlc_apps_params(dlc_apps_params);
+  omaha_request_params_->set_is_install(is_install_);
 }
 
 void UpdateAttempter::BuildUpdateActions(bool interactive) {
@@ -874,7 +815,7 @@
   processor_->set_delegate(this);
 
   // Initialize the default request params.
-  if (!omaha_request_params_->Init("", "", true)) {
+  if (!omaha_request_params_->Init("", "", {.interactive = true})) {
     LOG(ERROR) << "Unable to initialize Omaha request params.";
     return false;
   }
@@ -1100,15 +1041,7 @@
     LOG(INFO) << "Update attempt flags in use = 0x" << std::hex
               << current_update_attempt_flags_;
 
-    Update(forced_app_version_,
-           forced_omaha_url_,
-           params.target_channel,
-           params.target_version_prefix,
-           params.rollback_allowed,
-           params.rollback_data_save_requested,
-           params.rollback_allowed_milestones,
-           /*obey_proxies=*/false,
-           params.interactive);
+    Update(params);
     // Always clear the forced app_version and omaha_url after an update attempt
     // so the next update uses the defaults.
     forced_app_version_.clear();
diff --git a/update_attempter.h b/cros/update_attempter.h
similarity index 91%
rename from update_attempter.h
rename to cros/update_attempter.h
index dd958f5..0f4c952 100644
--- a/update_attempter.h
+++ b/cros/update_attempter.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_UPDATE_ATTEMPTER_H_
-#define UPDATE_ENGINE_UPDATE_ATTEMPTER_H_
+#ifndef UPDATE_ENGINE_CROS_UPDATE_ATTEMPTER_H_
+#define UPDATE_ENGINE_CROS_UPDATE_ATTEMPTER_H_
 
 #include <time.h>
 
@@ -30,22 +30,22 @@
 #include <base/time/time.h>
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
-#if USE_CHROME_NETWORK_PROXY
-#include "update_engine/chrome_browser_proxy_resolver.h"
-#endif  // USE_CHROME_NETWORK_PROXY
 #include "update_engine/certificate_checker.h"
 #include "update_engine/client_library/include/update_engine/update_status.h"
 #include "update_engine/common/action_processor.h"
 #include "update_engine/common/cpu_limiter.h"
+#include "update_engine/common/download_action.h"
 #include "update_engine/common/excluder_interface.h"
 #include "update_engine/common/proxy_resolver.h"
-#include "update_engine/omaha_request_builder_xml.h"
-#include "update_engine/omaha_request_params.h"
-#include "update_engine/omaha_response_handler_action.h"
-#include "update_engine/payload_consumer/download_action.h"
+#include "update_engine/common/service_observer_interface.h"
+#include "update_engine/common/system_state.h"
+#if USE_CHROME_NETWORK_PROXY
+#include "update_engine/cros/chrome_browser_proxy_resolver.h"
+#endif  // USE_CHROME_NETWORK_PROXY
+#include "update_engine/cros/omaha_request_builder_xml.h"
+#include "update_engine/cros/omaha_request_params.h"
+#include "update_engine/cros/omaha_response_handler_action.h"
 #include "update_engine/payload_consumer/postinstall_runner_action.h"
-#include "update_engine/service_observer_interface.h"
-#include "update_engine/system_state.h"
 #include "update_engine/update_manager/policy.h"
 #include "update_engine/update_manager/staging_utils.h"
 #include "update_engine/update_manager/update_manager.h"
@@ -76,21 +76,8 @@
   virtual bool ScheduleUpdates();
 
   // Checks for update and, if a newer version is available, attempts to update
-  // the system. Non-empty |in_app_version| or |in_update_url| prevents
-  // automatic detection of the parameter.  |target_channel| denotes a
-  // policy-mandated channel we are updating to, if not empty. If |obey_proxies|
-  // is true, the update will likely respect Chrome's proxy setting. For
-  // security reasons, we may still not honor them. |interactive| should be true
-  // if this was called from the user (ie dbus).
-  virtual void Update(const std::string& app_version,
-                      const std::string& omaha_url,
-                      const std::string& target_channel,
-                      const std::string& target_version_prefix,
-                      bool rollback_allowed,
-                      bool rollback_data_save_requested,
-                      int rollback_allowed_milestones,
-                      bool obey_proxies,
-                      bool interactive);
+  // the system.
+  virtual void Update(const chromeos_update_manager::UpdateCheckParams& params);
 
   // ActionProcessorDelegate methods:
   void ProcessingDone(const ActionProcessor* processor,
@@ -163,7 +150,7 @@
   // UPDATED_NEED_REBOOT. Returns true on success, false otherwise.
   bool RebootIfNeeded();
 
-  // Sets the DLC as active or inactive. See common_service.h
+  // Sets the DLC as active or inactive. See chromeos/common_service.h
   virtual bool SetDlcActiveValue(bool is_active, const std::string& dlc_id);
 
   // DownloadActionDelegate methods:
@@ -283,6 +270,8 @@
   FRIEND_TEST(UpdateAttempterTest, RollbackAfterInstall);
   FRIEND_TEST(UpdateAttempterTest, RollbackAllowed);
   FRIEND_TEST(UpdateAttempterTest, RollbackAllowedSetAndReset);
+  FRIEND_TEST(UpdateAttempterTest, ChannelDowngradeNoRollback);
+  FRIEND_TEST(UpdateAttempterTest, ChannelDowngradeRollback);
   FRIEND_TEST(UpdateAttempterTest, RollbackMetricsNotRollbackFailure);
   FRIEND_TEST(UpdateAttempterTest, RollbackMetricsNotRollbackSuccess);
   FRIEND_TEST(UpdateAttempterTest, RollbackMetricsRollbackFailure);
@@ -293,6 +282,7 @@
   FRIEND_TEST(UpdateAttempterTest, SessionIdTestOnOmahaRequestActions);
   FRIEND_TEST(UpdateAttempterTest, SetRollbackHappenedNotRollback);
   FRIEND_TEST(UpdateAttempterTest, SetRollbackHappenedRollback);
+  FRIEND_TEST(UpdateAttempterTest, TargetChannelHintSetAndReset);
   FRIEND_TEST(UpdateAttempterTest, TargetVersionPrefixSetAndReset);
   FRIEND_TEST(UpdateAttempterTest, UpdateAfterInstall);
   FRIEND_TEST(UpdateAttempterTest, UpdateAttemptFlagsCachedAtUpdateStart);
@@ -366,15 +356,8 @@
   // Helper method of Update() to calculate the update-related parameters
   // from various sources and set the appropriate state. Please refer to
   // Update() method for the meaning of the parameters.
-  bool CalculateUpdateParams(const std::string& app_version,
-                             const std::string& omaha_url,
-                             const std::string& target_channel,
-                             const std::string& target_version_prefix,
-                             bool rollback_allowed,
-                             bool rollback_data_save_requested,
-                             int rollback_allowed_milestones,
-                             bool obey_proxies,
-                             bool interactive);
+  bool CalculateUpdateParams(
+      const chromeos_update_manager::UpdateCheckParams& params);
 
   // Calculates all the scattering related parameters (such as waiting period,
   // which type of scattering is enabled, etc.) and also updates/deletes
@@ -590,4 +573,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_UPDATE_ATTEMPTER_H_
+#endif  // UPDATE_ENGINE_CROS_UPDATE_ATTEMPTER_H_
diff --git a/update_attempter_unittest.cc b/cros/update_attempter_unittest.cc
similarity index 95%
rename from update_attempter_unittest.cc
rename to cros/update_attempter_unittest.cc
index 305dbdb..f3211a0 100644
--- a/update_attempter_unittest.cc
+++ b/cros/update_attempter_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/update_attempter.h"
+#include "update_engine/cros/update_attempter.h"
 
 #include <stdint.h>
 
@@ -25,7 +25,8 @@
 #include <unordered_set>
 
 #include <base/files/file_util.h>
-#include <base/message_loop/message_loop.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/task/single_thread_task_executor.h>
 #include <brillo/message_loops/base_message_loop.h>
 #include <brillo/message_loops/message_loop.h>
 #include <brillo/message_loops/message_loop_utils.h>
@@ -42,16 +43,16 @@
 #include "update_engine/common/mock_action_processor.h"
 #include "update_engine/common/mock_http_fetcher.h"
 #include "update_engine/common/mock_prefs.h"
+#include "update_engine/common/mock_service_observer.h"
 #include "update_engine/common/platform_constants.h"
 #include "update_engine/common/prefs.h"
 #include "update_engine/common/test_utils.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/fake_system_state.h"
+#include "update_engine/cros/fake_system_state.h"
+#include "update_engine/cros/mock_p2p_manager.h"
+#include "update_engine/cros/mock_payload_state.h"
+#include "update_engine/cros/omaha_utils.h"
 #include "update_engine/libcurl_http_fetcher.h"
-#include "update_engine/mock_p2p_manager.h"
-#include "update_engine/mock_payload_state.h"
-#include "update_engine/mock_service_observer.h"
-#include "update_engine/omaha_utils.h"
 #include "update_engine/payload_consumer/filesystem_verifier_action.h"
 #include "update_engine/payload_consumer/install_plan.h"
 #include "update_engine/payload_consumer/payload_constants.h"
@@ -171,26 +172,10 @@
   explicit UpdateAttempterUnderTest(SystemState* system_state)
       : UpdateAttempter(system_state, nullptr) {}
 
-  void Update(const std::string& app_version,
-              const std::string& omaha_url,
-              const std::string& target_channel,
-              const std::string& target_version_prefix,
-              bool rollback_allowed,
-              bool rollback_data_save_requested,
-              int rollback_allowed_milestones,
-              bool obey_proxies,
-              bool interactive) override {
+  void Update(const UpdateCheckParams& params) override {
     update_called_ = true;
     if (do_update_) {
-      UpdateAttempter::Update(app_version,
-                              omaha_url,
-                              target_channel,
-                              target_version_prefix,
-                              rollback_allowed,
-                              rollback_data_save_requested,
-                              rollback_allowed_milestones,
-                              obey_proxies,
-                              interactive);
+      UpdateAttempter::Update(params);
       return;
     }
     LOG(INFO) << "[TEST] Update() disabled.";
@@ -332,8 +317,8 @@
   // |ProcessingDone()| related member functions.
   void TestProcessingDone();
 
-  base::MessageLoopForIO base_loop_;
-  brillo::BaseMessageLoop loop_{&base_loop_};
+  base::SingleThreadTaskExecutor base_loop_{base::MessagePumpType::IO};
+  brillo::BaseMessageLoop loop_{base_loop_.task_runner()};
 
   FakeSystemState fake_system_state_;
   UpdateAttempterUnderTest attempter_{&fake_system_state_};
@@ -425,7 +410,7 @@
 void UpdateAttempterTest::SessionIdTestChange() {
   EXPECT_NE(UpdateStatus::UPDATED_NEED_REBOOT, attempter_.status());
   const auto old_session_id = attempter_.session_id_;
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update({});
   EXPECT_NE(old_session_id, attempter_.session_id_);
   ScheduleQuitMainLoop();
 }
@@ -796,7 +781,7 @@
     EXPECT_CALL(*processor_, StartProcessing());
   }
 
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update({});
   loop_.PostTask(FROM_HERE,
                  base::Bind(&UpdateAttempterTest::UpdateTestVerify,
                             base::Unretained(this)));
@@ -996,7 +981,7 @@
   fake_system_state_.set_p2p_manager(&mock_p2p_manager);
   mock_p2p_manager.fake().SetP2PEnabled(false);
   EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(0);
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update({});
   EXPECT_FALSE(actual_using_p2p_for_downloading_);
   EXPECT_FALSE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -1018,7 +1003,7 @@
   mock_p2p_manager.fake().SetEnsureP2PRunningResult(false);
   mock_p2p_manager.fake().SetPerformHousekeepingResult(false);
   EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(0);
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update({});
   EXPECT_FALSE(actual_using_p2p_for_downloading());
   EXPECT_FALSE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -1041,7 +1026,7 @@
   mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
   mock_p2p_manager.fake().SetPerformHousekeepingResult(false);
   EXPECT_CALL(mock_p2p_manager, PerformHousekeeping());
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update({});
   EXPECT_FALSE(actual_using_p2p_for_downloading());
   EXPECT_FALSE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -1063,7 +1048,7 @@
   mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
   mock_p2p_manager.fake().SetPerformHousekeepingResult(true);
   EXPECT_CALL(mock_p2p_manager, PerformHousekeeping());
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update({});
   EXPECT_TRUE(actual_using_p2p_for_downloading());
   EXPECT_TRUE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -1086,15 +1071,7 @@
   mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
   mock_p2p_manager.fake().SetPerformHousekeepingResult(true);
   EXPECT_CALL(mock_p2p_manager, PerformHousekeeping());
-  attempter_.Update("",
-                    "",
-                    "",
-                    "",
-                    false,
-                    false,
-                    /*rollback_allowed_milestones=*/0,
-                    false,
-                    /*interactive=*/true);
+  attempter_.Update({.interactive = true});
   EXPECT_FALSE(actual_using_p2p_for_downloading());
   EXPECT_TRUE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -1124,7 +1101,7 @@
   attempter_.policy_provider_.reset(
       new policy::PolicyProvider(std::move(device_policy)));
 
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update({});
   EXPECT_EQ(scatter_factor_in_seconds, attempter_.scatter_factor_.InSeconds());
 
   ScheduleQuitMainLoop();
@@ -1162,7 +1139,7 @@
   attempter_.policy_provider_.reset(
       new policy::PolicyProvider(std::move(device_policy)));
 
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update({});
   EXPECT_EQ(scatter_factor_in_seconds, attempter_.scatter_factor_.InSeconds());
 
   // Make sure the file still exists.
@@ -1178,7 +1155,7 @@
   // However, if the count is already 0, it's not decremented. Test that.
   initial_value = 0;
   EXPECT_TRUE(fake_prefs.SetInt64(kPrefsUpdateCheckCount, initial_value));
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update({});
   EXPECT_TRUE(fake_prefs.Exists(kPrefsUpdateCheckCount));
   EXPECT_TRUE(fake_prefs.GetInt64(kPrefsUpdateCheckCount, &new_value));
   EXPECT_EQ(initial_value, new_value);
@@ -1225,15 +1202,7 @@
       new policy::PolicyProvider(std::move(device_policy)));
 
   // Trigger an interactive check so we can test that scattering is disabled.
-  attempter_.Update("",
-                    "",
-                    "",
-                    "",
-                    false,
-                    false,
-                    /*rollback_allowed_milestones=*/0,
-                    false,
-                    /*interactive=*/true);
+  attempter_.Update({.interactive = true});
   EXPECT_EQ(scatter_factor_in_seconds, attempter_.scatter_factor_.InSeconds());
 
   // Make sure scattering is disabled for manual (i.e. user initiated) update
@@ -1285,7 +1254,7 @@
   FakePrefs fake_prefs;
   SetUpStagingTest(kValidStagingSchedule, &fake_prefs);
 
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update({});
   // Check that prefs have the correct values.
   int64_t update_count;
   EXPECT_TRUE(fake_prefs.GetInt64(kPrefsUpdateCheckCount, &update_count));
@@ -1342,8 +1311,7 @@
   FakePrefs fake_prefs;
   SetUpStagingTest(kValidStagingSchedule, &fake_prefs);
 
-  attempter_.Update(
-      "", "", "", "", false, false, 0, false, /* interactive = */ true);
+  attempter_.Update({.interactive = true});
   CheckStagingOff();
 
   ScheduleQuitMainLoop();
@@ -1363,8 +1331,7 @@
   FakePrefs fake_prefs;
   SetUpStagingTest(kValidStagingSchedule, &fake_prefs);
 
-  attempter_.Update(
-      "", "", "", "", false, false, 0, false, /* interactive = */ true);
+  attempter_.Update({.interactive = true});
   CheckStagingOff();
 
   ScheduleQuitMainLoop();
@@ -1692,51 +1659,69 @@
 }
 
 TEST_F(UpdateAttempterTest, TargetVersionPrefixSetAndReset) {
-  attempter_.CalculateUpdateParams(
-      "", "", "", "1234", false, false, 4, false, false);
+  UpdateCheckParams params;
+  attempter_.CalculateUpdateParams({.target_version_prefix = "1234"});
   EXPECT_EQ("1234",
             fake_system_state_.request_params()->target_version_prefix());
 
-  attempter_.CalculateUpdateParams(
-      "", "", "", "", false, 4, false, false, false);
+  attempter_.CalculateUpdateParams({});
   EXPECT_TRUE(
       fake_system_state_.request_params()->target_version_prefix().empty());
 }
 
+TEST_F(UpdateAttempterTest, TargetChannelHintSetAndReset) {
+  attempter_.CalculateUpdateParams({.lts_tag = "hint"});
+  EXPECT_EQ("hint", fake_system_state_.request_params()->lts_tag());
+
+  attempter_.CalculateUpdateParams({});
+  EXPECT_TRUE(fake_system_state_.request_params()->lts_tag().empty());
+}
+
 TEST_F(UpdateAttempterTest, RollbackAllowedSetAndReset) {
-  attempter_.CalculateUpdateParams("",
-                                   "",
-                                   "",
-                                   "1234",
-                                   /*rollback_allowed=*/true,
-                                   /*rollback_data_save_requested=*/false,
-                                   /*rollback_allowed_milestones=*/4,
-                                   false,
-                                   false);
+  attempter_.CalculateUpdateParams({
+      .target_version_prefix = "1234",
+      .rollback_allowed = true,
+      .rollback_allowed_milestones = 4,
+  });
   EXPECT_TRUE(fake_system_state_.request_params()->rollback_allowed());
   EXPECT_EQ(4,
             fake_system_state_.request_params()->rollback_allowed_milestones());
 
-  attempter_.CalculateUpdateParams("",
-                                   "",
-                                   "",
-                                   "1234",
-                                   /*rollback_allowed=*/false,
-                                   /*rollback_data_save_requested=*/false,
-                                   /*rollback_allowed_milestones=*/4,
-                                   false,
-                                   false);
+  attempter_.CalculateUpdateParams({
+      .target_version_prefix = "1234",
+      .rollback_allowed_milestones = 4,
+  });
   EXPECT_FALSE(fake_system_state_.request_params()->rollback_allowed());
   EXPECT_EQ(4,
             fake_system_state_.request_params()->rollback_allowed_milestones());
 }
 
+TEST_F(UpdateAttempterTest, ChannelDowngradeNoRollback) {
+  base::ScopedTempDir tempdir;
+  ASSERT_TRUE(tempdir.CreateUniqueTempDir());
+  fake_system_state_.request_params()->set_root(tempdir.GetPath().value());
+  attempter_.CalculateUpdateParams({
+      .target_channel = "stable-channel",
+  });
+  EXPECT_FALSE(fake_system_state_.request_params()->is_powerwash_allowed());
+}
+
+TEST_F(UpdateAttempterTest, ChannelDowngradeRollback) {
+  base::ScopedTempDir tempdir;
+  ASSERT_TRUE(tempdir.CreateUniqueTempDir());
+  fake_system_state_.request_params()->set_root(tempdir.GetPath().value());
+  attempter_.CalculateUpdateParams({
+      .rollback_on_channel_downgrade = true,
+      .target_channel = "stable-channel",
+  });
+  EXPECT_TRUE(fake_system_state_.request_params()->is_powerwash_allowed());
+}
+
 TEST_F(UpdateAttempterTest, UpdateDeferredByPolicyTest) {
   // Construct an OmahaResponseHandlerAction that has processed an InstallPlan,
   // but the update is being deferred by the Policy.
   OmahaResponseHandlerAction response_action(&fake_system_state_);
   response_action.install_plan_.version = "a.b.c.d";
-  response_action.install_plan_.system_version = "b.c.d.e";
   response_action.install_plan_.payloads.push_back(
       {.size = 1234ULL, .type = InstallPayloadType::kFull});
   // Inform the UpdateAttempter that the OmahaResponseHandlerAction has
@@ -1845,7 +1830,7 @@
               SetRollbackHappened(false))
       .Times(expected_reset ? 1 : 0);
   attempter_.policy_provider_ = std::move(mock_policy_provider);
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update({});
   ScheduleQuitMainLoop();
 }
 
@@ -2186,7 +2171,7 @@
         .WillOnce(Return(false));
   attempter_.policy_provider_.reset(
       new policy::PolicyProvider(std::move(device_policy)));
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update({});
 
   EXPECT_EQ(token, attempter_.omaha_request_params_->autoupdate_token());
   ScheduleQuitMainLoop();
diff --git a/update_engine_client.cc b/cros/update_engine_client.cc
similarity index 99%
rename from update_engine_client.cc
rename to cros/update_engine_client.cc
index 31448ea..6f20f11 100644
--- a/update_engine_client.cc
+++ b/cros/update_engine_client.cc
@@ -37,7 +37,7 @@
 #include "update_engine/client.h"
 #include "update_engine/common/error_code.h"
 #include "update_engine/common/error_code_utils.h"
-#include "update_engine/omaha_utils.h"
+#include "update_engine/cros/omaha_utils.h"
 #include "update_engine/status_update_handler.h"
 #include "update_engine/update_status.h"
 #include "update_engine/update_status_utils.h"
diff --git a/payload_consumer/download_action.cc b/download_action.cc
similarity index 98%
rename from payload_consumer/download_action.cc
rename to download_action.cc
index ea99892..10dffd2 100644
--- a/payload_consumer/download_action.cc
+++ b/download_action.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/payload_consumer/download_action.h"
+#include "update_engine/common/download_action.h"
 
 #include <errno.h>
 
@@ -30,9 +30,9 @@
 #include "update_engine/common/error_code_utils.h"
 #include "update_engine/common/multi_range_http_fetcher.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/omaha_request_params.h"
-#include "update_engine/p2p_manager.h"
-#include "update_engine/payload_state_interface.h"
+#include "update_engine/cros/omaha_request_params.h"
+#include "update_engine/cros/p2p_manager.h"
+#include "update_engine/cros/payload_state_interface.h"
 
 using base::FilePath;
 using std::string;
diff --git a/payload_consumer/download_action_android_unittest.cc b/download_action_android_unittest.cc
similarity index 97%
rename from payload_consumer/download_action_android_unittest.cc
rename to download_action_android_unittest.cc
index f78845f..f222977 100644
--- a/payload_consumer/download_action_android_unittest.cc
+++ b/download_action_android_unittest.cc
@@ -23,10 +23,10 @@
 #include "update_engine/common/action_pipe.h"
 #include "update_engine/common/boot_control_stub.h"
 #include "update_engine/common/constants.h"
+#include "update_engine/common/download_action.h"
 #include "update_engine/common/mock_http_fetcher.h"
 #include "update_engine/common/mock_prefs.h"
 #include "update_engine/common/test_utils.h"
-#include "update_engine/payload_consumer/download_action.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
diff --git a/payload_consumer/download_action_unittest.cc b/download_action_unittest.cc
similarity index 98%
rename from payload_consumer/download_action_unittest.cc
rename to download_action_unittest.cc
index e6ca219..5264b0f 100644
--- a/payload_consumer/download_action_unittest.cc
+++ b/download_action_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/payload_consumer/download_action.h"
+#include "update_engine/common/download_action.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -34,14 +34,14 @@
 
 #include "update_engine/common/action_pipe.h"
 #include "update_engine/common/hash_calculator.h"
+#include "update_engine/common/mock_download_action.h"
 #include "update_engine/common/mock_http_fetcher.h"
 #include "update_engine/common/mock_prefs.h"
 #include "update_engine/common/test_utils.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/fake_p2p_manager_configuration.h"
-#include "update_engine/fake_system_state.h"
-#include "update_engine/mock_file_writer.h"
-#include "update_engine/payload_consumer/mock_download_action.h"
+#include "update_engine/cros/fake_p2p_manager_configuration.h"
+#include "update_engine/cros/fake_system_state.h"
+#include "update_engine/payload_consumer/mock_file_writer.h"
 #include "update_engine/update_manager/fake_update_manager.h"
 
 namespace chromeos_update_engine {
@@ -51,7 +51,6 @@
 using base::WriteFile;
 using std::string;
 using std::unique_ptr;
-using test_utils::ScopedTempFile;
 using testing::_;
 using testing::AtLeast;
 using testing::InSequence;
@@ -133,7 +132,6 @@
   loop.SetAsCurrent();
   FakeSystemState fake_system_state;
 
-  // TODO(adlr): see if we need a different file for build bots
   ScopedTempFile output_temp_file;
   TestDirectFileWriter writer;
   EXPECT_EQ(
diff --git a/image_properties_android.cc b/image_properties_android.cc
deleted file mode 100644
index 2d418b3..0000000
--- a/image_properties_android.cc
+++ /dev/null
@@ -1,246 +0,0 @@
-//
-// 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/image_properties.h"
-
-#include <fcntl.h>
-
-#include <string>
-
-#include <android-base/properties.h>
-#include <base/logging.h>
-#include <base/strings/string_util.h>
-#include <bootloader_message/bootloader_message.h>
-#include <brillo/osrelease_reader.h>
-#include <brillo/strings/string_utils.h>
-
-#include "update_engine/common/boot_control_interface.h"
-#include "update_engine/common/constants.h"
-#include "update_engine/common/platform_constants.h"
-#include "update_engine/common/prefs_interface.h"
-#include "update_engine/common/utils.h"
-#include "update_engine/system_state.h"
-
-using android::base::GetProperty;
-using std::string;
-
-namespace chromeos_update_engine {
-
-namespace {
-
-// Build time properties name used in Android Things.
-const char kProductId[] = "product_id";
-const char kProductVersion[] = "product_version";
-const char kSystemId[] = "system_id";
-const char kSystemVersion[] = "system_version";
-
-// The path to the product_components file which stores the version of each
-// components in OEM partition.
-const char kProductComponentsPath[] = "/oem/os-release.d/product_components";
-
-// Prefs used to store the powerwash settings.
-const char kPrefsImgPropPowerwashAllowed[] = "img-prop-powerwash-allowed";
-
-// System properties that identifies the "board".
-const char kPropProductName[] = "ro.product.name";
-const char kPropBuildFingerprint[] = "ro.build.fingerprint";
-const char kPropBuildType[] = "ro.build.type";
-
-// Default channel from factory.prop
-const char kPropDefaultChannel[] = "ro.update.default_channel";
-
-// A prefix added to the path, used for testing.
-const char* root_prefix = nullptr;
-
-string GetStringWithDefault(const brillo::OsReleaseReader& osrelease,
-                            const string& key,
-                            const string& default_value) {
-  string result;
-  if (osrelease.GetString(key, &result))
-    return result;
-  LOG(INFO) << "Cannot load ImageProperty " << key << ", using default value "
-            << default_value;
-  return default_value;
-}
-
-// Open misc partition for read or write and output the fd in |out_fd|.
-bool OpenMisc(bool write, int* out_fd) {
-  string misc_device;
-  int flags = write ? O_WRONLY | O_SYNC : O_RDONLY;
-  if (root_prefix) {
-    // Use a file for unittest and create one if doesn't exist.
-    misc_device = base::FilePath(root_prefix).Append("misc").value();
-    if (write)
-      flags |= O_CREAT;
-  } else {
-    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;
-    }
-  }
-
-  int fd = HANDLE_EINTR(open(misc_device.c_str(), flags, 0600));
-  if (fd < 0) {
-    PLOG(ERROR) << "Opening misc failed";
-    return false;
-  }
-  *out_fd = fd;
-  return true;
-}
-
-// The offset and size of the channel field in misc partition.
-constexpr size_t kChannelOffset =
-    BOOTLOADER_MESSAGE_OFFSET_IN_MISC +
-    offsetof(bootloader_message_ab, update_channel);
-constexpr size_t kChannelSize = sizeof(bootloader_message_ab::update_channel);
-
-// Read channel from misc partition to |out_channel|, return false if unable to
-// read misc or no channel is set in misc.
-bool ReadChannelFromMisc(string* out_channel) {
-  int fd;
-  TEST_AND_RETURN_FALSE(OpenMisc(false, &fd));
-  ScopedFdCloser fd_closer(&fd);
-  char channel[kChannelSize] = {0};
-  ssize_t bytes_read = 0;
-  if (!utils::PReadAll(
-          fd, channel, kChannelSize - 1, kChannelOffset, &bytes_read) ||
-      bytes_read != kChannelSize - 1) {
-    PLOG(ERROR) << "Reading update channel from misc failed";
-    return false;
-  }
-  if (channel[0] == '\0') {
-    LOG(INFO) << "No channel set in misc.";
-    return false;
-  }
-  if (!base::EndsWith(channel, "-channel", base::CompareCase::SENSITIVE)) {
-    LOG(ERROR) << "Channel " << channel << " doesn't end with -channel.";
-    return false;
-  }
-  out_channel->assign(channel);
-  return true;
-}
-
-// Write |in_channel| to misc partition, return false if failed to write.
-bool WriteChannelToMisc(const string& in_channel) {
-  int fd;
-  TEST_AND_RETURN_FALSE(OpenMisc(true, &fd));
-  ScopedFdCloser fd_closer(&fd);
-  if (in_channel.size() >= kChannelSize) {
-    LOG(ERROR) << "Channel name is too long: " << in_channel
-               << ", the maximum length is " << kChannelSize - 1;
-    return false;
-  }
-  char channel[kChannelSize] = {0};
-  memcpy(channel, in_channel.data(), in_channel.size());
-  if (!utils::PWriteAll(fd, channel, kChannelSize, kChannelOffset)) {
-    PLOG(ERROR) << "Writing update channel to misc failed";
-    return false;
-  }
-  return true;
-}
-
-string GetTargetChannel() {
-  string channel;
-  if (!ReadChannelFromMisc(&channel))
-    channel = GetProperty(kPropDefaultChannel, "stable-channel");
-  return channel;
-}
-}  // namespace
-
-namespace test {
-void SetImagePropertiesRootPrefix(const char* test_root_prefix) {
-  root_prefix = test_root_prefix;
-}
-}  // namespace test
-
-ImageProperties LoadImageProperties(SystemState* system_state) {
-  ImageProperties result;
-
-  brillo::OsReleaseReader osrelease;
-  if (root_prefix)
-    osrelease.LoadTestingOnly(base::FilePath(root_prefix));
-  else
-    osrelease.Load();
-  result.product_id =
-      GetStringWithDefault(osrelease, kProductId, "invalid-product");
-  result.system_id = GetStringWithDefault(
-      osrelease, kSystemId, "developer-boards:brillo-starter-board");
-  // Update the system id to match the prefix of product id for testing.
-  string prefix, not_used, system_id;
-  if (brillo::string_utils::SplitAtFirst(
-          result.product_id, ":", &prefix, &not_used, false) &&
-      brillo::string_utils::SplitAtFirst(
-          result.system_id, ":", &not_used, &system_id, false)) {
-    result.system_id = prefix + ":" + system_id;
-  }
-  result.canary_product_id = result.product_id;
-  result.version = GetStringWithDefault(osrelease, kProductVersion, "0.0.0.0");
-  result.system_version =
-      GetStringWithDefault(osrelease, kSystemVersion, "0.0.0.0");
-  // Can't read it with OsReleaseReader because it has multiple lines.
-  utils::ReadFile(kProductComponentsPath, &result.product_components);
-
-  result.board = GetProperty(kPropProductName, "brillo");
-  result.build_fingerprint = GetProperty(kPropBuildFingerprint, "none");
-  result.build_type = GetProperty(kPropBuildType, "");
-
-  // Android doesn't have channel information in system image, we try to read
-  // the channel of current slot from prefs and then fallback to use the
-  // persisted target channel as current channel.
-  string current_channel_key =
-      kPrefsChannelOnSlotPrefix +
-      std::to_string(system_state->boot_control()->GetCurrentSlot());
-  string current_channel;
-  if (!system_state->prefs()->Exists(current_channel_key) ||
-      !system_state->prefs()->GetString(current_channel_key, &current_channel))
-    current_channel = GetTargetChannel();
-  result.current_channel = current_channel;
-  result.allow_arbitrary_channels = true;
-
-  // Brillo only supports the official omaha URL.
-  result.omaha_url = constants::kOmahaDefaultProductionURL;
-
-  return result;
-}
-
-MutableImageProperties LoadMutableImageProperties(SystemState* system_state) {
-  MutableImageProperties result;
-  result.target_channel = GetTargetChannel();
-  if (!system_state->prefs()->GetBoolean(kPrefsImgPropPowerwashAllowed,
-                                         &result.is_powerwash_allowed)) {
-    result.is_powerwash_allowed = false;
-  }
-  return result;
-}
-
-bool StoreMutableImageProperties(SystemState* system_state,
-                                 const MutableImageProperties& properties) {
-  bool ret = true;
-  if (!WriteChannelToMisc(properties.target_channel))
-    ret = false;
-  if (!system_state->prefs()->SetBoolean(kPrefsImgPropPowerwashAllowed,
-                                         properties.is_powerwash_allowed))
-    ret = false;
-  return ret;
-}
-
-void LogImageProperties() {
-  // TODO(*): Implement this.
-}
-
-}  // namespace chromeos_update_engine
diff --git a/image_properties_android_unittest.cc b/image_properties_android_unittest.cc
deleted file mode 100644
index 607284a..0000000
--- a/image_properties_android_unittest.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-//
-// 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/image_properties.h"
-
-#include <string>
-
-#include <base/files/file_util.h>
-#include <base/files/scoped_temp_dir.h>
-#include <gtest/gtest.h>
-
-#include "update_engine/common/constants.h"
-#include "update_engine/common/fake_prefs.h"
-#include "update_engine/common/test_utils.h"
-#include "update_engine/fake_system_state.h"
-
-using chromeos_update_engine::test_utils::WriteFileString;
-using std::string;
-
-namespace chromeos_update_engine {
-
-class ImagePropertiesTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    // Create a uniquely named test directory.
-    ASSERT_TRUE(tempdir_.CreateUniqueTempDir());
-    osrelease_dir_ = tempdir_.GetPath().Append("etc/os-release.d");
-    EXPECT_TRUE(base::CreateDirectory(osrelease_dir_));
-    test::SetImagePropertiesRootPrefix(tempdir_.GetPath().value().c_str());
-  }
-
-  void WriteOsRelease(const string& key, const string& value) {
-    ASSERT_TRUE(WriteFileString(osrelease_dir_.Append(key).value(), value));
-  }
-
-  void WriteChannel(const string& channel) {
-    string misc(2080, '\0');
-    misc += channel;
-    misc.resize(4096);
-    ASSERT_TRUE(
-        WriteFileString(tempdir_.GetPath().Append("misc").value(), misc));
-  }
-
-  FakeSystemState fake_system_state_;
-
-  base::ScopedTempDir tempdir_;
-  base::FilePath osrelease_dir_;
-};
-
-TEST_F(ImagePropertiesTest, SimpleTest) {
-  WriteOsRelease("product_id", "abc");
-  WriteOsRelease("system_id", "def");
-  WriteOsRelease("product_version", "1.2.3.4");
-  WriteOsRelease("system_version", "5.6.7.8");
-  ImageProperties props = LoadImageProperties(&fake_system_state_);
-  EXPECT_EQ("abc", props.product_id);
-  EXPECT_EQ("def", props.system_id);
-  EXPECT_EQ("1.2.3.4", props.version);
-  EXPECT_EQ("5.6.7.8", props.system_version);
-  EXPECT_EQ("stable-channel", props.current_channel);
-  EXPECT_EQ(constants::kOmahaDefaultProductionURL, props.omaha_url);
-}
-
-TEST_F(ImagePropertiesTest, IDPrefixTest) {
-  WriteOsRelease("product_id", "abc:def");
-  WriteOsRelease("system_id", "foo:bar");
-  ImageProperties props = LoadImageProperties(&fake_system_state_);
-  EXPECT_EQ("abc:def", props.product_id);
-  EXPECT_EQ("abc:bar", props.system_id);
-}
-
-TEST_F(ImagePropertiesTest, IDInvalidPrefixTest) {
-  WriteOsRelease("product_id", "def");
-  WriteOsRelease("system_id", "foo:bar");
-  ImageProperties props = LoadImageProperties(&fake_system_state_);
-  EXPECT_EQ("def", props.product_id);
-  EXPECT_EQ("foo:bar", props.system_id);
-
-  WriteOsRelease("product_id", "abc:def");
-  WriteOsRelease("system_id", "bar");
-  props = LoadImageProperties(&fake_system_state_);
-  EXPECT_EQ("abc:def", props.product_id);
-  EXPECT_EQ("bar", props.system_id);
-}
-
-TEST_F(ImagePropertiesTest, LoadChannelTest) {
-  WriteChannel("unittest-channel");
-  ImageProperties props = LoadImageProperties(&fake_system_state_);
-  EXPECT_EQ("unittest-channel", props.current_channel);
-}
-
-TEST_F(ImagePropertiesTest, DefaultStableChannelTest) {
-  WriteChannel("");
-  ImageProperties props = LoadImageProperties(&fake_system_state_);
-  EXPECT_EQ("stable-channel", props.current_channel);
-}
-
-TEST_F(ImagePropertiesTest, StoreLoadMutableChannelTest) {
-  FakePrefs prefs;
-  fake_system_state_.set_prefs(&prefs);
-  WriteChannel("previous-channel");
-  MutableImageProperties props;
-  props.target_channel = "new-channel";
-  EXPECT_TRUE(StoreMutableImageProperties(&fake_system_state_, props));
-  MutableImageProperties loaded_props =
-      LoadMutableImageProperties(&fake_system_state_);
-  EXPECT_EQ(props.target_channel, loaded_props.target_channel);
-}
-
-}  // namespace chromeos_update_engine
diff --git a/init/update-engine.conf b/init/update-engine.conf
index ca54c4a..36c89d7 100644
--- a/init/update-engine.conf
+++ b/init/update-engine.conf
@@ -37,7 +37,17 @@
 # Put update_engine process in its own cgroup.
 # Default cpu.shares is 1024.
 post-start script
-  cgroup_dir="/sys/fs/cgroup/cpu/${UPSTART_JOB}"
-  mkdir -p "${cgroup_dir}"
-  echo $(status | cut -f 4 -d ' ') > "${cgroup_dir}/tasks"
+  pid=$(status | cut -f 4 -d ' ')
+
+  cgroup_cpu_dir="/sys/fs/cgroup/cpu/${UPSTART_JOB}"
+  mkdir -p "${cgroup_cpu_dir}"
+  echo ${pid} > "${cgroup_cpu_dir}/tasks"
+
+  # Assigns net_cls handle 1:1 to packets generated from update_engine. For
+  # routing and tagging purposes, that value will be redefined in
+  # patchpanel/routing_service.h .
+  cgroup_net_cls_dir="/sys/fs/cgroup/net_cls/${UPSTART_JOB}"
+  mkdir -p "${cgroup_net_cls_dir}"
+  echo ${pid} > "${cgroup_net_cls_dir}/tasks"
+  echo "0x10001" > "${cgroup_net_cls_dir}/net_cls.classid"
 end script
diff --git a/libcurl_http_fetcher.cc b/libcurl_http_fetcher.cc
index bce0920..1599aac 100644
--- a/libcurl_http_fetcher.cc
+++ b/libcurl_http_fetcher.cc
@@ -458,10 +458,10 @@
     // There's either more work to do or we are paused, so we just keep the
     // file descriptors to watch up to date and exit, until we are done with the
     // work and we are not paused.
-#ifdef __ANDROID__
-    // When there's no base::SingleThreadTaskRunner on current thread, it's not
-    // possible to watch file descriptors. Just poll it later. This usually
-    // happens if brillo::FakeMessageLoop is used.
+    //
+    // When there's no |base::SingleThreadTaskRunner| on current thread, it's
+    // not possible to watch file descriptors. Just poll it later. This usually
+    // happens if |brillo::FakeMessageLoop| is used.
     if (!base::ThreadTaskRunnerHandle::IsSet()) {
       MessageLoop::current()->PostDelayedTask(
           FROM_HERE,
@@ -470,7 +470,6 @@
           TimeDelta::FromSeconds(1));
       return;
     }
-#endif
     SetupMessageLoopSources();
     return;
   }
diff --git a/libcurl_http_fetcher_unittest.cc b/libcurl_http_fetcher_unittest.cc
index 874ef2e..5d67570 100644
--- a/libcurl_http_fetcher_unittest.cc
+++ b/libcurl_http_fetcher_unittest.cc
@@ -100,7 +100,6 @@
 
   libcurl_fetcher_.BeginTransfer("https://An-uNres0lvable-uRl.invalid");
 
-#ifdef __ANDROID__
   // It's slower on Android that libcurl handle may not finish within 1 cycle.
   // Will need to wait for more cycles until it finishes. Original test didn't
   // correctly handle when we need to re-watch libcurl fds.
@@ -108,10 +107,7 @@
          libcurl_fetcher_.GetAuxiliaryErrorCode() == ErrorCode::kSuccess) {
     loop_.RunOnce(true);
   }
-#else
-  // The first time it can't resolve.
-  loop_.RunOnce(true);
-#endif
+
   EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(),
             ErrorCode::kUnresolvedHostError);
 
@@ -141,7 +137,6 @@
   // easier to mock the part that depends on internet connectivity.
   libcurl_fetcher_.BeginTransfer("https://An-uNres0lvable-uRl.invalid");
 
-#ifdef __ANDROID__
   // It's slower on Android that libcurl handle may not finish within 1 cycle.
   // Will need to wait for more cycles until it finishes. Original test didn't
   // correctly handle when we need to re-watch libcurl fds.
@@ -149,10 +144,7 @@
          libcurl_fetcher_.GetAuxiliaryErrorCode() == ErrorCode::kSuccess) {
     loop_.RunOnce(true);
   }
-#else
-  // The first time it can't resolve.
-  loop_.RunOnce(true);
-#endif
+
   EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(),
             ErrorCode::kUnresolvedHostError);
 
@@ -165,7 +157,6 @@
           [this]() { libcurl_fetcher_.http_response_code_ = 0; }));
   libcurl_fetcher_.transfer_size_ = 10;
 
-#ifdef __ANDROID__
   // It's slower on Android that libcurl handle may not finish within 1 cycle.
   // Will need to wait for more cycles until it finishes. Original test didn't
   // correctly handle when we need to re-watch libcurl fds.
@@ -173,11 +164,7 @@
                                      ErrorCode::kUnresolvedHostError) {
     loop_.RunOnce(true);
   }
-#else
-  // This time the host is resolved. But after that again we can't resolve
-  // anymore (See above).
-  loop_.RunOnce(true);
-#endif
+
   EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(),
             ErrorCode::kUnresolvedHostRecovered);
 
diff --git a/main.cc b/main.cc
index ceb5b56..a23a08b 100644
--- a/main.cc
+++ b/main.cc
@@ -23,11 +23,11 @@
 #include <base/logging.h>
 #include <brillo/flag_helper.h>
 
+#include "update_engine/common/daemon_base.h"
+#include "update_engine/common/logging.h"
 #include "update_engine/common/subprocess.h"
 #include "update_engine/common/terminator.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/daemon_base.h"
-#include "update_engine/logging.h"
 
 using std::string;
 
diff --git a/metrics_utils.cc b/metrics_utils.cc
index da3a2c3..34da5a1 100644
--- a/metrics_utils.cc
+++ b/metrics_utils.cc
@@ -23,7 +23,6 @@
 #include "update_engine/common/clock_interface.h"
 #include "update_engine/common/constants.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/system_state.h"
 
 using base::Time;
 using base::TimeDelta;
@@ -95,6 +94,7 @@
     case ErrorCode::kPostinstallRunnerError:
     case ErrorCode::kPostinstallBootedFromFirmwareB:
     case ErrorCode::kPostinstallFirmwareRONotUpdatable:
+    case ErrorCode::kPostInstallMountError:
       return metrics::AttemptResult::kPostInstallFailed;
 
     case ErrorCode::kUserCanceled:
@@ -111,10 +111,6 @@
     case ErrorCode::kDownloadInvalidMetadataSignature:
     case ErrorCode::kOmahaResponseInvalid:
     case ErrorCode::kOmahaUpdateIgnoredPerPolicy:
-    // TODO(deymo): The next two items belong in their own category; they
-    // should not be counted as internal errors. b/27112092
-    case ErrorCode::kOmahaUpdateDeferredPerPolicy:
-    case ErrorCode::kNonCriticalUpdateInOOBE:
     case ErrorCode::kOmahaErrorInHTTPResponse:
     case ErrorCode::kDownloadMetadataSignatureMissingError:
     case ErrorCode::kOmahaUpdateDeferredForBackoff:
@@ -124,8 +120,13 @@
     case ErrorCode::kOmahaUpdateIgnoredOverCellular:
     case ErrorCode::kNoUpdate:
     case ErrorCode::kFirstActiveOmahaPingSentPersistenceError:
+    case ErrorCode::kPackageExcludedFromUpdate:
       return metrics::AttemptResult::kInternalError;
 
+    case ErrorCode::kOmahaUpdateDeferredPerPolicy:
+    case ErrorCode::kNonCriticalUpdateInOOBE:
+      return metrics::AttemptResult::kUpdateSkipped;
+
     // Special flags. These can't happen (we mask them out above) but
     // the compiler doesn't know that. Just break out so we can warn and
     // return |kInternalError|.
@@ -188,6 +189,7 @@
     case ErrorCode::kOmahaResponseHandlerError:
     case ErrorCode::kFilesystemCopierError:
     case ErrorCode::kPostinstallRunnerError:
+    case ErrorCode::kPostInstallMountError:
     case ErrorCode::kPayloadMismatchedType:
     case ErrorCode::kInstallDeviceOpenError:
     case ErrorCode::kKernelDeviceOpenError:
@@ -240,6 +242,7 @@
     case ErrorCode::kVerityCalculationError:
     case ErrorCode::kNotEnoughSpace:
     case ErrorCode::kDeviceCorrupted:
+    case ErrorCode::kPackageExcludedFromUpdate:
       break;
 
     // Special flags. These can't happen (we mask them out above) but
@@ -291,48 +294,6 @@
   return metrics::ConnectionType::kUnknown;
 }
 
-bool WallclockDurationHelper(SystemState* system_state,
-                             const std::string& state_variable_key,
-                             TimeDelta* out_duration) {
-  bool ret = false;
-
-  Time now = system_state->clock()->GetWallclockTime();
-  int64_t stored_value;
-  if (system_state->prefs()->GetInt64(state_variable_key, &stored_value)) {
-    Time stored_time = Time::FromInternalValue(stored_value);
-    if (stored_time > now) {
-      LOG(ERROR) << "Stored time-stamp used for " << state_variable_key
-                 << " is in the future.";
-    } else {
-      *out_duration = now - stored_time;
-      ret = true;
-    }
-  }
-
-  if (!system_state->prefs()->SetInt64(state_variable_key,
-                                       now.ToInternalValue())) {
-    LOG(ERROR) << "Error storing time-stamp in " << state_variable_key;
-  }
-
-  return ret;
-}
-
-bool MonotonicDurationHelper(SystemState* system_state,
-                             int64_t* storage,
-                             TimeDelta* out_duration) {
-  bool ret = false;
-
-  Time now = system_state->clock()->GetMonotonicTime();
-  if (*storage != 0) {
-    Time stored_time = Time::FromInternalValue(*storage);
-    *out_duration = now - stored_time;
-    ret = true;
-  }
-  *storage = now.ToInternalValue();
-
-  return ret;
-}
-
 int64_t GetPersistedValue(const std::string& key, PrefsInterface* prefs) {
   CHECK(prefs);
   if (!prefs->Exists(key))
@@ -402,8 +363,7 @@
     return false;
 
   Time system_updated_at = Time::FromInternalValue(stored_value);
-  base::TimeDelta time_to_reboot =
-      clock->GetMonotonicTime() - system_updated_at;
+  TimeDelta time_to_reboot = clock->GetMonotonicTime() - system_updated_at;
   if (time_to_reboot.ToInternalValue() < 0) {
     LOG(ERROR) << "time_to_reboot is negative - system_updated_at: "
                << utils::ToString(system_updated_at);
diff --git a/metrics_utils.h b/metrics_utils.h
index 8f1aad1..5952ec3 100644
--- a/metrics_utils.h
+++ b/metrics_utils.h
@@ -22,11 +22,11 @@
 #include <base/time/time.h>
 
 #include "update_engine/common/clock_interface.h"
+#include "update_engine/common/connection_utils.h"
 #include "update_engine/common/error_code.h"
+#include "update_engine/common/metrics_constants.h"
+#include "update_engine/common/metrics_reporter_interface.h"
 #include "update_engine/common/prefs_interface.h"
-#include "update_engine/connection_utils.h"
-#include "update_engine/metrics_constants.h"
-#include "update_engine/metrics_reporter_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -50,29 +50,6 @@
 metrics::ConnectionType GetConnectionType(ConnectionType type,
                                           ConnectionTethering tethering);
 
-// This function returns the duration on the wallclock since the last
-// time it was called for the same |state_variable_key| value.
-//
-// If the function returns |true|, the duration (always non-negative)
-// is returned in |out_duration|. If the function returns |false|
-// something went wrong or there was no previous measurement.
-bool WallclockDurationHelper(SystemState* system_state,
-                             const std::string& state_variable_key,
-                             base::TimeDelta* out_duration);
-
-// This function returns the duration on the monotonic clock since the
-// last time it was called for the same |storage| pointer.
-//
-// You should pass a pointer to a 64-bit integer in |storage| which
-// should be initialized to 0.
-//
-// If the function returns |true|, the duration (always non-negative)
-// is returned in |out_duration|. If the function returns |false|
-// something went wrong or there was no previous measurement.
-bool MonotonicDurationHelper(SystemState* system_state,
-                             int64_t* storage,
-                             base::TimeDelta* out_duration);
-
 // Returns the persisted value from prefs for the given key. It also
 // validates that the value returned is non-negative.
 int64_t GetPersistedValue(const std::string& key, PrefsInterface* prefs);
diff --git a/metrics_utils_unittest.cc b/metrics_utils_unittest.cc
index 6ea996f..cedd269 100644
--- a/metrics_utils_unittest.cc
+++ b/metrics_utils_unittest.cc
@@ -20,7 +20,6 @@
 
 #include "update_engine/common/fake_clock.h"
 #include "update_engine/common/fake_prefs.h"
-#include "update_engine/fake_system_state.h"
 
 namespace chromeos_update_engine {
 namespace metrics_utils {
@@ -74,116 +73,5 @@
       GetConnectionType(ConnectionType::kWifi, ConnectionTethering::kUnknown));
 }
 
-TEST(MetricsUtilsTest, WallclockDurationHelper) {
-  FakeSystemState fake_system_state;
-  FakeClock fake_clock;
-  base::TimeDelta duration;
-  const std::string state_variable_key = "test-prefs";
-  FakePrefs fake_prefs;
-
-  fake_system_state.set_clock(&fake_clock);
-  fake_system_state.set_prefs(&fake_prefs);
-
-  // Initialize wallclock to 1 sec.
-  fake_clock.SetWallclockTime(base::Time::FromInternalValue(1000000));
-
-  // First time called so no previous measurement available.
-  EXPECT_FALSE(metrics_utils::WallclockDurationHelper(
-      &fake_system_state, state_variable_key, &duration));
-
-  // Next time, we should get zero since the clock didn't advance.
-  EXPECT_TRUE(metrics_utils::WallclockDurationHelper(
-      &fake_system_state, state_variable_key, &duration));
-  EXPECT_EQ(duration.InSeconds(), 0);
-
-  // We can also call it as many times as we want with it being
-  // considered a failure.
-  EXPECT_TRUE(metrics_utils::WallclockDurationHelper(
-      &fake_system_state, state_variable_key, &duration));
-  EXPECT_EQ(duration.InSeconds(), 0);
-  EXPECT_TRUE(metrics_utils::WallclockDurationHelper(
-      &fake_system_state, state_variable_key, &duration));
-  EXPECT_EQ(duration.InSeconds(), 0);
-
-  // Advance the clock one second, then we should get 1 sec on the
-  // next call and 0 sec on the subsequent call.
-  fake_clock.SetWallclockTime(base::Time::FromInternalValue(2000000));
-  EXPECT_TRUE(metrics_utils::WallclockDurationHelper(
-      &fake_system_state, state_variable_key, &duration));
-  EXPECT_EQ(duration.InSeconds(), 1);
-  EXPECT_TRUE(metrics_utils::WallclockDurationHelper(
-      &fake_system_state, state_variable_key, &duration));
-  EXPECT_EQ(duration.InSeconds(), 0);
-
-  // Advance clock two seconds and we should get 2 sec and then 0 sec.
-  fake_clock.SetWallclockTime(base::Time::FromInternalValue(4000000));
-  EXPECT_TRUE(metrics_utils::WallclockDurationHelper(
-      &fake_system_state, state_variable_key, &duration));
-  EXPECT_EQ(duration.InSeconds(), 2);
-  EXPECT_TRUE(metrics_utils::WallclockDurationHelper(
-      &fake_system_state, state_variable_key, &duration));
-  EXPECT_EQ(duration.InSeconds(), 0);
-
-  // There's a possibility that the wallclock can go backwards (NTP
-  // adjustments, for example) so check that we properly handle this
-  // case.
-  fake_clock.SetWallclockTime(base::Time::FromInternalValue(3000000));
-  EXPECT_FALSE(metrics_utils::WallclockDurationHelper(
-      &fake_system_state, state_variable_key, &duration));
-  fake_clock.SetWallclockTime(base::Time::FromInternalValue(4000000));
-  EXPECT_TRUE(metrics_utils::WallclockDurationHelper(
-      &fake_system_state, state_variable_key, &duration));
-  EXPECT_EQ(duration.InSeconds(), 1);
-}
-
-TEST(MetricsUtilsTest, MonotonicDurationHelper) {
-  int64_t storage = 0;
-  FakeSystemState fake_system_state;
-  FakeClock fake_clock;
-  base::TimeDelta duration;
-
-  fake_system_state.set_clock(&fake_clock);
-
-  // Initialize monotonic clock to 1 sec.
-  fake_clock.SetMonotonicTime(base::Time::FromInternalValue(1000000));
-
-  // First time called so no previous measurement available.
-  EXPECT_FALSE(metrics_utils::MonotonicDurationHelper(
-      &fake_system_state, &storage, &duration));
-
-  // Next time, we should get zero since the clock didn't advance.
-  EXPECT_TRUE(metrics_utils::MonotonicDurationHelper(
-      &fake_system_state, &storage, &duration));
-  EXPECT_EQ(duration.InSeconds(), 0);
-
-  // We can also call it as many times as we want with it being
-  // considered a failure.
-  EXPECT_TRUE(metrics_utils::MonotonicDurationHelper(
-      &fake_system_state, &storage, &duration));
-  EXPECT_EQ(duration.InSeconds(), 0);
-  EXPECT_TRUE(metrics_utils::MonotonicDurationHelper(
-      &fake_system_state, &storage, &duration));
-  EXPECT_EQ(duration.InSeconds(), 0);
-
-  // Advance the clock one second, then we should get 1 sec on the
-  // next call and 0 sec on the subsequent call.
-  fake_clock.SetMonotonicTime(base::Time::FromInternalValue(2000000));
-  EXPECT_TRUE(metrics_utils::MonotonicDurationHelper(
-      &fake_system_state, &storage, &duration));
-  EXPECT_EQ(duration.InSeconds(), 1);
-  EXPECT_TRUE(metrics_utils::MonotonicDurationHelper(
-      &fake_system_state, &storage, &duration));
-  EXPECT_EQ(duration.InSeconds(), 0);
-
-  // Advance clock two seconds and we should get 2 sec and then 0 sec.
-  fake_clock.SetMonotonicTime(base::Time::FromInternalValue(4000000));
-  EXPECT_TRUE(metrics_utils::MonotonicDurationHelper(
-      &fake_system_state, &storage, &duration));
-  EXPECT_EQ(duration.InSeconds(), 2);
-  EXPECT_TRUE(metrics_utils::MonotonicDurationHelper(
-      &fake_system_state, &storage, &duration));
-  EXPECT_EQ(duration.InSeconds(), 0);
-}
-
 }  // namespace metrics_utils
 }  // namespace chromeos_update_engine
diff --git a/mock_boot_control_hal.h b/mock_boot_control_hal.h
deleted file mode 100644
index 4e9cb50..0000000
--- a/mock_boot_control_hal.h
+++ /dev/null
@@ -1,49 +0,0 @@
-//
-// 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 <android/hardware/boot/1.0/IBootControl.h>
-#include <stdint.h>
-
-#include <gmock/gmock.h>
-
-namespace chromeos_update_engine {
-
-class MockBootControlHal
-    : public ::android::hardware::boot::V1_0::IBootControl {
- public:
-  MOCK_METHOD0(getNumberSlots, ::android::hardware::Return<uint32_t>());
-  MOCK_METHOD0(getCurrentSlot, ::android::hardware::Return<uint32_t>());
-  MOCK_METHOD1(markBootSuccessful,
-               ::android::hardware::Return<void>(markBootSuccessful_cb));
-  MOCK_METHOD2(setActiveBootSlot,
-               ::android::hardware::Return<void>(uint32_t,
-                                                 setActiveBootSlot_cb));
-  MOCK_METHOD2(setSlotAsUnbootable,
-               ::android::hardware::Return<void>(uint32_t,
-                                                 setSlotAsUnbootable_cb));
-  MOCK_METHOD1(
-      isSlotBootable,
-      ::android::hardware::Return<::android::hardware::boot::V1_0::BoolResult>(
-          uint32_t));
-  MOCK_METHOD1(
-      isSlotMarkedSuccessful,
-      ::android::hardware::Return<::android::hardware::boot::V1_0::BoolResult>(
-          uint32_t));
-  MOCK_METHOD2(getSuffix,
-               ::android::hardware::Return<void>(uint32_t, getSuffix_cb));
-};
-
-}  // namespace chromeos_update_engine
diff --git a/mock_libcurl_http_fetcher.h b/mock_libcurl_http_fetcher.h
index a8ef0f4..a14f953 100644
--- a/mock_libcurl_http_fetcher.h
+++ b/mock_libcurl_http_fetcher.h
@@ -19,7 +19,7 @@
 
 #include <gmock/gmock.h>
 
-#include "update_engine/connection_manager_interface.h"
+#include "update_engine/libcurl_http_fetcher.h"
 
 namespace chromeos_update_engine {
 
diff --git a/payload_consumer/bzip_extent_writer_unittest.cc b/payload_consumer/bzip_extent_writer_unittest.cc
index 125e1e5..b587040 100644
--- a/payload_consumer/bzip_extent_writer_unittest.cc
+++ b/payload_consumer/bzip_extent_writer_unittest.cc
@@ -49,7 +49,7 @@
   void TearDown() override { fd_->Close(); }
 
   FileDescriptorPtr fd_;
-  test_utils::ScopedTempFile temp_file_{"BzipExtentWriterTest-file.XXXXXX"};
+  ScopedTempFile temp_file_{"BzipExtentWriterTest-file.XXXXXX"};
 };
 
 TEST_F(BzipExtentWriterTest, SimpleTest) {
diff --git a/payload_consumer/cached_file_descriptor_unittest.cc b/payload_consumer/cached_file_descriptor_unittest.cc
index d2965fc..b64420a 100644
--- a/payload_consumer/cached_file_descriptor_unittest.cc
+++ b/payload_consumer/cached_file_descriptor_unittest.cc
@@ -73,7 +73,7 @@
 
  protected:
   FileDescriptorPtr fd_{new EintrSafeFileDescriptor};
-  test_utils::ScopedTempFile temp_file_{"CachedFileDescriptor-file.XXXXXX"};
+  ScopedTempFile temp_file_{"CachedFileDescriptor-file.XXXXXX"};
   int value_{1};
   FileDescriptorPtr cfd_;
 };
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 87fc4cf..c3a321e 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -41,16 +41,17 @@
 #include <puffin/puffpatch.h>
 
 #include "update_engine/common/constants.h"
+#include "update_engine/common/download_action.h"
 #include "update_engine/common/error_code.h"
 #include "update_engine/common/error_code_utils.h"
 #include "update_engine/common/hardware_interface.h"
 #include "update_engine/common/prefs_interface.h"
 #include "update_engine/common/subprocess.h"
 #include "update_engine/common/terminator.h"
+#include "update_engine/common/utils.h"
 #include "update_engine/payload_consumer/bzip_extent_writer.h"
 #include "update_engine/payload_consumer/cached_file_descriptor.h"
 #include "update_engine/payload_consumer/certificate_parser_interface.h"
-#include "update_engine/payload_consumer/download_action.h"
 #include "update_engine/payload_consumer/extent_reader.h"
 #include "update_engine/payload_consumer/extent_writer.h"
 #include "update_engine/payload_consumer/partition_update_generator_interface.h"
@@ -197,12 +198,9 @@
   if (op_result)
     return true;
 
-  size_t partition_first_op_num =
-      current_partition_ ? acc_num_operations_[current_partition_ - 1] : 0;
   LOG(ERROR) << "Failed to perform " << op_type_name << " operation "
              << next_operation_num_ << ", which is the operation "
-             << next_operation_num_ - partition_first_op_num
-             << " in partition \""
+             << GetPartitionOperationNum() << " in partition \""
              << partitions_[current_partition_].partition_name() << "\"";
   if (*error == ErrorCode::kSuccess)
     *error = ErrorCode::kDownloadOperationExecutionError;
@@ -241,18 +239,29 @@
       install_plan_->partitions.size() - partitions_.size();
   const InstallPlan::Partition& install_part =
       install_plan_->partitions[num_previous_partitions + current_partition_];
-  partition_writer_ = std::make_unique<PartitionWriter>(
+  auto dynamic_control = boot_control_->GetDynamicPartitionControl();
+  partition_writer_ = partition_writer::CreatePartitionWriter(
       partition,
       install_part,
-      boot_control_->GetDynamicPartitionControl(),
+      dynamic_control,
       block_size_,
-      interactive_);
-
+      interactive_,
+      IsDynamicPartition(install_part.name));
   // Open source fds if we have a delta payload, or for partitions in the
   // partial update.
   bool source_may_exist = manifest_.partial_update() ||
                           payload_->type == InstallPayloadType::kDelta;
-  return partition_writer_->Init(install_plan_, source_may_exist);
+  const size_t partition_operation_num = GetPartitionOperationNum();
+
+  TEST_AND_RETURN_FALSE(partition_writer_->Init(
+      install_plan_, source_may_exist, partition_operation_num));
+  CheckpointUpdateProgress(true);
+  return true;
+}
+
+size_t DeltaPerformer::GetPartitionOperationNum() {
+  return next_operation_num_ -
+         (current_partition_ ? acc_num_operations_[current_partition_ - 1] : 0);
 }
 
 namespace {
@@ -276,15 +285,6 @@
 
 }  // namespace
 
-uint32_t DeltaPerformer::GetMinorVersion() const {
-  if (manifest_.has_minor_version()) {
-    return manifest_.minor_version();
-  }
-  return payload_->type == InstallPayloadType::kDelta
-             ? kMaxSupportedMinorPayloadVersion
-             : kFullPayloadMinorVersion;
-}
-
 bool DeltaPerformer::IsHeaderParsed() const {
   return metadata_size_ != 0;
 }
@@ -353,6 +353,7 @@
                  << "Trusting metadata size in payload = " << metadata_size_;
   }
 
+  // NOLINTNEXTLINE(whitespace/braces)
   auto [payload_verifier, perform_verification] = CreatePayloadVerifier();
   if (!payload_verifier) {
     LOG(ERROR) << "Failed to create payload verifier.";
@@ -496,6 +497,9 @@
     // We know there are more operations to perform because we didn't reach the
     // |num_total_operations_| limit yet.
     if (next_operation_num_ >= acc_num_operations_[current_partition_]) {
+      if (partition_writer_) {
+        TEST_AND_RETURN_FALSE(partition_writer_->FinishedInstallOps());
+      }
       CloseCurrentPartition();
       // Skip until there are operations for current_partition_.
       while (next_operation_num_ >= acc_num_operations_[current_partition_]) {
@@ -506,12 +510,9 @@
         return false;
       }
     }
-    const size_t partition_operation_num =
-        next_operation_num_ -
-        (current_partition_ ? acc_num_operations_[current_partition_ - 1] : 0);
 
     const InstallOperation& op =
-        partitions_[current_partition_].operations(partition_operation_num);
+        partitions_[current_partition_].operations(GetPartitionOperationNum());
 
     CopyDataToBuffer(&c_bytes, &count, op.data_length());
 
@@ -642,6 +643,11 @@
     }
   }
 
+  auto dynamic_control = boot_control_->GetDynamicPartitionControl();
+  CHECK_NE(dynamic_control, nullptr);
+  TEST_AND_RETURN_FALSE(dynamic_control->ListDynamicPartitionsForSlot(
+      install_plan_->target_slot, &dynamic_partitions_));
+
   // Partitions in manifest are no longer needed after preparing partitions.
   manifest_.clear_partitions();
   // TODO(xunchang) TBD: allow partial update only on devices with dynamic
@@ -1085,13 +1091,6 @@
     }
   }
 
-  if (manifest_.has_old_rootfs_info() || manifest_.has_new_rootfs_info() ||
-      manifest_.has_old_kernel_info() || manifest_.has_new_kernel_info() ||
-      manifest_.install_operations_size() != 0 ||
-      manifest_.kernel_install_operations_size() != 0) {
-    LOG(ERROR) << "Manifest contains deprecated fields.";
-    return ErrorCode::kPayloadMismatchedType;
-  }
   ErrorCode error_code = CheckTimestampError();
   if (error_code != ErrorCode::kSuccess) {
     if (error_code == ErrorCode::kPayloadTimestampError) {
@@ -1126,28 +1125,35 @@
   auto&& timestamp_valid = [this](const PartitionUpdate& partition,
                                   bool allow_empty_version,
                                   bool* downgrade_detected) -> ErrorCode {
+    const auto& partition_name = partition.partition_name();
     if (!partition.has_version()) {
+      if (hardware_->GetVersionForLogging(partition_name).empty()) {
+        LOG(INFO) << partition_name << " does't have version, skipping "
+                  << "downgrade check.";
+        return ErrorCode::kSuccess;
+      }
+
       if (allow_empty_version) {
         return ErrorCode::kSuccess;
       }
       LOG(ERROR)
-          << "PartitionUpdate " << partition.partition_name()
-          << " does ot have a version field. Not allowed in partial updates.";
+          << "PartitionUpdate " << partition_name
+          << " doesn't have a version field. Not allowed in partial updates.";
       return ErrorCode::kDownloadManifestParseError;
     }
 
-    auto error_code = hardware_->IsPartitionUpdateValid(
-        partition.partition_name(), partition.version());
+    auto error_code =
+        hardware_->IsPartitionUpdateValid(partition_name, partition.version());
     switch (error_code) {
       case ErrorCode::kSuccess:
         break;
       case ErrorCode::kPayloadTimestampError:
         *downgrade_detected = true;
-        LOG(WARNING) << "PartitionUpdate " << partition.partition_name()
+        LOG(WARNING) << "PartitionUpdate " << partition_name
                      << " has an older version than partition on device.";
         break;
       default:
-        LOG(ERROR) << "IsPartitionUpdateValid(" << partition.partition_name()
+        LOG(ERROR) << "IsPartitionUpdateValid(" << partition_name
                    << ") returned" << utils::ErrorCodeToString(error_code);
         break;
     }
@@ -1281,6 +1287,14 @@
     return ErrorCode::kPayloadSizeMismatchError;
   }
 
+  // Verifies the payload hash.
+  TEST_AND_RETURN_VAL(ErrorCode::kDownloadPayloadVerificationError,
+                      !payload_hash_calculator_.raw_hash().empty());
+  TEST_AND_RETURN_VAL(
+      ErrorCode::kPayloadHashMismatchError,
+      payload_hash_calculator_.raw_hash() == update_check_response_hash);
+
+  // NOLINTNEXTLINE(whitespace/braces)
   auto [payload_verifier, perform_verification] = CreatePayloadVerifier();
   if (!perform_verification) {
     LOG(WARNING) << "Not verifying signed delta payload -- missing public key.";
@@ -1291,13 +1305,6 @@
     return ErrorCode::kDownloadPayloadPubKeyVerificationError;
   }
 
-  // Verifies the payload hash.
-  TEST_AND_RETURN_VAL(ErrorCode::kDownloadPayloadVerificationError,
-                      !payload_hash_calculator_.raw_hash().empty());
-  TEST_AND_RETURN_VAL(
-      ErrorCode::kPayloadHashMismatchError,
-      payload_hash_calculator_.raw_hash() == update_check_response_hash);
-
   TEST_AND_RETURN_VAL(ErrorCode::kSignedDeltaPayloadExpectedError,
                       !signatures_message_data_.empty());
   brillo::Blob hash_data = signed_hash_calculator_.raw_hash();
@@ -1436,6 +1443,7 @@
       TEST_AND_RETURN_FALSE(
           prefs_->SetInt64(kPrefsUpdateStateNextDataLength, 0));
     }
+    partition_writer_->CheckpointUpdateProgress(GetPartitionOperationNum());
   }
   TEST_AND_RETURN_FALSE(
       prefs_->SetInt64(kPrefsUpdateStateNextOperation, next_operation_num_));
@@ -1503,4 +1511,10 @@
   return true;
 }
 
+bool DeltaPerformer::IsDynamicPartition(const std::string& part_name) {
+  return std::find(dynamic_partitions_.begin(),
+                   dynamic_partitions_.end(),
+                   part_name) != dynamic_partitions_.end();
+}
+
 }  // namespace chromeos_update_engine
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index d44f6c2..7df2bd3 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -170,9 +170,13 @@
   // Return true if header parsing is finished and no errors occurred.
   bool IsHeaderParsed() const;
 
-  // Returns the delta minor version. If this value is defined in the manifest,
-  // it returns that value, otherwise it returns the default value.
-  uint32_t GetMinorVersion() const;
+  // Compare |calculated_hash| with source hash in |operation|, return false and
+  // dump hash and set |error| if don't match.
+  // |source_fd| is the file descriptor of the source partition.
+  static bool ValidateSourceHash(const brillo::Blob& calculated_hash,
+                                 const InstallOperation& operation,
+                                 const FileDescriptorPtr source_fd,
+                                 ErrorCode* error);
 
   // Initialize partitions and allocate required space for an update with the
   // given |manifest|. |update_check_response_hash| is used to check if the
@@ -199,6 +203,12 @@
   FRIEND_TEST(DeltaPerformerTest, BrilloParsePayloadMetadataTest);
   FRIEND_TEST(DeltaPerformerTest, UsePublicKeyFromResponse);
 
+  // Obtain the operation index for current partition. If all operations for
+  // current partition is are finished, return # of operations. This is mostly
+  // intended to be used by CheckpointUpdateProgress, where partition writer
+  // needs to know the current operation number to properly checkpoint update.
+  size_t GetPartitionOperationNum();
+
   // Parse and move the update instructions of all partitions into our local
   // |partitions_| variable based on the version of the payload. Requires the
   // manifest to be parsed and valid.
@@ -299,6 +309,8 @@
   //   a generic error on the device.
   ErrorCode CheckTimestampError() const;
 
+  // Check if partition `part_name` is a dynamic partition.
+  bool IsDynamicPartition(const std::string& part_name);
   // Update Engine preference store.
   PrefsInterface* prefs_;
 
@@ -336,22 +348,22 @@
   // otherwise 0.
   size_t num_total_operations_{0};
 
-  // The list of partitions to update as found in the manifest major version 2.
-  // When parsing an older manifest format, the information is converted over to
-  // this format instead.
+  // The list of partitions to update as found in the manifest major
+  // version 2. When parsing an older manifest format, the information is
+  // converted over to this format instead.
   std::vector<PartitionUpdate> partitions_;
 
   // Index in the list of partitions (|partitions_| member) of the current
   // partition being processed.
   size_t current_partition_{0};
 
-  // Index of the next operation to perform in the manifest. The index is linear
-  // on the total number of operation on the manifest.
+  // Index of the next operation to perform in the manifest. The index is
+  // linear on the total number of operation on the manifest.
   size_t next_operation_num_{0};
 
   // A buffer used for accumulating downloaded data. Initially, it stores the
-  // payload metadata; once that's downloaded and parsed, it stores data for the
-  // next update operation.
+  // payload metadata; once that's downloaded and parsed, it stores data for
+  // the next update operation.
   brillo::Blob buffer_;
   // Offset of buffer_ in the binary blobs section of the update.
   uint64_t buffer_offset_{0};
@@ -393,8 +405,9 @@
   // If |true|, the update is user initiated (vs. periodic update checks).
   bool interactive_{false};
 
-  // The timeout after which we should force emitting a progress log (constant),
-  // and the actual point in time for the next forced log to be emitted.
+  // The timeout after which we should force emitting a progress log
+  // (constant), and the actual point in time for the next forced log to be
+  // emitted.
   const base::TimeDelta forced_progress_log_wait_{
       base::TimeDelta::FromSeconds(kProgressLogTimeoutSeconds)};
   base::TimeTicks forced_progress_log_time_;
@@ -407,6 +420,8 @@
 
   std::unique_ptr<PartitionWriter> partition_writer_;
 
+  // List of dynamic partitions on device.
+  std::vector<std::string> dynamic_partitions_;
   DISALLOW_COPY_AND_ASSIGN(DeltaPerformer);
 };
 
diff --git a/payload_consumer/delta_performer_fuzzer.cc b/payload_consumer/delta_performer_fuzzer.cc
index 73082c4..0ce5081 100644
--- a/payload_consumer/delta_performer_fuzzer.cc
+++ b/payload_consumer/delta_performer_fuzzer.cc
@@ -18,11 +18,11 @@
 #include <base/logging.h>
 #include <fuzzer/FuzzedDataProvider.h>
 
+#include "update_engine/common/download_action.h"
 #include "update_engine/common/fake_boot_control.h"
 #include "update_engine/common/fake_hardware.h"
 #include "update_engine/common/prefs.h"
 #include "update_engine/payload_consumer/delta_performer.h"
-#include "update_engine/payload_consumer/download_action.h"
 #include "update_engine/payload_consumer/install_plan.h"
 
 namespace chromeos_update_engine {
diff --git a/payload_consumer/delta_performer_integration_test.cc b/payload_consumer/delta_performer_integration_test.cc
index f2aeb03..374131e 100644
--- a/payload_consumer/delta_performer_integration_test.cc
+++ b/payload_consumer/delta_performer_integration_test.cc
@@ -37,12 +37,12 @@
 #include "update_engine/common/fake_boot_control.h"
 #include "update_engine/common/fake_hardware.h"
 #include "update_engine/common/fake_prefs.h"
+#include "update_engine/common/hardware_interface.h"
+#include "update_engine/common/mock_download_action.h"
 #include "update_engine/common/mock_prefs.h"
 #include "update_engine/common/test_utils.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/hardware_android.h"
 #include "update_engine/payload_consumer/install_plan.h"
-#include "update_engine/payload_consumer/mock_download_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"
@@ -52,7 +52,9 @@
 
 namespace chromeos_update_engine {
 
+using std::list;
 using std::string;
+using std::unique_ptr;
 using std::vector;
 using test_utils::GetBuildArtifactsPath;
 using test_utils::kRandomString;
@@ -76,22 +78,24 @@
 
 namespace {
 struct DeltaState {
-  string a_img;
-  string b_img;
-  string result_img;
+  unique_ptr<ScopedTempFile> a_img;
+  unique_ptr<ScopedTempFile> b_img;
+  unique_ptr<ScopedTempFile> result_img;
   size_t image_size;
 
-  string delta_path;
+  unique_ptr<ScopedTempFile> delta_file;
+  // The in-memory copy of delta file.
+  brillo::Blob delta;
   uint64_t metadata_size;
   uint32_t metadata_signature_size;
 
-  string old_kernel;
+  unique_ptr<ScopedTempFile> old_kernel;
   brillo::Blob old_kernel_data;
 
-  string new_kernel;
+  unique_ptr<ScopedTempFile> new_kernel;
   brillo::Blob new_kernel_data;
 
-  string result_kernel;
+  unique_ptr<ScopedTempFile> result_kernel;
   brillo::Blob result_kernel_data;
   size_t kernel_size;
 
@@ -99,9 +103,6 @@
   // the DeltaPerformer.
   InstallPlan install_plan;
 
-  // The in-memory copy of delta file.
-  brillo::Blob delta;
-
   // Mock and fake instances used by the delta performer.
   FakeBootControl fake_boot_control_;
   FakeHardware fake_hardware_;
@@ -155,7 +156,7 @@
     EXPECT_EQ(expected, performer.ValidateManifest());
   }
   void AddPartition(DeltaArchiveManifest* manifest,
-                    std::string name,
+                    string name,
                     int timestamp) {
     auto& partition = *manifest->add_partitions();
     partition.set_version(std::to_string(timestamp));
@@ -259,8 +260,7 @@
   }
   string signature_size_string = base::JoinString(signature_size_strings, ":");
 
-  test_utils::ScopedTempFile hash_file("hash.XXXXXX"),
-      metadata_hash_file("hash.XXXXXX");
+  ScopedTempFile hash_file("hash.XXXXXX"), metadata_hash_file("hash.XXXXXX");
   string delta_generator_path = GetBuildArtifactsPath("delta_generator");
   ASSERT_EQ(0,
             System(base::StringPrintf(
@@ -273,29 +273,27 @@
                 metadata_hash_file.path().c_str())));
 
   // Sign the hash with all private keys.
-  vector<test_utils::ScopedTempFile> sig_files, metadata_sig_files;
+  list<ScopedTempFile> sig_files, metadata_sig_files;
   vector<string> sig_file_paths, metadata_sig_file_paths;
   for (const auto& key_path : private_key_paths) {
     brillo::Blob hash, signature;
     ASSERT_TRUE(utils::ReadFile(hash_file.path(), &hash));
     ASSERT_TRUE(PayloadSigner::SignHash(hash, key_path, &signature));
 
-    test_utils::ScopedTempFile sig_file("signature.XXXXXX");
-    ASSERT_TRUE(test_utils::WriteFileVector(sig_file.path(), signature));
-    sig_file_paths.push_back(sig_file.path());
-    sig_files.push_back(std::move(sig_file));
+    sig_files.emplace_back("signature.XXXXXX");
+    ASSERT_TRUE(
+        test_utils::WriteFileVector(sig_files.back().path(), signature));
+    sig_file_paths.push_back(sig_files.back().path());
 
     brillo::Blob metadata_hash, metadata_signature;
     ASSERT_TRUE(utils::ReadFile(metadata_hash_file.path(), &metadata_hash));
     ASSERT_TRUE(
         PayloadSigner::SignHash(metadata_hash, key_path, &metadata_signature));
 
-    test_utils::ScopedTempFile metadata_sig_file("signature.XXXXXX");
-    ASSERT_TRUE(test_utils::WriteFileVector(metadata_sig_file.path(),
+    metadata_sig_files.emplace_back("metadata_signature.XXXXXX");
+    ASSERT_TRUE(test_utils::WriteFileVector(metadata_sig_files.back().path(),
                                             metadata_signature));
-
-    metadata_sig_file_paths.push_back(metadata_sig_file.path());
-    metadata_sig_files.push_back(std::move(metadata_sig_file));
+    metadata_sig_file_paths.push_back(metadata_sig_files.back().path());
   }
   string sig_files_string = base::JoinString(sig_file_paths, ":");
   string metadata_sig_files_string =
@@ -377,7 +375,7 @@
         GetBuildArtifactsPath(kUnittestPrivateKey2Path));
   }
 
-  std::string public_key;
+  string public_key;
   if (signature_test == kSignatureGeneratedShellRotateCl2) {
     public_key = GetBuildArtifactsPath(kUnittestPublicKey2Path);
   } else if (signature_test == kSignatureGeneratedShellECKey) {
@@ -397,44 +395,23 @@
                               SignatureTest signature_test,
                               DeltaState* state,
                               uint32_t minor_version) {
-  EXPECT_TRUE(utils::MakeTempFile("a_img.XXXXXX", &state->a_img, nullptr));
-  EXPECT_TRUE(utils::MakeTempFile("b_img.XXXXXX", &state->b_img, nullptr));
+  state->a_img.reset(new ScopedTempFile("a_img.XXXXXX"));
+  state->b_img.reset(new ScopedTempFile("b_img.XXXXXX"));
 
   // result_img is used in minor version 2. Instead of applying the update
   // in-place on A, we apply it to a new image, result_img.
-  EXPECT_TRUE(
-      utils::MakeTempFile("result_img.XXXXXX", &state->result_img, nullptr));
+  state->result_img.reset(new ScopedTempFile("result_img.XXXXXX"));
 
   EXPECT_TRUE(
       base::CopyFile(GetBuildArtifactsPath().Append("gen/disk_ext2_4k.img"),
-                     base::FilePath(state->a_img)));
+                     base::FilePath(state->a_img->path())));
 
-  state->image_size = utils::FileSize(state->a_img);
-
-  // Create ImageInfo A & B
-  ImageInfo old_image_info;
-  ImageInfo new_image_info;
-
-  if (!full_rootfs) {
-    old_image_info.set_channel("src-channel");
-    old_image_info.set_board("src-board");
-    old_image_info.set_version("src-version");
-    old_image_info.set_key("src-key");
-    old_image_info.set_build_channel("src-build-channel");
-    old_image_info.set_build_version("src-build-version");
-  }
-
-  new_image_info.set_channel("test-channel");
-  new_image_info.set_board("test-board");
-  new_image_info.set_version("test-version");
-  new_image_info.set_key("test-key");
-  new_image_info.set_build_channel("test-build-channel");
-  new_image_info.set_build_version("test-build-version");
+  state->image_size = utils::FileSize(state->a_img->path());
 
   // Make some changes to the A image.
   {
     string a_mnt;
-    ScopedLoopMounter b_mounter(state->a_img, &a_mnt, 0);
+    ScopedLoopMounter b_mounter(state->a_img->path(), &a_mnt, 0);
 
     brillo::Blob hardtocompress;
     while (hardtocompress.size() < 3 * kBlockSize) {
@@ -471,17 +448,18 @@
 
   // Create a result image with image_size bytes of garbage.
   brillo::Blob ones(state->image_size, 0xff);
-  EXPECT_TRUE(
-      utils::WriteFile(state->result_img.c_str(), ones.data(), ones.size()));
-  EXPECT_EQ(utils::FileSize(state->a_img), utils::FileSize(state->result_img));
+  EXPECT_TRUE(utils::WriteFile(
+      state->result_img->path().c_str(), ones.data(), ones.size()));
+  EXPECT_EQ(utils::FileSize(state->a_img->path()),
+            utils::FileSize(state->result_img->path()));
 
   EXPECT_TRUE(
       base::CopyFile(GetBuildArtifactsPath().Append("gen/disk_ext2_4k.img"),
-                     base::FilePath(state->b_img)));
+                     base::FilePath(state->b_img->path())));
   {
     // Make some changes to the B image.
     string b_mnt;
-    ScopedLoopMounter b_mounter(state->b_img, &b_mnt, 0);
+    ScopedLoopMounter b_mounter(state->b_img->path(), &b_mnt, 0);
     base::FilePath mnt_path(b_mnt);
 
     EXPECT_TRUE(base::CopyFile(mnt_path.Append("regular-small"),
@@ -529,18 +507,9 @@
         hardtocompress.size()));
   }
 
-  string old_kernel;
-  EXPECT_TRUE(
-      utils::MakeTempFile("old_kernel.XXXXXX", &state->old_kernel, nullptr));
-
-  string new_kernel;
-  EXPECT_TRUE(
-      utils::MakeTempFile("new_kernel.XXXXXX", &state->new_kernel, nullptr));
-
-  string result_kernel;
-  EXPECT_TRUE(utils::MakeTempFile(
-      "result_kernel.XXXXXX", &state->result_kernel, nullptr));
-
+  state->old_kernel.reset(new ScopedTempFile("old_kernel.XXXXXX"));
+  state->new_kernel.reset(new ScopedTempFile("new_kernel.XXXXXX"));
+  state->result_kernel.reset(new ScopedTempFile("result_kernel.XXXXXX"));
   state->kernel_size = kDefaultKernelSize;
   state->old_kernel_data.resize(kDefaultKernelSize);
   state->new_kernel_data.resize(state->old_kernel_data.size());
@@ -554,18 +523,17 @@
       std::begin(kNewData), std::end(kNewData), state->new_kernel_data.begin());
 
   // Write kernels to disk
-  EXPECT_TRUE(utils::WriteFile(state->old_kernel.c_str(),
+  EXPECT_TRUE(utils::WriteFile(state->old_kernel->path().c_str(),
                                state->old_kernel_data.data(),
                                state->old_kernel_data.size()));
-  EXPECT_TRUE(utils::WriteFile(state->new_kernel.c_str(),
+  EXPECT_TRUE(utils::WriteFile(state->new_kernel->path().c_str(),
                                state->new_kernel_data.data(),
                                state->new_kernel_data.size()));
-  EXPECT_TRUE(utils::WriteFile(state->result_kernel.c_str(),
+  EXPECT_TRUE(utils::WriteFile(state->result_kernel->path().c_str(),
                                state->result_kernel_data.data(),
                                state->result_kernel_data.size()));
 
-  EXPECT_TRUE(utils::MakeTempFile("delta.XXXXXX", &state->delta_path, nullptr));
-  LOG(INFO) << "delta path: " << state->delta_path;
+  state->delta_file.reset(new ScopedTempFile("delta.XXXXXX"));
   {
     const string private_key =
         signature_test == kSignatureGenerator
@@ -581,10 +549,10 @@
     if (!full_rootfs) {
       payload_config.source.partitions.emplace_back(kPartitionNameRoot);
       payload_config.source.partitions.emplace_back(kPartitionNameKernel);
-      payload_config.source.partitions.front().path = state->a_img;
+      payload_config.source.partitions.front().path = state->a_img->path();
       if (!full_kernel)
-        payload_config.source.partitions.back().path = state->old_kernel;
-      payload_config.source.image_info = old_image_info;
+        payload_config.source.partitions.back().path =
+            state->old_kernel->path();
       EXPECT_TRUE(payload_config.source.LoadImageSize());
       for (PartitionConfig& part : payload_config.source.partitions)
         EXPECT_TRUE(part.OpenFilesystem());
@@ -594,29 +562,30 @@
         payload_config.hard_chunk_size = 1024 * 1024;
     }
     payload_config.target.partitions.emplace_back(kPartitionNameRoot);
-    payload_config.target.partitions.back().path = state->b_img;
+    payload_config.target.partitions.back().path = state->b_img->path();
     payload_config.target.partitions.emplace_back(kPartitionNameKernel);
-    payload_config.target.partitions.back().path = state->new_kernel;
-    payload_config.target.image_info = new_image_info;
+    payload_config.target.partitions.back().path = state->new_kernel->path();
     EXPECT_TRUE(payload_config.target.LoadImageSize());
     for (PartitionConfig& part : payload_config.target.partitions)
       EXPECT_TRUE(part.OpenFilesystem());
 
     EXPECT_TRUE(payload_config.Validate());
-    EXPECT_TRUE(GenerateUpdatePayloadFile(
-        payload_config, state->delta_path, private_key, &state->metadata_size));
+    EXPECT_TRUE(GenerateUpdatePayloadFile(payload_config,
+                                          state->delta_file->path(),
+                                          private_key,
+                                          &state->metadata_size));
   }
   // Extend the "partitions" holding the file system a bit.
   EXPECT_EQ(0,
-            HANDLE_EINTR(truncate(state->a_img.c_str(),
+            HANDLE_EINTR(truncate(state->a_img->path().c_str(),
                                   state->image_size + 1024 * 1024)));
   EXPECT_EQ(static_cast<off_t>(state->image_size + 1024 * 1024),
-            utils::FileSize(state->a_img));
+            utils::FileSize(state->a_img->path()));
   EXPECT_EQ(0,
-            HANDLE_EINTR(truncate(state->b_img.c_str(),
+            HANDLE_EINTR(truncate(state->b_img->path().c_str(),
                                   state->image_size + 1024 * 1024)));
   EXPECT_EQ(static_cast<off_t>(state->image_size + 1024 * 1024),
-            utils::FileSize(state->b_img));
+            utils::FileSize(state->b_img->path()));
 
   if (signature_test == kSignatureGeneratedPlaceholder ||
       signature_test == kSignatureGeneratedPlaceholderMismatch) {
@@ -625,13 +594,13 @@
         GetBuildArtifactsPath(kUnittestPrivateKeyPath), &signature_size));
     LOG(INFO) << "Inserting placeholder signature.";
     ASSERT_TRUE(InsertSignaturePlaceholder(
-        signature_size, state->delta_path, &state->metadata_size));
+        signature_size, state->delta_file->path(), &state->metadata_size));
 
     if (signature_test == kSignatureGeneratedPlaceholderMismatch) {
       signature_size -= 1;
       LOG(INFO) << "Inserting mismatched placeholder signature.";
       ASSERT_FALSE(InsertSignaturePlaceholder(
-          signature_size, state->delta_path, &state->metadata_size));
+          signature_size, state->delta_file->path(), &state->metadata_size));
       return;
     }
   }
@@ -643,13 +612,13 @@
     // reflect the new size after adding the signature operation to the
     // manifest.
     LOG(INFO) << "Signing payload.";
-    SignGeneratedPayload(state->delta_path, &state->metadata_size);
+    SignGeneratedPayload(state->delta_file->path(), &state->metadata_size);
   } else if (signature_test == kSignatureGeneratedShell ||
              signature_test == kSignatureGeneratedShellECKey ||
              signature_test == kSignatureGeneratedShellBadKey ||
              signature_test == kSignatureGeneratedShellRotateCl1 ||
              signature_test == kSignatureGeneratedShellRotateCl2) {
-    SignGeneratedShellPayload(signature_test, state->delta_path);
+    SignGeneratedShellPayload(signature_test, state->delta_file->path());
   }
 }
 
@@ -663,7 +632,7 @@
                            uint32_t minor_version) {
   // Check the metadata.
   {
-    EXPECT_TRUE(utils::ReadFile(state->delta_path, &state->delta));
+    EXPECT_TRUE(utils::ReadFile(state->delta_file->path(), &state->delta));
     PayloadMetadata payload_metadata;
     EXPECT_TRUE(payload_metadata.ParsePayloadHeader(state->delta));
     state->metadata_size = payload_metadata.GetMetadataSize();
@@ -738,22 +707,6 @@
       EXPECT_FALSE(rootfs_part.old_partition_info().hash().empty());
     }
     EXPECT_FALSE(rootfs_part.new_partition_info().hash().empty());
-
-    EXPECT_EQ(manifest.new_image_info().channel(), "test-channel");
-    EXPECT_EQ(manifest.new_image_info().board(), "test-board");
-    EXPECT_EQ(manifest.new_image_info().version(), "test-version");
-    EXPECT_EQ(manifest.new_image_info().key(), "test-key");
-    EXPECT_EQ(manifest.new_image_info().build_channel(), "test-build-channel");
-    EXPECT_EQ(manifest.new_image_info().build_version(), "test-build-version");
-
-    if (!full_rootfs) {
-      EXPECT_EQ(manifest.old_image_info().channel(), "src-channel");
-      EXPECT_EQ(manifest.old_image_info().board(), "src-board");
-      EXPECT_EQ(manifest.old_image_info().version(), "src-version");
-      EXPECT_EQ(manifest.old_image_info().key(), "src-key");
-      EXPECT_EQ(manifest.old_image_info().build_channel(), "src-build-channel");
-      EXPECT_EQ(manifest.old_image_info().build_version(), "src-build-version");
-    }
   }
 
   MockPrefs prefs;
@@ -832,9 +785,10 @@
   (*performer)->set_public_key_path(public_key_path);
   (*performer)->set_update_certificates_path("");
 
-  EXPECT_EQ(static_cast<off_t>(state->image_size),
-            HashCalculator::RawHashOfFile(
-                state->a_img, state->image_size, &root_part.source_hash));
+  EXPECT_EQ(
+      static_cast<off_t>(state->image_size),
+      HashCalculator::RawHashOfFile(
+          state->a_img->path(), state->image_size, &root_part.source_hash));
   EXPECT_TRUE(HashCalculator::RawHashOfData(state->old_kernel_data,
                                             &kernel_part.source_hash));
 
@@ -842,13 +796,15 @@
   install_plan->partitions.clear();
 
   state->fake_boot_control_.SetPartitionDevice(
-      kPartitionNameRoot, install_plan->source_slot, state->a_img);
+      kPartitionNameRoot, install_plan->source_slot, state->a_img->path());
+  state->fake_boot_control_.SetPartitionDevice(kPartitionNameKernel,
+                                               install_plan->source_slot,
+                                               state->old_kernel->path());
   state->fake_boot_control_.SetPartitionDevice(
-      kPartitionNameKernel, install_plan->source_slot, state->old_kernel);
-  state->fake_boot_control_.SetPartitionDevice(
-      kPartitionNameRoot, install_plan->target_slot, state->result_img);
-  state->fake_boot_control_.SetPartitionDevice(
-      kPartitionNameKernel, install_plan->target_slot, state->result_kernel);
+      kPartitionNameRoot, install_plan->target_slot, state->result_img->path());
+  state->fake_boot_control_.SetPartitionDevice(kPartitionNameKernel,
+                                               install_plan->target_slot,
+                                               state->result_kernel->path());
 
   ErrorCode expected_error, actual_error;
   bool continue_writing;
@@ -927,12 +883,15 @@
     return;
   }
 
+  CompareFilesByBlock(state->result_kernel->path(),
+                      state->new_kernel->path(),
+                      state->kernel_size);
   CompareFilesByBlock(
-      state->result_kernel, state->new_kernel, state->kernel_size);
-  CompareFilesByBlock(state->result_img, state->b_img, state->image_size);
+      state->result_img->path(), state->b_img->path(), state->image_size);
 
   brillo::Blob updated_kernel_partition;
-  EXPECT_TRUE(utils::ReadFile(state->result_kernel, &updated_kernel_partition));
+  EXPECT_TRUE(
+      utils::ReadFile(state->result_kernel->path(), &updated_kernel_partition));
   ASSERT_GE(updated_kernel_partition.size(), base::size(kNewData));
   EXPECT_TRUE(std::equal(std::begin(kNewData),
                          std::end(kNewData),
@@ -951,9 +910,10 @@
 
   EXPECT_EQ(state->image_size, partitions[0].target_size);
   brillo::Blob expected_new_rootfs_hash;
-  EXPECT_EQ(static_cast<off_t>(state->image_size),
-            HashCalculator::RawHashOfFile(
-                state->b_img, state->image_size, &expected_new_rootfs_hash));
+  EXPECT_EQ(
+      static_cast<off_t>(state->image_size),
+      HashCalculator::RawHashOfFile(
+          state->b_img->path(), state->image_size, &expected_new_rootfs_hash));
   EXPECT_EQ(expected_new_rootfs_hash, partitions[0].target_hash);
 }
 
@@ -991,13 +951,6 @@
                     &state,
                     minor_version);
 
-  ScopedPathUnlinker a_img_unlinker(state.a_img);
-  ScopedPathUnlinker b_img_unlinker(state.b_img);
-  ScopedPathUnlinker new_img_unlinker(state.result_img);
-  ScopedPathUnlinker delta_unlinker(state.delta_path);
-  ScopedPathUnlinker old_kernel_unlinker(state.old_kernel);
-  ScopedPathUnlinker new_kernel_unlinker(state.new_kernel);
-  ScopedPathUnlinker result_kernel_unlinker(state.result_kernel);
   ApplyDeltaFile(full_kernel,
                  full_rootfs,
                  signature_test,
@@ -1015,11 +968,6 @@
   DeltaState state;
   uint64_t minor_version = kFullPayloadMinorVersion;
   GenerateDeltaFile(true, true, -1, kSignatureGenerated, &state, minor_version);
-  ScopedPathUnlinker a_img_unlinker(state.a_img);
-  ScopedPathUnlinker b_img_unlinker(state.b_img);
-  ScopedPathUnlinker delta_unlinker(state.delta_path);
-  ScopedPathUnlinker old_kernel_unlinker(state.old_kernel);
-  ScopedPathUnlinker new_kernel_unlinker(state.new_kernel);
   DeltaPerformer* performer = nullptr;
   ApplyDeltaFile(true,
                  true,
diff --git a/payload_consumer/delta_performer_unittest.cc b/payload_consumer/delta_performer_unittest.cc
index a5eb538..74f17d2 100644
--- a/payload_consumer/delta_performer_unittest.cc
+++ b/payload_consumer/delta_performer_unittest.cc
@@ -41,10 +41,10 @@
 #include "update_engine/common/fake_hardware.h"
 #include "update_engine/common/fake_prefs.h"
 #include "update_engine/common/hardware_interface.h"
+#include "update_engine/common/mock_download_action.h"
 #include "update_engine/common/test_utils.h"
 #include "update_engine/common/utils.h"
 #include "update_engine/payload_consumer/fake_file_descriptor.h"
-#include "update_engine/payload_consumer/mock_download_action.h"
 #include "update_engine/payload_consumer/payload_constants.h"
 #include "update_engine/payload_consumer/payload_metadata.h"
 #include "update_engine/payload_generator/bzip.h"
@@ -202,7 +202,7 @@
                                uint64_t major_version,
                                uint32_t minor_version,
                                PartitionConfig* old_part = nullptr) {
-    test_utils::ScopedTempFile blob_file("Blob-XXXXXX");
+    ScopedTempFile blob_file("Blob-XXXXXX");
     EXPECT_TRUE(test_utils::WriteFileVector(blob_file.path(), blob_data));
 
     PayloadGenerationConfig config;
@@ -228,15 +228,15 @@
     new_part.path = "/dev/zero";
     new_part.size = 1234;
 
-    payload.AddPartition(*old_part, new_part, aops, {});
+    payload.AddPartition(*old_part, new_part, aops, {}, 0);
 
     // We include a kernel partition without operations.
     old_part->name = kPartitionNameKernel;
     new_part.name = kPartitionNameKernel;
     new_part.size = 0;
-    payload.AddPartition(*old_part, new_part, {}, {});
+    payload.AddPartition(*old_part, new_part, {}, {}, 0);
 
-    test_utils::ScopedTempFile payload_file("Payload-XXXXXX");
+    ScopedTempFile payload_file("Payload-XXXXXX");
     string private_key =
         sign_payload ? GetBuildArtifactsPath(kUnittestPrivateKeyPath) : "";
     EXPECT_TRUE(payload.WritePayload(payload_file.path(),
@@ -287,7 +287,7 @@
                                   const string& source_path,
                                   const brillo::Blob& target_data,
                                   bool expect_success) {
-    test_utils::ScopedTempFile new_part("Partition-XXXXXX");
+    ScopedTempFile new_part("Partition-XXXXXX");
     EXPECT_TRUE(test_utils::WriteFileVector(new_part.path(), target_data));
 
     payload_.size = payload_data.size();
@@ -576,7 +576,7 @@
   EXPECT_TRUE(HashCalculator::RawHashOfData(expected_data, &src_hash));
   aop.op.set_src_sha256_hash(src_hash.data(), src_hash.size());
 
-  test_utils::ScopedTempFile source("Source-XXXXXX");
+  ScopedTempFile source("Source-XXXXXX");
   EXPECT_TRUE(test_utils::WriteFileVector(source.path(), expected_data));
 
   PartitionConfig old_part(kPartitionNameRoot);
@@ -604,7 +604,7 @@
   EXPECT_TRUE(HashCalculator::RawHashOfData(src, &src_hash));
   aop.op.set_src_sha256_hash(src_hash.data(), src_hash.size());
 
-  test_utils::ScopedTempFile source("Source-XXXXXX");
+  ScopedTempFile source("Source-XXXXXX");
   EXPECT_TRUE(test_utils::WriteFileVector(source.path(), src));
 
   PartitionConfig old_part(kPartitionNameRoot);
@@ -632,7 +632,7 @@
   EXPECT_TRUE(HashCalculator::RawHashOfData(expected_data, &src_hash));
   aop.op.set_src_sha256_hash(src_hash.data(), src_hash.size());
 
-  test_utils::ScopedTempFile source("Source-XXXXXX");
+  ScopedTempFile source("Source-XXXXXX");
   EXPECT_TRUE(test_utils::WriteFileVector(source.path(), actual_data));
 
   PartitionConfig old_part(kPartitionNameRoot);
@@ -645,9 +645,6 @@
   EXPECT_EQ(actual_data, ApplyPayload(payload_data, source.path(), false));
 }
 
-
-
-
 TEST_F(DeltaPerformerTest, ExtentsToByteStringTest) {
   uint64_t test[] = {1, 1, 4, 2, 0, 1};
   static_assert(base::size(test) % 2 == 0, "Array size uneven");
diff --git a/payload_consumer/extent_reader.cc b/payload_consumer/extent_reader.cc
index ad983ae..3c7329d 100644
--- a/payload_consumer/extent_reader.cc
+++ b/payload_consumer/extent_reader.cc
@@ -77,7 +77,7 @@
         std::min(count - bytes_read, cur_extent_bytes_left);
 
     ssize_t out_bytes_read;
-    TEST_AND_RETURN_FALSE(utils::PReadAll(
+    TEST_AND_RETURN_FALSE(utils::ReadAll(
         fd_,
         bytes + bytes_read,
         bytes_to_read,
diff --git a/payload_consumer/extent_reader_unittest.cc b/payload_consumer/extent_reader_unittest.cc
index b7059bc..686f14d 100644
--- a/payload_consumer/extent_reader_unittest.cc
+++ b/payload_consumer/extent_reader_unittest.cc
@@ -72,7 +72,7 @@
   }
 
   FileDescriptorPtr fd_;
-  test_utils::ScopedTempFile temp_file_{"ExtentReaderTest-file.XXXXXX"};
+  ScopedTempFile temp_file_{"ExtentReaderTest-file.XXXXXX"};
   brillo::Blob sample_;
 };
 
diff --git a/payload_consumer/extent_writer_unittest.cc b/payload_consumer/extent_writer_unittest.cc
index aef856b..afebb1a 100644
--- a/payload_consumer/extent_writer_unittest.cc
+++ b/payload_consumer/extent_writer_unittest.cc
@@ -59,7 +59,7 @@
   void WriteAlignedExtents(size_t chunk_size, size_t first_chunk_size);
 
   FileDescriptorPtr fd_;
-  test_utils::ScopedTempFile temp_file_{"ExtentWriterTest-file.XXXXXX"};
+  ScopedTempFile temp_file_{"ExtentWriterTest-file.XXXXXX"};
 };
 
 TEST_F(ExtentWriterTest, SimpleTest) {
diff --git a/payload_consumer/file_descriptor.cc b/payload_consumer/file_descriptor.cc
index 1de615c..7c69c1b 100644
--- a/payload_consumer/file_descriptor.cc
+++ b/payload_consumer/file_descriptor.cc
@@ -21,6 +21,7 @@
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <unistd.h>
 
 #include <base/posix/eintr_wrapper.h>
 
@@ -28,6 +29,12 @@
 
 namespace chromeos_update_engine {
 
+EintrSafeFileDescriptor::~EintrSafeFileDescriptor() {
+  if (IsOpen()) {
+    Close();
+  }
+}
+
 bool EintrSafeFileDescriptor::Open(const char* path, int flags, mode_t mode) {
   CHECK_EQ(fd_, -1);
   return ((fd_ = HANDLE_EINTR(open(path, flags, mode))) >= 0);
@@ -125,11 +132,17 @@
 
 bool EintrSafeFileDescriptor::Flush() {
   CHECK_GE(fd_, 0);
+  // Implemented as a No-Op, as delta_performer typically uses |O_DSYNC|, except
+  // in interactive settings.
+  fsync(fd_);
   return true;
 }
 
 bool EintrSafeFileDescriptor::Close() {
   CHECK_GE(fd_, 0);
+  // https://stackoverflow.com/questions/705454/does-linux-guarantee-the-contents-of-a-file-is-flushed-to-disc-after-close
+  // |close()| doesn't imply |fsync()|, we need to do it manually.
+  fsync(fd_);
   if (IGNORE_EINTR(close(fd_)))
     return false;
   fd_ = -1;
diff --git a/payload_consumer/file_descriptor.h b/payload_consumer/file_descriptor.h
index fb07ff0..faebcc1 100644
--- a/payload_consumer/file_descriptor.h
+++ b/payload_consumer/file_descriptor.h
@@ -111,6 +111,7 @@
 class EintrSafeFileDescriptor : public FileDescriptor {
  public:
   EintrSafeFileDescriptor() : fd_(-1) {}
+  ~EintrSafeFileDescriptor();
 
   // Interface methods.
   bool Open(const char* path, int flags, mode_t mode) override;
diff --git a/payload_consumer/file_descriptor_utils_unittest.cc b/payload_consumer/file_descriptor_utils_unittest.cc
index 48e610f..478893d 100644
--- a/payload_consumer/file_descriptor_utils_unittest.cc
+++ b/payload_consumer/file_descriptor_utils_unittest.cc
@@ -52,14 +52,13 @@
 class FileDescriptorUtilsTest : public ::testing::Test {
  protected:
   void SetUp() override {
-    EXPECT_TRUE(utils::MakeTempFile("fd_tgt.XXXXXX", &tgt_path_, nullptr));
-    EXPECT_TRUE(target_->Open(tgt_path_.c_str(), O_RDWR));
+    EXPECT_TRUE(target_->Open(tgt_file_.path().c_str(), O_RDWR));
   }
 
   // Check that the |target_| file contains |expected_contents|.
   void ExpectTarget(const std::string& expected_contents) {
     std::string target_contents;
-    EXPECT_TRUE(utils::ReadFile(tgt_path_, &target_contents));
+    EXPECT_TRUE(utils::ReadFile(tgt_file_.path(), &target_contents));
     EXPECT_EQ(expected_contents.size(), target_contents.size());
     if (target_contents != expected_contents) {
       ADD_FAILURE() << "Contents don't match.";
@@ -70,8 +69,7 @@
     }
   }
 
-  // Path to the target temporary file.
-  std::string tgt_path_;
+  ScopedTempFile tgt_file_{"fd_tgt.XXXXXX"};
 
   // Source and target file descriptor used for testing the tools.
   FakeFileDescriptor* fake_source_{new FakeFileDescriptor()};
diff --git a/payload_consumer/file_writer_unittest.cc b/payload_consumer/file_writer_unittest.cc
index 59cfe2b..3b959f3 100644
--- a/payload_consumer/file_writer_unittest.cc
+++ b/payload_consumer/file_writer_unittest.cc
@@ -35,8 +35,7 @@
 class FileWriterTest : public ::testing::Test {};
 
 TEST(FileWriterTest, SimpleTest) {
-  // Create a uniquely named file for testing.
-  test_utils::ScopedTempFile file("FileWriterTest-XXXXXX");
+  ScopedTempFile file("FileWriterTest-XXXXXX");
   DirectFileWriter file_writer;
   EXPECT_EQ(0,
             file_writer.Open(file.path().c_str(),
@@ -60,7 +59,7 @@
 
 TEST(FileWriterTest, WriteErrorTest) {
   // Create a uniquely named file for testing.
-  test_utils::ScopedTempFile file("FileWriterTest-XXXXXX");
+  ScopedTempFile file("FileWriterTest-XXXXXX");
   DirectFileWriter file_writer;
   EXPECT_EQ(0,
             file_writer.Open(file.path().c_str(),
diff --git a/payload_consumer/filesystem_verifier_action.cc b/payload_consumer/filesystem_verifier_action.cc
index 61917ea..634f03f 100644
--- a/payload_consumer/filesystem_verifier_action.cc
+++ b/payload_consumer/filesystem_verifier_action.cc
@@ -20,17 +20,22 @@
 #include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <unistd.h>
 
 #include <algorithm>
 #include <cstdlib>
+#include <memory>
 #include <string>
+#include <utility>
 
 #include <base/bind.h>
-#include <brillo/data_encoding.h>
-#include <brillo/streams/file_stream.h>
 #include <base/strings/string_util.h>
+#include <brillo/data_encoding.h>
+#include <brillo/message_loops/message_loop.h>
+#include <brillo/streams/file_stream.h>
 
 #include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/file_descriptor.h"
 
 using brillo::data_encoding::Base64Encode;
 using std::string;
@@ -59,18 +64,19 @@
     return;
   }
   install_plan_.Dump();
-
   StartPartitionHashing();
   abort_action_completer.set_should_complete(false);
 }
 
 void FilesystemVerifierAction::TerminateProcessing() {
+  brillo::MessageLoop::current()->CancelTask(pending_task_id_);
   cancelled_ = true;
   Cleanup(ErrorCode::kSuccess);  // error code is ignored if canceled_ is true.
 }
 
 void FilesystemVerifierAction::Cleanup(ErrorCode code) {
-  src_stream_.reset();
+  read_fd_.reset();
+  write_fd_.reset();
   // This memory is not used anymore.
   buffer_.clear();
 
@@ -88,6 +94,43 @@
   }
 }
 
+bool FilesystemVerifierAction::InitializeFdVABC() {
+  const InstallPlan::Partition& partition =
+      install_plan_.partitions[partition_index_];
+
+  read_fd_ = dynamic_control_->OpenCowReader(
+      partition.name, partition.source_path, true);
+  if (!read_fd_) {
+    LOG(ERROR) << "OpenCowReader(" << partition.name << ", "
+               << partition.source_path << ") failed.";
+    return false;
+  }
+  partition_size_ = partition.target_size;
+  // TODO(b/173432386): Support Verity writes for VABC.
+  CHECK_EQ(partition.fec_size, 0U);
+  CHECK_EQ(partition.hash_tree_size, 0U);
+  return true;
+}
+
+bool FilesystemVerifierAction::InitializeFd(const std::string& part_path) {
+  read_fd_ = FileDescriptorPtr(new EintrSafeFileDescriptor());
+  if (!read_fd_->Open(part_path.c_str(), O_RDONLY)) {
+    LOG(ERROR) << "Unable to open " << part_path << " for reading.";
+    return false;
+  }
+
+  // Can't re-use |read_fd_|, as verity writer may call `seek` to modify state
+  // of a file descriptor.
+  if (ShouldWriteVerity()) {
+    write_fd_ = FileDescriptorPtr(new EintrSafeFileDescriptor());
+    if (!write_fd_->Open(part_path.c_str(), O_RDWR)) {
+      LOG(ERROR) << "Unable to open " << part_path << " for Read/Write.";
+      return false;
+    }
+  }
+  return true;
+}
+
 void FilesystemVerifierAction::StartPartitionHashing() {
   if (partition_index_ == install_plan_.partitions.size()) {
     if (!install_plan_.untouched_dynamic_partitions.empty()) {
@@ -109,7 +152,6 @@
   }
   const InstallPlan::Partition& partition =
       install_plan_.partitions[partition_index_];
-
   string part_path;
   switch (verifier_step_) {
     case VerifierStep::kVerifySourceHash:
@@ -122,44 +164,40 @@
       break;
   }
 
-  if (part_path.empty()) {
-    if (partition_size_ == 0) {
-      LOG(INFO) << "Skip hashing partition " << partition_index_ << " ("
-                << partition.name << ") because size is 0.";
-      partition_index_++;
-      StartPartitionHashing();
-      return;
-    }
-    LOG(ERROR) << "Cannot hash partition " << partition_index_ << " ("
-               << partition.name
-               << ") because its device path cannot be determined.";
-    Cleanup(ErrorCode::kFilesystemVerifierError);
-    return;
-  }
-
   LOG(INFO) << "Hashing partition " << partition_index_ << " ("
             << partition.name << ") on device " << part_path;
-
-  brillo::ErrorPtr error;
-  src_stream_ =
-      brillo::FileStream::Open(base::FilePath(part_path),
-                               brillo::Stream::AccessMode::READ,
-                               brillo::FileStream::Disposition::OPEN_EXISTING,
-                               &error);
-
-  if (!src_stream_) {
-    LOG(ERROR) << "Unable to open " << part_path << " for reading";
+  auto success = false;
+  if (dynamic_control_->GetVirtualAbCompressionFeatureFlag().IsEnabled() &&
+      dynamic_control_->IsDynamicPartition(partition.name) &&
+      verifier_step_ == VerifierStep::kVerifyTargetHash) {
+    success = InitializeFdVABC();
+  } else {
+    if (part_path.empty()) {
+      if (partition_size_ == 0) {
+        LOG(INFO) << "Skip hashing partition " << partition_index_ << " ("
+                  << partition.name << ") because size is 0.";
+        partition_index_++;
+        StartPartitionHashing();
+        return;
+      }
+      LOG(ERROR) << "Cannot hash partition " << partition_index_ << " ("
+                 << partition.name
+                 << ") because its device path cannot be determined.";
+      Cleanup(ErrorCode::kFilesystemVerifierError);
+      return;
+    }
+    success = InitializeFd(part_path);
+  }
+  if (!success) {
     Cleanup(ErrorCode::kFilesystemVerifierError);
     return;
   }
-
   buffer_.resize(kReadFileBufferSize);
   hasher_ = std::make_unique<HashCalculator>();
 
   offset_ = 0;
-  if (verifier_step_ == VerifierStep::kVerifyTargetHash &&
-      install_plan_.write_verity) {
-    if (!verity_writer_->Init(partition)) {
+  if (ShouldWriteVerity()) {
+    if (!verity_writer_->Init(partition, read_fd_, write_fd_)) {
       Cleanup(ErrorCode::kVerityCalculationError);
       return;
     }
@@ -169,6 +207,14 @@
   ScheduleRead();
 }
 
+bool FilesystemVerifierAction::ShouldWriteVerity() {
+  const InstallPlan::Partition& partition =
+      install_plan_.partitions[partition_index_];
+  return verifier_step_ == VerifierStep::kVerifyTargetHash &&
+         install_plan_.write_verity &&
+         (partition.hash_tree_size > 0 || partition.fec_size > 0);
+}
+
 void FilesystemVerifierAction::ScheduleRead() {
   const InstallPlan::Partition& partition =
       install_plan_.partitions[partition_index_];
@@ -190,22 +236,21 @@
     return;
   }
 
-  bool read_async_ok = src_stream_->ReadAsync(
-      buffer_.data(),
-      bytes_to_read,
-      base::Bind(&FilesystemVerifierAction::OnReadDoneCallback,
-                 base::Unretained(this)),
-      base::Bind(&FilesystemVerifierAction::OnReadErrorCallback,
-                 base::Unretained(this)),
-      nullptr);
-
-  if (!read_async_ok) {
+  auto bytes_read = read_fd_->Read(buffer_.data(), bytes_to_read);
+  if (bytes_read < 0) {
     LOG(ERROR) << "Unable to schedule an asynchronous read from the stream.";
     Cleanup(ErrorCode::kError);
+  } else {
+    // We could just invoke |OnReadDoneCallback()|, it works. But |PostTask|
+    // is used so that users can cancel updates.
+    pending_task_id_ = brillo::MessageLoop::current()->PostTask(
+        base::Bind(&FilesystemVerifierAction::OnReadDone,
+                   base::Unretained(this),
+                   bytes_read));
   }
 }
 
-void FilesystemVerifierAction::OnReadDoneCallback(size_t bytes_read) {
+void FilesystemVerifierAction::OnReadDone(size_t bytes_read) {
   if (cancelled_) {
     Cleanup(ErrorCode::kError);
     return;
@@ -231,8 +276,7 @@
   UpdateProgress(
       (static_cast<double>(offset_) / partition_size_ + partition_index_) /
       install_plan_.partitions.size());
-  if (verifier_step_ == VerifierStep::kVerifyTargetHash &&
-      install_plan_.write_verity) {
+  if (ShouldWriteVerity()) {
     if (!verity_writer_->Update(offset_, buffer_.data(), bytes_read)) {
       Cleanup(ErrorCode::kVerityCalculationError);
       return;
@@ -249,12 +293,6 @@
   ScheduleRead();
 }
 
-void FilesystemVerifierAction::OnReadErrorCallback(const brillo::Error* error) {
-  // TODO(deymo): Transform the read-error into an specific ErrorCode.
-  LOG(ERROR) << "Asynchronous read failed.";
-  Cleanup(ErrorCode::kError);
-}
-
 void FilesystemVerifierAction::FinishPartitionHashing() {
   if (!hasher_->Finalize()) {
     LOG(ERROR) << "Unable to finalize the hash.";
@@ -278,8 +316,8 @@
         }
         // If we have not verified source partition yet, now that the target
         // partition does not match, and it's not a full payload, we need to
-        // switch to kVerifySourceHash step to check if it's because the source
-        // partition does not match either.
+        // switch to kVerifySourceHash step to check if it's because the
+        // source partition does not match either.
         verifier_step_ = VerifierStep::kVerifySourceHash;
       } else {
         partition_index_++;
@@ -315,17 +353,22 @@
       }
       // The action will skip kVerifySourceHash step if target partition hash
       // matches, if we are in this step, it means target hash does not match,
-      // and now that the source partition hash matches, we should set the error
-      // code to reflect the error in target partition.
-      // We only need to verify the source partition which the target hash does
-      // not match, the rest of the partitions don't matter.
+      // and now that the source partition hash matches, we should set the
+      // error code to reflect the error in target partition. We only need to
+      // verify the source partition which the target hash does not match, the
+      // rest of the partitions don't matter.
       Cleanup(ErrorCode::kNewRootfsVerificationError);
       return;
   }
   // Start hashing the next partition, if any.
   hasher_.reset();
   buffer_.clear();
-  src_stream_->CloseBlocking(nullptr);
+  if (read_fd_) {
+    read_fd_.reset();
+  }
+  if (write_fd_) {
+    write_fd_.reset();
+  }
   StartPartitionHashing();
 }
 
diff --git a/payload_consumer/filesystem_verifier_action.h b/payload_consumer/filesystem_verifier_action.h
index 6a8823a..b6df4b8 100644
--- a/payload_consumer/filesystem_verifier_action.h
+++ b/payload_consumer/filesystem_verifier_action.h
@@ -24,10 +24,11 @@
 #include <string>
 #include <vector>
 
-#include <brillo/streams/stream.h>
+#include <brillo/message_loops/message_loop.h>
 
 #include "update_engine/common/action.h"
 #include "update_engine/common/hash_calculator.h"
+#include "update_engine/payload_consumer/file_descriptor.h"
 #include "update_engine/payload_consumer/install_plan.h"
 #include "update_engine/payload_consumer/verity_writer_interface.h"
 
@@ -83,6 +84,9 @@
 
  private:
   friend class FilesystemVerifierActionTestDelegate;
+
+  // Return true if we need to write verity bytes.
+  bool ShouldWriteVerity();
   // Starts the hashing of the current partition. If there aren't any partitions
   // remaining to be hashed, it finishes the action.
   void StartPartitionHashing();
@@ -92,8 +96,7 @@
 
   // Called from the main loop when a single read from |src_stream_| succeeds or
   // fails, calling OnReadDoneCallback() and OnReadErrorCallback() respectively.
-  void OnReadDoneCallback(size_t bytes_read);
-  void OnReadErrorCallback(const brillo::Error* error);
+  void OnReadDone(size_t bytes_read);
 
   // When the read is done, finalize the hash checking of the current partition
   // and continue checking the next one.
@@ -107,6 +110,10 @@
   // Invoke delegate callback to report progress, if delegate is not null
   void UpdateProgress(double progress);
 
+  // Initialize read_fd_ and write_fd_
+  bool InitializeFd(const std::string& part_path);
+  bool InitializeFdVABC();
+
   // The type of the partition that we are verifying.
   VerifierStep verifier_step_ = VerifierStep::kVerifyTargetHash;
 
@@ -114,8 +121,15 @@
   // being hashed.
   size_t partition_index_{0};
 
-  // If not null, the FileStream used to read from the device.
-  brillo::StreamPtr src_stream_;
+  // If not null, the FileDescriptor used to read from the device.
+  // |read_fd_| and |write_fd_| will be initialized when we begin hashing a
+  // partition. They will be deallocated once we encounter an error or
+  // successfully finished hashing.
+  FileDescriptorPtr read_fd_;
+  // If not null, the FileDescriptor used to write to the device.
+  // For VABC, this will be different from |read_fd_|. For other cases
+  // this can be the same as |read_fd_|.
+  FileDescriptorPtr write_fd_;
 
   // Buffer for storing data we read.
   brillo::Blob buffer_;
@@ -144,6 +158,11 @@
   // An observer that observes progress updates of this action.
   FilesystemVerifyDelegate* delegate_{};
 
+  // Callback that should be cancelled on |TerminateProcessing|. Usually this
+  // points to pending read callbacks from async stream.
+  brillo::MessageLoop::TaskId pending_task_id_{
+      brillo::MessageLoop::kTaskIdNull};
+
   DISALLOW_COPY_AND_ASSIGN(FilesystemVerifierAction);
 };
 
diff --git a/payload_consumer/filesystem_verifier_action_unittest.cc b/payload_consumer/filesystem_verifier_action_unittest.cc
index 2971849..925fdab 100644
--- a/payload_consumer/filesystem_verifier_action_unittest.cc
+++ b/payload_consumer/filesystem_verifier_action_unittest.cc
@@ -72,7 +72,7 @@
     if (action->Type() == FilesystemVerifierAction::StaticType()) {
       ran_ = true;
       code_ = code;
-      EXPECT_FALSE(static_cast<FilesystemVerifierAction*>(action)->src_stream_);
+      EXPECT_FALSE(static_cast<FilesystemVerifierAction*>(action)->read_fd_);
     } else if (action->Type() ==
                ObjectCollectorAction<InstallPlan>::StaticType()) {
       auto collector_action =
@@ -92,7 +92,7 @@
 
 bool FilesystemVerifierActionTest::DoTest(bool terminate_early,
                                           bool hash_fail) {
-  test_utils::ScopedTempFile a_loop_file("a_loop_file.XXXXXX");
+  ScopedTempFile a_loop_file("a_loop_file.XXXXXX");
 
   // Make random data for a.
   const size_t kLoopFileSize = 10 * 1024 * 1024 + 512;
@@ -278,7 +278,7 @@
 
 #ifdef __ANDROID__
 TEST_F(FilesystemVerifierActionTest, RunAsRootWriteVerityTest) {
-  test_utils::ScopedTempFile part_file("part_file.XXXXXX");
+  ScopedTempFile part_file("part_file.XXXXXX");
   constexpr size_t filesystem_size = 200 * 4096;
   constexpr size_t part_size = 256 * 4096;
   brillo::Blob part_data(filesystem_size, 0x1);
@@ -340,7 +340,7 @@
 #endif  // __ANDROID__
 
 TEST_F(FilesystemVerifierActionTest, RunAsRootSkipWriteVerityTest) {
-  test_utils::ScopedTempFile part_file("part_file.XXXXXX");
+  ScopedTempFile part_file("part_file.XXXXXX");
   constexpr size_t filesystem_size = 200 * 4096;
   constexpr size_t part_size = 256 * 4096;
   brillo::Blob part_data(part_size);
@@ -384,4 +384,5 @@
   EXPECT_TRUE(delegate.ran());
   EXPECT_EQ(ErrorCode::kSuccess, delegate.code());
 }
+
 }  // namespace chromeos_update_engine
diff --git a/payload_consumer/install_plan.cc b/payload_consumer/install_plan.cc
index a313627..4a37836 100644
--- a/payload_consumer/install_plan.cc
+++ b/payload_consumer/install_plan.cc
@@ -82,11 +82,6 @@
   }
 
   string version_str = base::StringPrintf(", version: %s", version.c_str());
-  if (!system_version.empty()) {
-    version_str +=
-        base::StringPrintf(", system_version: %s", system_version.c_str());
-  }
-
   string url_str = download_url;
   if (base::StartsWith(
           url_str, "fd://", base::CompareCase::INSENSITIVE_ASCII)) {
@@ -98,8 +93,8 @@
             << version_str
             << ", source_slot: " << BootControlInterface::SlotName(source_slot)
             << ", target_slot: " << BootControlInterface::SlotName(target_slot)
-            << ", initial url: " << url_str << payloads_str
-            << partitions_str << ", hash_checks_mandatory: "
+            << ", initial url: " << url_str << payloads_str << partitions_str
+            << ", hash_checks_mandatory: "
             << utils::ToString(hash_checks_mandatory)
             << ", powerwash_required: " << utils::ToString(powerwash_required)
             << ", switch_slot_on_reboot: "
diff --git a/payload_consumer/install_plan.h b/payload_consumer/install_plan.h
index f04c650..ee1a72b 100644
--- a/payload_consumer/install_plan.h
+++ b/payload_consumer/install_plan.h
@@ -54,13 +54,11 @@
   bool is_resume{false};
   std::string download_url;  // url to download from
   std::string version;       // version we are installing.
-  // system version, if present and separate from version
-  std::string system_version;
 
   struct Payload {
     std::vector<std::string> payload_urls;  // URLs to download the payload
-    uint64_t size = 0;               // size of the payload
-    uint64_t metadata_size = 0;      // size of the metadata
+    uint64_t size = 0;                      // size of the payload
+    uint64_t metadata_size = 0;             // size of the metadata
     std::string metadata_signature;  // signature of the metadata in base64
     brillo::Blob hash;               // SHA256 hash of the payload
     InstallPayloadType type{InstallPayloadType::kUnknown};
diff --git a/mock_file_writer.h b/payload_consumer/mock_file_writer.h
similarity index 100%
rename from mock_file_writer.h
rename to payload_consumer/mock_file_writer.h
diff --git a/payload_consumer/mount_history.cc b/payload_consumer/mount_history.cc
index 43a75b3..1d2ec76 100644
--- a/payload_consumer/mount_history.cc
+++ b/payload_consumer/mount_history.cc
@@ -37,7 +37,7 @@
   brillo::Blob block0_buffer(kBlockSize);
   ssize_t bytes_read;
 
-  if (!utils::PReadAll(
+  if (!utils::ReadAll(
           blockdevice_fd, block0_buffer.data(), kBlockSize, 0, &bytes_read)) {
     LOG(WARNING) << "PReadAll failed";
     return;
diff --git a/payload_consumer/partition_update_generator_android.cc b/payload_consumer/partition_update_generator_android.cc
index d5d5313..4467182 100644
--- a/payload_consumer/partition_update_generator_android.cc
+++ b/payload_consumer/partition_update_generator_android.cc
@@ -32,10 +32,8 @@
 namespace chromeos_update_engine {
 
 PartitionUpdateGeneratorAndroid::PartitionUpdateGeneratorAndroid(
-    BootControlInterface* boot_control,
-    size_t block_size)
-    : boot_control_(boot_control),
-      block_size_(block_size) {}
+    BootControlInterface* boot_control, size_t block_size)
+    : boot_control_(boot_control), block_size_(block_size) {}
 
 bool PartitionUpdateGeneratorAndroid::
     GenerateOperationsForPartitionsNotInPayload(
@@ -43,6 +41,11 @@
         BootControlInterface::Slot target_slot,
         const std::set<std::string>& partitions_in_payload,
         std::vector<PartitionUpdate>* update_list) {
+#ifndef __ANDROID__
+  // Skip copying partitions for host verification.
+  return true;
+#endif
+
   auto ab_partitions = GetAbPartitionsOnDevice();
   if (ab_partitions.empty()) {
     LOG(ERROR) << "Failed to load static a/b partitions";
diff --git a/payload_consumer/partition_writer.cc b/payload_consumer/partition_writer.cc
index d47ebee..6f06dd2 100644
--- a/payload_consumer/partition_writer.cc
+++ b/payload_consumer/partition_writer.cc
@@ -253,27 +253,37 @@
   Close();
 }
 
+bool PartitionWriter::OpenSourcePartition(uint32_t source_slot,
+                                          bool source_may_exist) {
+  source_path_.clear();
+  if (!source_may_exist) {
+    return true;
+  }
+  if (install_part_.source_size > 0 && !install_part_.source_path.empty()) {
+    source_path_ = install_part_.source_path;
+    int err;
+    source_fd_ = OpenFile(source_path_.c_str(), O_RDONLY, false, &err);
+    if (source_fd_ == nullptr) {
+      LOG(ERROR) << "Unable to open source partition " << install_part_.name
+                 << " on slot " << BootControlInterface::SlotName(source_slot)
+                 << ", file " << source_path_;
+      return false;
+    }
+  }
+  return true;
+}
+
 bool PartitionWriter::Init(const InstallPlan* install_plan,
-                           bool source_may_exist) {
+                           bool source_may_exist,
+                           size_t next_op_index) {
   const PartitionUpdate& partition = partition_update_;
   uint32_t source_slot = install_plan->source_slot;
   uint32_t target_slot = install_plan->target_slot;
+  TEST_AND_RETURN_FALSE(OpenSourcePartition(source_slot, source_may_exist));
 
   // We shouldn't open the source partition in certain cases, e.g. some dynamic
   // partitions in delta payload, partitions included in the full payload for
   // partial updates. Use the source size as the indicator.
-  if (source_may_exist && install_part_.source_size > 0) {
-    source_path_ = install_part_.source_path;
-    int err;
-    source_fd_ = OpenFile(source_path_.c_str(), O_RDONLY, false, &err);
-    if (!source_fd_) {
-      LOG(ERROR) << "Unable to open source partition "
-                 << partition.partition_name() << " on slot "
-                 << BootControlInterface::SlotName(source_slot) << ", file "
-                 << source_path_;
-      return false;
-    }
-  }
 
   target_path_ = install_part_.target_path;
   int err;
@@ -308,7 +318,7 @@
                                               const void* data,
                                               size_t count) {
   // Setup the ExtentWriter stack based on the operation type.
-  std::unique_ptr<ExtentWriter> writer = std::make_unique<DirectExtentWriter>();
+  std::unique_ptr<ExtentWriter> writer = CreateBaseExtentWriter();
 
   if (operation.type() == InstallOperation::REPLACE_BZ) {
     writer.reset(new BzipExtentWriter(std::move(writer)));
@@ -320,7 +330,7 @@
       writer->Init(target_fd_, operation.dst_extents(), block_size_));
   TEST_AND_RETURN_FALSE(writer->Write(data, operation.data_length()));
 
-  return target_fd_->Flush();
+  return true;
 }
 
 bool PartitionWriter::PerformZeroOrDiscardOperation(
@@ -349,11 +359,11 @@
     for (uint64_t offset = 0; offset < length; offset += zeros.size()) {
       uint64_t chunk_length =
           std::min(length - offset, static_cast<uint64_t>(zeros.size()));
-      TEST_AND_RETURN_FALSE(utils::PWriteAll(
+      TEST_AND_RETURN_FALSE(utils::WriteAll(
           target_fd_, zeros.data(), chunk_length, start + offset));
     }
   }
-  return target_fd_->Flush();
+  return true;
 }
 
 bool PartitionWriter::PerformSourceCopyOperation(
@@ -464,8 +474,9 @@
                                                        block_size_,
                                                        nullptr));
   }
-  return target_fd_->Flush();
+  return true;
 }
+
 bool PartitionWriter::PerformSourceBsdiffOperation(
     const InstallOperation& operation,
     ErrorCode* error,
@@ -481,7 +492,7 @@
       std::move(reader),
       utils::BlocksInExtents(operation.src_extents()) * block_size_);
 
-  auto writer = std::make_unique<DirectExtentWriter>();
+  auto writer = CreateBaseExtentWriter();
   TEST_AND_RETURN_FALSE(
       writer->Init(target_fd_, operation.dst_extents(), block_size_));
   auto dst_file = std::make_unique<BsdiffExtentFile>(
@@ -492,7 +503,7 @@
                                         std::move(dst_file),
                                         reinterpret_cast<const uint8_t*>(data),
                                         count) == 0);
-  return target_fd_->Flush();
+  return true;
 }
 
 bool PartitionWriter::PerformPuffDiffOperation(
@@ -510,7 +521,7 @@
       std::move(reader),
       utils::BlocksInExtents(operation.src_extents()) * block_size_));
 
-  auto writer = std::make_unique<DirectExtentWriter>();
+  auto writer = CreateBaseExtentWriter();
   TEST_AND_RETURN_FALSE(
       writer->Init(target_fd_, operation.dst_extents(), block_size_));
   puffin::UniqueStreamPtr dst_stream(new PuffinExtentStream(
@@ -524,7 +535,7 @@
                         reinterpret_cast<const uint8_t*>(data),
                         count,
                         kMaxCacheSize));
-  return target_fd_->Flush();
+  return true;
 }
 
 FileDescriptorPtr PartitionWriter::ChooseSourceFD(
@@ -641,4 +652,13 @@
   source_ecc_open_failure_ = false;
   return -err;
 }
+
+void PartitionWriter::CheckpointUpdateProgress(size_t next_op_index) {
+  target_fd_->Flush();
+}
+
+std::unique_ptr<ExtentWriter> PartitionWriter::CreateBaseExtentWriter() {
+  return std::make_unique<DirectExtentWriter>();
+}
+
 }  // namespace chromeos_update_engine
diff --git a/payload_consumer/partition_writer.h b/payload_consumer/partition_writer.h
index 624a411..4b420d2 100644
--- a/payload_consumer/partition_writer.h
+++ b/payload_consumer/partition_writer.h
@@ -18,12 +18,14 @@
 #define UPDATE_ENGINE_PARTITION_WRITER_H_
 
 #include <cstdint>
+#include <memory>
 #include <string>
 
 #include <brillo/secure_blob.h>
 #include <gtest/gtest_prod.h>
 
 #include "update_engine/common/dynamic_partition_control_interface.h"
+#include "update_engine/payload_consumer/extent_writer.h"
 #include "update_engine/payload_consumer/file_descriptor.h"
 #include "update_engine/payload_consumer/install_plan.h"
 #include "update_engine/update_metadata.pb.h"
@@ -35,7 +37,7 @@
                   DynamicPartitionControlInterface* dynamic_control,
                   size_t block_size,
                   bool is_interactive);
-  ~PartitionWriter();
+  virtual ~PartitionWriter();
   static bool ValidateSourceHash(const brillo::Blob& calculated_hash,
                                  const InstallOperation& operation,
                                  const FileDescriptorPtr source_fd,
@@ -43,36 +45,54 @@
 
   // Perform necessary initialization work before InstallOperation can be
   // applied to this partition
-  [[nodiscard]] bool Init(const InstallPlan* install_plan,
-                          bool source_may_exist);
+  [[nodiscard]] virtual bool Init(const InstallPlan* install_plan,
+                                  bool source_may_exist,
+                                  size_t next_op_index);
 
+  // |CheckpointUpdateProgress| will be called after SetNextOpIndex(), but it's
+  // optional. DeltaPerformer may or may not call this everytime an operation is
+  // applied.
+  //   |next_op_index| is index of next operation that should be applied.
+  // |next_op_index-1| is the last operation that is already applied.
+  virtual void CheckpointUpdateProgress(size_t next_op_index);
+
+  // Close partition writer, when calling this function there's no guarantee
+  // that all |InstallOperations| are sent to |PartitionWriter|. This function
+  // will be called even if we are pausing/aborting the update.
   int Close();
 
   // These perform a specific type of operation and return true on success.
   // |error| will be set if source hash mismatch, otherwise |error| might not be
   // set even if it fails.
-  [[nodiscard]] bool PerformReplaceOperation(const InstallOperation& operation,
-                                             const void* data,
-                                             size_t count);
-  [[nodiscard]] bool PerformZeroOrDiscardOperation(
+  [[nodiscard]] virtual bool PerformReplaceOperation(
+      const InstallOperation& operation, const void* data, size_t count);
+  [[nodiscard]] virtual bool PerformZeroOrDiscardOperation(
       const InstallOperation& operation);
 
-  [[nodiscard]] bool PerformSourceCopyOperation(
+  [[nodiscard]] virtual bool PerformSourceCopyOperation(
       const InstallOperation& operation, ErrorCode* error);
-  [[nodiscard]] bool PerformSourceBsdiffOperation(
+  [[nodiscard]] virtual bool PerformSourceBsdiffOperation(
       const InstallOperation& operation,
       ErrorCode* error,
       const void* data,
       size_t count);
-  [[nodiscard]] bool PerformPuffDiffOperation(const InstallOperation& operation,
-                                              ErrorCode* error,
-                                              const void* data,
-                                              size_t count);
+  [[nodiscard]] virtual bool PerformPuffDiffOperation(
+      const InstallOperation& operation,
+      ErrorCode* error,
+      const void* data,
+      size_t count);
 
- private:
+  // |DeltaPerformer| calls this when all Install Ops are sent to partition
+  // writer. No |Perform*Operation| methods will be called in the future, and
+  // the partition writer is expected to be closed soon.
+  [[nodiscard]] virtual bool FinishedInstallOps() { return true; }
+
+ protected:
   friend class PartitionWriterTest;
   FRIEND_TEST(PartitionWriterTest, ChooseSourceFDTest);
 
+  bool OpenSourcePartition(uint32_t source_slot, bool source_may_exist);
+
   bool OpenCurrentECCPartition();
   // For a given operation, choose the source fd to be used (raw device or error
   // correction device) based on the source operation hash.
@@ -80,6 +100,7 @@
   // the |error| accordingly.
   FileDescriptorPtr ChooseSourceFD(const InstallOperation& operation,
                                    ErrorCode* error);
+  [[nodiscard]] virtual std::unique_ptr<ExtentWriter> CreateBaseExtentWriter();
 
   const PartitionUpdate& partition_update_;
   const InstallPlan::Partition& install_part_;
@@ -108,6 +129,18 @@
   // error corrected.
   bool source_ecc_open_failure_{false};
 };
+
+namespace partition_writer {
+// Return a PartitionWriter instance for perform InstallOps on this partition.
+// Uses VABCPartitionWriter for Virtual AB Compression
+std::unique_ptr<PartitionWriter> CreatePartitionWriter(
+    const PartitionUpdate& partition_update,
+    const InstallPlan::Partition& install_part,
+    DynamicPartitionControlInterface* dynamic_control,
+    size_t block_size,
+    bool is_interactive,
+    bool is_dynamic_partition);
+}  // namespace partition_writer
 }  // namespace chromeos_update_engine
 
 #endif
diff --git a/payload_consumer/partition_writer_factory_android.cc b/payload_consumer/partition_writer_factory_android.cc
new file mode 100644
index 0000000..0c9f7ea
--- /dev/null
+++ b/payload_consumer/partition_writer_factory_android.cc
@@ -0,0 +1,54 @@
+//
+// 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 <cstddef>
+#include <memory>
+
+#include <base/logging.h>
+
+#include "update_engine/payload_consumer/vabc_partition_writer.h"
+
+namespace chromeos_update_engine::partition_writer {
+
+std::unique_ptr<PartitionWriter> CreatePartitionWriter(
+    const PartitionUpdate& partition_update,
+    const InstallPlan::Partition& install_part,
+    DynamicPartitionControlInterface* dynamic_control,
+    size_t block_size,
+    bool is_interactive,
+    bool is_dynamic_partition) {
+  if (dynamic_control &&
+      dynamic_control->GetVirtualAbCompressionFeatureFlag().IsEnabled() &&
+      is_dynamic_partition) {
+    LOG(INFO)
+        << "Virtual AB Compression Enabled, using VABC Partition Writer for `"
+        << install_part.name << '`';
+    return std::make_unique<VABCPartitionWriter>(partition_update,
+                                                 install_part,
+                                                 dynamic_control,
+                                                 block_size,
+                                                 is_interactive);
+  } else {
+    LOG(INFO) << "Virtual AB Compression disabled, using Partition Writer for `"
+              << install_part.name << '`';
+    return std::make_unique<PartitionWriter>(partition_update,
+                                             install_part,
+                                             dynamic_control,
+                                             block_size,
+                                             is_interactive);
+  }
+}
+}  // namespace chromeos_update_engine::partition_writer
diff --git a/payload_consumer/partition_writer_factory_chromeos.cc b/payload_consumer/partition_writer_factory_chromeos.cc
new file mode 100644
index 0000000..609f043
--- /dev/null
+++ b/payload_consumer/partition_writer_factory_chromeos.cc
@@ -0,0 +1,38 @@
+//
+// 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 <cstddef>
+#include <memory>
+
+#include <base/logging.h>
+
+#include "update_engine/payload_consumer/partition_writer.h"
+
+namespace chromeos_update_engine::partition_writer {
+std::unique_ptr<PartitionWriter> CreatePartitionWriter(
+    const PartitionUpdate& partition_update,
+    const InstallPlan::Partition& install_part,
+    DynamicPartitionControlInterface* dynamic_control,
+    size_t block_size,
+    bool is_interactive,
+    bool is_dynamic_partition) {
+  return std::make_unique<PartitionWriter>(partition_update,
+                                           install_part,
+                                           dynamic_control,
+                                           block_size,
+                                           is_interactive);
+}
+}  // namespace chromeos_update_engine::partition_writer
diff --git a/payload_consumer/partition_writer_unittest.cc b/payload_consumer/partition_writer_unittest.cc
index c1ff4f4..91e5e26 100644
--- a/payload_consumer/partition_writer_unittest.cc
+++ b/payload_consumer/partition_writer_unittest.cc
@@ -81,14 +81,14 @@
 
   brillo::Blob PerformSourceCopyOp(const InstallOperation& op,
                                    const brillo::Blob blob_data) {
-    test_utils::ScopedTempFile source_partition("Blob-XXXXXX");
+    ScopedTempFile source_partition("Blob-XXXXXX");
     DirectExtentWriter extent_writer;
     FileDescriptorPtr fd(new EintrSafeFileDescriptor());
     EXPECT_TRUE(fd->Open(source_partition.path().c_str(), O_RDWR));
     EXPECT_TRUE(extent_writer.Init(fd, op.src_extents(), kBlockSize));
     EXPECT_TRUE(extent_writer.Write(blob_data.data(), blob_data.size()));
 
-    test_utils::ScopedTempFile target_partition("Blob-XXXXXX");
+    ScopedTempFile target_partition("Blob-XXXXXX");
 
     install_part_.source_path = source_partition.path();
     install_part_.target_path = target_partition.path();
@@ -96,8 +96,9 @@
     install_part_.target_size = blob_data.size();
 
     ErrorCode error;
-    EXPECT_TRUE(writer_.Init(&install_plan_, true));
+    EXPECT_TRUE(writer_.Init(&install_plan_, true, 0));
     EXPECT_TRUE(writer_.PerformSourceCopyOperation(op, &error));
+    writer_.CheckpointUpdateProgress(1);
 
     brillo::Blob output_data;
     EXPECT_TRUE(utils::ReadFile(target_partition.path(), &output_data));
@@ -120,7 +121,7 @@
 // file descriptor when the size of the error corrected one is too small.
 TEST_F(PartitionWriterTest, ErrorCorrectionSourceCopyWhenNoHashFallbackTest) {
   constexpr size_t kCopyOperationSize = 4 * 4096;
-  test_utils::ScopedTempFile source("Source-XXXXXX");
+  ScopedTempFile source("Source-XXXXXX");
   // Setup the source path with the right expected data.
   brillo::Blob expected_data = FakeFileDescriptorData(kCopyOperationSize);
   EXPECT_TRUE(test_utils::WriteFileVector(source.path(), expected_data));
@@ -172,7 +173,7 @@
 
 TEST_F(PartitionWriterTest, ChooseSourceFDTest) {
   constexpr size_t kSourceSize = 4 * 4096;
-  test_utils::ScopedTempFile source("Source-XXXXXX");
+  ScopedTempFile source("Source-XXXXXX");
   // Write invalid data to the source image, which doesn't match the expected
   // hash.
   brillo::Blob invalid_data(kSourceSize, 0x55);
diff --git a/payload_consumer/payload_verifier.cc b/payload_consumer/payload_verifier.cc
index 24e337e..85902c8 100644
--- a/payload_consumer/payload_verifier.cc
+++ b/payload_consumer/payload_verifier.cc
@@ -203,7 +203,7 @@
   //
   // openssl rsautl -verify -pubin -inkey <(echo pem_public_key)
   //   -in |sig_data| -out |out_hash_data|
-  RSA* rsa = EVP_PKEY_get0_RSA(public_key);
+  RSA* rsa = EVP_PKEY_get0_RSA(const_cast<EVP_PKEY*>(public_key));
 
   TEST_AND_RETURN_FALSE(rsa != nullptr);
   unsigned int keysize = RSA_size(rsa);
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index e8fa81b..d51241f 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -56,16 +56,29 @@
 
 void PostinstallRunnerAction::PerformAction() {
   CHECK(HasInputObject());
+  CHECK(boot_control_);
   install_plan_ = GetInputObject();
 
+  auto dynamic_control = boot_control_->GetDynamicPartitionControl();
+  CHECK(dynamic_control);
+
+  // Mount snapshot partitions for Virtual AB Compression Compression.
+  if (dynamic_control->GetVirtualAbCompressionFeatureFlag().IsEnabled()) {
+    // Before calling MapAllPartitions to map snapshot devices, all CowWriters
+    // must be closed, and MapAllPartitions() should be called.
+    dynamic_control->UnmapAllPartitions();
+    if (!dynamic_control->MapAllPartitions()) {
+      return CompletePostinstall(ErrorCode::kPostInstallMountError);
+    }
+  }
+
   // We always powerwash when rolling back, however policy can determine
   // if this is a full/normal powerwash, or a special rollback powerwash
   // that retains a small amount of system state such as enrollment and
   // network configuration. In both cases all user accounts are deleted.
   if (install_plan_.powerwash_required || install_plan_.is_rollback) {
-    bool save_rollback_data =
-        install_plan_.is_rollback && install_plan_.rollback_data_save_requested;
-    if (hardware_->SchedulePowerwash(save_rollback_data)) {
+    if (hardware_->SchedulePowerwash(
+            install_plan_.rollback_data_save_requested)) {
       powerwash_scheduled_ = true;
     } else {
       return CompletePostinstall(ErrorCode::kPostinstallPowerwashError);
diff --git a/payload_consumer/postinstall_runner_action_unittest.cc b/payload_consumer/postinstall_runner_action_unittest.cc
index cf5158b..cce86e9 100644
--- a/payload_consumer/postinstall_runner_action_unittest.cc
+++ b/payload_consumer/postinstall_runner_action_unittest.cc
@@ -26,9 +26,14 @@
 
 #include <base/bind.h>
 #include <base/files/file_util.h>
+#if BASE_VER < 780000  // Android
 #include <base/message_loop/message_loop.h>
+#endif  // BASE_VER < 780000
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
+#if BASE_VER >= 780000  // CrOS
+#include <base/task/single_thread_task_executor.h>
+#endif  // BASE_VER >= 780000
 #include <brillo/message_loops/base_message_loop.h>
 #include <brillo/message_loops/message_loop_utils.h>
 #include <gmock/gmock.h>
@@ -40,7 +45,7 @@
 #include "update_engine/common/subprocess.h"
 #include "update_engine/common/test_utils.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/mock_payload_state.h"
+#include "update_engine/cros/mock_payload_state.h"
 
 using brillo::MessageLoop;
 using chromeos_update_engine::test_utils::ScopedLoopbackDeviceBinder;
@@ -156,8 +161,13 @@
   }
 
  protected:
+#if BASE_VER < 780000  // Android
   base::MessageLoopForIO base_loop_;
   brillo::BaseMessageLoop loop_{&base_loop_};
+#else   // CrOS
+  base::SingleThreadTaskExecutor base_loop_{base::MessagePumpType::IO};
+  brillo::BaseMessageLoop loop_{base_loop_.task_runner()};
+#endif  // BASE_VER < 780000
   brillo::AsynchronousSignalHandler async_signal_handler_;
   Subprocess subprocess_;
 
diff --git a/payload_consumer/snapshot_extent_writer.cc b/payload_consumer/snapshot_extent_writer.cc
new file mode 100644
index 0000000..5693c9b
--- /dev/null
+++ b/payload_consumer/snapshot_extent_writer.cc
@@ -0,0 +1,113 @@
+//
+// 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/payload_consumer/snapshot_extent_writer.h"
+
+#include <algorithm>
+#include <cstdint>
+
+#include <libsnapshot/cow_writer.h>
+
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+SnapshotExtentWriter::SnapshotExtentWriter(
+    android::snapshot::ICowWriter* cow_writer)
+    : cow_writer_(cow_writer) {
+  CHECK_NE(cow_writer, nullptr);
+}
+
+SnapshotExtentWriter::~SnapshotExtentWriter() {
+  CHECK(buffer_.empty()) << buffer_.size();
+}
+
+bool SnapshotExtentWriter::Init(
+    FileDescriptorPtr /*fd*/,
+    const google::protobuf::RepeatedPtrField<Extent>& extents,
+    uint32_t block_size) {
+  extents_ = extents;
+  cur_extent_idx_ = 0;
+  buffer_.clear();
+  buffer_.reserve(block_size);
+  block_size_ = block_size;
+  return true;
+}
+
+size_t SnapshotExtentWriter::ConsumeWithBuffer(const uint8_t* data,
+                                               size_t count) {
+  CHECK_LT(cur_extent_idx_, static_cast<size_t>(extents_.size()));
+  const auto& cur_extent = extents_[cur_extent_idx_];
+  const auto cur_extent_size = cur_extent.num_blocks() * block_size_;
+
+  if (buffer_.empty() && count >= cur_extent_size) {
+    TEST_AND_RETURN_FALSE(cow_writer_->AddRawBlocks(
+        cur_extent.start_block(), data, cur_extent_size));
+    if (!next_extent()) {
+      CHECK_EQ(count, cur_extent_size)
+          << "Exhausted all blocks, but still have " << count - cur_extent_size
+          << " bytes left";
+    }
+    return cur_extent_size;
+  }
+  CHECK_LT(buffer_.size(), cur_extent_size)
+      << "Data left in buffer should never be >= cur_extent_size, otherwise "
+         "we should have send that data to CowWriter. Buffer size: "
+      << buffer_.size() << " current extent size: " << cur_extent_size;
+  size_t bytes_to_copy =
+      std::min<size_t>(count, cur_extent_size - buffer_.size());
+  CHECK_GT(bytes_to_copy, 0U);
+
+  buffer_.insert(buffer_.end(), data, data + bytes_to_copy);
+  CHECK_LE(buffer_.size(), cur_extent_size);
+
+  if (buffer_.size() == cur_extent_size) {
+    TEST_AND_RETURN_FALSE(cow_writer_->AddRawBlocks(
+        cur_extent.start_block(), buffer_.data(), buffer_.size()));
+    buffer_.clear();
+    if (!next_extent()) {
+      CHECK_EQ(count, bytes_to_copy) << "Exhausted all blocks, but still have "
+                                     << count - bytes_to_copy << " bytes left";
+    }
+  }
+  return bytes_to_copy;
+}
+
+// Returns true on success.
+// This will construct a COW_REPLACE operation and forward it to CowWriter. It
+// is important that caller does not perform SOURCE_COPY operation on this
+// class, otherwise raw data will be stored. Caller should find ways to use
+// COW_COPY whenever possible.
+bool SnapshotExtentWriter::Write(const void* bytes, size_t count) {
+  if (count == 0) {
+    return true;
+  }
+  CHECK_NE(extents_.size(), 0);
+
+  auto data = static_cast<const uint8_t*>(bytes);
+  while (count > 0) {
+    auto bytes_written = ConsumeWithBuffer(data, count);
+    data += bytes_written;
+    count -= bytes_written;
+  }
+  return true;
+}
+
+bool SnapshotExtentWriter::next_extent() {
+  cur_extent_idx_++;
+  return cur_extent_idx_ < static_cast<size_t>(extents_.size());
+}
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/snapshot_extent_writer.h b/payload_consumer/snapshot_extent_writer.h
new file mode 100644
index 0000000..fb4b548
--- /dev/null
+++ b/payload_consumer/snapshot_extent_writer.h
@@ -0,0 +1,55 @@
+//
+// 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 <cstdint>
+#include <vector>
+
+#include <libsnapshot/cow_writer.h>
+
+#include "update_engine/payload_consumer/extent_writer.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+class SnapshotExtentWriter : public chromeos_update_engine::ExtentWriter {
+ public:
+  explicit SnapshotExtentWriter(android::snapshot::ICowWriter* cow_writer);
+  ~SnapshotExtentWriter();
+  // Returns true on success.
+  bool Init(FileDescriptorPtr fd,
+            const google::protobuf::RepeatedPtrField<Extent>& extents,
+            uint32_t block_size) override;
+  // Returns true on success.
+  // This will construct a COW_REPLACE operation and forward it to CowWriter. It
+  // is important that caller does not perform SOURCE_COPY operation on this
+  // class, otherwise raw data will be stored. Caller should find ways to use
+  // COW_COPY whenever possible.
+  bool Write(const void* bytes, size_t count) override;
+
+ private:
+  bool next_extent();
+  size_t ConsumeWithBuffer(const uint8_t* bytes, size_t count);
+  // It's a non-owning pointer, because PartitionWriter owns the CowWruter. This
+  // allows us to use a single instance of CowWriter for all operations applied
+  // to the same partition.
+  android::snapshot::ICowWriter* cow_writer_;
+  google::protobuf::RepeatedPtrField<Extent> extents_;
+  size_t cur_extent_idx_;
+  std::vector<uint8_t> buffer_;
+  size_t block_size_;
+};
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/snapshot_extent_writer_unittest.cc b/payload_consumer/snapshot_extent_writer_unittest.cc
new file mode 100644
index 0000000..0e22482
--- /dev/null
+++ b/payload_consumer/snapshot_extent_writer_unittest.cc
@@ -0,0 +1,180 @@
+//
+// 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 <array>
+#include <cstring>
+#include <map>
+#include <numeric>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <google/protobuf/message_lite.h>
+#include <libsnapshot/cow_writer.h>
+
+#include "update_engine/payload_consumer/snapshot_extent_writer.h"
+#include "update_engine/payload_generator/delta_diff_generator.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+class FakeCowWriter : public android::snapshot::ICowWriter {
+ public:
+  struct CowOp {
+    enum { COW_COPY, COW_REPLACE, COW_ZERO } type;
+    std::vector<unsigned char> data;
+    union {
+      size_t source_block;
+      size_t num_blocks;
+    };
+  };
+  using ICowWriter::ICowWriter;
+  ~FakeCowWriter() = default;
+
+  bool EmitCopy(uint64_t new_block, uint64_t old_block) override {
+    operations_[new_block] = {.type = CowOp::COW_COPY,
+                              .source_block = static_cast<size_t>(old_block)};
+    return true;
+  }
+  bool EmitRawBlocks(uint64_t new_block_start,
+                     const void* data,
+                     size_t size) override {
+    auto&& op = operations_[new_block_start];
+    const auto uint8_ptr = static_cast<const unsigned char*>(data);
+    op.data.insert(op.data.end(), uint8_ptr, uint8_ptr + size);
+    return true;
+  }
+  bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override {
+    operations_[new_block_start] = {.type = CowOp::COW_ZERO};
+    return true;
+  }
+  bool Finalize() override {
+    finalize_called_ = true;
+    return true;
+  }
+
+  bool EmitLabel(uint64_t label) {
+    label_count_++;
+    return true;
+  }
+
+  // Return number of bytes the cow image occupies on disk.
+  uint64_t GetCowSize() override {
+    return std::accumulate(
+        operations_.begin(), operations_.end(), 0, [](auto&& acc, auto&& op) {
+          return acc + op.second.data.size();
+        });
+  }
+  bool Contains(size_t block) {
+    return operations_.find(block) != operations_.end();
+  }
+  bool finalize_called_ = true;
+  size_t label_count_ = 0;
+  std::map<size_t, CowOp> operations_;
+};
+
+class SnapshotExtentWriterTest : public ::testing::Test {
+ public:
+  void SetUp() override {}
+
+ protected:
+  android::snapshot::CowOptions options_ = {
+      .block_size = static_cast<uint32_t>(kBlockSize)};
+  FakeCowWriter cow_writer_{options_};
+  SnapshotExtentWriter writer_{&cow_writer_};
+};
+
+void AddExtent(google::protobuf::RepeatedPtrField<Extent>* extents,
+               size_t start_block,
+               size_t num_blocks) {
+  auto&& extent = extents->Add();
+  extent->set_start_block(start_block);
+  extent->set_num_blocks(num_blocks);
+}
+
+TEST_F(SnapshotExtentWriterTest, BufferWrites) {
+  google::protobuf::RepeatedPtrField<Extent> extents;
+  AddExtent(&extents, 123, 1);
+  writer_.Init(nullptr, extents, kBlockSize);
+
+  std::vector<uint8_t> buf(kBlockSize, 0);
+  buf[123] = 231;
+  buf[231] = 123;
+  buf[buf.size() - 1] = 255;
+
+  writer_.Write(buf.data(), kBlockSize - 1);
+  ASSERT_TRUE(cow_writer_.operations_.empty())
+      << "Haven't send data of a complete block yet, CowWriter should not be "
+         "invoked.";
+  writer_.Write(buf.data() + kBlockSize - 1, 1);
+  ASSERT_TRUE(cow_writer_.Contains(123))
+      << "Once a block of data is sent to SnapshotExtentWriter, it should "
+         "forward data to cow_writer.";
+  ASSERT_EQ(cow_writer_.operations_.size(), 1U);
+  ASSERT_EQ(buf, cow_writer_.operations_[123].data);
+}
+
+TEST_F(SnapshotExtentWriterTest, NonBufferedWrites) {
+  google::protobuf::RepeatedPtrField<Extent> extents;
+  AddExtent(&extents, 123, 1);
+  AddExtent(&extents, 125, 1);
+  writer_.Init(nullptr, extents, kBlockSize);
+
+  std::vector<uint8_t> buf(kBlockSize * 2, 0);
+  buf[123] = 231;
+  buf[231] = 123;
+  buf[buf.size() - 1] = 255;
+
+  writer_.Write(buf.data(), buf.size());
+  ASSERT_TRUE(cow_writer_.Contains(123));
+  ASSERT_TRUE(cow_writer_.Contains(125));
+
+  ASSERT_EQ(cow_writer_.operations_.size(), 2U);
+  auto actual_data = cow_writer_.operations_[123].data;
+  actual_data.insert(actual_data.end(),
+                     cow_writer_.operations_[125].data.begin(),
+                     cow_writer_.operations_[125].data.end());
+  ASSERT_EQ(buf, actual_data);
+}
+
+TEST_F(SnapshotExtentWriterTest, WriteAcrossBlockBoundary) {
+  google::protobuf::RepeatedPtrField<Extent> extents;
+  AddExtent(&extents, 123, 1);
+  AddExtent(&extents, 125, 2);
+  writer_.Init(nullptr, extents, kBlockSize);
+
+  std::vector<uint8_t> buf(kBlockSize * 3);
+  std::memset(buf.data(), 0, buf.size());
+  buf[123] = 231;
+  buf[231] = 123;
+  buf[buf.size() - 1] = 255;
+  buf[kBlockSize - 1] = 254;
+
+  writer_.Write(buf.data(), kBlockSize - 1);
+  ASSERT_TRUE(cow_writer_.operations_.empty())
+      << "Haven't send data of a complete block yet, CowWriter should not be "
+         "invoked.";
+  writer_.Write(buf.data() + kBlockSize - 1, 1 + kBlockSize * 2);
+  ASSERT_TRUE(cow_writer_.Contains(123));
+  ASSERT_TRUE(cow_writer_.Contains(125));
+
+  ASSERT_EQ(cow_writer_.operations_.size(), 2U);
+  auto actual_data = cow_writer_.operations_[123].data;
+  actual_data.insert(actual_data.end(),
+                     cow_writer_.operations_[125].data.begin(),
+                     cow_writer_.operations_[125].data.end());
+  ASSERT_EQ(buf, actual_data);
+}
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/vabc_partition_writer.cc b/payload_consumer/vabc_partition_writer.cc
new file mode 100644
index 0000000..5cb7989
--- /dev/null
+++ b/payload_consumer/vabc_partition_writer.cc
@@ -0,0 +1,166 @@
+//
+// 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/payload_consumer/vabc_partition_writer.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <libsnapshot/cow_writer.h>
+
+#include "update_engine/common/cow_operation_convert.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/extent_writer.h"
+#include "update_engine/payload_consumer/file_descriptor.h"
+#include "update_engine/payload_consumer/install_plan.h"
+#include "update_engine/payload_consumer/partition_writer.h"
+#include "update_engine/payload_consumer/snapshot_extent_writer.h"
+
+namespace chromeos_update_engine {
+// Expected layout of COW file:
+// === Beginning of Cow Image ===
+// All Source Copy Operations
+// ========== Label 0 ==========
+// Operation 0 in PartitionUpdate
+// ========== Label 1 ==========
+// Operation 1 in PartitionUpdate
+// ========== label 2 ==========
+// Operation 2 in PartitionUpdate
+// ========== label 3 ==========
+// .
+// .
+// .
+
+// When resuming, pass |next_op_index_| as label to
+// |InitializeWithAppend|.
+// For example, suppose we finished writing SOURCE_COPY, and we finished writing
+// operation 2 completely. Update is suspended when we are half way through
+// operation 3.
+// |cnext_op_index_| would be 3, so we pass 3 as
+// label to |InitializeWithAppend|. The CowWriter will retain all data before
+// label 3, Which contains all operation 2's data, but none of operation 3's
+// data.
+
+bool VABCPartitionWriter::Init(const InstallPlan* install_plan,
+                               bool source_may_exist,
+                               size_t next_op_index) {
+  TEST_AND_RETURN_FALSE(install_plan != nullptr);
+  TEST_AND_RETURN_FALSE(
+      OpenSourcePartition(install_plan->source_slot, source_may_exist));
+  std::optional<std::string> source_path;
+  if (!install_part_.source_path.empty()) {
+    // TODO(zhangkelvin) Make |source_path| a std::optional<std::string>
+    source_path = install_part_.source_path;
+  }
+  cow_writer_ = dynamic_control_->OpenCowWriter(
+      install_part_.name, source_path, install_plan->is_resume);
+  TEST_AND_RETURN_FALSE(cow_writer_ != nullptr);
+
+  // ===== Resume case handling code goes here ====
+  // It is possible that the SOURCE_COPY are already written but
+  // |next_op_index_| is still 0. In this case we discard previously written
+  // SOURCE_COPY, and start over.
+  if (install_plan->is_resume && next_op_index > 0) {
+    LOG(INFO) << "Resuming update on partition `"
+              << partition_update_.partition_name() << "` op index "
+              << next_op_index;
+    TEST_AND_RETURN_FALSE(cow_writer_->InitializeAppend(next_op_index));
+    return true;
+  } else {
+    TEST_AND_RETURN_FALSE(cow_writer_->Initialize());
+  }
+
+  // ==============================================
+
+  // TODO(zhangkelvin) Rewrite this in C++20 coroutine once that's available.
+  auto converted = ConvertToCowOperations(partition_update_.operations(),
+                                          partition_update_.merge_operations());
+
+  WriteAllCowOps(block_size_, converted, cow_writer_.get(), source_fd_);
+  return true;
+}
+
+bool VABCPartitionWriter::WriteAllCowOps(
+    size_t block_size,
+    const std::vector<CowOperation>& converted,
+    android::snapshot::ICowWriter* cow_writer,
+    FileDescriptorPtr source_fd) {
+  std::vector<uint8_t> buffer(block_size);
+
+  for (const auto& cow_op : converted) {
+    switch (cow_op.op) {
+      case CowOperation::CowCopy:
+        TEST_AND_RETURN_FALSE(
+            cow_writer->AddCopy(cow_op.dst_block, cow_op.src_block));
+        break;
+      case CowOperation::CowReplace:
+        ssize_t bytes_read = 0;
+        TEST_AND_RETURN_FALSE(utils::ReadAll(source_fd,
+                                             buffer.data(),
+                                             block_size,
+                                             cow_op.src_block * block_size,
+                                             &bytes_read));
+        if (bytes_read <= 0 || static_cast<size_t>(bytes_read) != block_size) {
+          LOG(ERROR) << "source_fd->Read failed: " << bytes_read;
+          return false;
+        }
+        TEST_AND_RETURN_FALSE(cow_writer->AddRawBlocks(
+            cow_op.dst_block, buffer.data(), block_size));
+        break;
+    }
+  }
+
+  return true;
+}
+
+std::unique_ptr<ExtentWriter> VABCPartitionWriter::CreateBaseExtentWriter() {
+  return std::make_unique<SnapshotExtentWriter>(cow_writer_.get());
+}
+
+[[nodiscard]] bool VABCPartitionWriter::PerformZeroOrDiscardOperation(
+    const InstallOperation& operation) {
+  for (const auto& extent : operation.dst_extents()) {
+    TEST_AND_RETURN_FALSE(
+        cow_writer_->AddZeroBlocks(extent.start_block(), extent.num_blocks()));
+  }
+  return true;
+}
+
+[[nodiscard]] bool VABCPartitionWriter::PerformSourceCopyOperation(
+    const InstallOperation& operation, ErrorCode* error) {
+  // TODO(zhangkelvin) Probably just ignore SOURCE_COPY? They should be taken
+  // care of during Init();
+  return true;
+}
+
+void VABCPartitionWriter::CheckpointUpdateProgress(size_t next_op_index) {
+  // No need to call fsync/sync, as CowWriter flushes after a label is added
+  // added.
+  cow_writer_->AddLabel(next_op_index);
+}
+
+[[nodiscard]] bool VABCPartitionWriter::FinishedInstallOps() {
+  // Add a hardcoded magic label to indicate end of all install ops. This label
+  // is needed by filesystem verification, don't remove.
+  return cow_writer_->AddLabel(kEndOfInstallLabel);
+}
+
+VABCPartitionWriter::~VABCPartitionWriter() {
+  cow_writer_->Finalize();
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/vabc_partition_writer.h b/payload_consumer/vabc_partition_writer.h
new file mode 100644
index 0000000..7fb2a2c
--- /dev/null
+++ b/payload_consumer/vabc_partition_writer.h
@@ -0,0 +1,63 @@
+//
+// 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_VABC_PARTITION_WRITER_H_
+#define UPDATE_ENGINE_VABC_PARTITION_WRITER_H_
+
+#include <memory>
+#include <vector>
+
+#include <libsnapshot/snapshot_writer.h>
+
+#include "update_engine/common/cow_operation_convert.h"
+#include "update_engine/payload_consumer/install_plan.h"
+#include "update_engine/payload_consumer/partition_writer.h"
+
+namespace chromeos_update_engine {
+class VABCPartitionWriter final : public PartitionWriter {
+ public:
+  using PartitionWriter::PartitionWriter;
+  [[nodiscard]] bool Init(const InstallPlan* install_plan,
+                          bool source_may_exist,
+                          size_t next_op_index) override;
+  ~VABCPartitionWriter() override;
+
+  [[nodiscard]] std::unique_ptr<ExtentWriter> CreateBaseExtentWriter() override;
+
+  // Only ZERO and SOURCE_COPY InstallOperations are treated special by VABC
+  // Partition Writer. These operations correspond to COW_ZERO and COW_COPY. All
+  // other operations just get converted to COW_REPLACE.
+  [[nodiscard]] bool PerformZeroOrDiscardOperation(
+      const InstallOperation& operation) override;
+  [[nodiscard]] bool PerformSourceCopyOperation(
+      const InstallOperation& operation, ErrorCode* error) override;
+
+  void CheckpointUpdateProgress(size_t next_op_index) override;
+
+  static bool WriteAllCowOps(size_t block_size,
+                             const std::vector<CowOperation>& converted,
+                             android::snapshot::ICowWriter* cow_writer,
+                             FileDescriptorPtr source_fd);
+
+  [[nodiscard]] bool FinishedInstallOps() override;
+
+ private:
+  std::unique_ptr<android::snapshot::ISnapshotWriter> cow_writer_;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif
diff --git a/payload_consumer/verity_writer_android.cc b/payload_consumer/verity_writer_android.cc
index d5437b6..864d9a1 100644
--- a/payload_consumer/verity_writer_android.cc
+++ b/payload_consumer/verity_writer_android.cc
@@ -29,6 +29,7 @@
 }
 
 #include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/file_descriptor.h"
 
 namespace chromeos_update_engine {
 
@@ -39,7 +40,16 @@
 }  // namespace verity_writer
 
 bool VerityWriterAndroid::Init(const InstallPlan::Partition& partition) {
+  auto read_fd = FileDescriptorPtr(new EintrSafeFileDescriptor());
+  TEST_AND_RETURN_FALSE(read_fd->Open(partition.target_path.c_str(), O_RDWR));
+  return Init(partition, read_fd, read_fd);
+}
+bool VerityWriterAndroid::Init(const InstallPlan::Partition& partition,
+                               FileDescriptorPtr read_fd,
+                               FileDescriptorPtr write_fd) {
   partition_ = &partition;
+  read_fd_ = read_fd;
+  write_fd_ = write_fd;
 
   if (partition_->hash_tree_size != 0 || partition_->fec_size != 0) {
     utils::SetBlockDeviceReadOnly(partition_->target_path, false);
@@ -82,18 +92,18 @@
 
       if (end_offset == hash_tree_data_end) {
         // All hash tree data blocks has been hashed, write hash tree to disk.
-        int fd = HANDLE_EINTR(open(partition_->target_path.c_str(), O_WRONLY));
-        if (fd < 0) {
-          PLOG(ERROR) << "Failed to open " << partition_->target_path
-                      << " to write hash tree.";
-          return false;
-        }
-        ScopedFdCloser fd_closer(&fd);
-
         LOG(INFO) << "Writing verity hash tree to " << partition_->target_path;
         TEST_AND_RETURN_FALSE(hash_tree_builder_->BuildHashTree());
-        TEST_AND_RETURN_FALSE(hash_tree_builder_->WriteHashTreeToFd(
-            fd, partition_->hash_tree_offset));
+        TEST_AND_RETURN_FALSE_ERRNO(
+            write_fd_->Seek(partition_->hash_tree_offset, SEEK_SET));
+        auto success = hash_tree_builder_->WriteHashTree(
+            [write_fd_(this->write_fd_)](auto data, auto size) {
+              return utils::WriteAll(write_fd_, data, size);
+            });
+        // hashtree builder already prints error messages.
+        if (!success) {
+          return false;
+        }
         hash_tree_builder_.reset();
       }
     }
@@ -103,7 +113,8 @@
         partition_->fec_data_offset + partition_->fec_data_size;
     if (offset < fec_data_end && offset + size >= fec_data_end) {
       LOG(INFO) << "Writing verity FEC to " << partition_->target_path;
-      TEST_AND_RETURN_FALSE(EncodeFEC(partition_->target_path,
+      TEST_AND_RETURN_FALSE(EncodeFEC(read_fd_,
+                                      write_fd_,
                                       partition_->fec_data_offset,
                                       partition_->fec_data_size,
                                       partition_->fec_offset,
@@ -116,7 +127,8 @@
   return true;
 }
 
-bool VerityWriterAndroid::EncodeFEC(const std::string& path,
+bool VerityWriterAndroid::EncodeFEC(FileDescriptorPtr read_fd,
+                                    FileDescriptorPtr write_fd,
                                     uint64_t data_offset,
                                     uint64_t data_size,
                                     uint64_t fec_offset,
@@ -135,13 +147,6 @@
       init_rs_char(FEC_PARAMS(fec_roots)), &free_rs_char);
   TEST_AND_RETURN_FALSE(rs_char != nullptr);
 
-  int fd = HANDLE_EINTR(open(path.c_str(), verify_mode ? O_RDONLY : O_RDWR));
-  if (fd < 0) {
-    PLOG(ERROR) << "Failed to open " << path << " to write FEC.";
-    return false;
-  }
-  ScopedFdCloser fd_closer(&fd);
-
   for (size_t i = 0; i < rounds; i++) {
     // Encodes |block_size| number of rs blocks each round so that we can read
     // one block each time instead of 1 byte to increase random read
@@ -154,13 +159,13 @@
       // Don't read past |data_size|, treat them as 0.
       if (offset < data_size) {
         ssize_t bytes_read = 0;
-        TEST_AND_RETURN_FALSE(utils::PReadAll(fd,
+        TEST_AND_RETURN_FALSE(utils::PReadAll(read_fd,
                                               buffer.data(),
                                               buffer.size(),
                                               data_offset + offset,
                                               &bytes_read));
-        TEST_AND_RETURN_FALSE(bytes_read ==
-                              static_cast<ssize_t>(buffer.size()));
+        TEST_AND_RETURN_FALSE(bytes_read >= 0);
+        TEST_AND_RETURN_FALSE(static_cast<size_t>(bytes_read) == buffer.size());
       }
       for (size_t k = 0; k < buffer.size(); k++) {
         rs_blocks[k * rs_n + j] = buffer[k];
@@ -179,17 +184,42 @@
       brillo::Blob fec_read(fec.size());
       ssize_t bytes_read = 0;
       TEST_AND_RETURN_FALSE(utils::PReadAll(
-          fd, fec_read.data(), fec_read.size(), fec_offset, &bytes_read));
-      TEST_AND_RETURN_FALSE(bytes_read ==
-                            static_cast<ssize_t>(fec_read.size()));
+          read_fd, fec_read.data(), fec_read.size(), fec_offset, &bytes_read));
+      TEST_AND_RETURN_FALSE(bytes_read >= 0);
+      TEST_AND_RETURN_FALSE(static_cast<size_t>(bytes_read) == fec_read.size());
       TEST_AND_RETURN_FALSE(fec == fec_read);
     } else {
-      TEST_AND_RETURN_FALSE(
-          utils::PWriteAll(fd, fec.data(), fec.size(), fec_offset));
+      CHECK(write_fd);
+      if (!utils::PWriteAll(write_fd, fec.data(), fec.size(), fec_offset)) {
+        PLOG(ERROR) << "EncodeFEC write() failed";
+        return false;
+      }
     }
     fec_offset += fec.size();
   }
 
   return true;
 }
+
+bool VerityWriterAndroid::EncodeFEC(const std::string& path,
+                                    uint64_t data_offset,
+                                    uint64_t data_size,
+                                    uint64_t fec_offset,
+                                    uint64_t fec_size,
+                                    uint32_t fec_roots,
+                                    uint32_t block_size,
+                                    bool verify_mode) {
+  FileDescriptorPtr fd(new EintrSafeFileDescriptor());
+  TEST_AND_RETURN_FALSE(
+      fd->Open(path.c_str(), verify_mode ? O_RDONLY : O_RDWR));
+  return EncodeFEC(fd,
+                   fd,
+                   data_offset,
+                   data_size,
+                   fec_offset,
+                   fec_size,
+                   fec_roots,
+                   block_size,
+                   verify_mode);
+}
 }  // namespace chromeos_update_engine
diff --git a/payload_consumer/verity_writer_android.h b/payload_consumer/verity_writer_android.h
index 05a5856..7dfac0f 100644
--- a/payload_consumer/verity_writer_android.h
+++ b/payload_consumer/verity_writer_android.h
@@ -22,6 +22,7 @@
 
 #include <verity/hash_tree_builder.h>
 
+#include "payload_consumer/file_descriptor.h"
 #include "update_engine/payload_consumer/verity_writer_interface.h"
 
 namespace chromeos_update_engine {
@@ -31,7 +32,10 @@
   VerityWriterAndroid() = default;
   ~VerityWriterAndroid() override = default;
 
-  bool Init(const InstallPlan::Partition& partition) override;
+  bool Init(const InstallPlan::Partition& partition,
+            FileDescriptorPtr read_fd,
+            FileDescriptorPtr write_fd) override;
+  bool Init(const InstallPlan::Partition& partition);
   bool Update(uint64_t offset, const uint8_t* buffer, size_t size) override;
 
   // Read [data_offset : data_offset + data_size) from |path| and encode FEC
@@ -40,6 +44,15 @@
   // in each Update() like hash tree, because for every rs block, its data are
   // spreaded across entire |data_size|, unless we can cache all data in
   // memory, we have to re-read them from disk.
+  static bool EncodeFEC(FileDescriptorPtr read_fd,
+                        FileDescriptorPtr write_fd,
+                        uint64_t data_offset,
+                        uint64_t data_size,
+                        uint64_t fec_offset,
+                        uint64_t fec_size,
+                        uint32_t fec_roots,
+                        uint32_t block_size,
+                        bool verify_mode);
   static bool EncodeFEC(const std::string& path,
                         uint64_t data_offset,
                         uint64_t data_size,
@@ -52,6 +65,8 @@
  private:
   const InstallPlan::Partition* partition_ = nullptr;
 
+  FileDescriptorPtr read_fd_;
+  FileDescriptorPtr write_fd_;
   std::unique_ptr<HashTreeBuilder> hash_tree_builder_;
 
   DISALLOW_COPY_AND_ASSIGN(VerityWriterAndroid);
diff --git a/payload_consumer/verity_writer_android_unittest.cc b/payload_consumer/verity_writer_android_unittest.cc
index f943ce8..ec22ffb 100644
--- a/payload_consumer/verity_writer_android_unittest.cc
+++ b/payload_consumer/verity_writer_android_unittest.cc
@@ -39,7 +39,7 @@
 
   VerityWriterAndroid verity_writer_;
   InstallPlan::Partition partition_;
-  test_utils::ScopedTempFile temp_file_;
+  ScopedTempFile temp_file_;
 };
 
 TEST_F(VerityWriterAndroidTest, SimpleTest) {
diff --git a/payload_consumer/verity_writer_interface.h b/payload_consumer/verity_writer_interface.h
index a3ecef3..db7988e 100644
--- a/payload_consumer/verity_writer_interface.h
+++ b/payload_consumer/verity_writer_interface.h
@@ -22,6 +22,7 @@
 
 #include <base/macros.h>
 
+#include "payload_consumer/file_descriptor.h"
 #include "update_engine/payload_consumer/install_plan.h"
 
 namespace chromeos_update_engine {
@@ -30,6 +31,9 @@
  public:
   virtual ~VerityWriterInterface() = default;
 
+  virtual bool Init(const InstallPlan::Partition& partition,
+                    FileDescriptorPtr read_fd,
+                    FileDescriptorPtr write_fd) = 0;
   virtual bool Init(const InstallPlan::Partition& partition) = 0;
   // Update partition data at [offset : offset + size) stored in |buffer|.
   // Data not in |hash_tree_data_extent| or |fec_data_extent| is ignored.
diff --git a/payload_consumer/verity_writer_stub.cc b/payload_consumer/verity_writer_stub.cc
index a0e2467..314ec7e 100644
--- a/payload_consumer/verity_writer_stub.cc
+++ b/payload_consumer/verity_writer_stub.cc
@@ -26,7 +26,9 @@
 }
 }  // namespace verity_writer
 
-bool VerityWriterStub::Init(const InstallPlan::Partition& partition) {
+bool VerityWriterStub::Init(const InstallPlan::Partition& partition,
+                            FileDescriptorPtr read_fd,
+                            FileDescriptorPtr write_fd) {
   return partition.hash_tree_size == 0 && partition.fec_size == 0;
 }
 
diff --git a/payload_consumer/verity_writer_stub.h b/payload_consumer/verity_writer_stub.h
index ea5e574..f8d68ca 100644
--- a/payload_consumer/verity_writer_stub.h
+++ b/payload_consumer/verity_writer_stub.h
@@ -26,7 +26,9 @@
   VerityWriterStub() = default;
   ~VerityWriterStub() override = default;
 
-  bool Init(const InstallPlan::Partition& partition) override;
+  bool Init(const InstallPlan::Partition& partition,
+            FileDescriptorPtr read_fd,
+            FileDescriptorPtr write_fd) override;
   bool Update(uint64_t offset, const uint8_t* buffer, size_t size) override;
 
  private:
diff --git a/payload_generator/ab_generator_unittest.cc b/payload_generator/ab_generator_unittest.cc
index 7a95284..84eeb77 100644
--- a/payload_generator/ab_generator_unittest.cc
+++ b/payload_generator/ab_generator_unittest.cc
@@ -70,8 +70,7 @@
       part_data.push_back(dis(gen));
   }
   ASSERT_EQ(part_size, part_data.size());
-  test_utils::ScopedTempFile part_file(
-      "SplitReplaceOrReplaceXzTest_part.XXXXXX");
+  ScopedTempFile part_file("SplitReplaceOrReplaceXzTest_part.XXXXXX");
   ASSERT_TRUE(test_utils::WriteFileVector(part_file.path(), part_data));
 
   // Create original operation and blob data.
@@ -107,8 +106,7 @@
   aop.name = "SplitTestOp";
 
   // Create the data file.
-  test_utils::ScopedTempFile data_file(
-      "SplitReplaceOrReplaceXzTest_data.XXXXXX");
+  ScopedTempFile data_file("SplitReplaceOrReplaceXzTest_data.XXXXXX");
   EXPECT_TRUE(test_utils::WriteFileVector(data_file.path(), op_blob));
   int data_fd = open(data_file.path().c_str(), O_RDWR, 000);
   EXPECT_GE(data_fd, 0);
@@ -220,8 +218,7 @@
       part_data.push_back(dis(gen));
   }
   ASSERT_EQ(part_size, part_data.size());
-  test_utils::ScopedTempFile part_file(
-      "MergeReplaceOrReplaceXzTest_part.XXXXXX");
+  ScopedTempFile part_file("MergeReplaceOrReplaceXzTest_part.XXXXXX");
   ASSERT_TRUE(test_utils::WriteFileVector(part_file.path(), part_data));
 
   // Create original operations and blob data.
@@ -271,8 +268,7 @@
   aops.push_back(second_aop);
 
   // Create the data file.
-  test_utils::ScopedTempFile data_file(
-      "MergeReplaceOrReplaceXzTest_data.XXXXXX");
+  ScopedTempFile data_file("MergeReplaceOrReplaceXzTest_data.XXXXXX");
   EXPECT_TRUE(test_utils::WriteFileVector(data_file.path(), blob_data));
   int data_fd = open(data_file.path().c_str(), O_RDWR, 000);
   EXPECT_GE(data_fd, 0);
@@ -561,7 +557,7 @@
   second_aop.op = second_op;
   aops.push_back(second_aop);
 
-  test_utils::ScopedTempFile src_part_file("AddSourceHashTest_src_part.XXXXXX");
+  ScopedTempFile src_part_file("AddSourceHashTest_src_part.XXXXXX");
   brillo::Blob src_data(kBlockSize);
   test_utils::FillWithData(&src_data);
   ASSERT_TRUE(test_utils::WriteFileVector(src_part_file.path(), src_data));
diff --git a/payload_generator/blob_file_writer_unittest.cc b/payload_generator/blob_file_writer_unittest.cc
index 487bc73..f4dcafb 100644
--- a/payload_generator/blob_file_writer_unittest.cc
+++ b/payload_generator/blob_file_writer_unittest.cc
@@ -31,24 +31,21 @@
 class BlobFileWriterTest : public ::testing::Test {};
 
 TEST(BlobFileWriterTest, SimpleTest) {
-  string blob_path;
-  int blob_fd;
-  EXPECT_TRUE(
-      utils::MakeTempFile("BlobFileWriterTest.XXXXXX", &blob_path, &blob_fd));
+  ScopedTempFile blob_file("BlobFileWriterTest.XXXXXX", true);
   off_t blob_file_size = 0;
-  BlobFileWriter blob_file(blob_fd, &blob_file_size);
+  BlobFileWriter blob_file_writer(blob_file.fd(), &blob_file_size);
 
-  off_t blob_size = 1024;
-  brillo::Blob blob(blob_size);
+  const off_t kBlobSize = 1024;
+  brillo::Blob blob(kBlobSize);
   FillWithData(&blob);
-  EXPECT_EQ(0, blob_file.StoreBlob(blob));
-  EXPECT_EQ(blob_size, blob_file.StoreBlob(blob));
+  EXPECT_EQ(0, blob_file_writer.StoreBlob(blob));
+  EXPECT_EQ(kBlobSize, blob_file_writer.StoreBlob(blob));
 
-  brillo::Blob stored_blob(blob_size);
+  brillo::Blob stored_blob(kBlobSize);
   ssize_t bytes_read;
-  ASSERT_TRUE(
-      utils::PReadAll(blob_fd, stored_blob.data(), blob_size, 0, &bytes_read));
-  EXPECT_EQ(bytes_read, blob_size);
+  ASSERT_TRUE(utils::PReadAll(
+      blob_file.fd(), stored_blob.data(), kBlobSize, 0, &bytes_read));
+  EXPECT_EQ(bytes_read, kBlobSize);
   EXPECT_EQ(blob, stored_blob);
 }
 
diff --git a/payload_generator/block_mapping_unittest.cc b/payload_generator/block_mapping_unittest.cc
index 9b9b4f1..017548a 100644
--- a/payload_generator/block_mapping_unittest.cc
+++ b/payload_generator/block_mapping_unittest.cc
@@ -36,8 +36,8 @@
 class BlockMappingTest : public ::testing::Test {
  protected:
   // Old new partition files used in testing.
-  test_utils::ScopedTempFile old_part_{"BlockMappingTest_old.XXXXXX"};
-  test_utils::ScopedTempFile new_part_{"BlockMappingTest_new.XXXXXX"};
+  ScopedTempFile old_part_{"BlockMappingTest_old.XXXXXX"};
+  ScopedTempFile new_part_{"BlockMappingTest_new.XXXXXX"};
 
   size_t block_size_{1024};
   BlockMapping bm_{block_size_};  // BlockMapping under test.
diff --git a/payload_generator/boot_img_filesystem_unittest.cc b/payload_generator/boot_img_filesystem_unittest.cc
index 0b115e0..7805156 100644
--- a/payload_generator/boot_img_filesystem_unittest.cc
+++ b/payload_generator/boot_img_filesystem_unittest.cc
@@ -63,7 +63,7 @@
     return boot_img;
   }
 
-  test_utils::ScopedTempFile boot_file_;
+  ScopedTempFile boot_file_;
 };
 
 TEST_F(BootImgFilesystemTest, SimpleTest) {
diff --git a/payload_generator/cow_size_estimator.cc b/payload_generator/cow_size_estimator.cc
new file mode 100644
index 0000000..3eb0aca
--- /dev/null
+++ b/payload_generator/cow_size_estimator.cc
@@ -0,0 +1,110 @@
+//
+// 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/payload_generator/cow_size_estimator.h"
+
+#include <utility>
+#include <vector>
+
+#include <libsnapshot/cow_writer.h>
+
+#include "android-base/unique_fd.h"
+#include "update_engine/common/cow_operation_convert.h"
+#include "update_engine/payload_consumer/vabc_partition_writer.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+using android::snapshot::CowWriter;
+
+void PerformReplaceOp(const InstallOperation& op,
+                      CowWriter* writer,
+                      FileDescriptorPtr target_fd,
+                      size_t block_size) {
+  std::vector<unsigned char> buffer;
+  for (const auto& extent : op.dst_extents()) {
+    buffer.resize(extent.num_blocks() * block_size);
+    // No need to read from payload.bin then decompress, just read from target
+    // directly.
+    ssize_t bytes_read = 0;
+    auto success = utils::ReadAll(target_fd,
+                                  buffer.data(),
+                                  buffer.size(),
+                                  extent.start_block() * block_size,
+                                  &bytes_read);
+    CHECK(success);
+    CHECK_EQ(static_cast<size_t>(bytes_read), buffer.size());
+    writer->AddRawBlocks(extent.start_block(), buffer.data(), buffer.size());
+  }
+}
+
+void PerformZeroOp(const InstallOperation& op,
+                   CowWriter* writer,
+                   size_t block_size) {
+  for (const auto& extent : op.dst_extents()) {
+    writer->AddZeroBlocks(extent.start_block(), extent.num_blocks());
+  }
+}
+
+size_t EstimateCowSize(
+    FileDescriptorPtr source_fd,
+    FileDescriptorPtr target_fd,
+    const google::protobuf::RepeatedPtrField<InstallOperation>& operations,
+    const google::protobuf::RepeatedPtrField<CowMergeOperation>&
+        merge_operations,
+    size_t block_size) {
+  android::snapshot::CowWriter cow_writer{
+      {.block_size = static_cast<uint32_t>(block_size), .compression = "gz"}};
+  // CowWriter treats -1 as special value, will discard all the data but still
+  // reports Cow size. Good for estimation purposes
+  cow_writer.Initialize(android::base::borrowed_fd{-1});
+
+  const auto converted = ConvertToCowOperations(operations, merge_operations);
+  VABCPartitionWriter::WriteAllCowOps(
+      block_size, converted, &cow_writer, source_fd);
+  cow_writer.AddLabel(0);
+  for (const auto& op : operations) {
+    switch (op.type()) {
+      case InstallOperation::REPLACE:
+      case InstallOperation::REPLACE_BZ:
+      case InstallOperation::REPLACE_XZ:
+        PerformReplaceOp(op, &cow_writer, target_fd, block_size);
+        break;
+      case InstallOperation::ZERO:
+      case InstallOperation::DISCARD:
+        PerformZeroOp(op, &cow_writer, block_size);
+        break;
+      case InstallOperation::SOURCE_COPY:
+      case InstallOperation::MOVE:
+        // Already handeled by WriteAllCowOps,
+        break;
+      case InstallOperation::SOURCE_BSDIFF:
+      case InstallOperation::BROTLI_BSDIFF:
+      case InstallOperation::PUFFDIFF:
+      case InstallOperation::BSDIFF:
+        // We might do something special by adding CowBsdiff to CowWriter.
+        // For now proceed the same way as normal REPLACE operation.
+        PerformReplaceOp(op, &cow_writer, target_fd, block_size);
+        break;
+    }
+    // Arbitrary label number, we won't be resuming use these labels here.
+    // They are emitted just to keep size estimates accurate. As update_engine
+    // emits 1 label for every op.
+    cow_writer.AddLabel(2);
+  }
+  // TODO(zhangkelvin) Take FEC extents into account once VABC stabilizes
+  return cow_writer.GetCowSize();
+}
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/cow_size_estimator.h b/payload_generator/cow_size_estimator.h
new file mode 100644
index 0000000..cba89b5
--- /dev/null
+++ b/payload_generator/cow_size_estimator.h
@@ -0,0 +1,36 @@
+//
+// 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 <cstddef>
+
+#include <update_engine/update_metadata.pb.h>
+
+#include "update_engine/payload_consumer/file_descriptor.h"
+
+namespace chromeos_update_engine {
+// Given file descriptor to the source image, target image, and list of
+// operations, estimate the size of COW image if the operations are applied on
+// Virtual AB Compression enabled device. This is intended to be used by update
+// generators to put an estimate cow size in OTA payload. When installing an OTA
+// update, libsnapshot will take this estimate as a hint to allocate spaces.
+size_t EstimateCowSize(
+    FileDescriptorPtr source_fd,
+    FileDescriptorPtr target_fd,
+    const google::protobuf::RepeatedPtrField<InstallOperation>& operations,
+    const google::protobuf::RepeatedPtrField<CowMergeOperation>&
+        merge_operations,
+    size_t block_size);
+
+}  // namespace chromeos_update_engine
diff --git a/metrics_reporter_stub.cc b/payload_generator/cow_size_estimator_stub.cc
similarity index 60%
copy from metrics_reporter_stub.cc
copy to payload_generator/cow_size_estimator_stub.cc
index 81664a5..9d94d63 100644
--- a/metrics_reporter_stub.cc
+++ b/payload_generator/cow_size_estimator_stub.cc
@@ -1,5 +1,5 @@
 //
-// Copyright (C) 2017 The Android Open Source Project
+// 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.
@@ -14,18 +14,18 @@
 // limitations under the License.
 //
 
-#include "update_engine/metrics_reporter_stub.h"
-
-#include <memory>
+#include "update_engine/payload_generator/cow_size_estimator.h"
 
 namespace chromeos_update_engine {
 
-namespace metrics {
-
-std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter() {
-  return std::make_unique<MetricsReporterStub>();
+size_t EstimateCowSize(
+    FileDescriptorPtr source_fd,
+    FileDescriptorPtr target_fd,
+    const google::protobuf::RepeatedPtrField<InstallOperation>& operations,
+    const google::protobuf::RepeatedPtrField<CowMergeOperation>&
+        merge_operations,
+    size_t block_size) {
+  return 0;
 }
 
-}  // namespace metrics
-
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/delta_diff_generator.cc b/payload_generator/delta_diff_generator.cc
index c2b35ee..74d43fd 100644
--- a/payload_generator/delta_diff_generator.cc
+++ b/payload_generator/delta_diff_generator.cc
@@ -33,14 +33,17 @@
 
 #include "update_engine/common/utils.h"
 #include "update_engine/payload_consumer/delta_performer.h"
+#include "update_engine/payload_consumer/file_descriptor.h"
 #include "update_engine/payload_consumer/payload_constants.h"
 #include "update_engine/payload_generator/ab_generator.h"
 #include "update_engine/payload_generator/annotated_operation.h"
 #include "update_engine/payload_generator/blob_file_writer.h"
+#include "update_engine/payload_generator/cow_size_estimator.h"
 #include "update_engine/payload_generator/delta_diff_utils.h"
 #include "update_engine/payload_generator/full_update_generator.h"
 #include "update_engine/payload_generator/merge_sequence_generator.h"
 #include "update_engine/payload_generator/payload_file.h"
+#include "update_engine/update_metadata.pb.h"
 
 using std::string;
 using std::unique_ptr;
@@ -53,6 +56,18 @@
 const size_t kBlockSize = 4096;  // bytes
 
 class PartitionProcessor : public base::DelegateSimpleThread::Delegate {
+  bool IsDynamicPartition(const std::string& partition_name) {
+    for (const auto& group :
+         config_.target.dynamic_partition_metadata->groups()) {
+      const auto& names = group.partition_names();
+      if (std::find(names.begin(), names.end(), partition_name) !=
+          names.end()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
  public:
   explicit PartitionProcessor(
       const PayloadGenerationConfig& config,
@@ -61,6 +76,7 @@
       BlobFileWriter* file_writer,
       std::vector<AnnotatedOperation>* aops,
       std::vector<CowMergeOperation>* cow_merge_sequence,
+      size_t* cow_size,
       std::unique_ptr<chromeos_update_engine::OperationsGenerator> strategy)
       : config_(config),
         old_part_(old_part),
@@ -68,11 +84,13 @@
         file_writer_(file_writer),
         aops_(aops),
         cow_merge_sequence_(cow_merge_sequence),
+        cow_size_(cow_size),
         strategy_(std::move(strategy)) {}
   PartitionProcessor(PartitionProcessor&&) noexcept = default;
+
   void Run() override {
     LOG(INFO) << "Started an async task to process partition "
-              << old_part_.name;
+              << new_part_.name;
     bool success = strategy_->GenerateOperations(
         config_, old_part_, new_part_, file_writer_, aops_);
     if (!success) {
@@ -85,13 +103,38 @@
     bool snapshot_enabled =
         config_.target.dynamic_partition_metadata &&
         config_.target.dynamic_partition_metadata->snapshot_enabled();
-    if (old_part_.path.empty() || !snapshot_enabled) {
+    if (!snapshot_enabled || !IsDynamicPartition(new_part_.name)) {
       return;
     }
-    auto generator = MergeSequenceGenerator::Create(*aops_);
-    if (!generator || !generator->Generate(cow_merge_sequence_)) {
-      LOG(FATAL) << "Failed to generate merge sequence";
+    if (!old_part_.path.empty()) {
+      auto generator = MergeSequenceGenerator::Create(*aops_);
+      if (!generator || !generator->Generate(cow_merge_sequence_)) {
+        LOG(FATAL) << "Failed to generate merge sequence";
+      }
     }
+
+    LOG(INFO) << "Estimating COW size for partition: " << new_part_.name;
+    // Need the contents of source/target image bytes when doing
+    // dry run.
+    FileDescriptorPtr source_fd{new EintrSafeFileDescriptor()};
+    source_fd->Open(old_part_.path.c_str(), O_RDONLY);
+
+    auto target_fd = std::make_unique<EintrSafeFileDescriptor>();
+    target_fd->Open(new_part_.path.c_str(), O_RDONLY);
+
+    google::protobuf::RepeatedPtrField<InstallOperation> operations;
+
+    for (const AnnotatedOperation& aop : *aops_) {
+      *operations.Add() = aop.op;
+    }
+    *cow_size_ = EstimateCowSize(
+        source_fd,
+        std::move(target_fd),
+        operations,
+        {cow_merge_sequence_->begin(), cow_merge_sequence_->end()},
+        config_.block_size);
+    LOG(INFO) << "Estimated COW size for partition: " << new_part_.name << " "
+              << *cow_size_;
   }
 
  private:
@@ -101,6 +144,7 @@
   BlobFileWriter* file_writer_;
   std::vector<AnnotatedOperation>* aops_;
   std::vector<CowMergeOperation>* cow_merge_sequence_;
+  size_t* cow_size_;
   std::unique_ptr<chromeos_update_engine::OperationsGenerator> strategy_;
   DISALLOW_COPY_AND_ASSIGN(PartitionProcessor);
 };
@@ -119,18 +163,10 @@
   PayloadFile payload;
   TEST_AND_RETURN_FALSE(payload.Init(config));
 
-  const string kTempFileTemplate("CrAU_temp_data.XXXXXX");
-  string temp_file_path;
-  int data_file_fd;
-  TEST_AND_RETURN_FALSE(
-      utils::MakeTempFile(kTempFileTemplate, &temp_file_path, &data_file_fd));
-  ScopedPathUnlinker temp_file_unlinker(temp_file_path);
-  TEST_AND_RETURN_FALSE(data_file_fd >= 0);
-
+  ScopedTempFile data_file("CrAU_temp_data.XXXXXX", true);
   {
     off_t data_file_size = 0;
-    ScopedFdCloser data_file_fd_closer(&data_file_fd);
-    BlobFileWriter blob_file(data_file_fd, &data_file_size);
+    BlobFileWriter blob_file(data_file.fd(), &data_file_size);
     if (config.is_delta) {
       TEST_AND_RETURN_FALSE(config.source.partitions.size() ==
                             config.target.partitions.size());
@@ -138,8 +174,12 @@
     PartitionConfig empty_part("");
     std::vector<std::vector<AnnotatedOperation>> all_aops;
     all_aops.resize(config.target.partitions.size());
+
     std::vector<std::vector<CowMergeOperation>> all_merge_sequences;
     all_merge_sequences.resize(config.target.partitions.size());
+
+    std::vector<size_t> all_cow_sizes(config.target.partitions.size(), 0);
+
     std::vector<PartitionProcessor> partition_tasks{};
     auto thread_count = std::min<int>(diff_utils::GetMaxThreads(),
                                       config.target.partitions.size());
@@ -157,10 +197,12 @@
       unique_ptr<OperationsGenerator> strategy;
       if (!old_part.path.empty()) {
         // Delta update.
-        LOG(INFO) << "Using generator ABGenerator().";
+        LOG(INFO) << "Using generator ABGenerator() for partition "
+                  << new_part.name;
         strategy.reset(new ABGenerator());
       } else {
-        LOG(INFO) << "Using generator FullUpdateGenerator().";
+        LOG(INFO) << "Using generator FullUpdateGenerator() for partition "
+                  << new_part.name;
         strategy.reset(new FullUpdateGenerator());
       }
 
@@ -171,6 +213,7 @@
                                                    &blob_file,
                                                    &all_aops[i],
                                                    &all_merge_sequences[i],
+                                                   &all_cow_sizes[i],
                                                    std::move(strategy)));
     }
     thread_pool.Start();
@@ -187,14 +230,16 @@
           payload.AddPartition(old_part,
                                new_part,
                                std::move(all_aops[i]),
-                               std::move(all_merge_sequences[i])));
+                               std::move(all_merge_sequences[i]),
+                               all_cow_sizes[i]));
     }
   }
+  data_file.CloseFd();
 
   LOG(INFO) << "Writing payload file...";
   // Write payload file to disk.
   TEST_AND_RETURN_FALSE(payload.WritePayload(
-      output_path, temp_file_path, private_key_path, metadata_size));
+      output_path, data_file.path(), private_key_path, metadata_size));
 
   LOG(INFO) << "All done. Successfully created delta file with "
             << "metadata size = " << *metadata_size;
diff --git a/payload_generator/delta_diff_utils.cc b/payload_generator/delta_diff_utils.cc
index 220c7ae..3c025e1 100644
--- a/payload_generator/delta_diff_utils.cc
+++ b/payload_generator/delta_diff_utils.cc
@@ -822,17 +822,13 @@
         // Only Puffdiff if both files have at least one deflate left.
         if (!src_deflates.empty() && !dst_deflates.empty()) {
           brillo::Blob puffdiff_delta;
-          string temp_file_path;
-          TEST_AND_RETURN_FALSE(utils::MakeTempFile(
-              "puffdiff-delta.XXXXXX", &temp_file_path, nullptr));
-          ScopedPathUnlinker temp_file_unlinker(temp_file_path);
-
+          ScopedTempFile temp_file("puffdiff-delta.XXXXXX");
           // Perform PuffDiff operation.
           TEST_AND_RETURN_FALSE(puffin::PuffDiff(old_data,
                                                  new_data,
                                                  src_deflates,
                                                  dst_deflates,
-                                                 temp_file_path,
+                                                 temp_file.path(),
                                                  &puffdiff_delta));
           TEST_AND_RETURN_FALSE(puffdiff_delta.size() > 0);
           if (IsDiffOperationBetter(operation,
diff --git a/payload_generator/delta_diff_utils_unittest.cc b/payload_generator/delta_diff_utils_unittest.cc
index 0857f9c..f2db1bd 100644
--- a/payload_generator/delta_diff_utils_unittest.cc
+++ b/payload_generator/delta_diff_utils_unittest.cc
@@ -69,13 +69,12 @@
 // Create a fake filesystem of the given |size| and initialize the partition
 // holding it in the PartitionConfig |part|.
 void CreatePartition(PartitionConfig* part,
-                     const string& pattern,
+                     ScopedTempFile* part_file,
                      uint64_t block_size,
                      off_t size) {
-  int fd = -1;
-  ASSERT_TRUE(utils::MakeTempFile(pattern.c_str(), &part->path, &fd));
-  ASSERT_EQ(0, ftruncate(fd, size));
-  ASSERT_EQ(0, close(fd));
+  part->path = part_file->path();
+  ASSERT_EQ(0, ftruncate(part_file->fd(), size));
+  part_file->CloseFd();
   part->fs_interface.reset(new FakeFilesystem(block_size, size / block_size));
   part->size = size;
 }
@@ -112,30 +111,20 @@
 
   void SetUp() override {
     CreatePartition(&old_part_,
-                    "DeltaDiffUtilsTest-old_part-XXXXXX",
+                    &old_part_file_,
                     block_size_,
                     block_size_ * kDefaultBlockCount);
     CreatePartition(&new_part_,
-                    "DeltaDiffUtilsTest-old_part-XXXXXX",
+                    &new_part_file_,
                     block_size_,
                     block_size_ * kDefaultBlockCount);
-    ASSERT_TRUE(utils::MakeTempFile(
-        "DeltaDiffUtilsTest-blob-XXXXXX", &blob_path_, &blob_fd_));
-  }
-
-  void TearDown() override {
-    unlink(old_part_.path.c_str());
-    unlink(new_part_.path.c_str());
-    if (blob_fd_ != -1)
-      close(blob_fd_);
-    unlink(blob_path_.c_str());
   }
 
   // Helper function to call DeltaMovedAndZeroBlocks() using this class' data
   // members. This simply avoids repeating all the arguments that never change.
   bool RunDeltaMovedAndZeroBlocks(ssize_t chunk_blocks,
                                   uint32_t minor_version) {
-    BlobFileWriter blob_file(blob_fd_, &blob_size_);
+    BlobFileWriter blob_file(tmp_blob_file_.fd(), &blob_size_);
     PayloadVersion version(kBrilloMajorPayloadVersion, minor_version);
     ExtentRanges old_zero_blocks;
     return diff_utils::DeltaMovedAndZeroBlocks(&aops_,
@@ -155,10 +144,11 @@
   // with
   PartitionConfig old_part_{"part"};
   PartitionConfig new_part_{"part"};
+  ScopedTempFile old_part_file_{"DeltaDiffUtilsTest-old_part-XXXXXX", true};
+  ScopedTempFile new_part_file_{"DeltaDiffUtilsTest-new_part-XXXXXX", true};
 
   // The file holding the output blob from the various diff utils functions.
-  string blob_path_;
-  int blob_fd_{-1};
+  ScopedTempFile tmp_blob_file_{"DeltaDiffUtilsTest-blob-XXXXXX", true};
   off_t blob_size_{0};
 
   size_t block_size_{kBlockSize};
@@ -173,7 +163,7 @@
   new_part_.verity.hash_tree_extent = ExtentForRange(20, 30);
   new_part_.verity.fec_extent = ExtentForRange(40, 50);
 
-  BlobFileWriter blob_file(blob_fd_, &blob_size_);
+  BlobFileWriter blob_file(tmp_blob_file_.fd(), &blob_size_);
   EXPECT_TRUE(diff_utils::DeltaReadPartition(
       &aops_,
       old_part_,
diff --git a/payload_generator/ext2_filesystem_unittest.cc b/payload_generator/ext2_filesystem_unittest.cc
index 54600e9..88e1538 100644
--- a/payload_generator/ext2_filesystem_unittest.cc
+++ b/payload_generator/ext2_filesystem_unittest.cc
@@ -62,7 +62,7 @@
 class Ext2FilesystemTest : public ::testing::Test {};
 
 TEST_F(Ext2FilesystemTest, InvalidFilesystem) {
-  test_utils::ScopedTempFile fs_filename_{"Ext2FilesystemTest-XXXXXX"};
+  ScopedTempFile fs_filename_{"Ext2FilesystemTest-XXXXXX"};
   ASSERT_EQ(0, truncate(fs_filename_.path().c_str(), kDefaultFilesystemSize));
   unique_ptr<Ext2Filesystem> fs =
       Ext2Filesystem::CreateFromFile(fs_filename_.path());
diff --git a/payload_generator/extent_ranges_unittest.cc b/payload_generator/extent_ranges_unittest.cc
index 326e936..f55bb73 100644
--- a/payload_generator/extent_ranges_unittest.cc
+++ b/payload_generator/extent_ranges_unittest.cc
@@ -52,8 +52,8 @@
   }
 }
 
-#define EXPECT_RANGE_EQ(ranges, var)                      \
-  do {                                                    \
+#define EXPECT_RANGE_EQ(ranges, var)                       \
+  do {                                                     \
     ExpectRangeEq(ranges, var, base::size(var), __LINE__); \
   } while (0)
 
diff --git a/payload_generator/extent_utils.h b/payload_generator/extent_utils.h
index 9763b1f..f870b29 100644
--- a/payload_generator/extent_utils.h
+++ b/payload_generator/extent_utils.h
@@ -20,6 +20,8 @@
 #include <string>
 #include <vector>
 
+#include <base/logging.h>
+
 #include "update_engine/payload_consumer/payload_constants.h"
 #include "update_engine/update_metadata.pb.h"
 
@@ -83,6 +85,43 @@
 
 bool operator==(const Extent& a, const Extent& b);
 
+// TODO(zhangkelvin) This is ugly. Rewrite using C++20's coroutine once
+// that's available. Unfortunately with C++17 this is the best I could do.
+
+// An iterator that takes a sequence of extents, and iterate over blocks
+// inside this sequence of extents.
+// Example usage:
+
+// BlockIterator it1{src_extents};
+// while(!it1.is_end()) {
+//    auto block = *it1;
+//    Do stuff with |block|
+// }
+struct BlockIterator {
+  explicit BlockIterator(
+      const google::protobuf::RepeatedPtrField<Extent>& src_extents)
+      : src_extents_(src_extents) {}
+
+  BlockIterator& operator++() {
+    CHECK_LT(cur_extent_, src_extents_.size());
+    block_offset_++;
+    if (block_offset_ >= src_extents_[cur_extent_].num_blocks()) {
+      cur_extent_++;
+      block_offset_ = 0;
+    }
+    return *this;
+  }
+
+  [[nodiscard]] bool is_end() { return cur_extent_ >= src_extents_.size(); }
+  [[nodiscard]] uint64_t operator*() {
+    return src_extents_[cur_extent_].start_block() + block_offset_;
+  }
+
+  const google::protobuf::RepeatedPtrField<Extent>& src_extents_;
+  int cur_extent_ = 0;
+  size_t block_offset_ = 0;
+};
+
 }  // namespace chromeos_update_engine
 
 #endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_EXTENT_UTILS_H_
diff --git a/payload_generator/full_update_generator_unittest.cc b/payload_generator/full_update_generator_unittest.cc
index 5f39e8b..d3b3491 100644
--- a/payload_generator/full_update_generator_unittest.cc
+++ b/payload_generator/full_update_generator_unittest.cc
@@ -41,11 +41,9 @@
     config_.block_size = 4096;
 
     new_part_conf.path = part_file_.path();
-    EXPECT_TRUE(utils::MakeTempFile(
-        "FullUpdateTest_blobs.XXXXXX", &out_blobs_path_, &out_blobs_fd_));
 
-    blob_file_.reset(new BlobFileWriter(out_blobs_fd_, &out_blobs_length_));
-    out_blobs_unlinker_.reset(new ScopedPathUnlinker(out_blobs_path_));
+    blob_file_writer_.reset(
+        new BlobFileWriter(blob_file_.fd(), &out_blobs_length_));
   }
 
   PayloadGenerationConfig config_;
@@ -54,14 +52,11 @@
   vector<AnnotatedOperation> aops;
 
   // Output file holding the payload blobs.
-  string out_blobs_path_;
-  int out_blobs_fd_{-1};
   off_t out_blobs_length_{0};
-  ScopedFdCloser out_blobs_fd_closer_{&out_blobs_fd_};
-  test_utils::ScopedTempFile part_file_{"FullUpdateTest_partition.XXXXXX"};
+  ScopedTempFile part_file_{"FullUpdateTest_partition.XXXXXX"};
 
-  std::unique_ptr<BlobFileWriter> blob_file_;
-  std::unique_ptr<ScopedPathUnlinker> out_blobs_unlinker_;
+  ScopedTempFile blob_file_{"FullUpdateTest_blobs.XXXXXX", true};
+  std::unique_ptr<BlobFileWriter> blob_file_writer_;
 
   // FullUpdateGenerator under test.
   FullUpdateGenerator generator_;
@@ -77,7 +72,7 @@
   EXPECT_TRUE(generator_.GenerateOperations(config_,
                                             new_part_conf,  // this is ignored
                                             new_part_conf,
-                                            blob_file_.get(),
+                                            blob_file_writer_.get(),
                                             &aops));
   int64_t new_part_chunks = new_part_conf.size / config_.hard_chunk_size;
   EXPECT_EQ(new_part_chunks, static_cast<int64_t>(aops.size()));
@@ -108,7 +103,7 @@
   EXPECT_TRUE(generator_.GenerateOperations(config_,
                                             new_part_conf,  // this is ignored
                                             new_part_conf,
-                                            blob_file_.get(),
+                                            blob_file_writer_.get(),
                                             &aops));
   // new_part has one chunk and a half.
   EXPECT_EQ(2U, aops.size());
@@ -129,7 +124,7 @@
   EXPECT_TRUE(generator_.GenerateOperations(config_,
                                             new_part_conf,  // this is ignored
                                             new_part_conf,
-                                            blob_file_.get(),
+                                            blob_file_writer_.get(),
                                             &aops));
 
   // new_part has less than one chunk.
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc
index dd41a29..5bbeee4 100644
--- a/payload_generator/generate_delta_main.cc
+++ b/payload_generator/generate_delta_main.cc
@@ -18,6 +18,7 @@
 #include <string>
 #include <vector>
 
+#include <base/bind.h>
 #include <base/files/file_path.h>
 #include <base/files/file_util.h>
 #include <base/logging.h>
@@ -29,13 +30,13 @@
 #include <brillo/message_loops/base_message_loop.h>
 #include <xz.h>
 
+#include "update_engine/common/download_action.h"
 #include "update_engine/common/fake_boot_control.h"
 #include "update_engine/common/fake_hardware.h"
 #include "update_engine/common/file_fetcher.h"
 #include "update_engine/common/prefs.h"
 #include "update_engine/common/terminator.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/payload_consumer/download_action.h"
 #include "update_engine/payload_consumer/filesystem_verifier_action.h"
 #include "update_engine/payload_consumer/payload_constants.h"
 #include "update_engine/payload_generator/delta_diff_generator.h"
@@ -74,38 +75,6 @@
   }
 }
 
-bool ParseImageInfo(const string& channel,
-                    const string& board,
-                    const string& version,
-                    const string& key,
-                    const string& build_channel,
-                    const string& build_version,
-                    ImageInfo* image_info) {
-  // All of these arguments should be present or missing.
-  bool empty = channel.empty();
-
-  CHECK_EQ(channel.empty(), empty);
-  CHECK_EQ(board.empty(), empty);
-  CHECK_EQ(version.empty(), empty);
-  CHECK_EQ(key.empty(), empty);
-
-  if (empty)
-    return false;
-
-  image_info->set_channel(channel);
-  image_info->set_board(board);
-  image_info->set_version(version);
-  image_info->set_key(key);
-
-  image_info->set_build_channel(build_channel.empty() ? channel
-                                                      : build_channel);
-
-  image_info->set_build_version(build_version.empty() ? version
-                                                      : build_version);
-
-  return true;
-}
-
 void CalculateHashForSigning(const vector<size_t>& sizes,
                              const string& out_hash_file,
                              const string& out_metadata_hash_file,
@@ -214,8 +183,11 @@
   install_plan.source_slot =
       config.is_delta ? 0 : BootControlInterface::kInvalidSlot;
   install_plan.target_slot = 1;
-  payload.type =
-      config.is_delta ? InstallPayloadType::kDelta : InstallPayloadType::kFull;
+  // For partial updates, we always write kDelta to the payload. Make it
+  // consistent for host simulation.
+  payload.type = config.is_delta || config.is_partial_update
+                     ? InstallPayloadType::kDelta
+                     : InstallPayloadType::kFull;
   payload.size = utils::FileSize(payload_file);
   // TODO(senj): This hash is only correct for unsigned payload, need to support
   // signed payload using PayloadSigner.
@@ -266,7 +238,9 @@
   processor.EnqueueAction(std::move(install_plan_action));
   processor.EnqueueAction(std::move(download_action));
   processor.EnqueueAction(std::move(filesystem_verifier_action));
-  processor.StartProcessing();
+  loop.PostTask(FROM_HERE,
+                base::Bind(&ActionProcessor::StartProcessing,
+                           base::Unretained(&processor)));
   loop.Run();
   CHECK_EQ(delegate.code_, ErrorCode::kSuccess);
   LOG(INFO) << "Completed applying " << (config.is_delta ? "delta" : "full")
@@ -426,51 +400,6 @@
       "The per-partition maximum timestamps which the OS allowed to apply this "
       "payload. Passed in comma separated pairs, e.x. system:1234,vendor:5678");
 
-  DEFINE_string(old_channel,
-                "",
-                "The channel for the old image. 'dev-channel', 'npo-channel', "
-                "etc. Ignored, except during delta generation.");
-  DEFINE_string(old_board,
-                "",
-                "The board for the old image. 'x86-mario', 'lumpy', "
-                "etc. Ignored, except during delta generation.");
-  DEFINE_string(
-      old_version, "", "The build version of the old image. 1.2.3, etc.");
-  DEFINE_string(old_key,
-                "",
-                "The key used to sign the old image. 'premp', 'mp', 'mp-v3',"
-                " etc");
-  DEFINE_string(old_build_channel,
-                "",
-                "The channel for the build of the old image. 'dev-channel', "
-                "etc, but will never contain special channels such as "
-                "'npo-channel'. Ignored, except during delta generation.");
-  DEFINE_string(old_build_version,
-                "",
-                "The version of the build containing the old image.");
-
-  DEFINE_string(new_channel,
-                "",
-                "The channel for the new image. 'dev-channel', 'npo-channel', "
-                "etc. Ignored, except during delta generation.");
-  DEFINE_string(new_board,
-                "",
-                "The board for the new image. 'x86-mario', 'lumpy', "
-                "etc. Ignored, except during delta generation.");
-  DEFINE_string(
-      new_version, "", "The build version of the new image. 1.2.3, etc.");
-  DEFINE_string(new_key,
-                "",
-                "The key used to sign the new image. 'premp', 'mp', 'mp-v3',"
-                " etc");
-  DEFINE_string(new_build_channel,
-                "",
-                "The channel for the build of the new image. 'dev-channel', "
-                "etc, but will never contain special channels such as "
-                "'npo-channel'. Ignored, except during delta generation.");
-  DEFINE_string(new_build_version,
-                "",
-                "The version of the build containing the new image.");
   DEFINE_string(new_postinstall_config_file,
                 "",
                 "A config file specifying postinstall related metadata. "
@@ -501,7 +430,11 @@
   Terminator::Init();
 
   logging::LoggingSettings log_settings;
+#if BASE_VER < 780000
   log_settings.log_file = "delta_generator.log";
+#else
+  log_settings.log_file_path = "delta_generator.log";
+#endif
   log_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
   log_settings.lock_log = logging::LOCK_LOG_FILE;
   log_settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE;
@@ -646,6 +579,10 @@
     }
   }
 
+  if (FLAGS_is_partial_update) {
+    payload_config.is_partial_update = true;
+  }
+
   if (!FLAGS_in_file.empty()) {
     return ApplyPayload(FLAGS_in_file, payload_config) ? 0 : 1;
   }
@@ -674,30 +611,8 @@
     CHECK(payload_config.target.ValidateDynamicPartitionMetadata());
   }
 
-  if (FLAGS_is_partial_update) {
-    payload_config.is_partial_update = true;
-  }
-
   CHECK(!FLAGS_out_file.empty());
 
-  // Ignore failures. These are optional arguments.
-  ParseImageInfo(FLAGS_new_channel,
-                 FLAGS_new_board,
-                 FLAGS_new_version,
-                 FLAGS_new_key,
-                 FLAGS_new_build_channel,
-                 FLAGS_new_build_version,
-                 &payload_config.target.image_info);
-
-  // Ignore failures. These are optional arguments.
-  ParseImageInfo(FLAGS_old_channel,
-                 FLAGS_old_board,
-                 FLAGS_old_version,
-                 FLAGS_old_key,
-                 FLAGS_old_build_channel,
-                 FLAGS_old_build_version,
-                 &payload_config.source.image_info);
-
   payload_config.rootfs_partition_size = FLAGS_rootfs_partition_size;
 
   if (payload_config.is_delta) {
diff --git a/payload_generator/mapfile_filesystem_unittest.cc b/payload_generator/mapfile_filesystem_unittest.cc
index 36ae3bf..57b672b 100644
--- a/payload_generator/mapfile_filesystem_unittest.cc
+++ b/payload_generator/mapfile_filesystem_unittest.cc
@@ -55,8 +55,8 @@
 
 class MapfileFilesystemTest : public ::testing::Test {
  protected:
-  test_utils::ScopedTempFile temp_file_{"mapfile_file.XXXXXX"};
-  test_utils::ScopedTempFile temp_mapfile_{"mapfile_mapfile.XXXXXX"};
+  ScopedTempFile temp_file_{"mapfile_file.XXXXXX"};
+  ScopedTempFile temp_mapfile_{"mapfile_mapfile.XXXXXX"};
 };
 
 TEST_F(MapfileFilesystemTest, EmptyFilesystem) {
diff --git a/payload_generator/merge_sequence_generator_unittest.cc b/payload_generator/merge_sequence_generator_unittest.cc
index 567ede1..1f0c2ea 100644
--- a/payload_generator/merge_sequence_generator_unittest.cc
+++ b/payload_generator/merge_sequence_generator_unittest.cc
@@ -116,6 +116,20 @@
             merge_after.at(transfers[2]));
 }
 
+TEST_F(MergeSequenceGeneratorTest, FindDependencyEdgeCase) {
+  std::vector<CowMergeOperation> transfers = {
+      CreateCowMergeOperation(ExtentForRange(10, 10), ExtentForRange(15, 10)),
+      CreateCowMergeOperation(ExtentForRange(40, 10), ExtentForRange(50, 10)),
+      CreateCowMergeOperation(ExtentForRange(59, 10), ExtentForRange(60, 10)),
+  };
+
+  std::map<CowMergeOperation, std::set<CowMergeOperation>> merge_after;
+  FindDependency(transfers, &merge_after);
+  ASSERT_EQ(std::set<CowMergeOperation>(), merge_after.at(transfers[0]));
+  ASSERT_EQ(std::set<CowMergeOperation>(), merge_after.at(transfers[1]));
+  ASSERT_EQ(merge_after[transfers[2]].size(), 1U);
+}
+
 TEST_F(MergeSequenceGeneratorTest, FindDependency_ReusedSourceBlocks) {
   std::vector<CowMergeOperation> transfers = {
       CreateCowMergeOperation(ExtentForRange(5, 10), ExtentForRange(15, 10)),
diff --git a/payload_generator/payload_file.cc b/payload_generator/payload_file.cc
index 49dff4e..33c0749 100644
--- a/payload_generator/payload_file.cc
+++ b/payload_generator/payload_file.cc
@@ -64,13 +64,6 @@
   TEST_AND_RETURN_FALSE(config.version.Validate());
   major_version_ = config.version.major;
   manifest_.set_minor_version(config.version.minor);
-
-  if (!config.source.ImageInfoIsEmpty())
-    *(manifest_.mutable_old_image_info()) = config.source.image_info;
-
-  if (!config.target.ImageInfoIsEmpty())
-    *(manifest_.mutable_new_image_info()) = config.target.image_info;
-
   manifest_.set_block_size(config.block_size);
   manifest_.set_max_timestamp(config.max_timestamp);
 
@@ -87,8 +80,10 @@
 bool PayloadFile::AddPartition(const PartitionConfig& old_conf,
                                const PartitionConfig& new_conf,
                                vector<AnnotatedOperation> aops,
-                               vector<CowMergeOperation> merge_sequence) {
+                               vector<CowMergeOperation> merge_sequence,
+                               size_t cow_size) {
   Partition part;
+  part.cow_size = cow_size;
   part.name = new_conf.name;
   part.aops = std::move(aops);
   part.cow_merge_sequence = std::move(merge_sequence);
@@ -110,11 +105,9 @@
                                const string& private_key_path,
                                uint64_t* metadata_size_out) {
   // Reorder the data blobs with the manifest_.
-  string ordered_blobs_path;
-  TEST_AND_RETURN_FALSE(utils::MakeTempFile(
-      "CrAU_temp_data.ordered.XXXXXX", &ordered_blobs_path, nullptr));
-  ScopedPathUnlinker ordered_blobs_unlinker(ordered_blobs_path);
-  TEST_AND_RETURN_FALSE(ReorderDataBlobs(data_blobs_path, ordered_blobs_path));
+  ScopedTempFile ordered_blobs_file("CrAU_temp_data.ordered.XXXXXX");
+  TEST_AND_RETURN_FALSE(
+      ReorderDataBlobs(data_blobs_path, ordered_blobs_file.path()));
 
   // Check that install op blobs are in order.
   uint64_t next_blob_offset = 0;
@@ -138,6 +131,9 @@
     if (!part.version.empty()) {
       partition->set_version(part.version);
     }
+    if (part.cow_size > 0) {
+      partition->set_estimate_cow_size(part.cow_size);
+    }
     if (part.postinstall.run) {
       partition->set_run_postinstall(true);
       if (!part.postinstall.path.empty())
@@ -238,7 +234,7 @@
 
   // Append the data blobs.
   LOG(INFO) << "Writing final delta file data blobs...";
-  int blobs_fd = open(ordered_blobs_path.c_str(), O_RDONLY, 0);
+  int blobs_fd = open(ordered_blobs_file.path().c_str(), O_RDONLY, 0);
   ScopedFdCloser blobs_fd_closer(&blobs_fd);
   TEST_AND_RETURN_FALSE(blobs_fd >= 0);
   for (;;) {
diff --git a/payload_generator/payload_file.h b/payload_generator/payload_file.h
index 8b17956..3a45793 100644
--- a/payload_generator/payload_file.h
+++ b/payload_generator/payload_file.h
@@ -44,7 +44,8 @@
   bool AddPartition(const PartitionConfig& old_conf,
                     const PartitionConfig& new_conf,
                     std::vector<AnnotatedOperation> aops,
-                    std::vector<CowMergeOperation> merge_sequence);
+                    std::vector<CowMergeOperation> merge_sequence,
+                    size_t cow_size);
 
   // Write the payload to the |payload_file| file. The operations reference
   // blobs in the |data_blobs_path| file and the blobs will be reordered in the
@@ -100,6 +101,7 @@
     VerityConfig verity;
     // Per partition timestamp.
     std::string version;
+    size_t cow_size;
   };
 
   std::vector<Partition> part_vec_;
diff --git a/payload_generator/payload_file_unittest.cc b/payload_generator/payload_file_unittest.cc
index 45faebb9..1fd36f5 100644
--- a/payload_generator/payload_file_unittest.cc
+++ b/payload_generator/payload_file_unittest.cc
@@ -36,7 +36,7 @@
 };
 
 TEST_F(PayloadFileTest, ReorderBlobsTest) {
-  test_utils::ScopedTempFile orig_blobs("ReorderBlobsTest.orig.XXXXXX");
+  ScopedTempFile orig_blobs("ReorderBlobsTest.orig.XXXXXX");
 
   // The operations have three blob and one gap (the whitespace):
   // Rootfs operation 1: [8, 3] bcd
@@ -45,7 +45,7 @@
   string orig_data = "kernel abcd";
   EXPECT_TRUE(test_utils::WriteFileString(orig_blobs.path(), orig_data));
 
-  test_utils::ScopedTempFile new_blobs("ReorderBlobsTest.new.XXXXXX");
+  ScopedTempFile new_blobs("ReorderBlobsTest.new.XXXXXX");
 
   payload_.part_vec_.resize(2);
 
diff --git a/payload_generator/payload_generation_config.cc b/payload_generator/payload_generation_config.cc
index 9c5832d..ef2f240 100644
--- a/payload_generator/payload_generation_config.cc
+++ b/payload_generator/payload_generation_config.cc
@@ -103,7 +103,6 @@
 }
 
 bool ImageConfig::ValidateIsEmpty() const {
-  TEST_AND_RETURN_FALSE(ImageInfoIsEmpty());
   return partitions.empty();
 }
 
@@ -215,13 +214,6 @@
   return true;
 }
 
-bool ImageConfig::ImageInfoIsEmpty() const {
-  return image_info.board().empty() && image_info.key().empty() &&
-         image_info.channel().empty() && image_info.version().empty() &&
-         image_info.build_channel().empty() &&
-         image_info.build_version().empty();
-}
-
 PayloadVersion::PayloadVersion(uint64_t major_version, uint32_t minor_version) {
   major = major_version;
   minor = minor_version;
@@ -293,9 +285,6 @@
       TEST_AND_RETURN_FALSE(part.verity.IsEmpty());
     }
 
-    // If new_image_info is present, old_image_info must be present.
-    TEST_AND_RETURN_FALSE(source.ImageInfoIsEmpty() ==
-                          target.ImageInfoIsEmpty());
   } else {
     // All the "source" image fields must be empty for full payloads.
     TEST_AND_RETURN_FALSE(source.ValidateIsEmpty());
diff --git a/payload_generator/payload_generation_config.h b/payload_generator/payload_generation_config.h
index ec63043..1d88101 100644
--- a/payload_generator/payload_generation_config.h
+++ b/payload_generator/payload_generation_config.h
@@ -149,13 +149,6 @@
   // Validate |dynamic_partition_metadata| against |partitions|.
   bool ValidateDynamicPartitionMetadata() const;
 
-  // Returns whether the |image_info| field is empty.
-  bool ImageInfoIsEmpty() const;
-
-  // The ImageInfo message defined in the update_metadata.proto file describes
-  // the metadata of the image.
-  ImageInfo image_info;
-
   // The updated partitions.
   std::vector<PartitionConfig> partitions;
 
diff --git a/payload_generator/payload_generation_config_android_unittest.cc b/payload_generator/payload_generation_config_android_unittest.cc
index 44eaf55..e87b034 100644
--- a/payload_generator/payload_generation_config_android_unittest.cc
+++ b/payload_generator/payload_generation_config_android_unittest.cc
@@ -138,8 +138,7 @@
   }
 
   ImageConfig image_config_;
-  test_utils::ScopedTempFile temp_file_{
-      "PayloadGenerationConfigAndroidTest.XXXXXX"};
+  ScopedTempFile temp_file_{"PayloadGenerationConfigAndroidTest.XXXXXX"};
 };
 
 TEST_F(PayloadGenerationConfigAndroidTest, LoadVerityConfigSimpleTest) {
diff --git a/payload_generator/payload_properties.cc b/payload_generator/payload_properties.cc
index bc82eb7..bcf4fbd 100644
--- a/payload_generator/payload_properties.cc
+++ b/payload_generator/payload_properties.cc
@@ -47,8 +47,6 @@
 // These are needed by the Nebraska and devserver.
 const char kPayloadPropertyJsonPayloadSize[] = "size";
 const char kPayloadPropertyJsonIsDelta[] = "is_delta";
-const char kPayloadPropertyJsonTargetVersion[] = "target_version";
-const char kPayloadPropertyJsonSourceVersion[] = "source_version";
 }  // namespace
 
 PayloadProperties::PayloadProperties(const string& payload_path)
@@ -65,10 +63,6 @@
   properties.SetInteger(kPayloadPropertyJsonPayloadSize, payload_size_);
   properties.SetString(kPayloadPropertyJsonPayloadHash, payload_hash_);
   properties.SetBoolean(kPayloadPropertyJsonIsDelta, is_delta_);
-  properties.SetString(kPayloadPropertyJsonTargetVersion, target_version_);
-  if (is_delta_) {
-    properties.SetString(kPayloadPropertyJsonSourceVersion, source_version_);
-  }
 
   return base::JSONWriter::Write(properties, json_str);
 }
@@ -119,23 +113,11 @@
     metadata_signatures_ = base::JoinString(base64_signatures, ":");
   }
 
-  is_delta_ = manifest.has_old_image_info() ||
-              std::any_of(manifest.partitions().begin(),
+  is_delta_ = std::any_of(manifest.partitions().begin(),
                           manifest.partitions().end(),
                           [](const PartitionUpdate& part) {
                             return part.has_old_partition_info();
                           });
-
-  if (manifest.has_new_image_info()) {
-    target_version_ = manifest.new_image_info().version();
-  } else {
-    target_version_ = "99999.0.0";
-  }
-
-  // No need to set the source version if it was not a delta payload.
-  if (is_delta_ && manifest.has_old_image_info()) {
-    source_version_ = manifest.old_image_info().version();
-  }
   return true;
 }
 
diff --git a/payload_generator/payload_properties.h b/payload_generator/payload_properties.h
index 3b34511..846b181 100644
--- a/payload_generator/payload_properties.h
+++ b/payload_generator/payload_properties.h
@@ -62,9 +62,6 @@
   // Whether the payload is a delta (true) or full (false).
   bool is_delta_;
 
-  std::string target_version_;
-  std::string source_version_;
-
   DISALLOW_COPY_AND_ASSIGN(PayloadProperties);
 };
 
diff --git a/payload_generator/payload_properties_unittest.cc b/payload_generator/payload_properties_unittest.cc
index e0072fc..0ff364f 100644
--- a/payload_generator/payload_properties_unittest.cc
+++ b/payload_generator/payload_properties_unittest.cc
@@ -40,7 +40,6 @@
 #include "update_engine/payload_generator/payload_file.h"
 #include "update_engine/payload_generator/payload_generation_config.h"
 
-using chromeos_update_engine::test_utils::ScopedTempFile;
 using std::string;
 using std::unique_ptr;
 using std::vector;
@@ -57,19 +56,9 @@
     PayloadGenerationConfig config;
     config.version.major = kBrilloMajorPayloadVersion;
     config.version.minor = kSourceMinorPayloadVersion;
-    config.source.image_info.set_version("123.0.0");
-    config.target.image_info.set_version("456.7.8");
     PayloadFile payload;
     EXPECT_TRUE(payload.Init(config));
 
-    const string kTempFileTemplate = "temp_data.XXXXXX";
-    int data_file_fd;
-    string temp_file_path;
-    EXPECT_TRUE(
-        utils::MakeTempFile(kTempFileTemplate, &temp_file_path, &data_file_fd));
-    ScopedPathUnlinker temp_file_unlinker(temp_file_path);
-    EXPECT_LE(0, data_file_fd);
-
     const auto SetupPartitionConfig =
         [](PartitionConfig* config, const string& path, size_t size) {
           config->path = path;
@@ -79,8 +68,8 @@
       string zeros(size, '\0');
       EXPECT_TRUE(utils::WriteFile(path, zeros.c_str(), zeros.size()));
     };
-    ScopedTempFile old_part_file;
-    ScopedTempFile new_part_file;
+    ScopedTempFile old_part_file("old_part.XXXXXX");
+    ScopedTempFile new_part_file("new_part.XXXXXX");
     PartitionConfig old_part(kPartitionNameRoot);
     PartitionConfig new_part(kPartitionNameRoot);
     SetupPartitionConfig(&old_part, old_part_file.path(), 0);
@@ -93,19 +82,20 @@
 
     vector<AnnotatedOperation> aops;
     off_t data_file_size = 0;
-    BlobFileWriter blob_file_writer(data_file_fd, &data_file_size);
+    ScopedTempFile data_file("temp_data.XXXXXX", true);
+    BlobFileWriter blob_file_writer(data_file.fd(), &data_file_size);
     // Generate the operations using the strategy we selected above.
     EXPECT_TRUE(strategy->GenerateOperations(
         config, old_part, new_part, &blob_file_writer, &aops));
 
-    payload.AddPartition(old_part, new_part, aops, {});
+    payload.AddPartition(old_part, new_part, aops, {}, 0);
 
     uint64_t metadata_size;
     EXPECT_TRUE(payload.WritePayload(
-        payload_file.path(), temp_file_path, "", &metadata_size));
+        payload_file_.path(), data_file.path(), "", &metadata_size));
   }
 
-  ScopedTempFile payload_file;
+  ScopedTempFile payload_file_{"payload_file.XXXXXX"};
 };
 
 // Validate the hash of file exists within the output.
@@ -114,28 +104,26 @@
       "{"
       R"("is_delta":true,)"
       R"("metadata_signature":"",)"
-      R"("metadata_size":187,)"
-      R"("sha256_hex":"Rtrj9v3xXhrAi1741HAojtGxAQEOZ7mDyhzskIF4PJc=",)"
-      R"("size":233,)"
-      R"("source_version":"123.0.0",)"
-      R"("target_version":"456.7.8",)"
+      R"("metadata_size":165,)"
+      R"("sha256_hex":"cV7kfZBH3K0B6QJHxxykDh6b6x0WgVOmc63whPLOy7U=",)"
+      R"("size":211,)"
       R"("version":2)"
       "}";
   string json;
   EXPECT_TRUE(
-      PayloadProperties(payload_file.path()).GetPropertiesAsJson(&json));
+      PayloadProperties(payload_file_.path()).GetPropertiesAsJson(&json));
   EXPECT_EQ(kJsonProperties, json) << "JSON contents:\n" << json;
 }
 
 // Validate the hash of file and metadata are within the output.
 TEST_F(PayloadPropertiesTest, GetPropertiesAsKeyValueTestHash) {
   constexpr char kKeyValueProperties[] =
-      "FILE_HASH=Rtrj9v3xXhrAi1741HAojtGxAQEOZ7mDyhzskIF4PJc=\n"
-      "FILE_SIZE=233\n"
-      "METADATA_HASH=kiXTexy/s2aPttf4+r8KRZWYZ6FYvwhU6rJGcnnI+U0=\n"
-      "METADATA_SIZE=187\n";
+      "FILE_HASH=cV7kfZBH3K0B6QJHxxykDh6b6x0WgVOmc63whPLOy7U=\n"
+      "FILE_SIZE=211\n"
+      "METADATA_HASH=aEKYyzJt2E8Gz8fzB+gmekN5mriotZCSq6R+kDfdeV4=\n"
+      "METADATA_SIZE=165\n";
   string key_value;
-  EXPECT_TRUE(PayloadProperties{payload_file.path()}.GetPropertiesAsKeyValue(
+  EXPECT_TRUE(PayloadProperties{payload_file_.path()}.GetPropertiesAsKeyValue(
       &key_value));
   EXPECT_EQ(kKeyValueProperties, key_value) << "Key Value contents:\n"
                                             << key_value;
diff --git a/payload_generator/payload_signer.cc b/payload_generator/payload_signer.cc
index c3264c1..d9f0dd7 100644
--- a/payload_generator/payload_signer.cc
+++ b/payload_generator/payload_signer.cc
@@ -241,8 +241,6 @@
                                            DeltaArchiveManifest* manifest) {
   LOG(INFO) << "Making room for signature in file";
   manifest->set_signatures_offset(signature_blob_offset);
-  LOG(INFO) << "set? " << manifest->has_signatures_offset();
-  manifest->set_signatures_offset(signature_blob_offset);
   manifest->set_signatures_size(signature_blob_length);
 }
 
@@ -321,7 +319,6 @@
                                                  signature.data(),
                                                  rsa,
                                                  RSA_NO_PADDING);
-
     if (signature_size < 0) {
       LOG(ERROR) << "Signing hash failed: "
                  << ERR_error_string(ERR_get_error(), nullptr);
diff --git a/payload_generator/payload_signer_unittest.cc b/payload_generator/payload_signer_unittest.cc
index fe62997..2a0b394 100644
--- a/payload_generator/payload_signer_unittest.cc
+++ b/payload_generator/payload_signer_unittest.cc
@@ -167,7 +167,7 @@
 }
 
 TEST_F(PayloadSignerTest, SkipMetadataSignatureTest) {
-  test_utils::ScopedTempFile payload_file("payload.XXXXXX");
+  ScopedTempFile payload_file("payload.XXXXXX");
   PayloadGenerationConfig config;
   config.version.major = kBrilloMajorPayloadVersion;
   PayloadFile payload;
@@ -194,7 +194,7 @@
 }
 
 TEST_F(PayloadSignerTest, VerifySignedPayloadTest) {
-  test_utils::ScopedTempFile payload_file("payload.XXXXXX");
+  ScopedTempFile payload_file("payload.XXXXXX");
   PayloadGenerationConfig config;
   config.version.major = kBrilloMajorPayloadVersion;
   PayloadFile payload;
diff --git a/payload_generator/squashfs_filesystem.cc b/payload_generator/squashfs_filesystem.cc
index 6152d7d..a41e283 100644
--- a/payload_generator/squashfs_filesystem.cc
+++ b/payload_generator/squashfs_filesystem.cc
@@ -72,15 +72,10 @@
 }
 
 bool GetFileMapContent(const string& sqfs_path, string* map) {
-  // Create a tmp file
-  string map_file;
-  TEST_AND_RETURN_FALSE(
-      utils::MakeTempFile("squashfs_file_map.XXXXXX", &map_file, nullptr));
-  ScopedPathUnlinker map_unlinker(map_file);
-
+  ScopedTempFile map_file("squashfs_file_map.XXXXXX");
   // Run unsquashfs to get the system file map.
   // unsquashfs -m <map-file> <squashfs-file>
-  vector<string> cmd = {"unsquashfs", "-m", map_file, sqfs_path};
+  vector<string> cmd = {"unsquashfs", "-m", map_file.path(), sqfs_path};
   string stdout, stderr;
   int exit_code;
   if (!Subprocess::SynchronousExec(cmd, &exit_code, &stdout, &stderr) ||
@@ -89,7 +84,7 @@
                << stdout << " and stderr content: " << stderr;
     return false;
   }
-  TEST_AND_RETURN_FALSE(utils::ReadFile(map_file, map));
+  TEST_AND_RETURN_FALSE(utils::ReadFile(map_file.path(), map));
   return true;
 }
 
diff --git a/power_manager_android.cc b/power_manager_android.cc
deleted file mode 100644
index 63a0351..0000000
--- a/power_manager_android.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-//
-// 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/power_manager_android.h"
-
-#include <memory>
-
-#include <base/logging.h>
-
-namespace chromeos_update_engine {
-
-namespace power_manager {
-std::unique_ptr<PowerManagerInterface> CreatePowerManager() {
-  return std::unique_ptr<PowerManagerInterface>(new PowerManagerAndroid());
-}
-}  // namespace power_manager
-
-bool PowerManagerAndroid::RequestReboot() {
-  LOG(WARNING) << "PowerManager not implemented.";
-  return false;
-}
-
-}  // namespace chromeos_update_engine
diff --git a/power_manager_android.h b/power_manager_android.h
deleted file mode 100644
index 86399ab..0000000
--- a/power_manager_android.h
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-// 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_POWER_MANAGER_ANDROID_H_
-#define UPDATE_ENGINE_POWER_MANAGER_ANDROID_H_
-
-#include <base/macros.h>
-
-#include "update_engine/power_manager_interface.h"
-
-namespace chromeos_update_engine {
-
-class PowerManagerAndroid : public PowerManagerInterface {
- public:
-  PowerManagerAndroid() = default;
-  ~PowerManagerAndroid() override = default;
-
-  // PowerManagerInterface overrides.
-  bool RequestReboot() override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(PowerManagerAndroid);
-};
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_POWER_MANAGER_ANDROID_H_
diff --git a/sample_images/generate_payloads.sh b/sample_images/generate_payloads.sh
new file mode 100755
index 0000000..ee64229
--- /dev/null
+++ b/sample_images/generate_payloads.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+#
+# 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.
+#
+
+# This script generates some sample payloads from the images in
+# sample_images.tar.bz2. and packages them in the sample_payloads.tar.xz file.
+# The payloads are then used in paycheck_unittests.py. The file names
+# must match the ones used in update_payload ebuild and paycheck_unittests.py.
+
+set -e
+
+TEMP_IMG_DIR=./sample_images
+OLD_KERNEL="${TEMP_IMG_DIR}/disk_ext2_4k_empty.img"
+OLD_ROOT="${TEMP_IMG_DIR}/disk_sqfs_empty.img"
+NEW_KERNEL="${TEMP_IMG_DIR}/disk_ext2_4k.img"
+NEW_ROOT="${TEMP_IMG_DIR}/disk_sqfs_default.img"
+
+
+mkdir -p "${TEMP_IMG_DIR}"
+tar -xvf sample_images.tar.bz2 -C "${TEMP_IMG_DIR}"
+
+echo "Generating full payload"
+delta_generator --out_file=full_payload.bin \
+                --partition_names=kernel:root \
+                --new_partitions="${NEW_KERNEL}":"${NEW_ROOT}"
+
+echo "Generating delta payload"
+delta_generator --out_file=delta_payload.bin \
+                --partition_names=kernel:root \
+                --new_partitions="${NEW_KERNEL}":"${NEW_ROOT}" \
+                --old_partitions="${OLD_KERNEL}":"${OLD_ROOT}" --minor_version=6
+
+echo "Creating sample_payloads.tar"
+tar -cJf sample_payloads.tar.xz {delta,full}_payload.bin
+
+rm -rf "${TEMP_IMG_DIR}" {delta,full}_payload.bin
+
+echo "Done"
diff --git a/sample_images/sample_payloads.tar.xz b/sample_images/sample_payloads.tar.xz
new file mode 100644
index 0000000..d0bf6d9
--- /dev/null
+++ b/sample_images/sample_payloads.tar.xz
Binary files differ
diff --git a/scripts/brillo_update_payload b/scripts/brillo_update_payload
index 3bc87bd..32bb92c 100755
--- a/scripts/brillo_update_payload
+++ b/scripts/brillo_update_payload
@@ -196,6 +196,7 @@
   DEFINE_string is_partial_update "" \
     "Optional: True if the payload is for partial update. i.e. it only updates \
 a subset of partitions on device."
+  DEFINE_string full_boot "" "Will include full boot image"
 fi
 if [[ "${COMMAND}" == "hash" || "${COMMAND}" == "sign" ]]; then
   DEFINE_string unsigned_payload "" "Path to the input unsigned payload."
@@ -649,7 +650,12 @@
     fi
     partition_names+="${part}"
     new_partitions+="${DST_PARTITIONS[${part}]}"
-    old_partitions+="${SRC_PARTITIONS[${part}]:-}"
+    if [ "${FLAGS_full_boot}" == "true" ] && [ "${part}" == "boot" ]; then
+      # Skip boot partition.
+      old_partitions+=""
+    else
+      old_partitions+="${SRC_PARTITIONS[${part}]:-}"
+    fi
     new_mapfiles+="${DST_PARTITIONS_MAP[${part}]:-}"
     old_mapfiles+="${SRC_PARTITIONS_MAP[${part}]:-}"
   done
diff --git a/scripts/paycheck.py b/scripts/paycheck.py
index f4ccca2..cb1713f 100755
--- a/scripts/paycheck.py
+++ b/scripts/paycheck.py
@@ -27,6 +27,7 @@
 import sys
 import tempfile
 
+# pylint: disable=redefined-builtin
 from six.moves import zip
 from update_payload import error
 
@@ -92,9 +93,6 @@
   check_args.add_argument('-c', '--check', action='store_true', default=False,
                           help=('force payload integrity check (e.g. before '
                                 'applying)'))
-  check_args.add_argument('-D', '--describe', action='store_true',
-                          default=False,
-                          help='Print a friendly description of the payload.')
   check_args.add_argument('-r', '--report', metavar='FILE',
                           help="dump payload report (`-' for stdout)")
   check_args.add_argument('-t', '--type', dest='assert_type',
@@ -209,9 +207,6 @@
       # Initialize payload.
       payload.Init()
 
-      if args.describe:
-        payload.Describe()
-
       # Perform payload integrity checks.
       if args.check:
         report_file = None
diff --git a/scripts/paycheck_unittest.py b/scripts/paycheck_unittest.py
new file mode 100755
index 0000000..a90d269
--- /dev/null
+++ b/scripts/paycheck_unittest.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+"""Unit testing paycheck.py."""
+
+# This test requires new (Y) and old (X) images, as well as a full payload
+# from image Y and a delta payload from Y to X for each partition.
+# Payloads are from sample_images/generate_payloads.
+#
+# The test performs the following:
+#
+# - It statically applies the full and delta payloads.
+#
+# - It applies full_payload to yield a new kernel (kern.part) and rootfs
+#   (root.part) and compares them to the new image partitions.
+#
+# - It applies delta_payload to the old image to yield a new kernel and rootfs
+#   and compares them to the new image partitions.
+#
+# Previously test_paycheck.sh. Run with update_payload ebuild.
+
+# Disable check for function names to avoid errors based on old code
+# pylint: disable=invalid-name
+
+import filecmp
+import os
+import subprocess
+import unittest
+
+
+class PaycheckTest(unittest.TestCase):
+  """Test paycheck functions."""
+
+  def setUp(self):
+    self.tmpdir = os.getenv('T')
+
+    self._full_payload = os.path.join(self.tmpdir, 'full_payload.bin')
+    self._delta_payload = os.path.join(self.tmpdir, 'delta_payload.bin')
+
+    self._new_kernel = os.path.join(self.tmpdir, 'disk_ext2_4k.img')
+    self._new_root = os.path.join(self.tmpdir, 'disk_sqfs_default.img')
+    self._old_kernel = os.path.join(self.tmpdir,
+                                    'disk_ext2_4k_empty.img')
+    self._old_root = os.path.join(self.tmpdir, 'disk_sqfs_empty.img')
+
+    # Temp output files.
+    self._kernel_part = os.path.join(self.tmpdir, 'kern.part')
+    self._root_part = os.path.join(self.tmpdir, 'root.part')
+
+  def checkPayload(self, type_arg, payload):
+    """Checks Payload."""
+    self.assertEqual(0, subprocess.check_call(['./paycheck.py', '-t',
+                                               type_arg, payload]))
+
+  def testFullPayload(self):
+    """Checks the full payload statically."""
+    self.checkPayload('full', self._full_payload)
+
+  def testDeltaPayload(self):
+    """Checks the delta payload statically."""
+    self.checkPayload('delta', self._delta_payload)
+
+  def testApplyFullPayload(self):
+    """Applies full payloads and compares results to new sample images."""
+    self.assertEqual(0, subprocess.check_call(['./paycheck.py',
+                                               self._full_payload,
+                                               '--part_names', 'kernel', 'root',
+                                               '--out_dst_part_paths',
+                                               self._kernel_part,
+                                               self._root_part]))
+
+    # Check if generated full image is equal to sample image.
+    self.assertTrue(filecmp.cmp(self._kernel_part, self._new_kernel))
+    self.assertTrue(filecmp.cmp(self._root_part, self._new_root))
+
+  def testApplyDeltaPayload(self):
+    """Applies delta to old image and checks against new sample images."""
+    self.assertEqual(0, subprocess.check_call(['./paycheck.py',
+                                               self._delta_payload,
+                                               '--part_names', 'kernel', 'root',
+                                               '--src_part_paths',
+                                               self._old_kernel, self._old_root,
+                                               '--out_dst_part_paths',
+                                               self._kernel_part,
+                                               self._root_part]))
+
+    self.assertTrue(filecmp.cmp(self._kernel_part, self._new_kernel))
+    self.assertTrue(filecmp.cmp(self._root_part, self._new_root))
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/scripts/payload_info.py b/scripts/payload_info.py
index 7625ee8..8343d21 100755
--- a/scripts/payload_info.py
+++ b/scripts/payload_info.py
@@ -75,8 +75,11 @@
       DisplayValue('  Number of "%s" ops' % partition.partition_name,
                    len(partition.operations))
     for partition in manifest.partitions:
-      DisplayValue("Timestamp for " +
+      DisplayValue("  Timestamp for " +
                    partition.partition_name, partition.version)
+    for partition in manifest.partitions:
+      DisplayValue("  COW Size for " +
+                   partition.partition_name, partition.estimate_cow_size)
     DisplayValue('Block size', manifest.block_size)
     DisplayValue('Minor version', manifest.minor_version)
 
diff --git a/scripts/run_unittests b/scripts/run_unittests
index 0d301ba..db5ed73 100755
--- a/scripts/run_unittests
+++ b/scripts/run_unittests
@@ -26,5 +26,6 @@
 done
 
 ./payload_info_unittest.py
+./paycheck_unittest.py
 
 exit 0
diff --git a/scripts/test_paycheck.sh b/scripts/test_paycheck.sh
deleted file mode 100755
index 239b984..0000000
--- a/scripts/test_paycheck.sh
+++ /dev/null
@@ -1,169 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2013 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.
-#
-
-# A test script for paycheck.py and the update_payload.py library.
-#
-# This script requires three payload files, along with a metadata signature for
-# each, and a public key for verifying signatures. Payload include:
-#
-# - A full payload for release X (old_full_payload)
-#
-# - A full payload for release Y (new_full_payload), where Y > X
-#
-# - A delta payload from X to Y (delta_payload)
-#
-# The test performs the following:
-#
-# - It verifies each payload against its metadata signature, also asserting the
-#   payload type. Another artifact is a human-readable payload report, which
-#   is output to stdout to be inspected by the user.
-#
-# - It applies old_full_payload to yield old kernel (old_kern.part) and rootfs
-#   (old_root.part) partitions.
-#
-# - It applies delta_payload to old_{kern,root}.part to yield new kernel
-#   (new_delta_kern.part) and rootfs (new_delta_root.part) partitions.
-#
-# - It applies new_full_payload to yield reference new kernel
-#   (new_full_kern.part) and rootfs (new_full_root.part) partitions.
-#
-# - It compares new_{delta,full}_kern.part and new_{delta,full}_root.part to
-#   ensure that they are binary identical.
-#
-# If all steps have completed successfully we know with high certainty that
-# paycheck.py (and hence update_payload.py) correctly parses both full and delta
-# payloads, and applies them to yield the expected result. Finally, each
-# paycheck.py execution is timed.
-
-
-# Stop on errors, unset variables.
-set -e
-set -u
-
-# Temporary image files.
-OLD_KERN_PART=old_kern.part
-OLD_ROOT_PART=old_root.part
-NEW_DELTA_KERN_PART=new_delta_kern.part
-NEW_DELTA_ROOT_PART=new_delta_root.part
-NEW_FULL_KERN_PART=new_full_kern.part
-NEW_FULL_ROOT_PART=new_full_root.part
-CROS_PARTS="kernel root"
-
-
-log() {
-  echo "$@" >&2
-}
-
-die() {
-  log "$@"
-  exit 1
-}
-
-usage_and_exit() {
-  cat >&2 <<EOF
-Usage: ${0##*/} old_full_payload delta_payload new_full_payload
-EOF
-  exit
-}
-
-check_payload() {
-  payload_file=$1
-  payload_type=$2
-
-  time ${paycheck} -t ${payload_type} ${payload_file}
-}
-
-apply_full_payload() {
-  payload_file=$1
-  out_dst_kern_part="$2/$3"
-  out_dst_root_part="$2/$4"
-
-  time ${paycheck} ${payload_file} \
-    --part_names ${CROS_PARTS} \
-    --out_dst_part_paths ${out_dst_kern_part} ${out_dst_root_part}
-}
-
-apply_delta_payload() {
-  payload_file=$1
-  out_dst_kern_part="$2/$3"
-  out_dst_root_part="$2/$4"
-  dst_kern_part="$2/$5"
-  dst_root_part="$2/$6"
-  src_kern_part="$2/$7"
-  src_root_part="$2/$8"
-
-  time ${paycheck} ${payload_file} \
-    --part_names ${CROS_PARTS} \
-    --out_dst_part_paths ${out_dst_kern_part} ${out_dst_root_part} \
-    --dst_part_paths ${dst_kern_part} ${dst_root_part} \
-    --src_part_paths ${src_kern_part} ${src_root_part}
-}
-
-main() {
-  # Read command-line arguments.
-  if [ $# == 1 ] && [ "$1" == "-h" ]; then
-    usage_and_exit
-  elif [ $# != 3 ]; then
-    die "Error: unexpected number of arguments"
-  fi
-  old_full_payload="$1"
-  delta_payload="$2"
-  new_full_payload="$3"
-
-  # Find paycheck.py
-  paycheck=${0%/*}/paycheck.py
-  if [ -z "${paycheck}" ] || [ ! -x ${paycheck} ]; then
-    die "cannot find ${paycheck} or file is not executable"
-  fi
-
-  # Check the payloads statically.
-  log "Checking payloads..."
-  check_payload "${old_full_payload}" full
-  check_payload "${new_full_payload}" full
-  check_payload "${delta_payload}" delta
-  log "Done"
-
-  # Apply full/delta payloads and verify results are identical.
-  tmpdir="$(mktemp -d --tmpdir test_paycheck.XXXXXXXX)"
-  log "Initiating application of payloads at $tmpdir"
-
-  log "Applying old full payload..."
-  apply_full_payload "${old_full_payload}" "${tmpdir}" "${OLD_KERN_PART}" \
-    "${OLD_ROOT_PART}"
-  log "Done"
-
-  log "Applying new full payload..."
-  apply_full_payload "${new_full_payload}" "${tmpdir}" "${NEW_FULL_KERN_PART}" \
-    "${NEW_FULL_ROOT_PART}"
-  log "Done"
-
-  log "Applying delta payload to old partitions..."
-  apply_delta_payload "${delta_payload}" "${tmpdir}" "${NEW_DELTA_KERN_PART}" \
-    "${NEW_DELTA_ROOT_PART}" "${NEW_FULL_KERN_PART}" \
-    "${NEW_FULL_ROOT_PART}" "${OLD_KERN_PART}" "${OLD_ROOT_PART}"
-  log "Done"
-
-  log "Comparing results of delta and new full updates..."
-  diff "${tmpdir}/${NEW_FULL_KERN_PART}" "${tmpdir}/${NEW_DELTA_KERN_PART}"
-  diff "${tmpdir}/${NEW_FULL_ROOT_PART}" "${tmpdir}/${NEW_DELTA_ROOT_PART}"
-  log "Done"
-
-  log "Cleaning up"
-  rm -fr "${tmpdir}"
-}
-
-main "$@"
diff --git a/scripts/update_device.py b/scripts/update_device.py
index 1cd4b6a..757c147 100755
--- a/scripts/update_device.py
+++ b/scripts/update_device.py
@@ -17,6 +17,7 @@
 
 """Send an A/B update to an Android device over adb."""
 
+from __future__ import print_function
 from __future__ import absolute_import
 
 import argparse
@@ -305,6 +306,7 @@
     logging.info('Server Terminated')
 
   def StopServer(self):
+    self._httpd.shutdown()
     self._httpd.socket.close()
 
 
@@ -318,13 +320,13 @@
   """Return the command to run to start the update in the Android device."""
   ota = AndroidOTAPackage(ota_filename, secondary)
   headers = ota.properties
-  headers += 'USER_AGENT=Dalvik (something, something)\n'
-  headers += 'NETWORK_ID=0\n'
-  headers += extra_headers
+  headers += b'USER_AGENT=Dalvik (something, something)\n'
+  headers += b'NETWORK_ID=0\n'
+  headers += extra_headers.encode()
 
   return ['update_engine_client', '--update', '--follow',
           '--payload=%s' % payload_url, '--offset=%d' % ota.offset,
-          '--size=%d' % ota.size, '--headers="%s"' % headers]
+          '--size=%d' % ota.size, '--headers="%s"' % headers.decode()]
 
 
 def OmahaUpdateCommand(omaha_url):
@@ -401,6 +403,8 @@
                       help='Extra headers to pass to the device.')
   parser.add_argument('--secondary', action='store_true',
                       help='Update with the secondary payload in the package.')
+  parser.add_argument('--no-slot-switch', action='store_true',
+                      help='Do not perform slot switch after the update.')
   args = parser.parse_args()
   logging.basicConfig(
       level=logging.WARNING if args.no_verbose else logging.INFO)
@@ -418,6 +422,9 @@
   help_cmd = ['shell', 'su', '0', 'update_engine_client', '--help']
   use_omaha = 'omaha' in dut.adb_output(help_cmd)
 
+  if args.no_slot_switch:
+    args.extra_headers += "\nSWITCH_SLOT_ON_REBOOT=0"
+
   if args.file:
     # Update via pushing a file to /data.
     device_ota_file = os.path.join(OTA_PACKAGE_PATH, 'debug.zip')
diff --git a/scripts/update_payload/checker.py b/scripts/update_payload/checker.py
index 4c65516..56a9370 100644
--- a/scripts/update_payload/checker.py
+++ b/scripts/update_payload/checker.py
@@ -35,6 +35,7 @@
 import os
 import subprocess
 
+# pylint: disable=redefined-builtin
 from six.moves import range
 
 from update_payload import common
@@ -71,6 +72,7 @@
     4: (_TYPE_DELTA,),
     5: (_TYPE_DELTA,),
     6: (_TYPE_DELTA,),
+    7: (_TYPE_DELTA,),
 }
 
 
@@ -1148,17 +1150,13 @@
       sig_report = report.AddSubReport(sig_name)
 
       # Check: Signature contains mandatory fields.
-      self._CheckMandatoryField(sig, 'version', sig_report, sig_name)
       self._CheckMandatoryField(sig, 'data', None, sig_name)
       sig_report.AddField('data len', len(sig.data))
 
       # Check: Signatures pertains to actual payload hash.
-      if sig.version == 1:
+      if sig.data:
         self._CheckSha256Signature(sig.data, pubkey_file_name,
                                    payload_hasher.digest(), sig_name)
-      else:
-        raise error.PayloadError('Unknown signature version (%d).' %
-                                 sig.version)
 
   def Run(self, pubkey_file_name=None, metadata_sig_file=None, metadata_size=0,
           part_sizes=None, report_out_file=None):
diff --git a/scripts/update_payload/payload.py b/scripts/update_payload/payload.py
index 78b8e2c..fe3a450 100644
--- a/scripts/update_payload/payload.py
+++ b/scripts/update_payload/payload.py
@@ -232,31 +232,6 @@
 
     self.is_init = True
 
-  def Describe(self):
-    """Emits the payload embedded description data to standard output."""
-    def _DescribeImageInfo(description, image_info):
-      """Display info about the image."""
-      def _DisplayIndentedValue(name, value):
-        print('  {:<14} {}'.format(name+':', value))
-
-      print('%s:' % description)
-      _DisplayIndentedValue('Channel', image_info.channel)
-      _DisplayIndentedValue('Board', image_info.board)
-      _DisplayIndentedValue('Version', image_info.version)
-      _DisplayIndentedValue('Key', image_info.key)
-
-      if image_info.build_channel != image_info.channel:
-        _DisplayIndentedValue('Build channel', image_info.build_channel)
-
-      if image_info.build_version != image_info.version:
-        _DisplayIndentedValue('Build version', image_info.build_version)
-
-    if self.manifest.HasField('old_image_info'):
-      _DescribeImageInfo('Old Image', self.manifest.old_image_info)
-
-    if self.manifest.HasField('new_image_info'):
-      _DescribeImageInfo('New Image', self.manifest.new_image_info)
-
   def _AssertInit(self):
     """Raises an exception if the object was not initialized."""
     if not self.is_init:
diff --git a/scripts/update_payload/update_metadata_pb2.py b/scripts/update_payload/update_metadata_pb2.py
index 841cd22..ea4bc59 100644
--- a/scripts/update_payload/update_metadata_pb2.py
+++ b/scripts/update_payload/update_metadata_pb2.py
@@ -18,7 +18,7 @@
   package='chromeos_update_engine',
   syntax='proto2',
   serialized_options=b'H\003',
-  serialized_pb=b'\n\x15update_metadata.proto\x12\x16\x63hromeos_update_engine\"1\n\x06\x45xtent\x12\x13\n\x0bstart_block\x18\x01 \x01(\x04\x12\x12\n\nnum_blocks\x18\x02 \x01(\x04\"\x9f\x01\n\nSignatures\x12@\n\nsignatures\x18\x01 \x03(\x0b\x32,.chromeos_update_engine.Signatures.Signature\x1aO\n\tSignature\x12\x13\n\x07version\x18\x01 \x01(\rB\x02\x18\x01\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x1f\n\x17unpadded_signature_size\x18\x03 \x01(\x07\"+\n\rPartitionInfo\x12\x0c\n\x04size\x18\x01 \x01(\x04\x12\x0c\n\x04hash\x18\x02 \x01(\x0c\"w\n\tImageInfo\x12\r\n\x05\x62oard\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\t\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\t\x12\x0f\n\x07version\x18\x04 \x01(\t\x12\x15\n\rbuild_channel\x18\x05 \x01(\t\x12\x15\n\rbuild_version\x18\x06 \x01(\t\"\xee\x03\n\x10InstallOperation\x12;\n\x04type\x18\x01 \x02(\x0e\x32-.chromeos_update_engine.InstallOperation.Type\x12\x13\n\x0b\x64\x61ta_offset\x18\x02 \x01(\x04\x12\x13\n\x0b\x64\x61ta_length\x18\x03 \x01(\x04\x12\x33\n\x0bsrc_extents\x18\x04 \x03(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x12\n\nsrc_length\x18\x05 \x01(\x04\x12\x33\n\x0b\x64st_extents\x18\x06 \x03(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x12\n\ndst_length\x18\x07 \x01(\x04\x12\x18\n\x10\x64\x61ta_sha256_hash\x18\x08 \x01(\x0c\x12\x17\n\x0fsrc_sha256_hash\x18\t \x01(\x0c\"\xad\x01\n\x04Type\x12\x0b\n\x07REPLACE\x10\x00\x12\x0e\n\nREPLACE_BZ\x10\x01\x12\x0c\n\x04MOVE\x10\x02\x1a\x02\x08\x01\x12\x0e\n\x06\x42SDIFF\x10\x03\x1a\x02\x08\x01\x12\x0f\n\x0bSOURCE_COPY\x10\x04\x12\x11\n\rSOURCE_BSDIFF\x10\x05\x12\x0e\n\nREPLACE_XZ\x10\x08\x12\x08\n\x04ZERO\x10\x06\x12\x0b\n\x07\x44ISCARD\x10\x07\x12\x11\n\rBROTLI_BSDIFF\x10\n\x12\x0c\n\x08PUFFDIFF\x10\t\"\xe8\x05\n\x0fPartitionUpdate\x12\x16\n\x0epartition_name\x18\x01 \x02(\t\x12\x17\n\x0frun_postinstall\x18\x02 \x01(\x08\x12\x18\n\x10postinstall_path\x18\x03 \x01(\t\x12\x17\n\x0f\x66ilesystem_type\x18\x04 \x01(\t\x12M\n\x17new_partition_signature\x18\x05 \x03(\x0b\x32,.chromeos_update_engine.Signatures.Signature\x12\x41\n\x12old_partition_info\x18\x06 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12\x41\n\x12new_partition_info\x18\x07 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12<\n\noperations\x18\x08 \x03(\x0b\x32(.chromeos_update_engine.InstallOperation\x12\x1c\n\x14postinstall_optional\x18\t \x01(\x08\x12=\n\x15hash_tree_data_extent\x18\n \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x38\n\x10hash_tree_extent\x18\x0b \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x1b\n\x13hash_tree_algorithm\x18\x0c \x01(\t\x12\x16\n\x0ehash_tree_salt\x18\r \x01(\x0c\x12\x37\n\x0f\x66\x65\x63_data_extent\x18\x0e \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x32\n\nfec_extent\x18\x0f \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x14\n\tfec_roots\x18\x10 \x01(\r:\x01\x32\x12\x0f\n\x07version\x18\x11 \x01(\t\"L\n\x15\x44ynamicPartitionGroup\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0c\n\x04size\x18\x02 \x01(\x04\x12\x17\n\x0fpartition_names\x18\x03 \x03(\t\"s\n\x18\x44ynamicPartitionMetadata\x12=\n\x06groups\x18\x01 \x03(\x0b\x32-.chromeos_update_engine.DynamicPartitionGroup\x12\x18\n\x10snapshot_enabled\x18\x02 \x01(\x08\"\xe1\x06\n\x14\x44\x65ltaArchiveManifest\x12H\n\x12install_operations\x18\x01 \x03(\x0b\x32(.chromeos_update_engine.InstallOperationB\x02\x18\x01\x12O\n\x19kernel_install_operations\x18\x02 \x03(\x0b\x32(.chromeos_update_engine.InstallOperationB\x02\x18\x01\x12\x18\n\nblock_size\x18\x03 \x01(\r:\x04\x34\x30\x39\x36\x12\x19\n\x11signatures_offset\x18\x04 \x01(\x04\x12\x17\n\x0fsignatures_size\x18\x05 \x01(\x04\x12\x42\n\x0fold_kernel_info\x18\x06 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12\x42\n\x0fnew_kernel_info\x18\x07 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12\x42\n\x0fold_rootfs_info\x18\x08 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12\x42\n\x0fnew_rootfs_info\x18\t \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12\x39\n\x0eold_image_info\x18\n \x01(\x0b\x32!.chromeos_update_engine.ImageInfo\x12\x39\n\x0enew_image_info\x18\x0b \x01(\x0b\x32!.chromeos_update_engine.ImageInfo\x12\x18\n\rminor_version\x18\x0c \x01(\r:\x01\x30\x12;\n\npartitions\x18\r \x03(\x0b\x32\'.chromeos_update_engine.PartitionUpdate\x12\x15\n\rmax_timestamp\x18\x0e \x01(\x03\x12T\n\x1a\x64ynamic_partition_metadata\x18\x0f \x01(\x0b\x32\x30.chromeos_update_engine.DynamicPartitionMetadata\x12\x16\n\x0epartial_update\x18\x10 \x01(\x08\x42\x02H\x03'
+  serialized_pb=b'\n\x15update_metadata.proto\x12\x16\x63hromeos_update_engine\"1\n\x06\x45xtent\x12\x13\n\x0bstart_block\x18\x01 \x01(\x04\x12\x12\n\nnum_blocks\x18\x02 \x01(\x04\"\x9f\x01\n\nSignatures\x12@\n\nsignatures\x18\x01 \x03(\x0b\x32,.chromeos_update_engine.Signatures.Signature\x1aO\n\tSignature\x12\x13\n\x07version\x18\x01 \x01(\rB\x02\x18\x01\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x1f\n\x17unpadded_signature_size\x18\x03 \x01(\x07\"+\n\rPartitionInfo\x12\x0c\n\x04size\x18\x01 \x01(\x04\x12\x0c\n\x04hash\x18\x02 \x01(\x0c\"w\n\tImageInfo\x12\r\n\x05\x62oard\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\t\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\t\x12\x0f\n\x07version\x18\x04 \x01(\t\x12\x15\n\rbuild_channel\x18\x05 \x01(\t\x12\x15\n\rbuild_version\x18\x06 \x01(\t\"\xee\x03\n\x10InstallOperation\x12;\n\x04type\x18\x01 \x02(\x0e\x32-.chromeos_update_engine.InstallOperation.Type\x12\x13\n\x0b\x64\x61ta_offset\x18\x02 \x01(\x04\x12\x13\n\x0b\x64\x61ta_length\x18\x03 \x01(\x04\x12\x33\n\x0bsrc_extents\x18\x04 \x03(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x12\n\nsrc_length\x18\x05 \x01(\x04\x12\x33\n\x0b\x64st_extents\x18\x06 \x03(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x12\n\ndst_length\x18\x07 \x01(\x04\x12\x18\n\x10\x64\x61ta_sha256_hash\x18\x08 \x01(\x0c\x12\x17\n\x0fsrc_sha256_hash\x18\t \x01(\x0c\"\xad\x01\n\x04Type\x12\x0b\n\x07REPLACE\x10\x00\x12\x0e\n\nREPLACE_BZ\x10\x01\x12\x0c\n\x04MOVE\x10\x02\x1a\x02\x08\x01\x12\x0e\n\x06\x42SDIFF\x10\x03\x1a\x02\x08\x01\x12\x0f\n\x0bSOURCE_COPY\x10\x04\x12\x11\n\rSOURCE_BSDIFF\x10\x05\x12\x0e\n\nREPLACE_XZ\x10\x08\x12\x08\n\x04ZERO\x10\x06\x12\x0b\n\x07\x44ISCARD\x10\x07\x12\x11\n\rBROTLI_BSDIFF\x10\n\x12\x0c\n\x08PUFFDIFF\x10\t\"\xcf\x01\n\x11\x43owMergeOperation\x12<\n\x04type\x18\x01 \x01(\x0e\x32..chromeos_update_engine.CowMergeOperation.Type\x12\x32\n\nsrc_extent\x18\x02 \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x32\n\ndst_extent\x18\x03 \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\"\x14\n\x04Type\x12\x0c\n\x08\x43OW_COPY\x10\x00\"\xc8\x06\n\x0fPartitionUpdate\x12\x16\n\x0epartition_name\x18\x01 \x02(\t\x12\x17\n\x0frun_postinstall\x18\x02 \x01(\x08\x12\x18\n\x10postinstall_path\x18\x03 \x01(\t\x12\x17\n\x0f\x66ilesystem_type\x18\x04 \x01(\t\x12M\n\x17new_partition_signature\x18\x05 \x03(\x0b\x32,.chromeos_update_engine.Signatures.Signature\x12\x41\n\x12old_partition_info\x18\x06 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12\x41\n\x12new_partition_info\x18\x07 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12<\n\noperations\x18\x08 \x03(\x0b\x32(.chromeos_update_engine.InstallOperation\x12\x1c\n\x14postinstall_optional\x18\t \x01(\x08\x12=\n\x15hash_tree_data_extent\x18\n \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x38\n\x10hash_tree_extent\x18\x0b \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x1b\n\x13hash_tree_algorithm\x18\x0c \x01(\t\x12\x16\n\x0ehash_tree_salt\x18\r \x01(\x0c\x12\x37\n\x0f\x66\x65\x63_data_extent\x18\x0e \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x32\n\nfec_extent\x18\x0f \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x14\n\tfec_roots\x18\x10 \x01(\r:\x01\x32\x12\x0f\n\x07version\x18\x11 \x01(\t\x12\x43\n\x10merge_operations\x18\x12 \x03(\x0b\x32).chromeos_update_engine.CowMergeOperation\x12\x19\n\x11\x65stimate_cow_size\x18\x13 \x01(\x04\"L\n\x15\x44ynamicPartitionGroup\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0c\n\x04size\x18\x02 \x01(\x04\x12\x17\n\x0fpartition_names\x18\x03 \x03(\t\"s\n\x18\x44ynamicPartitionMetadata\x12=\n\x06groups\x18\x01 \x03(\x0b\x32-.chromeos_update_engine.DynamicPartitionGroup\x12\x18\n\x10snapshot_enabled\x18\x02 \x01(\x08\"\xe1\x06\n\x14\x44\x65ltaArchiveManifest\x12H\n\x12install_operations\x18\x01 \x03(\x0b\x32(.chromeos_update_engine.InstallOperationB\x02\x18\x01\x12O\n\x19kernel_install_operations\x18\x02 \x03(\x0b\x32(.chromeos_update_engine.InstallOperationB\x02\x18\x01\x12\x18\n\nblock_size\x18\x03 \x01(\r:\x04\x34\x30\x39\x36\x12\x19\n\x11signatures_offset\x18\x04 \x01(\x04\x12\x17\n\x0fsignatures_size\x18\x05 \x01(\x04\x12\x42\n\x0fold_kernel_info\x18\x06 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12\x42\n\x0fnew_kernel_info\x18\x07 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12\x42\n\x0fold_rootfs_info\x18\x08 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12\x42\n\x0fnew_rootfs_info\x18\t \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12\x39\n\x0eold_image_info\x18\n \x01(\x0b\x32!.chromeos_update_engine.ImageInfo\x12\x39\n\x0enew_image_info\x18\x0b \x01(\x0b\x32!.chromeos_update_engine.ImageInfo\x12\x18\n\rminor_version\x18\x0c \x01(\r:\x01\x30\x12;\n\npartitions\x18\r \x03(\x0b\x32\'.chromeos_update_engine.PartitionUpdate\x12\x15\n\rmax_timestamp\x18\x0e \x01(\x03\x12T\n\x1a\x64ynamic_partition_metadata\x18\x0f \x01(\x0b\x32\x30.chromeos_update_engine.DynamicPartitionMetadata\x12\x16\n\x0epartial_update\x18\x10 \x01(\x08\x42\x02H\x03'
 )
 
 
@@ -81,6 +81,24 @@
 )
 _sym_db.RegisterEnumDescriptor(_INSTALLOPERATION_TYPE)
 
+_COWMERGEOPERATION_TYPE = _descriptor.EnumDescriptor(
+  name='Type',
+  full_name='chromeos_update_engine.CowMergeOperation.Type',
+  filename=None,
+  file=DESCRIPTOR,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='COW_COPY', index=0, number=0,
+      serialized_options=None,
+      type=None),
+  ],
+  containing_type=None,
+  serialized_options=None,
+  serialized_start=1113,
+  serialized_end=1133,
+)
+_sym_db.RegisterEnumDescriptor(_COWMERGEOPERATION_TYPE)
+
 
 _EXTENT = _descriptor.Descriptor(
   name='Extent',
@@ -387,6 +405,52 @@
 )
 
 
+_COWMERGEOPERATION = _descriptor.Descriptor(
+  name='CowMergeOperation',
+  full_name='chromeos_update_engine.CowMergeOperation',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='type', full_name='chromeos_update_engine.CowMergeOperation.type', index=0,
+      number=1, type=14, cpp_type=8, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='src_extent', full_name='chromeos_update_engine.CowMergeOperation.src_extent', index=1,
+      number=2, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='dst_extent', full_name='chromeos_update_engine.CowMergeOperation.dst_extent', index=2,
+      number=3, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+    _COWMERGEOPERATION_TYPE,
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=926,
+  serialized_end=1133,
+)
+
+
 _PARTITIONUPDATE = _descriptor.Descriptor(
   name='PartitionUpdate',
   full_name='chromeos_update_engine.PartitionUpdate',
@@ -513,6 +577,20 @@
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='merge_operations', full_name='chromeos_update_engine.PartitionUpdate.merge_operations', index=17,
+      number=18, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='estimate_cow_size', full_name='chromeos_update_engine.PartitionUpdate.estimate_cow_size', index=18,
+      number=19, type=4, cpp_type=4, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
   ],
   extensions=[
   ],
@@ -525,8 +603,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=926,
-  serialized_end=1670,
+  serialized_start=1136,
+  serialized_end=1976,
 )
 
 
@@ -570,8 +648,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1672,
-  serialized_end=1748,
+  serialized_start=1978,
+  serialized_end=2054,
 )
 
 
@@ -608,8 +686,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1750,
-  serialized_end=1865,
+  serialized_start=2056,
+  serialized_end=2171,
 )
 
 
@@ -744,8 +822,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1868,
-  serialized_end=2733,
+  serialized_start=2174,
+  serialized_end=3039,
 )
 
 _SIGNATURES_SIGNATURE.containing_type = _SIGNATURES
@@ -754,6 +832,10 @@
 _INSTALLOPERATION.fields_by_name['src_extents'].message_type = _EXTENT
 _INSTALLOPERATION.fields_by_name['dst_extents'].message_type = _EXTENT
 _INSTALLOPERATION_TYPE.containing_type = _INSTALLOPERATION
+_COWMERGEOPERATION.fields_by_name['type'].enum_type = _COWMERGEOPERATION_TYPE
+_COWMERGEOPERATION.fields_by_name['src_extent'].message_type = _EXTENT
+_COWMERGEOPERATION.fields_by_name['dst_extent'].message_type = _EXTENT
+_COWMERGEOPERATION_TYPE.containing_type = _COWMERGEOPERATION
 _PARTITIONUPDATE.fields_by_name['new_partition_signature'].message_type = _SIGNATURES_SIGNATURE
 _PARTITIONUPDATE.fields_by_name['old_partition_info'].message_type = _PARTITIONINFO
 _PARTITIONUPDATE.fields_by_name['new_partition_info'].message_type = _PARTITIONINFO
@@ -762,6 +844,7 @@
 _PARTITIONUPDATE.fields_by_name['hash_tree_extent'].message_type = _EXTENT
 _PARTITIONUPDATE.fields_by_name['fec_data_extent'].message_type = _EXTENT
 _PARTITIONUPDATE.fields_by_name['fec_extent'].message_type = _EXTENT
+_PARTITIONUPDATE.fields_by_name['merge_operations'].message_type = _COWMERGEOPERATION
 _DYNAMICPARTITIONMETADATA.fields_by_name['groups'].message_type = _DYNAMICPARTITIONGROUP
 _DELTAARCHIVEMANIFEST.fields_by_name['install_operations'].message_type = _INSTALLOPERATION
 _DELTAARCHIVEMANIFEST.fields_by_name['kernel_install_operations'].message_type = _INSTALLOPERATION
@@ -778,6 +861,7 @@
 DESCRIPTOR.message_types_by_name['PartitionInfo'] = _PARTITIONINFO
 DESCRIPTOR.message_types_by_name['ImageInfo'] = _IMAGEINFO
 DESCRIPTOR.message_types_by_name['InstallOperation'] = _INSTALLOPERATION
+DESCRIPTOR.message_types_by_name['CowMergeOperation'] = _COWMERGEOPERATION
 DESCRIPTOR.message_types_by_name['PartitionUpdate'] = _PARTITIONUPDATE
 DESCRIPTOR.message_types_by_name['DynamicPartitionGroup'] = _DYNAMICPARTITIONGROUP
 DESCRIPTOR.message_types_by_name['DynamicPartitionMetadata'] = _DYNAMICPARTITIONMETADATA
@@ -827,6 +911,13 @@
   })
 _sym_db.RegisterMessage(InstallOperation)
 
+CowMergeOperation = _reflection.GeneratedProtocolMessageType('CowMergeOperation', (_message.Message,), {
+  'DESCRIPTOR' : _COWMERGEOPERATION,
+  '__module__' : 'update_metadata_pb2'
+  # @@protoc_insertion_point(class_scope:chromeos_update_engine.CowMergeOperation)
+  })
+_sym_db.RegisterMessage(CowMergeOperation)
+
 PartitionUpdate = _reflection.GeneratedProtocolMessageType('PartitionUpdate', (_message.Message,), {
   'DESCRIPTOR' : _PARTITIONUPDATE,
   '__module__' : 'update_metadata_pb2'
diff --git a/stable/Android.bp b/stable/Android.bp
index 337ae96..a415ac5 100644
--- a/stable/Android.bp
+++ b/stable/Android.bp
@@ -18,6 +18,13 @@
 // ========================================================
 aidl_interface {
     name: "libupdate_engine_stable",
+
+    // This header library is available to core and product modules.
+    // Right now, vendor_available is the only way to specify this.
+    // vendor modules should NOT use this library.
+    // TODO(b/150902910): change this to product_available.
+    vendor_available: true,
+
     srcs: [
         "android/os/IUpdateEngineStable.aidl",
         "android/os/IUpdateEngineStableCallback.aidl",
@@ -40,10 +47,10 @@
 
 // update_engine_stable_client (type: executable)
 // ========================================================
-// update_engine console client installed to APEXes
+// update_engine console client installed to APEXes.
 cc_binary {
     name: "update_engine_stable_client",
-
+    product_specific: true,
     header_libs: [
         "libupdate_engine_headers",
     ],
diff --git a/stable/update_engine_stable_client.cc b/stable/update_engine_stable_client.cc
index da203c4..17f66b6 100644
--- a/stable/update_engine_stable_client.cc
+++ b/stable/update_engine_stable_client.cc
@@ -32,7 +32,6 @@
 #include <android/binder_ibinder.h>
 #include <common/error_code.h>
 #include <gflags/gflags.h>
-#include <utils/StrongPointer.h>
 
 namespace chromeos_update_engine::internal {
 
diff --git a/test_http_server.cc b/test_http_server.cc
index 1c3a2e0..a2f1e05 100644
--- a/test_http_server.cc
+++ b/test_http_server.cc
@@ -190,7 +190,7 @@
                     string("HTTP/1.1 ") + Itoa(return_code) + " " +
                         GetHttpResponseDescription(return_code) +
                         EOL "Content-Type: application/octet-stream" EOL
-                        "Connection: close" EOL);
+                            "Connection: close" EOL);
   if (ret < 0)
     return -1;
   written += ret;
@@ -409,7 +409,6 @@
     return;
   WriteString(fd, "Connection: close" EOL);
   WriteString(fd, "Location: " + url + EOL);
-
 }
 
 // Generate a page not found error response with actual text payload. Return
diff --git a/update_boot_flags_action_unittest.cc b/update_boot_flags_action_unittest.cc
index 1b2bfa5..26cbe90 100644
--- a/update_boot_flags_action_unittest.cc
+++ b/update_boot_flags_action_unittest.cc
@@ -22,18 +22,17 @@
 #include <base/bind.h>
 #include <gtest/gtest.h>
 
-#include "update_engine/fake_system_state.h"
+#include "update_engine/common/fake_boot_control.h"
 
 namespace chromeos_update_engine {
 
 class UpdateBootFlagsActionTest : public ::testing::Test {
- public:
-  FakeSystemState fake_system_state_;
+ protected:
+  FakeBootControl boot_control_;
 };
 
 TEST_F(UpdateBootFlagsActionTest, SimpleTest) {
-  auto boot_control = fake_system_state_.fake_boot_control();
-  auto action = std::make_unique<UpdateBootFlagsAction>(boot_control);
+  auto action = std::make_unique<UpdateBootFlagsAction>(&boot_control_);
   ActionProcessor processor;
   processor.EnqueueAction(std::move(action));
 
@@ -49,9 +48,8 @@
   UpdateBootFlagsAction::updated_boot_flags_ = false;
   UpdateBootFlagsAction::is_running_ = false;
 
-  auto boot_control = fake_system_state_.fake_boot_control();
-  auto action1 = std::make_unique<UpdateBootFlagsAction>(boot_control);
-  auto action2 = std::make_unique<UpdateBootFlagsAction>(boot_control);
+  auto action1 = std::make_unique<UpdateBootFlagsAction>(&boot_control_);
+  auto action2 = std::make_unique<UpdateBootFlagsAction>(&boot_control_);
   ActionProcessor processor1, processor2;
   processor1.EnqueueAction(std::move(action1));
   processor2.EnqueueAction(std::move(action2));
diff --git a/update_engine.conf.chromeos b/update_engine.conf.chromeos
new file mode 100644
index 0000000..af213ad
--- /dev/null
+++ b/update_engine.conf.chromeos
@@ -0,0 +1,2 @@
+PAYLOAD_MAJOR_VERSION=2
+PAYLOAD_MINOR_VERSION=6
diff --git a/update_manager/android_things_policy.cc b/update_manager/android_things_policy.cc
index a76ea48..c4fa75a 100644
--- a/update_manager/android_things_policy.cc
+++ b/update_manager/android_things_policy.cc
@@ -58,10 +58,12 @@
   // Set the default return values.
   result->updates_enabled = true;
   result->target_channel.clear();
+  result->lts_tag.clear();
   result->target_version_prefix.clear();
   result->rollback_allowed = false;
   result->rollback_data_save_requested = false;
   result->rollback_allowed_milestones = -1;
+  result->rollback_on_channel_downgrade = false;
   result->interactive = false;
 
   // Build a list of policies to consult.  Note that each policy may modify the
diff --git a/update_manager/boxed_value.cc b/update_manager/boxed_value.cc
index b031dfc..907eb95 100644
--- a/update_manager/boxed_value.cc
+++ b/update_manager/boxed_value.cc
@@ -23,9 +23,10 @@
 
 #include <base/strings/string_number_conversions.h>
 #include <base/time/time.h>
+#include <base/version.h>
 
+#include "update_engine/common/connection_utils.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/connection_utils.h"
 #include "update_engine/update_manager/rollback_prefs.h"
 #include "update_engine/update_manager/shill_provider.h"
 #include "update_engine/update_manager/updater_provider.h"
@@ -234,4 +235,30 @@
   return retval;
 }
 
+template <>
+string BoxedValue::ValuePrinter<ChannelDowngradeBehavior>(const void* value) {
+  const ChannelDowngradeBehavior* val =
+      reinterpret_cast<const ChannelDowngradeBehavior*>(value);
+  switch (*val) {
+    case ChannelDowngradeBehavior::kUnspecified:
+      return "Unspecified";
+    case ChannelDowngradeBehavior::kWaitForVersionToCatchUp:
+      return "Wait for the target channel to catch up";
+    case ChannelDowngradeBehavior::kRollback:
+      return "Roll back and powerwash on channel downgrade";
+    case ChannelDowngradeBehavior::kAllowUserToConfigure:
+      return "User decides on channel downgrade behavior";
+  }
+  NOTREACHED();
+  return "Unknown";
+}
+
+template <>
+string BoxedValue::ValuePrinter<base::Version>(const void* value) {
+  const base::Version* val = reinterpret_cast<const base::Version*>(value);
+  if (val->IsValid())
+    return val->GetString();
+  return "Unknown";
+}
+
 }  // namespace chromeos_update_manager
diff --git a/update_manager/chromeos_policy.cc b/update_manager/chromeos_policy.cc
index be5f914..4c651b7 100644
--- a/update_manager/chromeos_policy.cc
+++ b/update_manager/chromeos_policy.cc
@@ -156,6 +156,7 @@
     case ErrorCode::kUnresolvedHostRecovered:
     case ErrorCode::kNotEnoughSpace:
     case ErrorCode::kDeviceCorrupted:
+    case ErrorCode::kPackageExcludedFromUpdate:
       LOG(INFO) << "Not changing URL index or failure count due to error "
                 << chromeos_update_engine::utils::ErrorCodeToString(err_code)
                 << " (" << static_cast<int>(err_code) << ")";
@@ -216,9 +217,11 @@
   // Set the default return values.
   result->updates_enabled = true;
   result->target_channel.clear();
+  result->lts_tag.clear();
   result->target_version_prefix.clear();
   result->rollback_allowed = false;
   result->rollback_allowed_milestones = -1;
+  result->rollback_on_channel_downgrade = false;
   result->interactive = false;
 
   EnoughSlotsAbUpdatesPolicyImpl enough_slots_ab_updates_policy;
diff --git a/update_manager/chromeos_policy_unittest.cc b/update_manager/chromeos_policy_unittest.cc
index 414ac0d..996db2b 100644
--- a/update_manager/chromeos_policy_unittest.cc
+++ b/update_manager/chromeos_policy_unittest.cc
@@ -262,6 +262,8 @@
       new bool(false));
   fake_state_.device_policy_provider()->var_release_channel()->reset(
       new string("foo-channel"));
+  fake_state_.device_policy_provider()->var_release_lts_tag()->reset(
+      new string("foo-hint"));
 
   UpdateCheckParams result;
   ExpectPolicyStatus(
@@ -270,6 +272,7 @@
   EXPECT_EQ("1.2", result.target_version_prefix);
   EXPECT_EQ(5, result.rollback_allowed_milestones);
   EXPECT_EQ("foo-channel", result.target_channel);
+  EXPECT_EQ("foo-hint", result.lts_tag);
   EXPECT_FALSE(result.interactive);
 }
 
@@ -338,6 +341,26 @@
       EvalStatus::kAskMeAgainLater, &Policy::UpdateCheckAllowed, &result);
 }
 
+TEST_F(UmChromeOSPolicyTest, TestUpdateCheckIntervalTimeout) {
+  fake_state_.updater_provider()
+      ->var_test_update_check_interval_timeout()
+      ->reset(new int64_t(10));
+  fake_state_.system_provider()->var_is_official_build()->reset(
+      new bool(false));
+
+  // The first time, update should not be allowed.
+  UpdateCheckParams result;
+  ExpectPolicyStatus(
+      EvalStatus::kAskMeAgainLater, &Policy::UpdateCheckAllowed, &result);
+
+  // After moving the time forward more than the update check interval, it
+  // should now allow for update.
+  fake_clock_.SetWallclockTime(fake_clock_.GetWallclockTime() +
+                               TimeDelta::FromSeconds(11));
+  ExpectPolicyStatus(
+      EvalStatus::kSucceeded, &Policy::UpdateCheckAllowed, &result);
+}
+
 TEST_F(UmChromeOSPolicyTest,
        UpdateCheckAllowedUpdatesDisabledWhenNotEnoughSlotsAbUpdates) {
   // UpdateCheckAllowed should return false (kSucceeded) if the image booted
diff --git a/update_manager/default_policy.cc b/update_manager/default_policy.cc
index 81ab795..7ca414b 100644
--- a/update_manager/default_policy.cc
+++ b/update_manager/default_policy.cc
@@ -40,9 +40,11 @@
                                              UpdateCheckParams* result) const {
   result->updates_enabled = true;
   result->target_channel.clear();
+  result->lts_tag.clear();
   result->target_version_prefix.clear();
   result->rollback_allowed = false;
   result->rollback_allowed_milestones = -1;  // No version rolls should happen.
+  result->rollback_on_channel_downgrade = false;
   result->interactive = false;
 
   // Ensure that the minimum interval is set. If there's no clock, this defaults
diff --git a/update_manager/device_policy_provider.h b/update_manager/device_policy_provider.h
index b68fe96..a59f2a3 100644
--- a/update_manager/device_policy_provider.h
+++ b/update_manager/device_policy_provider.h
@@ -21,6 +21,7 @@
 #include <string>
 
 #include <base/time/time.h>
+#include <base/version.h>
 #include <policy/libpolicy.h>
 
 #include "update_engine/update_manager/provider.h"
@@ -44,6 +45,8 @@
 
   virtual Variable<bool>* var_release_channel_delegated() = 0;
 
+  virtual Variable<std::string>* var_release_lts_tag() = 0;
+
   virtual Variable<bool>* var_update_disabled() = 0;
 
   virtual Variable<std::string>* var_target_version_prefix() = 0;
@@ -85,6 +88,15 @@
   virtual Variable<WeeklyTimeIntervalVector>*
   var_disallowed_time_intervals() = 0;
 
+  // Variable that determins whether we should powerwash and rollback on channel
+  // downgrade for enrolled devices.
+  virtual Variable<ChannelDowngradeBehavior>*
+  var_channel_downgrade_behavior() = 0;
+
+  // Variable that contains Chrome OS minimum required version. It contains a
+  // Chrome OS version number.
+  virtual Variable<base::Version>* var_device_minimum_version() = 0;
+
  protected:
   DevicePolicyProvider() {}
 
diff --git a/update_manager/enterprise_device_policy_impl.cc b/update_manager/enterprise_device_policy_impl.cc
index dea38ba..ce8150e 100644
--- a/update_manager/enterprise_device_policy_impl.cc
+++ b/update_manager/enterprise_device_policy_impl.cc
@@ -62,13 +62,37 @@
           ec->GetValue(system_provider->var_kiosk_required_platform_version());
       if (!kiosk_required_platform_version_p) {
         LOG(INFO) << "Kiosk app required platform version is not fetched, "
-                     "blocking update checks";
+                     "blocking update checks.";
         return EvalStatus::kAskMeAgainLater;
+      } else if (kiosk_required_platform_version_p->empty()) {
+        // The platform version could not be fetched several times. Update
+        // based on |DeviceMinimumVersion| instead (crbug.com/1048931).
+        const base::Version* device_minimum_version_p =
+            ec->GetValue(dp_provider->var_device_minimum_version());
+        const base::Version* current_version_p(
+            ec->GetValue(system_provider->var_chromeos_version()));
+        if (device_minimum_version_p && device_minimum_version_p->IsValid() &&
+            current_version_p && current_version_p->IsValid() &&
+            *current_version_p > *device_minimum_version_p) {
+          // Do not update if the current version is newer than the minimum
+          // version.
+          LOG(INFO) << "Reading kiosk app required platform version failed "
+                       "repeatedly but current version is newer than "
+                       "DeviceMinimumVersion. Blocking update checks. "
+                       "Current version: "
+                    << *current_version_p
+                    << " DeviceMinimumVersion: " << *device_minimum_version_p;
+          return EvalStatus::kAskMeAgainLater;
+        }
+        LOG(WARNING) << "Reading kiosk app required platform version failed "
+                        "repeatedly. Attempting an update without it now.";
+        // An empty string for |target_version_prefix| allows arbitrary updates.
+        result->target_version_prefix = "";
+      } else {
+        result->target_version_prefix = *kiosk_required_platform_version_p;
+        LOG(INFO) << "Allow kiosk app to control Chrome version policy is set, "
+                  << "target version is " << result->target_version_prefix;
       }
-
-      result->target_version_prefix = *kiosk_required_platform_version_p;
-      LOG(INFO) << "Allow kiosk app to control Chrome version policy is set, "
-                << "target version is " << result->target_version_prefix;
       // TODO(hunyadym): Add support for allowing rollback using the manifest
       // (if policy doesn't specify otherwise).
     } else {
@@ -117,14 +141,29 @@
     if (rollback_allowed_milestones_p)
       result->rollback_allowed_milestones = *rollback_allowed_milestones_p;
 
-    // Determine whether a target channel is dictated by policy.
+    // Determine whether a target channel is dictated by policy and whether we
+    // should rollback in case that channel is more stable.
     const bool* release_channel_delegated_p =
         ec->GetValue(dp_provider->var_release_channel_delegated());
     if (release_channel_delegated_p && !(*release_channel_delegated_p)) {
       const string* release_channel_p =
           ec->GetValue(dp_provider->var_release_channel());
-      if (release_channel_p)
+      if (release_channel_p) {
         result->target_channel = *release_channel_p;
+        const ChannelDowngradeBehavior* channel_downgrade_behavior_p =
+            ec->GetValue(dp_provider->var_channel_downgrade_behavior());
+        if (channel_downgrade_behavior_p &&
+            *channel_downgrade_behavior_p ==
+                ChannelDowngradeBehavior::kRollback) {
+          result->rollback_on_channel_downgrade = true;
+        }
+      }
+    }
+
+    const string* release_lts_tag_p =
+        ec->GetValue(dp_provider->var_release_lts_tag());
+    if (release_lts_tag_p) {
+      result->lts_tag = *release_lts_tag_p;
     }
   }
   return EvalStatus::kContinue;
diff --git a/update_manager/enterprise_device_policy_impl_unittest.cc b/update_manager/enterprise_device_policy_impl_unittest.cc
new file mode 100644
index 0000000..f27715e
--- /dev/null
+++ b/update_manager/enterprise_device_policy_impl_unittest.cc
@@ -0,0 +1,161 @@
+//
+// 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/update_manager/enterprise_device_policy_impl.h"
+
+#include <memory>
+
+#include "update_engine/update_manager/policy_test_utils.h"
+
+namespace chromeos_update_manager {
+
+class UmEnterpriseDevicePolicyImplTest : public UmPolicyTestBase {
+ protected:
+  UmEnterpriseDevicePolicyImplTest() : UmPolicyTestBase() {
+    policy_ = std::make_unique<EnterpriseDevicePolicyImpl>();
+  }
+
+  void SetUpDefaultState() override {
+    UmPolicyTestBase::SetUpDefaultState();
+
+    fake_state_.device_policy_provider()->var_device_policy_is_loaded()->reset(
+        new bool(true));
+  }
+};
+
+TEST_F(UmEnterpriseDevicePolicyImplTest, KioskAppVersionSet) {
+  fake_state_.device_policy_provider()->var_update_disabled()->reset(
+      new bool(true));
+  fake_state_.device_policy_provider()
+      ->var_allow_kiosk_app_control_chrome_version()
+      ->reset(new bool(true));
+
+  fake_state_.system_provider()->var_kiosk_required_platform_version()->reset(
+      new std::string("1234.5.6"));
+
+  UpdateCheckParams result;
+  ExpectPolicyStatus(
+      EvalStatus::kContinue, &Policy::UpdateCheckAllowed, &result);
+  EXPECT_EQ(result.target_version_prefix, "1234.5.6");
+}
+
+TEST_F(UmEnterpriseDevicePolicyImplTest, KioskAppVersionUnreadableNoUpdate) {
+  fake_state_.device_policy_provider()->var_update_disabled()->reset(
+      new bool(true));
+  fake_state_.device_policy_provider()
+      ->var_allow_kiosk_app_control_chrome_version()
+      ->reset(new bool(true));
+
+  fake_state_.system_provider()->var_kiosk_required_platform_version()->reset(
+      nullptr);
+
+  UpdateCheckParams result;
+  ExpectPolicyStatus(
+      EvalStatus::kAskMeAgainLater, &Policy::UpdateCheckAllowed, &result);
+}
+
+TEST_F(UmEnterpriseDevicePolicyImplTest, KioskAppVersionUnreadableUpdate) {
+  fake_state_.device_policy_provider()->var_update_disabled()->reset(
+      new bool(true));
+  fake_state_.device_policy_provider()
+      ->var_allow_kiosk_app_control_chrome_version()
+      ->reset(new bool(true));
+
+  // The real variable returns an empty string after several unsuccessful
+  // reading attempts. Fake this by setting it directly to empty string.
+  fake_state_.system_provider()->var_kiosk_required_platform_version()->reset(
+      new std::string(""));
+
+  UpdateCheckParams result;
+  ExpectPolicyStatus(
+      EvalStatus::kContinue, &Policy::UpdateCheckAllowed, &result);
+  EXPECT_EQ(result.target_version_prefix, "");
+}
+
+TEST_F(UmEnterpriseDevicePolicyImplTest,
+       KioskAppVersionUnreadableUpdateWithMinVersion) {
+  fake_state_.device_policy_provider()->var_update_disabled()->reset(
+      new bool(true));
+  fake_state_.device_policy_provider()
+      ->var_allow_kiosk_app_control_chrome_version()
+      ->reset(new bool(true));
+
+  // The real variable returns an empty string after several unsuccessful
+  // reading attempts. Fake this by setting it directly to empty string.
+  fake_state_.system_provider()->var_kiosk_required_platform_version()->reset(
+      new std::string(""));
+  // Update if the minimum version is above the current OS version.
+  fake_state_.device_policy_provider()->var_device_minimum_version()->reset(
+      new base::Version("2.0.0"));
+  fake_state_.system_provider()->var_chromeos_version()->reset(
+      new base::Version("1.0.0"));
+
+  UpdateCheckParams result;
+  ExpectPolicyStatus(
+      EvalStatus::kContinue, &Policy::UpdateCheckAllowed, &result);
+  EXPECT_EQ(result.target_version_prefix, "");
+}
+
+TEST_F(UmEnterpriseDevicePolicyImplTest,
+       KioskAppVersionUnreadableNoUpdateWithMinVersion) {
+  fake_state_.device_policy_provider()->var_update_disabled()->reset(
+      new bool(true));
+  fake_state_.device_policy_provider()
+      ->var_allow_kiosk_app_control_chrome_version()
+      ->reset(new bool(true));
+
+  // The real variable returns an empty string after several unsuccessful
+  // reading attempts. Fake this by setting it directly to empty string.
+  fake_state_.system_provider()->var_kiosk_required_platform_version()->reset(
+      new std::string(""));
+  // Block update if the minimum version is below the current OS version.
+  fake_state_.device_policy_provider()->var_device_minimum_version()->reset(
+      new base::Version("1.0.0"));
+  fake_state_.system_provider()->var_chromeos_version()->reset(
+      new base::Version("2.0.0"));
+
+  UpdateCheckParams result;
+  ExpectPolicyStatus(
+      EvalStatus::kAskMeAgainLater, &Policy::UpdateCheckAllowed, &result);
+}
+
+TEST_F(UmEnterpriseDevicePolicyImplTest, ChannelDowngradeBehaviorNoRollback) {
+  fake_state_.device_policy_provider()->var_release_channel_delegated()->reset(
+      new bool(false));
+  fake_state_.device_policy_provider()->var_release_channel()->reset(
+      new std::string("stable-channel"));
+
+  UpdateCheckParams result;
+  ExpectPolicyStatus(
+      EvalStatus::kContinue, &Policy::UpdateCheckAllowed, &result);
+  EXPECT_FALSE(result.rollback_on_channel_downgrade);
+}
+
+TEST_F(UmEnterpriseDevicePolicyImplTest, ChannelDowngradeBehaviorRollback) {
+  fake_state_.device_policy_provider()->var_release_channel_delegated()->reset(
+      new bool(false));
+  fake_state_.device_policy_provider()->var_release_channel()->reset(
+      new std::string("stable-channel"));
+  fake_state_.device_policy_provider()->var_channel_downgrade_behavior()->reset(
+      new ChannelDowngradeBehavior(ChannelDowngradeBehavior::kRollback));
+
+  UpdateCheckParams result;
+  ExpectPolicyStatus(
+      EvalStatus::kContinue, &Policy::UpdateCheckAllowed, &result);
+  EXPECT_TRUE(result.rollback_on_channel_downgrade);
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/evaluation_context-inl.h b/update_manager/evaluation_context-inl.h
index 59d85da..82861fa 100644
--- a/update_manager/evaluation_context-inl.h
+++ b/update_manager/evaluation_context-inl.h
@@ -39,7 +39,7 @@
   std::string errmsg;
   const T* result =
       var->GetValue(RemainingTime(evaluation_monotonic_deadline_), &errmsg);
-  if (result == nullptr) {
+  if (result == nullptr && !var->IsMissingOk()) {
     LOG(WARNING) << "Error reading Variable " << var->GetName() << ": \""
                  << errmsg << "\"";
   }
diff --git a/update_manager/fake_device_policy_provider.h b/update_manager/fake_device_policy_provider.h
index 86bdef1..55d66b3 100644
--- a/update_manager/fake_device_policy_provider.h
+++ b/update_manager/fake_device_policy_provider.h
@@ -42,6 +42,10 @@
     return &var_release_channel_delegated_;
   }
 
+  FakeVariable<std::string>* var_release_lts_tag() override {
+    return &var_release_lts_tag_;
+  }
+
   FakeVariable<bool>* var_update_disabled() override {
     return &var_update_disabled_;
   }
@@ -91,6 +95,15 @@
     return &var_disallowed_time_intervals_;
   }
 
+  FakeVariable<ChannelDowngradeBehavior>* var_channel_downgrade_behavior()
+      override {
+    return &var_channel_downgrade_behavior_;
+  }
+
+  FakeVariable<base::Version>* var_device_minimum_version() override {
+    return &var_device_minimum_version_;
+  }
+
  private:
   FakeVariable<bool> var_device_policy_is_loaded_{"policy_is_loaded",
                                                   kVariableModePoll};
@@ -98,6 +111,8 @@
                                                  kVariableModePoll};
   FakeVariable<bool> var_release_channel_delegated_{"release_channel_delegated",
                                                     kVariableModePoll};
+  FakeVariable<std::string> var_release_lts_tag_{"release_lts_tag",
+                                                 kVariableModePoll};
   FakeVariable<bool> var_update_disabled_{"update_disabled", kVariableModePoll};
   FakeVariable<std::string> var_target_version_prefix_{"target_version_prefix",
                                                        kVariableModePoll};
@@ -120,6 +135,10 @@
       "auto_launched_kiosk_app_id", kVariableModePoll};
   FakeVariable<WeeklyTimeIntervalVector> var_disallowed_time_intervals_{
       "disallowed_time_intervals", kVariableModePoll};
+  FakeVariable<ChannelDowngradeBehavior> var_channel_downgrade_behavior_{
+      "channel_downgrade_behavior", kVariableModePoll};
+  FakeVariable<base::Version> var_device_minimum_version_{
+      "device_minimum_version", kVariableModePoll};
 
   DISALLOW_COPY_AND_ASSIGN(FakeDevicePolicyProvider);
 };
diff --git a/update_manager/fake_system_provider.h b/update_manager/fake_system_provider.h
index f54951b..b320c01 100644
--- a/update_manager/fake_system_provider.h
+++ b/update_manager/fake_system_provider.h
@@ -50,6 +50,10 @@
     return &var_kiosk_required_platform_version_;
   }
 
+  FakeVariable<base::Version>* var_chromeos_version() override {
+    return &var_version_;
+  }
+
  private:
   FakeVariable<bool> var_is_normal_boot_mode_{"is_normal_boot_mode",
                                               kVariableModeConst};
@@ -60,6 +64,8 @@
   FakeVariable<unsigned int> var_num_slots_{"num_slots", kVariableModePoll};
   FakeVariable<std::string> var_kiosk_required_platform_version_{
       "kiosk_required_platform_version", kVariableModePoll};
+  FakeVariable<base::Version> var_version_{"chromeos_version",
+                                           kVariableModePoll};
 
   DISALLOW_COPY_AND_ASSIGN(FakeSystemProvider);
 };
diff --git a/update_manager/fake_updater_provider.h b/update_manager/fake_updater_provider.h
index 7295765..d967f42 100644
--- a/update_manager/fake_updater_provider.h
+++ b/update_manager/fake_updater_provider.h
@@ -83,6 +83,10 @@
     return &var_update_restrictions_;
   }
 
+  FakeVariable<int64_t>* var_test_update_check_interval_timeout() override {
+    return &var_test_update_check_interval_timeout_;
+  }
+
  private:
   FakeVariable<base::Time> var_updater_started_time_{"updater_started_time",
                                                      kVariableModePoll};
@@ -108,6 +112,8 @@
       "forced_update_requested", kVariableModeAsync};
   FakeVariable<UpdateRestrictions> var_update_restrictions_{
       "update_restrictions", kVariableModePoll};
+  FakeVariable<int64_t> var_test_update_check_interval_timeout_{
+      "test_update_check_interval_timeout", kVariableModePoll};
 
   DISALLOW_COPY_AND_ASSIGN(FakeUpdaterProvider);
 };
diff --git a/update_manager/next_update_check_policy_impl.cc b/update_manager/next_update_check_policy_impl.cc
index 6f9748e..0a78718 100644
--- a/update_manager/next_update_check_policy_impl.cc
+++ b/update_manager/next_update_check_policy_impl.cc
@@ -72,6 +72,11 @@
       ec->GetValue(updater_provider->var_updater_started_time());
   POLICY_CHECK_VALUE_AND_FAIL(updater_started_time, error);
 
+  // This value is used for testing only and it will get deleted after the first
+  // time it is read.
+  const int64_t* interval_timeout =
+      ec->GetValue(updater_provider->var_test_update_check_interval_timeout());
+
   const Time* last_checked_time =
       ec->GetValue(updater_provider->var_last_checked_time());
 
@@ -83,13 +88,21 @@
   // If this is the first attempt, compute and return an initial value.
   if (last_checked_time == nullptr ||
       *last_checked_time < *updater_started_time) {
-    *next_update_check = *updater_started_time +
-                         FuzzedInterval(&prng,
-                                        constants.timeout_initial_interval,
-                                        constants.timeout_regular_fuzz);
+    TimeDelta time_diff =
+        interval_timeout == nullptr
+            ? FuzzedInterval(&prng,
+                             constants.timeout_initial_interval,
+                             constants.timeout_regular_fuzz)
+            : TimeDelta::FromSeconds(*interval_timeout);
+    *next_update_check = *updater_started_time + time_diff;
     return EvalStatus::kSucceeded;
   }
 
+  if (interval_timeout != nullptr) {
+    *next_update_check =
+        *last_checked_time + TimeDelta::FromSeconds(*interval_timeout);
+    return EvalStatus::kSucceeded;
+  }
   // Check whether the server is enforcing a poll interval; if not, this value
   // will be zero.
   const unsigned int* server_dictated_poll_interval =
diff --git a/update_manager/official_build_check_policy_impl.cc b/update_manager/official_build_check_policy_impl.cc
index 096f7bf..e80c09f 100644
--- a/update_manager/official_build_check_policy_impl.cc
+++ b/update_manager/official_build_check_policy_impl.cc
@@ -27,8 +27,16 @@
   const bool* is_official_build_p =
       ec->GetValue(state->system_provider()->var_is_official_build());
   if (is_official_build_p != nullptr && !(*is_official_build_p)) {
-    LOG(INFO) << "Unofficial build, blocking periodic update checks.";
-    return EvalStatus::kAskMeAgainLater;
+    const int64_t* interval_timeout_p = ec->GetValue(
+        state->updater_provider()->var_test_update_check_interval_timeout());
+    // The |interval_timeout | is used for testing only to test periodic
+    // update checks on unofficial images.
+    if (interval_timeout_p == nullptr) {
+      LOG(INFO) << "Unofficial build, blocking periodic update checks.";
+      return EvalStatus::kAskMeAgainLater;
+    }
+    LOG(INFO) << "Unofficial build, but periodic update check interval "
+              << "timeout is defined, so update is not blocked.";
   }
   return EvalStatus::kContinue;
 }
diff --git a/update_manager/policy.h b/update_manager/policy.h
index 844a4d0..ad6994c 100644
--- a/update_manager/policy.h
+++ b/update_manager/policy.h
@@ -43,26 +43,32 @@
 // Parameters of an update check. These parameters are determined by the
 // UpdateCheckAllowed policy.
 struct UpdateCheckParams {
-  bool updates_enabled;  // Whether the auto-updates are enabled on this build.
+  // Whether the auto-updates are enabled on this build.
+  bool updates_enabled{true};
 
   // Attributes pertaining to the case where update checks are allowed.
   //
   // A target version prefix, if imposed by policy; otherwise, an empty string.
   std::string target_version_prefix;
   // Specifies whether rollback images are allowed by device policy.
-  bool rollback_allowed;
+  bool rollback_allowed{false};
   // Specifies if rollbacks should attempt to preserve some system state.
-  bool rollback_data_save_requested;
+  bool rollback_data_save_requested{false};
   // Specifies the number of Chrome milestones rollback should be allowed,
   // starting from the stable version at any time. Value is -1 if unspecified
   // (e.g. no device policy is available yet), in this case no version
   // roll-forward should happen.
-  int rollback_allowed_milestones;
+  int rollback_allowed_milestones{0};
+  // Whether a rollback with data save should be initiated on channel
+  // downgrade (e.g. beta to stable).
+  bool rollback_on_channel_downgrade{false};
   // A target channel, if so imposed by policy; otherwise, an empty string.
   std::string target_channel;
+  // Specifies if the channel hint, e.g. LTS (Long Term Support) updates.
+  std::string lts_tag;
 
   // Whether the allowed update is interactive (user-initiated) or periodic.
-  bool interactive;
+  bool interactive{false};
 };
 
 // Input arguments to UpdateCanStart.
diff --git a/update_manager/policy_utils.h b/update_manager/policy_utils.h
index 3204780..dc606f2 100644
--- a/update_manager/policy_utils.h
+++ b/update_manager/policy_utils.h
@@ -55,7 +55,6 @@
     EvalStatus status =
         (policy->*policy_method)(ec, state, error, result, args...);
     if (status != EvalStatus::kContinue) {
-      LOG(INFO) << "decision by " << policy->PolicyRequestName(policy_method);
       return status;
     }
   }
diff --git a/update_manager/real_device_policy_provider.cc b/update_manager/real_device_policy_provider.cc
index 781e2ac..05091d9 100644
--- a/update_manager/real_device_policy_provider.cc
+++ b/update_manager/real_device_policy_provider.cc
@@ -25,8 +25,8 @@
 #include <base/time/time.h>
 #include <policy/device_policy.h>
 
+#include "update_engine/common/connection_utils.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/connection_utils.h"
 #include "update_engine/update_manager/generic_variables.h"
 
 using base::TimeDelta;
@@ -104,9 +104,10 @@
 }
 
 template <typename T>
-void RealDevicePolicyProvider::UpdateVariable(AsyncCopyVariable<T>* var,
-                                              bool (DevicePolicy::*getter)(T*)
-                                                  const) {
+void RealDevicePolicyProvider::UpdateVariable(
+    AsyncCopyVariable<T>* var,
+    // NOLINTNEXTLINE(readability/casting)
+    bool (DevicePolicy::*getter)(T*) const) {
   T new_value;
   if (policy_provider_->device_policy_is_loaded() &&
       (policy_provider_->GetDevicePolicy().*getter)(&new_value)) {
@@ -208,6 +209,21 @@
   return true;
 }
 
+bool RealDevicePolicyProvider::ConvertChannelDowngradeBehavior(
+    ChannelDowngradeBehavior* channel_downgrade_behavior) const {
+  int behavior;
+  if (!policy_provider_->GetDevicePolicy().GetChannelDowngradeBehavior(
+          &behavior)) {
+    return false;
+  }
+  if (behavior < static_cast<int>(ChannelDowngradeBehavior::kFirstValue) ||
+      behavior > static_cast<int>(ChannelDowngradeBehavior::kLastValue)) {
+    return false;
+  }
+  *channel_downgrade_behavior = static_cast<ChannelDowngradeBehavior>(behavior);
+  return true;
+}
+
 void RealDevicePolicyProvider::RefreshDevicePolicy() {
   if (!policy_provider_->Reload()) {
     LOG(INFO) << "No device policies/settings present.";
@@ -219,6 +235,7 @@
   UpdateVariable(&var_release_channel_, &DevicePolicy::GetReleaseChannel);
   UpdateVariable(&var_release_channel_delegated_,
                  &DevicePolicy::GetReleaseChannelDelegated);
+  UpdateVariable(&var_release_lts_tag_, &DevicePolicy::GetReleaseLtsTag);
   UpdateVariable(&var_update_disabled_, &DevicePolicy::GetUpdateDisabled);
   UpdateVariable(&var_target_version_prefix_,
                  &DevicePolicy::GetTargetVersionPrefix);
@@ -245,6 +262,10 @@
                  &DevicePolicy::GetAutoLaunchedKioskAppId);
   UpdateVariable(&var_disallowed_time_intervals_,
                  &RealDevicePolicyProvider::ConvertDisallowedTimeIntervals);
+  UpdateVariable(&var_channel_downgrade_behavior_,
+                 &RealDevicePolicyProvider::ConvertChannelDowngradeBehavior);
+  UpdateVariable(&var_device_minimum_version_,
+                 &DevicePolicy::GetHighestDeviceMinimumVersion);
 }
 
 }  // namespace chromeos_update_manager
diff --git a/update_manager/real_device_policy_provider.h b/update_manager/real_device_policy_provider.h
index 9da052d..ebda8fd 100644
--- a/update_manager/real_device_policy_provider.h
+++ b/update_manager/real_device_policy_provider.h
@@ -64,6 +64,10 @@
     return &var_release_channel_delegated_;
   }
 
+  Variable<std::string>* var_release_lts_tag() override {
+    return &var_release_lts_tag_;
+  }
+
   Variable<bool>* var_update_disabled() override {
     return &var_update_disabled_;
   }
@@ -109,6 +113,15 @@
     return &var_disallowed_time_intervals_;
   }
 
+  Variable<ChannelDowngradeBehavior>* var_channel_downgrade_behavior()
+      override {
+    return &var_channel_downgrade_behavior_;
+  }
+
+  Variable<base::Version>* var_device_minimum_version() override {
+    return &var_device_minimum_version_;
+  }
+
  private:
   FRIEND_TEST(UmRealDevicePolicyProviderTest, RefreshScheduledTest);
   FRIEND_TEST(UmRealDevicePolicyProviderTest, NonExistentDevicePolicyReloaded);
@@ -170,6 +183,11 @@
   // devices do not have an owner).
   bool ConvertHasOwner(bool* has_owner) const;
 
+  // Wrapper for |DevicePolicy::GetChannelDowngradeBehavior| that converts the
+  // result to |ChannelDowngradeBehavior|.
+  bool ConvertChannelDowngradeBehavior(
+      ChannelDowngradeBehavior* channel_downgrade_behavior) const;
+
   // Used for fetching information about the device policy.
   policy::PolicyProvider* policy_provider_;
 
@@ -191,6 +209,7 @@
   AsyncCopyVariable<std::string> var_release_channel_{"release_channel"};
   AsyncCopyVariable<bool> var_release_channel_delegated_{
       "release_channel_delegated"};
+  AsyncCopyVariable<std::string> var_release_lts_tag_{"release_lts_tag"};
   AsyncCopyVariable<bool> var_update_disabled_{"update_disabled"};
   AsyncCopyVariable<std::string> var_target_version_prefix_{
       "target_version_prefix"};
@@ -211,6 +230,10 @@
       "update_time_restrictions"};
   AsyncCopyVariable<std::string> var_auto_launched_kiosk_app_id_{
       "auto_launched_kiosk_app_id"};
+  AsyncCopyVariable<ChannelDowngradeBehavior> var_channel_downgrade_behavior_{
+      "channel_downgrade_behavior"};
+  AsyncCopyVariable<base::Version> var_device_minimum_version_{
+      "device_minimum_version"};
 
   DISALLOW_COPY_AND_ASSIGN(RealDevicePolicyProvider);
 };
diff --git a/update_manager/real_device_policy_provider_unittest.cc b/update_manager/real_device_policy_provider_unittest.cc
index 84debd1..32396d6 100644
--- a/update_manager/real_device_policy_provider_unittest.cc
+++ b/update_manager/real_device_policy_provider_unittest.cc
@@ -34,7 +34,7 @@
 
 #include "update_engine/common/test_utils.h"
 #if USE_DBUS
-#include "update_engine/dbus_test_utils.h"
+#include "update_engine/cros/dbus_test_utils.h"
 #endif  // USE_DBUS
 #include "update_engine/update_manager/umtest_utils.h"
 
@@ -177,6 +177,7 @@
 
   UmTestUtils::ExpectVariableNotSet(provider_->var_release_channel());
   UmTestUtils::ExpectVariableNotSet(provider_->var_release_channel_delegated());
+  UmTestUtils::ExpectVariableNotSet(provider_->var_release_lts_tag());
   UmTestUtils::ExpectVariableNotSet(provider_->var_update_disabled());
   UmTestUtils::ExpectVariableNotSet(provider_->var_target_version_prefix());
   UmTestUtils::ExpectVariableNotSet(
@@ -194,6 +195,8 @@
   UmTestUtils::ExpectVariableNotSet(
       provider_->var_auto_launched_kiosk_app_id());
   UmTestUtils::ExpectVariableNotSet(provider_->var_disallowed_time_intervals());
+  UmTestUtils::ExpectVariableNotSet(
+      provider_->var_channel_downgrade_behavior());
 }
 
 TEST_F(UmRealDevicePolicyProviderTest, ValuesUpdated) {
@@ -376,4 +379,70 @@
       provider_->var_disallowed_time_intervals());
 }
 
+TEST_F(UmRealDevicePolicyProviderTest, ChannelDowngradeBehaviorConverted) {
+  SetUpExistentDevicePolicy();
+  EXPECT_CALL(mock_device_policy_, GetChannelDowngradeBehavior(_))
+#if USE_DBUS
+      .Times(2)
+#else
+      .Times(1)
+#endif  // USE_DBUS
+      .WillRepeatedly(DoAll(SetArgPointee<0>(static_cast<int>(
+                                ChannelDowngradeBehavior::kRollback)),
+                            Return(true)));
+  EXPECT_TRUE(provider_->Init());
+  loop_.RunOnce(false);
+
+  UmTestUtils::ExpectVariableHasValue(
+      ChannelDowngradeBehavior::kRollback,
+      provider_->var_channel_downgrade_behavior());
+}
+
+TEST_F(UmRealDevicePolicyProviderTest, ChannelDowngradeBehaviorTooSmall) {
+  SetUpExistentDevicePolicy();
+  EXPECT_CALL(mock_device_policy_, GetChannelDowngradeBehavior(_))
+#if USE_DBUS
+      .Times(2)
+#else
+      .Times(1)
+#endif  // USE_DBUS
+      .WillRepeatedly(DoAll(SetArgPointee<0>(-1), Return(true)));
+  EXPECT_TRUE(provider_->Init());
+  loop_.RunOnce(false);
+
+  UmTestUtils::ExpectVariableNotSet(
+      provider_->var_channel_downgrade_behavior());
+}
+
+TEST_F(UmRealDevicePolicyProviderTest, ChannelDowngradeBehaviorTooLarge) {
+  SetUpExistentDevicePolicy();
+  EXPECT_CALL(mock_device_policy_, GetChannelDowngradeBehavior(_))
+#if USE_DBUS
+      .Times(2)
+#else
+      .Times(1)
+#endif  // USE_DBUS
+      .WillRepeatedly(DoAll(SetArgPointee<0>(10), Return(true)));
+  EXPECT_TRUE(provider_->Init());
+  loop_.RunOnce(false);
+
+  UmTestUtils::ExpectVariableNotSet(
+      provider_->var_channel_downgrade_behavior());
+}
+
+TEST_F(UmRealDevicePolicyProviderTest, DeviceMinimumVersionPolicySet) {
+  SetUpExistentDevicePolicy();
+
+  base::Version device_minimum_version("13315.60.12");
+
+  EXPECT_CALL(mock_device_policy_, GetHighestDeviceMinimumVersion(_))
+      .WillRepeatedly(
+          DoAll(SetArgPointee<0>(device_minimum_version), Return(true)));
+  EXPECT_TRUE(provider_->Init());
+  loop_.RunOnce(false);
+
+  UmTestUtils::ExpectVariableHasValue(device_minimum_version,
+                                      provider_->var_device_minimum_version());
+}
+
 }  // namespace chromeos_update_manager
diff --git a/update_manager/real_shill_provider.h b/update_manager/real_shill_provider.h
index ec5c570..baa2cdc 100644
--- a/update_manager/real_shill_provider.h
+++ b/update_manager/real_shill_provider.h
@@ -28,7 +28,7 @@
 #include <dbus/object_path.h>
 
 #include "update_engine/common/clock_interface.h"
-#include "update_engine/shill_proxy_interface.h"
+#include "update_engine/cros/shill_proxy_interface.h"
 #include "update_engine/update_manager/generic_variables.h"
 #include "update_engine/update_manager/shill_provider.h"
 
diff --git a/update_manager/real_shill_provider_unittest.cc b/update_manager/real_shill_provider_unittest.cc
index 505f2f8..682c233 100644
--- a/update_manager/real_shill_provider_unittest.cc
+++ b/update_manager/real_shill_provider_unittest.cc
@@ -29,8 +29,8 @@
 
 #include "update_engine/common/fake_clock.h"
 #include "update_engine/common/test_utils.h"
-#include "update_engine/dbus_test_utils.h"
-#include "update_engine/fake_shill_proxy.h"
+#include "update_engine/cros/dbus_test_utils.h"
+#include "update_engine/cros/fake_shill_proxy.h"
 #include "update_engine/update_manager/umtest_utils.h"
 
 using base::Time;
diff --git a/update_manager/real_system_provider.cc b/update_manager/real_system_provider.cc
index a900071..4e88b07 100644
--- a/update_manager/real_system_provider.cc
+++ b/update_manager/real_system_provider.cc
@@ -24,7 +24,10 @@
 #include <kiosk-app/dbus-proxies.h>
 #endif  // USE_CHROME_KIOSK_APP
 
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/hardware_interface.h"
 #include "update_engine/common/utils.h"
+#include "update_engine/cros/omaha_request_params.h"
 #include "update_engine/update_manager/generic_variables.h"
 #include "update_engine/update_manager/variable.h"
 
@@ -64,9 +67,10 @@
     std::unique_ptr<T> result(new T());
     if (!func_.Run(result.get())) {
       if (failed_attempts_ >= kRetryPollVariableMaxRetry) {
-        // Give up on the retries, set back the desired polling interval and
-        // return the default.
+        // Give up on the retries and set back the desired polling interval.
         this->SetPollInterval(base_interval_);
+        // Release the result instead of returning a |nullptr| to indicate that
+        // the result could not be fetched.
         return result.release();
       }
       this->SetPollInterval(
@@ -96,19 +100,19 @@
 
 bool RealSystemProvider::Init() {
   var_is_normal_boot_mode_.reset(new ConstCopyVariable<bool>(
-      "is_normal_boot_mode", hardware_->IsNormalBootMode()));
+      "is_normal_boot_mode", system_state_->hardware()->IsNormalBootMode()));
 
   var_is_official_build_.reset(new ConstCopyVariable<bool>(
-      "is_official_build", hardware_->IsOfficialBuild()));
+      "is_official_build", system_state_->hardware()->IsOfficialBuild()));
 
   var_is_oobe_complete_.reset(new CallCopyVariable<bool>(
       "is_oobe_complete",
       base::Bind(&chromeos_update_engine::HardwareInterface::IsOOBEComplete,
-                 base::Unretained(hardware_),
+                 base::Unretained(system_state_->hardware()),
                  nullptr)));
 
   var_num_slots_.reset(new ConstCopyVariable<unsigned int>(
-      "num_slots", boot_control_->GetNumSlots()));
+      "num_slots", system_state_->boot_control()->GetNumSlots()));
 
   var_kiosk_required_platform_version_.reset(new RetryPollVariable<string>(
       "kiosk_required_platform_version",
@@ -116,6 +120,10 @@
       base::Bind(&RealSystemProvider::GetKioskAppRequiredPlatformVersion,
                  base::Unretained(this))));
 
+  var_chromeos_version_.reset(new ConstCopyVariable<base::Version>(
+      "chromeos_version",
+      base::Version(system_state_->request_params()->app_version())));
+
   return true;
 }
 
diff --git a/update_manager/real_system_provider.h b/update_manager/real_system_provider.h
index 114c6ea..ffa1467 100644
--- a/update_manager/real_system_provider.h
+++ b/update_manager/real_system_provider.h
@@ -20,8 +20,9 @@
 #include <memory>
 #include <string>
 
-#include "update_engine/common/boot_control_interface.h"
-#include "update_engine/common/hardware_interface.h"
+#include <base/version.h>
+
+#include "update_engine/common/system_state.h"
 #include "update_engine/update_manager/system_provider.h"
 
 namespace org {
@@ -36,16 +37,13 @@
 class RealSystemProvider : public SystemProvider {
  public:
   RealSystemProvider(
-      chromeos_update_engine::HardwareInterface* hardware,
-      chromeos_update_engine::BootControlInterface* boot_control,
+      chromeos_update_engine::SystemState* system_state,
       org::chromium::KioskAppServiceInterfaceProxyInterface* kiosk_app_proxy)
-      : hardware_(hardware),
 #if USE_CHROME_KIOSK_APP
-        boot_control_(boot_control),
-        kiosk_app_proxy_(kiosk_app_proxy) {
+      : system_state_(system_state), kiosk_app_proxy_(kiosk_app_proxy) {
   }
 #else
-        boot_control_(boot_control) {
+      system_state_(system_state) {
   }
 #endif  // USE_CHROME_KIOSK_APP
 
@@ -72,6 +70,10 @@
     return var_kiosk_required_platform_version_.get();
   }
 
+  Variable<base::Version>* var_chromeos_version() override {
+    return var_chromeos_version_.get();
+  }
+
  private:
   bool GetKioskAppRequiredPlatformVersion(
       std::string* required_platform_version);
@@ -81,9 +83,9 @@
   std::unique_ptr<Variable<bool>> var_is_oobe_complete_;
   std::unique_ptr<Variable<unsigned int>> var_num_slots_;
   std::unique_ptr<Variable<std::string>> var_kiosk_required_platform_version_;
+  std::unique_ptr<Variable<base::Version>> var_chromeos_version_;
 
-  chromeos_update_engine::HardwareInterface* const hardware_;
-  chromeos_update_engine::BootControlInterface* const boot_control_;
+  chromeos_update_engine::SystemState* const system_state_;
 #if USE_CHROME_KIOSK_APP
   org::chromium::KioskAppServiceInterfaceProxyInterface* const kiosk_app_proxy_;
 #endif  // USE_CHROME_KIOSK_APP
diff --git a/update_manager/real_system_provider_unittest.cc b/update_manager/real_system_provider_unittest.cc
index f654f7a..8add690 100644
--- a/update_manager/real_system_provider_unittest.cc
+++ b/update_manager/real_system_provider_unittest.cc
@@ -24,6 +24,7 @@
 
 #include "update_engine/common/fake_boot_control.h"
 #include "update_engine/common/fake_hardware.h"
+#include "update_engine/cros/fake_system_state.h"
 #include "update_engine/update_manager/umtest_utils.h"
 #if USE_CHROME_KIOSK_APP
 #include "kiosk-app/dbus-proxies.h"
@@ -54,17 +55,15 @@
         .WillByDefault(
             DoAll(SetArgPointee<0>(kRequiredPlatformVersion), Return(true)));
 
-    provider_.reset(new RealSystemProvider(
-        &fake_hardware_, &fake_boot_control_, kiosk_app_proxy_mock_.get()));
+    provider_.reset(new RealSystemProvider(&fake_system_state_,
+                                           kiosk_app_proxy_mock_.get()));
 #else
-    provider_.reset(
-        new RealSystemProvider(&fake_hardware_, &fake_boot_control_, nullptr));
+    provider_.reset(new RealSystemProvider(&fake_system_state, nullptr));
 #endif  // USE_CHROME_KIOSK_APP
     EXPECT_TRUE(provider_->Init());
   }
 
-  chromeos_update_engine::FakeHardware fake_hardware_;
-  chromeos_update_engine::FakeBootControl fake_boot_control_;
+  chromeos_update_engine::FakeSystemState fake_system_state_;
   unique_ptr<RealSystemProvider> provider_;
 
 #if USE_CHROME_KIOSK_APP
@@ -77,18 +76,29 @@
   EXPECT_NE(nullptr, provider_->var_is_official_build());
   EXPECT_NE(nullptr, provider_->var_is_oobe_complete());
   EXPECT_NE(nullptr, provider_->var_kiosk_required_platform_version());
+  EXPECT_NE(nullptr, provider_->var_chromeos_version());
 }
 
 TEST_F(UmRealSystemProviderTest, IsOOBECompleteTrue) {
-  fake_hardware_.SetIsOOBEComplete(base::Time());
+  fake_system_state_.fake_hardware()->SetIsOOBEComplete(base::Time());
   UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_oobe_complete());
 }
 
 TEST_F(UmRealSystemProviderTest, IsOOBECompleteFalse) {
-  fake_hardware_.UnsetIsOOBEComplete();
+  fake_system_state_.fake_hardware()->UnsetIsOOBEComplete();
   UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_oobe_complete());
 }
 
+TEST_F(UmRealSystemProviderTest, VersionFromRequestParams) {
+  fake_system_state_.request_params()->set_app_version("1.2.3");
+  // Call |Init| again to pick up the version.
+  EXPECT_TRUE(provider_->Init());
+
+  base::Version version("1.2.3");
+  UmTestUtils::ExpectVariableHasValue(version,
+                                      provider_->var_chromeos_version());
+}
+
 #if USE_CHROME_KIOSK_APP
 TEST_F(UmRealSystemProviderTest, KioskRequiredPlatformVersion) {
   UmTestUtils::ExpectVariableHasValue(
@@ -119,6 +129,22 @@
       std::string(kRequiredPlatformVersion),
       provider_->var_kiosk_required_platform_version());
 }
+
+TEST_F(UmRealSystemProviderTest, KioskRequiredPlatformVersionRepeatedFailure) {
+  // Simulate unreadable platform version. The variable should return a
+  // null pointer |kRetryPollVariableMaxRetry| times and then return an empty
+  // string to indicate that it gave up.
+  constexpr int kNumMethodCalls = 5;
+  EXPECT_CALL(*kiosk_app_proxy_mock_, GetRequiredPlatformVersion)
+      .Times(kNumMethodCalls + 1)
+      .WillRepeatedly(Return(false));
+  for (int i = 0; i < kNumMethodCalls; ++i) {
+    UmTestUtils::ExpectVariableNotSet(
+        provider_->var_kiosk_required_platform_version());
+  }
+  UmTestUtils::ExpectVariableHasValue(
+      std::string(""), provider_->var_kiosk_required_platform_version());
+}
 #else
 TEST_F(UmRealSystemProviderTest, KioskRequiredPlatformVersion) {
   UmTestUtils::ExpectVariableHasValue(
diff --git a/update_manager/real_updater_provider.cc b/update_manager/real_updater_provider.cc
index 1f9af0d..e975b80 100644
--- a/update_manager/real_updater_provider.cc
+++ b/update_manager/real_updater_provider.cc
@@ -18,6 +18,7 @@
 
 #include <inttypes.h>
 
+#include <algorithm>
 #include <string>
 
 #include <base/bind.h>
@@ -28,8 +29,8 @@
 #include "update_engine/client_library/include/update_engine/update_status.h"
 #include "update_engine/common/clock_interface.h"
 #include "update_engine/common/prefs.h"
-#include "update_engine/omaha_request_params.h"
-#include "update_engine/update_attempter.h"
+#include "update_engine/cros/omaha_request_params.h"
+#include "update_engine/cros/update_attempter.h"
 #include "update_engine/update_status_utils.h"
 
 using base::StringPrintf;
@@ -441,6 +442,46 @@
   DISALLOW_COPY_AND_ASSIGN(UpdateRestrictionsVariable);
 };
 
+// A variable class for reading timeout interval prefs value.
+class TestUpdateCheckIntervalTimeoutVariable : public Variable<int64_t> {
+ public:
+  TestUpdateCheckIntervalTimeoutVariable(
+      const string& name, chromeos_update_engine::PrefsInterface* prefs)
+      : Variable<int64_t>(name, kVariableModePoll),
+        prefs_(prefs),
+        read_count_(0) {
+    SetMissingOk();
+  }
+  ~TestUpdateCheckIntervalTimeoutVariable() = default;
+
+ private:
+  const int64_t* GetValue(TimeDelta /* timeout */,
+                          string* /* errmsg */) override {
+    auto key = chromeos_update_engine::kPrefsTestUpdateCheckIntervalTimeout;
+    int64_t result;
+    if (prefs_ && prefs_->Exists(key) && prefs_->GetInt64(key, &result)) {
+      // This specific value is used for testing only. So it should not be kept
+      // around and should be deleted after a few reads.
+      if (++read_count_ > 5)
+        prefs_->Delete(key);
+
+      // Limit the timeout interval to 10 minutes so it is not abused if it is
+      // seen on official images.
+      return new int64_t(std::min(result, static_cast<int64_t>(10 * 60)));
+    }
+    return nullptr;
+  }
+
+  chromeos_update_engine::PrefsInterface* prefs_;
+
+  // Counts how many times this variable is read. This is used to delete the
+  // underlying file defining the variable after a certain number of reads in
+  // order to prevent any abuse of this variable.
+  int read_count_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestUpdateCheckIntervalTimeoutVariable);
+};
+
 // RealUpdaterProvider methods.
 
 RealUpdaterProvider::RealUpdaterProvider(SystemState* system_state)
@@ -474,6 +515,9 @@
           "server_dictated_poll_interval", system_state_)),
       var_forced_update_requested_(new ForcedUpdateRequestedVariable(
           "forced_update_requested", system_state_)),
-      var_update_restrictions_(new UpdateRestrictionsVariable(
-          "update_restrictions", system_state_)) {}
+      var_update_restrictions_(
+          new UpdateRestrictionsVariable("update_restrictions", system_state_)),
+      var_test_update_check_interval_timeout_(
+          new TestUpdateCheckIntervalTimeoutVariable(
+              "test_update_check_interval_timeout", system_state_->prefs())) {}
 }  // namespace chromeos_update_manager
diff --git a/update_manager/real_updater_provider.h b/update_manager/real_updater_provider.h
index 1b46895..a32e7e9 100644
--- a/update_manager/real_updater_provider.h
+++ b/update_manager/real_updater_provider.h
@@ -20,7 +20,7 @@
 #include <memory>
 #include <string>
 
-#include "update_engine/system_state.h"
+#include "update_engine/common/system_state.h"
 #include "update_engine/update_manager/generic_variables.h"
 #include "update_engine/update_manager/updater_provider.h"
 
@@ -94,6 +94,10 @@
     return var_update_restrictions_.get();
   }
 
+  Variable<int64_t>* var_test_update_check_interval_timeout() override {
+    return var_test_update_check_interval_timeout_.get();
+  }
+
  private:
   // A pointer to the update engine's system state aggregator.
   chromeos_update_engine::SystemState* system_state_;
@@ -114,6 +118,7 @@
   std::unique_ptr<Variable<unsigned int>> var_server_dictated_poll_interval_;
   std::unique_ptr<Variable<UpdateRequestStatus>> var_forced_update_requested_;
   std::unique_ptr<Variable<UpdateRestrictions>> var_update_restrictions_;
+  std::unique_ptr<Variable<int64_t>> var_test_update_check_interval_timeout_;
 
   DISALLOW_COPY_AND_ASSIGN(RealUpdaterProvider);
 };
diff --git a/update_manager/real_updater_provider_unittest.cc b/update_manager/real_updater_provider_unittest.cc
index fb7a763..0dc56ac 100644
--- a/update_manager/real_updater_provider_unittest.cc
+++ b/update_manager/real_updater_provider_unittest.cc
@@ -25,9 +25,9 @@
 
 #include "update_engine/common/fake_clock.h"
 #include "update_engine/common/fake_prefs.h"
-#include "update_engine/fake_system_state.h"
-#include "update_engine/mock_update_attempter.h"
-#include "update_engine/omaha_request_params.h"
+#include "update_engine/cros/fake_system_state.h"
+#include "update_engine/cros/mock_update_attempter.h"
+#include "update_engine/cros/omaha_request_params.h"
 #include "update_engine/update_manager/umtest_utils.h"
 
 using base::Time;
@@ -327,7 +327,7 @@
 TEST_F(UmRealUpdaterProviderTest, GetCurrChannelOkay) {
   const string kChannelName("foo-channel");
   OmahaRequestParams request_params(&fake_sys_state_);
-  request_params.Init("", "", false);
+  request_params.Init("", "", {});
   request_params.set_current_channel(kChannelName);
   fake_sys_state_.set_request_params(&request_params);
   UmTestUtils::ExpectVariableHasValue(kChannelName,
@@ -336,7 +336,7 @@
 
 TEST_F(UmRealUpdaterProviderTest, GetCurrChannelFailEmpty) {
   OmahaRequestParams request_params(&fake_sys_state_);
-  request_params.Init("", "", false);
+  request_params.Init("", "", {});
   request_params.set_current_channel("");
   fake_sys_state_.set_request_params(&request_params);
   UmTestUtils::ExpectVariableNotSet(provider_->var_curr_channel());
@@ -345,7 +345,7 @@
 TEST_F(UmRealUpdaterProviderTest, GetNewChannelOkay) {
   const string kChannelName("foo-channel");
   OmahaRequestParams request_params(&fake_sys_state_);
-  request_params.Init("", "", false);
+  request_params.Init("", "", {});
   request_params.set_target_channel(kChannelName);
   fake_sys_state_.set_request_params(&request_params);
   UmTestUtils::ExpectVariableHasValue(kChannelName,
@@ -354,7 +354,7 @@
 
 TEST_F(UmRealUpdaterProviderTest, GetNewChannelFailEmpty) {
   OmahaRequestParams request_params(&fake_sys_state_);
-  request_params.Init("", "", false);
+  request_params.Init("", "", {});
   request_params.set_target_channel("");
   fake_sys_state_.set_request_params(&request_params);
   UmTestUtils::ExpectVariableNotSet(provider_->var_new_channel());
@@ -445,4 +445,29 @@
   UmTestUtils::ExpectVariableHasValue(UpdateRestrictions::kNone,
                                       provider_->var_update_restrictions());
 }
+
+TEST_F(UmRealUpdaterProviderTest, TestUpdateCheckIntervalTimeout) {
+  UmTestUtils::ExpectVariableNotSet(
+      provider_->var_test_update_check_interval_timeout());
+  fake_prefs_.SetInt64(
+      chromeos_update_engine::kPrefsTestUpdateCheckIntervalTimeout, 1);
+  UmTestUtils::ExpectVariableHasValue(
+      static_cast<int64_t>(1),
+      provider_->var_test_update_check_interval_timeout());
+
+  // Make sure the value does not exceed a threshold of 10 minutes.
+  fake_prefs_.SetInt64(
+      chromeos_update_engine::kPrefsTestUpdateCheckIntervalTimeout, 11 * 60);
+  // The next 5 reads should return valid values.
+  for (int i = 0; i < 5; ++i)
+    UmTestUtils::ExpectVariableHasValue(
+        static_cast<int64_t>(10 * 60),
+        provider_->var_test_update_check_interval_timeout());
+
+  // Just to make sure it is not cached anywhere and deleted. The variable is
+  // allowd to be read 6 times.
+  UmTestUtils::ExpectVariableNotSet(
+      provider_->var_test_update_check_interval_timeout());
+}
+
 }  // namespace chromeos_update_manager
diff --git a/update_manager/rollback_prefs.h b/update_manager/rollback_prefs.h
index 9567701..6cbc447 100644
--- a/update_manager/rollback_prefs.h
+++ b/update_manager/rollback_prefs.h
@@ -35,6 +35,19 @@
   kMaxValue = 4
 };
 
+// Whether the device should do rollback and powerwash on channel downgrade.
+// Matches chrome_device_policy.proto's
+// |AutoUpdateSettingsProto::ChannelDowngradeBehavior|.
+enum class ChannelDowngradeBehavior {
+  kUnspecified = 0,
+  kWaitForVersionToCatchUp = 1,
+  kRollback = 2,
+  kAllowUserToConfigure = 3,
+  // These values must be kept up to date.
+  kFirstValue = kUnspecified,
+  kLastValue = kAllowUserToConfigure
+};
+
 }  // namespace chromeos_update_manager
 
 #endif  // UPDATE_ENGINE_UPDATE_MANAGER_ROLLBACK_PREFS_H_
diff --git a/update_manager/shill_provider.h b/update_manager/shill_provider.h
index c7bb2e2..ebe7a3a 100644
--- a/update_manager/shill_provider.h
+++ b/update_manager/shill_provider.h
@@ -19,7 +19,7 @@
 
 #include <base/time/time.h>
 
-#include "update_engine/connection_utils.h"
+#include "update_engine/common/connection_utils.h"
 #include "update_engine/update_manager/provider.h"
 #include "update_engine/update_manager/variable.h"
 
diff --git a/update_manager/staging_utils.cc b/update_manager/staging_utils.cc
index f4f685c..e8f07bb 100644
--- a/update_manager/staging_utils.cc
+++ b/update_manager/staging_utils.cc
@@ -27,7 +27,7 @@
 #include "update_engine/common/constants.h"
 #include "update_engine/common/hardware_interface.h"
 #include "update_engine/common/prefs_interface.h"
-#include "update_engine/system_state.h"
+#include "update_engine/common/system_state.h"
 
 using base::TimeDelta;
 using chromeos_update_engine::kPrefsWallClockStagingWaitPeriod;
diff --git a/update_manager/state_factory.cc b/update_manager/state_factory.cc
index 78cec6a..a95a5a8 100644
--- a/update_manager/state_factory.cc
+++ b/update_manager/state_factory.cc
@@ -25,7 +25,7 @@
 
 #include "update_engine/common/clock_interface.h"
 #if USE_DBUS
-#include "update_engine/dbus_connection.h"
+#include "update_engine/cros/dbus_connection.h"
 #endif  // USE_DBUS
 #include "update_engine/update_manager/fake_shill_provider.h"
 #include "update_engine/update_manager/real_config_provider.h"
@@ -36,7 +36,7 @@
 #include "update_engine/update_manager/real_time_provider.h"
 #include "update_engine/update_manager/real_updater_provider.h"
 #if USE_SHILL
-#include "update_engine/shill_proxy.h"
+#include "update_engine/cros/shill_proxy.h"
 #include "update_engine/update_manager/real_shill_provider.h"
 #endif  // USE_SHILL
 
@@ -69,8 +69,8 @@
   unique_ptr<FakeShillProvider> shill_provider(new FakeShillProvider());
 #endif  // USE_SHILL
   unique_ptr<RealRandomProvider> random_provider(new RealRandomProvider());
-  unique_ptr<RealSystemProvider> system_provider(new RealSystemProvider(
-      system_state->hardware(), system_state->boot_control(), kiosk_app_proxy));
+  unique_ptr<RealSystemProvider> system_provider(
+      new RealSystemProvider(system_state, kiosk_app_proxy));
 
   unique_ptr<RealTimeProvider> time_provider(new RealTimeProvider(clock));
   unique_ptr<RealUpdaterProvider> updater_provider(
diff --git a/update_manager/state_factory.h b/update_manager/state_factory.h
index 1c1c1d9..ac3bf6b 100644
--- a/update_manager/state_factory.h
+++ b/update_manager/state_factory.h
@@ -17,7 +17,7 @@
 #ifndef UPDATE_ENGINE_UPDATE_MANAGER_STATE_FACTORY_H_
 #define UPDATE_ENGINE_UPDATE_MANAGER_STATE_FACTORY_H_
 
-#include "update_engine/system_state.h"
+#include "update_engine/common/system_state.h"
 #include "update_engine/update_manager/state.h"
 
 namespace org {
diff --git a/update_manager/system_provider.h b/update_manager/system_provider.h
index 13e188b..8eb14e3 100644
--- a/update_manager/system_provider.h
+++ b/update_manager/system_provider.h
@@ -17,6 +17,10 @@
 #ifndef UPDATE_ENGINE_UPDATE_MANAGER_SYSTEM_PROVIDER_H_
 #define UPDATE_ENGINE_UPDATE_MANAGER_SYSTEM_PROVIDER_H_
 
+#include <string>
+
+#include <base/version.h>
+
 #include "update_engine/update_manager/provider.h"
 #include "update_engine/update_manager/variable.h"
 
@@ -46,6 +50,9 @@
   // with zero delay kiosk app if any.
   virtual Variable<std::string>* var_kiosk_required_platform_version() = 0;
 
+  // Chrome OS version number as provided by |ImagePropeties|.
+  virtual Variable<base::Version>* var_chromeos_version() = 0;
+
  protected:
   SystemProvider() {}
 
diff --git a/update_manager/update_manager-inl.h b/update_manager/update_manager-inl.h
index a1d172d..550642c 100644
--- a/update_manager/update_manager-inl.h
+++ b/update_manager/update_manager-inl.h
@@ -49,7 +49,6 @@
   ec->ResetEvaluation();
 
   const std::string policy_name = policy_->PolicyRequestName(policy_method);
-  LOG(INFO) << policy_name << ": START";
 
   // First try calling the actual policy.
   std::string error;
@@ -71,8 +70,6 @@
     }
   }
 
-  LOG(INFO) << policy_name << ": END";
-
   return status;
 }
 
diff --git a/update_manager/updater_provider.h b/update_manager/updater_provider.h
index 81ffb41..86af1c8 100644
--- a/update_manager/updater_provider.h
+++ b/update_manager/updater_provider.h
@@ -116,6 +116,10 @@
   // for all updates.
   virtual Variable<UpdateRestrictions>* var_update_restrictions() = 0;
 
+  // A variable that returns the number of seconds for the first update check to
+  // happen.
+  virtual Variable<int64_t>* var_test_update_check_interval_timeout() = 0;
+
  protected:
   UpdaterProvider() {}
 
diff --git a/update_manager/variable.h b/update_manager/variable.h
index 6c7d350..9ac7dae 100644
--- a/update_manager/variable.h
+++ b/update_manager/variable.h
@@ -83,6 +83,10 @@
   // variable. In other case, it returns 0.
   base::TimeDelta GetPollInterval() const { return poll_interval_; }
 
+  // Returns true, if the value for this variable is expected to be missing
+  // sometimes so we can avoid printing confusing error logs.
+  bool IsMissingOk() const { return missing_ok_; }
+
   // Adds and removes observers for value changes on the variable. This only
   // works for kVariableAsync variables since the other modes don't track value
   // changes. Adding the same observer twice has no effect.
@@ -115,6 +119,8 @@
     poll_interval_ = poll_interval;
   }
 
+  void SetMissingOk() { missing_ok_ = true; }
+
   // Calls ValueChanged on all the observers.
   void NotifyValueChanged() {
     // Fire all the observer methods from the main loop as single call. In order
@@ -140,7 +146,8 @@
       : name_(name),
         mode_(mode),
         poll_interval_(mode == kVariableModePoll ? poll_interval
-                                                 : base::TimeDelta()) {}
+                                                 : base::TimeDelta()),
+        missing_ok_(false) {}
 
   void OnValueChangedNotification() {
     // A ValueChanged() method can change the list of observers, for example
@@ -174,6 +181,9 @@
   // The list of value changes observers.
   std::list<BaseVariable::ObserverInterface*> observer_list_;
 
+  // Defines whether this variable is expected to have no value.
+  bool missing_ok_;
+
   DISALLOW_COPY_AND_ASSIGN(BaseVariable);
 };
 
diff --git a/update_metadata.proto b/update_metadata.proto
index 373ee5e..452b89d 100644
--- a/update_metadata.proto
+++ b/update_metadata.proto
@@ -153,16 +153,16 @@
 //
 // All fields will be set, if this message is present.
 message ImageInfo {
-  optional string board = 1;
-  optional string key = 2;
-  optional string channel = 3;
-  optional string version = 4;
+  optional string board = 1 [deprecated = true];
+  optional string key = 2 [deprecated = true];
+  optional string channel = 3 [deprecated = true];
+  optional string version = 4 [deprecated = true];
 
   // If these values aren't present, they should be assumed to match
   // the equivalent value above. They are normally only different for
   // special image types such as nplusone images.
-  optional string build_channel = 5;
-  optional string build_version = 6;
+  optional string build_channel = 5 [deprecated = true];
+  optional string build_version = 6 [deprecated = true];
 }
 
 message InstallOperation {
@@ -173,15 +173,15 @@
     BSDIFF = 3 [deprecated = true];  // The data is a bsdiff binary diff.
 
     // On minor version 2 or newer, these operations are supported:
-    SOURCE_COPY = 4; // Copy from source to target partition
-    SOURCE_BSDIFF = 5; // Like BSDIFF, but read from source partition
+    SOURCE_COPY = 4;    // Copy from source to target partition
+    SOURCE_BSDIFF = 5;  // Like BSDIFF, but read from source partition
 
     // On minor version 3 or newer and on major version 2 or newer, these
     // operations are supported:
-    REPLACE_XZ = 8; // Replace destination extents w/ attached xz data.
+    REPLACE_XZ = 8;  // Replace destination extents w/ attached xz data.
 
     // On minor version 4 or newer, these operations are supported:
-    ZERO = 6;  // Write zeros in the destination.
+    ZERO = 6;     // Write zeros in the destination.
     DISCARD = 7;  // Discard the destination blocks, reading as undefined.
     BROTLI_BSDIFF = 10;  // Like SOURCE_BSDIFF, but compressed with brotli.
 
@@ -314,6 +314,11 @@
   // skip writing the raw bytes for these extents. During snapshot merge, the
   // bytes will read from the source partitions instead.
   repeated CowMergeOperation merge_operations = 18;
+
+  // Estimated size for COW image. This is used by libsnapshot
+  // as a hint. If set to 0, libsnapshot should use alternative
+  // methods for estimating size.
+  optional uint64 estimate_cow_size = 19;
 }
 
 message DynamicPartitionGroup {
@@ -371,9 +376,9 @@
   optional PartitionInfo new_rootfs_info = 9 [deprecated = true];
 
   // old_image_info will only be present for delta images.
-  optional ImageInfo old_image_info = 10;
+  optional ImageInfo old_image_info = 10 [deprecated = true];
 
-  optional ImageInfo new_image_info = 11;
+  optional ImageInfo new_image_info = 11 [deprecated = true];
 
   // The minor version, also referred as "delta version", of the payload.
   // Minor version 0 is full payload, everything else is delta payload.