Snap for 8426163 from aac87573f533444fac35d1f4af0430182afe5d09 to mainline-tzdata2-release

Change-Id: I0bf492235f67c57a57eea2e4f737beeeca2a0f9f
diff --git a/.clang-format b/.clang-format
index 3044f59..3b6a627 100644
--- a/.clang-format
+++ b/.clang-format
@@ -37,7 +37,3 @@
 IncludeBlocks: Preserve
 PointerAlignment: Left
 TabWidth: 2
-
-# cpplint.py does smarter #include sorting than clang-format (the former ignores
-# case and changes '-' to '_').
-SortIncludes: false
diff --git a/Android.bp b/Android.bp
index d74e78f..3287b7b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -15,23 +15,6 @@
 //
 
 // AIDL interface between libupdate_engine and framework.jar
-package {
-    default_applicable_licenses: ["system_update_engine_license"],
-}
-
-// Added automatically by a large-scale-change
-// See: http://go/android-license-faq
-license {
-    name: "system_update_engine_license",
-    visibility: [":__subpackages__"],
-    license_kinds: [
-        "SPDX-license-identifier-Apache-2.0",
-    ],
-    license_text: [
-        "NOTICE",
-    ],
-}
-
 filegroup {
     name: "libupdate_engine_aidl",
     srcs: [
@@ -46,7 +29,12 @@
 
     cflags: [
         "-DBASE_VER=576279",
+        "-DUSE_BINDER=1",
+        "-DUSE_CHROME_NETWORK_PROXY=0",
+        "-DUSE_CHROME_KIOSK_APP=0",
         "-DUSE_HWID_OVERRIDE=0",
+        "-DUSE_MTD=0",
+        "-DUSE_OMAHA=0",
         "-D_FILE_OFFSET_BITS=64",
         "-D_POSIX_C_SOURCE=199309L",
         "-Wa,--noexecstack",
@@ -66,7 +54,7 @@
     ],
     include_dirs: ["system"],
     local_include_dirs: ["client_library/include"],
-    header_libs: ["libgtest_prod_headers"],
+    static_libs: ["libgtest_prod"],
     shared_libs: [
         "libbrillo-stream",
         "libbrillo",
@@ -97,44 +85,6 @@
     },
 }
 
-// libcow_operation_convert (type: library)
-// ========================================================
-cc_library_static {
-    name: "libpayload_extent_utils",
-    defaults: [
-        "ue_defaults",
-    ],
-    host_supported: true,
-    recovery_available: true,
-    srcs: [
-        "payload_generator/extent_utils.cc",
-    ],
-    static_libs: [
-        "update_metadata-protos",
-    ],
-}
-
-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",
-        "libpayload_extent_utils",
-        "libbrotli",
-        "libz",
-    ],
-}
-
 // update_metadata-protos (type: static_library)
 // ========================================================
 // Protobufs.
@@ -173,22 +123,15 @@
         "libbz",
         "libbspatch",
         "libbrotli",
-        "libc++fs",
         "libfec_rs",
         "libpuffpatch",
         "libverity_tree",
-        "libsnapshot_cow",
-        "libbrotli",
-        "libz",
-        "libpayload_extent_ranges",
-        "libpayload_extent_utils",
-        "libcow_operation_convert",
     ],
     shared_libs: [
+        "libziparchive",
         "libbase",
         "libcrypto",
         "libfec",
-        "libziparchive",
     ],
 }
 
@@ -202,7 +145,6 @@
     recovery_available: true,
 
     srcs: [
-        "aosp/platform_constants_android.cc",
         "common/action_processor.cc",
         "common/boot_control_stub.cc",
         "common/clock.cc",
@@ -216,6 +158,7 @@
         "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",
@@ -224,8 +167,8 @@
         "payload_consumer/bzip_extent_writer.cc",
         "payload_consumer/cached_file_descriptor.cc",
         "payload_consumer/certificate_parser_android.cc",
-        "payload_consumer/cow_writer_file_descriptor.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",
@@ -237,15 +180,10 @@
         "payload_consumer/payload_constants.cc",
         "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",
         "payload_consumer/fec_file_descriptor.cc",
-        "payload_consumer/partition_update_generator_android.cc",
     ],
 }
 
@@ -262,8 +200,6 @@
         "libgsi",
         "libpayload_consumer",
         "libsnapshot",
-        "libsnapshot_cow",
-        "libz",
         "update_metadata-protos",
     ],
     shared_libs: [
@@ -305,10 +241,10 @@
     recovery_available: true,
 
     srcs: [
-        "aosp/boot_control_android.cc",
-        "aosp/cleanup_previous_update_action.cc",
-        "aosp/dynamic_partition_control_android.cc",
-        "aosp/dynamic_partition_utils.cc",
+        "boot_control_android.cc",
+        "cleanup_previous_update_action.cc",
+        "dynamic_partition_control_android.cc",
+        "dynamic_partition_utils.cc",
     ],
 }
 
@@ -325,15 +261,10 @@
     ],
 
     static_libs: [
-        "libavb",
-        "libavb_user",
-        "gkiprops",
         "libpayload_consumer",
         "libupdate_engine_boot_control",
-        "PlatformProperties",
     ],
     shared_libs: [
-        "apex_aidl_interface-cpp",
         "libandroid_net",
         "libbase",
         "libbinder",
@@ -342,15 +273,11 @@
         "libbrillo-binder",
         "libcurl",
         "libcutils",
-        "libupdate_engine_stable-V1-cpp",
         "liblog",
         "libssl",
         "libstatslog",
         "libutils",
     ],
-    whole_static_libs: [
-        "com.android.sysprop.apex",
-    ],
 }
 
 cc_library_static {
@@ -371,20 +298,17 @@
 
     srcs: [
         ":libupdate_engine_aidl",
-        "common/system_state.cc",
-        "aosp/apex_handler_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",
+        "binder_service_android.cc",
         "certificate_checker.cc",
-        "download_action.cc",
+        "daemon.cc",
+        "daemon_state_android.cc",
+        "hardware_android.cc",
         "libcurl_http_fetcher.cc",
+        "logging_android.cc",
+        "metrics_reporter_android.cc",
         "metrics_utils.cc",
+        "network_selector_android.cc",
+        "update_attempter_android.cc",
         "update_boot_flags_action.cc",
         "update_status_utils.cc",
     ],
@@ -406,7 +330,7 @@
         "otacerts",
     ],
 
-    srcs: ["main.cc", "aosp/metrics_reporter_android.cc"],
+    srcs: ["main.cc"],
     init_rc: ["update_engine.rc"],
 }
 
@@ -429,18 +353,15 @@
     // TODO(deymo): Remove external/cros/system_api/dbus once the strings are moved
     // out of the DBus interface.
     include_dirs: ["external/cros/system_api/dbus"],
-    header_libs: ["libgtest_prod_headers"],
 
     srcs: [
-        "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",
-        "common/system_state.cc",
-        "download_action.cc",
+        "hardware_android.cc",
+        "logging_android.cc",
+        "metrics_reporter_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",
     ],
@@ -461,9 +382,9 @@
         // We add the static versions of the shared libraries that are not installed to
         // recovery image due to size concerns. Need to include all the static library
         // dependencies of these static libraries.
-        "gkiprops",
         "libevent",
         "libmodpb64",
+        "libgtest_prod",
         "libprotobuf-cpp-lite",
         "libbrillo-stream",
         "libbrillo",
@@ -485,6 +406,54 @@
     ],
 }
 
+// libupdate_engine_client (type: shared_library)
+// ========================================================
+cc_library_shared {
+    name: "libupdate_engine_client",
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+        "-DUSE_BINDER=1",
+    ],
+    export_include_dirs: ["client_library/include"],
+    include_dirs: [
+        // TODO(deymo): Remove "external/cros/system_api/dbus" when dbus is not used.
+        "external/cros/system_api/dbus",
+        "system",
+    ],
+
+    aidl: {
+        local_include_dirs: ["binder_bindings"],
+    },
+
+    shared_libs: [
+        "libchrome",
+        "libbrillo",
+        "libbinder",
+        "libbrillo-binder",
+        "libutils",
+    ],
+
+    srcs: [
+        ":libupdate_engine_client_aidl",
+        "client_library/client.cc",
+        "client_library/client_binder.cc",
+        "parcelable_update_engine_status.cc",
+        "update_status_utils.cc",
+    ],
+}
+
+filegroup {
+    name: "libupdate_engine_client_aidl",
+    srcs: [
+        "binder_bindings/android/brillo/IUpdateEngine.aidl",
+        "binder_bindings/android/brillo/IUpdateEngineStatusCallback.aidl",
+    ],
+    path: "binder_bindings",
+}
+
 // update_engine_client (type: executable)
 // ========================================================
 // update_engine console client.
@@ -509,8 +478,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",
     ],
 }
@@ -540,8 +509,6 @@
         "libpuffdiff",
         "libverity_tree",
         "update_metadata-protos",
-        "libpayload_extent_utils",
-        "libcow_size_estimator",
     ],
     shared_libs: [
         "libbase",
@@ -550,40 +517,6 @@
 }
 
 cc_library_static {
-    name: "libpayload_extent_ranges",
-    defaults: [
-        "ue_defaults",
-    ],
-    host_supported: true,
-    recovery_available: true,
-    srcs: [
-        "payload_generator/extent_ranges.cc",
-    ],
-    static_libs: [
-        "update_metadata-protos",
-    ],
-}
-
-cc_library_static {
-    name: "libcow_size_estimator",
-    defaults: [
-        "ue_defaults",
-        "update_metadata-protos_exports"
-    ],
-    host_supported: true,
-    recovery_available: true,
-    srcs: [
-        "payload_generator/cow_size_estimator.cc",
-    ],
-    static_libs: [
-        "update_metadata-protos",
-        "libbase",
-        "libsnapshot_cow",
-        "libcow_operation_convert",
-    ],
-}
-
-cc_library_static {
     name: "libpayload_generator",
     defaults: [
         "ue_defaults",
@@ -592,29 +525,32 @@
     host_supported: true,
 
     srcs: [
-        "common/system_state.cc",
-        "download_action.cc",
         "payload_generator/ab_generator.cc",
         "payload_generator/annotated_operation.cc",
         "payload_generator/blob_file_writer.cc",
         "payload_generator/block_mapping.cc",
         "payload_generator/boot_img_filesystem.cc",
         "payload_generator/bzip.cc",
+        "payload_generator/cycle_breaker.cc",
         "payload_generator/deflate_utils.cc",
         "payload_generator/delta_diff_generator.cc",
         "payload_generator/delta_diff_utils.cc",
         "payload_generator/ext2_filesystem.cc",
         "payload_generator/extent_ranges.cc",
+        "payload_generator/extent_utils.cc",
         "payload_generator/full_update_generator.cc",
+        "payload_generator/graph_types.cc",
+        "payload_generator/graph_utils.cc",
+        "payload_generator/inplace_generator.cc",
         "payload_generator/mapfile_filesystem.cc",
-        "payload_generator/merge_sequence_generator.cc",
         "payload_generator/payload_file.cc",
         "payload_generator/payload_generation_config_android.cc",
         "payload_generator/payload_generation_config.cc",
-        "payload_generator/payload_properties.cc",
         "payload_generator/payload_signer.cc",
         "payload_generator/raw_filesystem.cc",
         "payload_generator/squashfs_filesystem.cc",
+        "payload_generator/tarjan.cc",
+        "payload_generator/topological_sort.cc",
         "payload_generator/xz_android.cc",
     ],
 }
@@ -765,22 +701,16 @@
     test_suites: ["device-tests"],
 
     srcs: [
-        "aosp/apex_handler_android_unittest.cc",
-        "aosp/cleanup_previous_update_action_unittest.cc",
-        "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",
@@ -788,24 +718,18 @@
         "common/terminator_unittest.cc",
         "common/test_utils.cc",
         "common/utils_unittest.cc",
-        "download_action_android_unittest.cc",
-        "libcurl_http_fetcher_unittest.cc",
+        "dynamic_partition_control_android_unittest.cc",
         "payload_consumer/bzip_extent_writer_unittest.cc",
         "payload_consumer/cached_file_descriptor_unittest.cc",
-        "payload_consumer/cow_writer_file_descriptor_unittest.cc",
         "payload_consumer/certificate_parser_android_unittest.cc",
         "payload_consumer/delta_performer_integration_test.cc",
         "payload_consumer/delta_performer_unittest.cc",
-        "payload_consumer/partition_writer_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",
         "payload_consumer/filesystem_verifier_action_unittest.cc",
-        "payload_consumer/install_plan_unittest.cc",
-        "payload_consumer/partition_update_generator_android_unittest.cc",
         "payload_consumer/postinstall_runner_action_unittest.cc",
         "payload_consumer/verity_writer_android_unittest.cc",
         "payload_consumer/xz_extent_writer_unittest.cc",
@@ -813,6 +737,7 @@
         "payload_generator/blob_file_writer_unittest.cc",
         "payload_generator/block_mapping_unittest.cc",
         "payload_generator/boot_img_filesystem_unittest.cc",
+        "payload_generator/cycle_breaker_unittest.cc",
         "payload_generator/deflate_utils_unittest.cc",
         "payload_generator/delta_diff_utils_unittest.cc",
         "payload_generator/ext2_filesystem_unittest.cc",
@@ -820,28 +745,30 @@
         "payload_generator/extent_utils_unittest.cc",
         "payload_generator/fake_filesystem.cc",
         "payload_generator/full_update_generator_unittest.cc",
+        "payload_generator/graph_utils_unittest.cc",
+        "payload_generator/inplace_generator_unittest.cc",
         "payload_generator/mapfile_filesystem_unittest.cc",
-        "payload_generator/merge_sequence_generator_unittest.cc",
         "payload_generator/payload_file_unittest.cc",
         "payload_generator/payload_generation_config_android_unittest.cc",
         "payload_generator/payload_generation_config_unittest.cc",
-        "payload_generator/payload_properties_unittest.cc",
         "payload_generator/payload_signer_unittest.cc",
         "payload_generator/squashfs_filesystem_unittest.cc",
+        "payload_generator/tarjan_unittest.cc",
+        "payload_generator/topological_sort_unittest.cc",
         "payload_generator/zip_unittest.cc",
         "testrunner.cc",
-        "update_status_utils_unittest.cc",
+        "update_attempter_android_unittest.cc",
     ],
 }
 
 // Brillo update payload generation script
 // ========================================================
-sh_binary {
+cc_prebuilt_binary {
     name: "brillo_update_payload",
     device_supported: false,
     host_supported: true,
 
-    src: "scripts/brillo_update_payload",
+    srcs: ["scripts/brillo_update_payload"],
     required: [
         "delta_generator",
         "shflags",
@@ -855,49 +782,11 @@
     },
 }
 
-// update_engine header library
-cc_library_headers {
-    name: "libupdate_engine_headers",
-
-    // This header library is available to core and product modules.
-    product_available: true,
-
-    export_include_dirs: ["."],
-    apex_available: [
-        "com.android.gki.*",
-        "//apex_available:platform",
-    ],
-    host_supported: true,
-    recovery_available: true,
-    ramdisk_available: true,
-
-    target: {
-        darwin: {
-            enabled: false,
-        },
-    }
-}
-
-cc_binary_host {
-    name: "cow_converter",
-    defaults: [
-        "ue_defaults",
-        "libpayload_consumer_exports",
-    ],
+// AIDL interface between libupdate_engine and the Things jar.
+filegroup {
+    name: "things_update_engine_aidl",
     srcs: [
-        "aosp/cow_converter.cc",
-    ],
-    static_libs: [
-        "liblog",
-        "libbrotli",
-        "libbase",
-        "libcow_operation_convert",
-        "libcow_size_estimator",
-        "libpayload_consumer",
-        "libpayload_extent_ranges",
-        "libpayload_extent_utils",
-        "libsnapshot_cow",
-        "libz",
-        "update_metadata-protos",
+        "binder_bindings/android/brillo/IUpdateEngine.aidl",
+        "binder_bindings/android/brillo/IUpdateEngineStatusCallback.aidl",
     ],
 }
diff --git a/BUILD.gn b/BUILD.gn
deleted file mode 100644
index e60d33b..0000000
--- a/BUILD.gn
+++ /dev/null
@@ -1,609 +0,0 @@
-#
-# Copyright (C) 2019 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# Stop linter from complaining XXX_unittest.cc naming.
-# gnlint: disable=GnLintSourceFileNames
-
-import("//common-mk/generate-dbus-adaptors.gni")
-import("//common-mk/generate-dbus-proxies.gni")
-import("//common-mk/openssl_pem.gni")
-import("//common-mk/pkg_config.gni")
-import("//common-mk/proto_library.gni")
-import("//update_engine/tar_bunzip2.gni")
-
-group("all") {
-  deps = [
-    ":delta_generator",
-    ":libpayload_consumer",
-    ":libpayload_generator",
-    ":libupdate_engine",
-    ":libupdate_engine_client",
-    ":update_engine",
-    ":update_engine-dbus-adaptor",
-    ":update_engine-dbus-kiosk-app-client",
-    ":update_engine_client",
-    ":update_metadata-protos",
-  ]
-
-  if (use.test) {
-    deps += [
-      ":test_http_server",
-      ":test_subprocess",
-      ":update_engine-test_images",
-      ":update_engine-testkeys",
-      ":update_engine-testkeys-ec",
-      ":update_engine_test_libs",
-      ":update_engine_unittests",
-    ]
-  }
-
-  if (use.fuzzer) {
-    deps += [
-      ":update_engine_delta_performer_fuzzer",
-      ":update_engine_omaha_request_action_fuzzer",
-    ]
-  }
-}
-
-pkg_config("target_defaults") {
-  cflags_cc = [
-    "-fno-strict-aliasing",
-    "-Wnon-virtual-dtor",
-  ]
-  cflags = [ "-ffunction-sections" ]
-  ldflags = [ "-Wl,--gc-sections" ]
-  defines = [
-    "__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}",
-  ]
-  include_dirs = [
-    # We need this include dir because we include all the local code as
-    # "update_engine/...".
-    "${platform2_root}",
-    "${platform2_root}/update_engine/client_library/include",
-  ]
-
-  # NOSORT
-  pkg_deps = [
-    "libbrillo",
-    "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
-    # order.
-    "system_api",
-  ]
-  if (use.fuzzer) {
-    pkg_deps += [ "protobuf" ]
-  } else {
-    pkg_deps += [ "protobuf-lite" ]
-  }
-}
-
-# Protobufs.
-proto_library("update_metadata-protos") {
-  proto_in_dir = "."
-  proto_out_dir = "include/update_engine"
-  sources = [ "update_metadata.proto" ]
-}
-
-# Chrome D-Bus bindings.
-generate_dbus_adaptors("update_engine-dbus-adaptor") {
-  dbus_adaptors_out_dir = "include/dbus_bindings"
-  sources = [ "dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml" ]
-}
-
-generate_dbus_proxies("update_engine-dbus-kiosk-app-client") {
-  mock_output_file = "include/kiosk-app/dbus-proxy-mocks.h"
-  proxy_output_file = "include/kiosk-app/dbus-proxies.h"
-  sources = [ "dbus_bindings/org.chromium.KioskAppService.dbus-xml" ]
-}
-
-# The payload application component and common dependencies.
-static_library("libpayload_consumer") {
-  sources = [
-    "common/action_processor.cc",
-    "common/boot_control_stub.cc",
-    "common/clock.cc",
-    "common/constants.cc",
-    "common/cpu_limiter.cc",
-    "common/dynamic_partition_control_stub.cc",
-    "common/error_code_utils.cc",
-    "common/hash_calculator.cc",
-    "common/http_common.cc",
-    "common/http_fetcher.cc",
-    "common/hwid_override.cc",
-    "common/multi_range_http_fetcher.cc",
-    "common/prefs.cc",
-    "common/proxy_resolver.cc",
-    "common/subprocess.cc",
-    "common/terminator.cc",
-    "common/utils.cc",
-    "cros/platform_constants_chromeos.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/extent_reader.cc",
-    "payload_consumer/extent_writer.cc",
-    "payload_consumer/file_descriptor.cc",
-    "payload_consumer/file_descriptor_utils.cc",
-    "payload_consumer/file_writer.cc",
-    "payload_consumer/filesystem_verifier_action.cc",
-    "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",
-    "payload_consumer/postinstall_runner_action.cc",
-    "payload_consumer/verity_writer_stub.cc",
-    "payload_consumer/xz_extent_writer.cc",
-  ]
-  configs += [ ":target_defaults" ]
-  libs = [
-    "bz2",
-    "rt",
-  ]
-
-  # TODO(crbug.com/1082873): Remove after fixing usage of deprecated
-  # declarations.
-  cflags_cc = [ "-Wno-error=deprecated-declarations" ]
-
-  # TODO(deymo): Remove unused dependencies once we stop including files
-  # from the root directory.
-  all_dependent_pkg_deps = [
-    "libbspatch",
-    "libcrypto",
-    "libpuffpatch",
-    "xz-embedded",
-  ]
-  public_deps = [ ":update_metadata-protos" ]
-}
-
-# The main daemon static_library with all the code used to check for updates
-# with Omaha and expose a DBus daemon.
-static_library("libupdate_engine") {
-  sources = [
-    "certificate_checker.cc",
-    "common/connection_utils.cc",
-    "common/system_state.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",
-    "cros/download_action_chromeos.cc",
-    "libcurl_http_fetcher.cc",
-    "metrics_utils.cc",
-    "update_boot_flags_action.cc",
-    "update_manager/boxed_value.cc",
-    "update_manager/chromeos_policy.cc",
-    "update_manager/default_policy.cc",
-    "update_manager/enough_slots_ab_updates_policy_impl.cc",
-    "update_manager/enterprise_device_policy_impl.cc",
-    "update_manager/enterprise_rollback_policy_impl.cc",
-    "update_manager/evaluation_context.cc",
-    "update_manager/interactive_update_policy_impl.cc",
-    "update_manager/minimum_version_policy_impl.cc",
-    "update_manager/next_update_check_policy_impl.cc",
-    "update_manager/official_build_check_policy_impl.cc",
-    "update_manager/out_of_box_experience_policy_impl.cc",
-    "update_manager/policy.cc",
-    "update_manager/policy_test_utils.cc",
-    "update_manager/real_config_provider.cc",
-    "update_manager/real_device_policy_provider.cc",
-    "update_manager/real_random_provider.cc",
-    "update_manager/real_shill_provider.cc",
-    "update_manager/real_system_provider.cc",
-    "update_manager/real_time_provider.cc",
-    "update_manager/real_updater_provider.cc",
-    "update_manager/staging_utils.cc",
-    "update_manager/state_factory.cc",
-    "update_manager/update_manager.cc",
-    "update_manager/update_time_restrictions_monitor.cc",
-    "update_manager/update_time_restrictions_policy_impl.cc",
-    "update_manager/weekly_time.cc",
-    "update_status_utils.cc",
-  ]
-  configs += [ ":target_defaults" ]
-  libs = [
-    "bz2",
-    "policy",
-    "rootdev",
-    "rt",
-  ]
-  all_dependent_pkg_deps = [
-    "dbus-1",
-    "expat",
-    "libcurl",
-    "libdebugd-client",
-    "libmetrics",
-    "libpower_manager-client",
-    "libsession_manager-client",
-    "libshill-client",
-    "libssl",
-    "libupdate_engine-client",
-    "vboot_host",
-  ]
-  deps = [
-    ":libpayload_consumer",
-    ":update_engine-dbus-adaptor",
-    ":update_engine-dbus-kiosk-app-client",
-    ":update_metadata-protos",
-  ]
-
-  if (use.dlc) {
-    all_dependent_pkg_deps += [ "libdlcservice-client" ]
-  }
-
-  if (use.chrome_network_proxy) {
-    sources += [ "cros/chrome_browser_proxy_resolver.cc" ]
-  }
-
-  if (use.dlc) {
-    sources += [
-      "cros/dlcservice_chromeos.cc",
-      "cros/excluder_chromeos.cc",
-    ]
-  } else {
-    sources += [
-      "common/dlcservice_stub.cc",
-      "common/excluder_stub.cc",
-    ]
-  }
-}
-
-# update_engine daemon.
-executable("update_engine") {
-  sources = [ "main.cc" ]
-  configs += [ ":target_defaults" ]
-  deps = [ ":libupdate_engine" ]
-}
-
-# update_engine client library.
-static_library("libupdate_engine_client") {
-  sources = [
-    "client_library/client_dbus.cc",
-    "update_status_utils.cc",
-  ]
-  include_dirs = [ "client_library/include" ]
-  configs += [ ":target_defaults" ]
-  pkg_deps = [
-    "dbus-1",
-    "libupdate_engine-client",
-  ]
-}
-
-# update_engine console client.
-executable("update_engine_client") {
-  sources = [
-    "common/error_code_utils.cc",
-    "cros/omaha_utils.cc",
-    "cros/update_engine_client.cc",
-  ]
-  configs += [ ":target_defaults" ]
-  deps = [ ":libupdate_engine_client" ]
-}
-
-# server-side code. This is used for delta_generator and unittests but not
-# for any client code.
-static_library("libpayload_generator") {
-  sources = [
-    "common/file_fetcher.cc",
-    "common/system_state.cc",
-    "cros/real_system_state.cc",
-    "download_action.cc",
-    "payload_generator/ab_generator.cc",
-    "payload_generator/annotated_operation.cc",
-    "payload_generator/blob_file_writer.cc",
-    "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",
-    "payload_generator/ext2_filesystem.cc",
-    "payload_generator/extent_ranges.cc",
-    "payload_generator/extent_utils.cc",
-    "payload_generator/full_update_generator.cc",
-    "payload_generator/mapfile_filesystem.cc",
-    "payload_generator/merge_sequence_generator.cc",
-    "payload_generator/payload_file.cc",
-    "payload_generator/payload_generation_config.cc",
-    "payload_generator/payload_generation_config_chromeos.cc",
-    "payload_generator/payload_properties.cc",
-    "payload_generator/payload_signer.cc",
-    "payload_generator/raw_filesystem.cc",
-    "payload_generator/squashfs_filesystem.cc",
-    "payload_generator/xz_chromeos.cc",
-  ]
-  configs += [ ":target_defaults" ]
-  all_dependent_pkg_deps = [
-    "ext2fs",
-    "libbsdiff",
-    "liblzma",
-    "libpuffdiff",
-  ]
-  deps = [
-    ":libpayload_consumer",
-    ":update_metadata-protos",
-  ]
-
-  # TODO(crbug.com/1082873): Remove after fixing usage of deprecated
-  # declarations.
-  cflags_cc = [ "-Wno-error=deprecated-declarations" ]
-}
-
-# server-side delta generator.
-executable("delta_generator") {
-  sources = [ "payload_generator/generate_delta_main.cc" ]
-  configs += [ ":target_defaults" ]
-  configs -= [ "//common-mk:pie" ]
-  deps = [
-    ":libpayload_consumer",
-    ":libpayload_generator",
-  ]
-}
-
-if (use.test || use.fuzzer) {
-  static_library("update_engine_test_libs") {
-    sources = [
-      "common/fake_prefs.cc",
-      "common/mock_http_fetcher.cc",
-      "common/test_utils.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",
-    ]
-
-    # TODO(crbug.com/887845): After library odering issue is fixed,
-    # //common-mk:test can be moved in all_dependent_configs and
-    # //common-mk:test in each test configs can be removed.
-    configs += [
-      "//common-mk:test",
-      ":target_defaults",
-    ]
-    pkg_deps = [ "libshill-client-test" ]
-    deps = [ ":libupdate_engine" ]
-  }
-}
-
-if (use.test) {
-  # Public keys used for unit testing.
-  genopenssl_key("update_engine-testkeys") {
-    openssl_pem_in_dir = "."
-    openssl_pem_out_dir = "include/update_engine"
-    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 = "."
-    sources = [ "sample_images/sample_images.tar.bz2" ]
-  }
-
-  # Test HTTP Server.
-  executable("test_http_server") {
-    sources = [
-      "common/http_common.cc",
-      "test_http_server.cc",
-    ]
-
-    # //common-mk:test should be on the top.
-    # TODO(crbug.com/887845): Remove this after library odering issue is fixed.
-    configs += [
-      "//common-mk:test",
-      ":target_defaults",
-    ]
-  }
-
-  # Test subprocess helper.
-  executable("test_subprocess") {
-    sources = [ "test_subprocess.cc" ]
-
-    # //common-mk:test should be on the top.
-    # TODO(crbug.com/887845): Remove this after library odering issue is fixed.
-    configs += [
-      "//common-mk:test",
-      ":target_defaults",
-    ]
-  }
-
-  # Main unittest file.
-  executable("update_engine_unittests") {
-    sources = [
-      "certificate_checker_unittest.cc",
-      "common/action_pipe_unittest.cc",
-      "common/action_processor_unittest.cc",
-      "common/action_unittest.cc",
-      "common/cpu_limiter_unittest.cc",
-      "common/hash_calculator_unittest.cc",
-      "common/http_fetcher_unittest.cc",
-      "common/hwid_override_unittest.cc",
-      "common/prefs_unittest.cc",
-      "common/proxy_resolver_unittest.cc",
-      "common/subprocess_unittest.cc",
-      "common/terminator_unittest.cc",
-      "common/utils_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",
-      "cros/download_action_chromeos_unittest.cc",
-      "libcurl_http_fetcher_unittest.cc",
-      "metrics_utils_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/extent_reader_unittest.cc",
-      "payload_consumer/extent_writer_unittest.cc",
-      "payload_consumer/file_descriptor_utils_unittest.cc",
-      "payload_consumer/file_writer_unittest.cc",
-      "payload_consumer/filesystem_verifier_action_unittest.cc",
-      "payload_consumer/install_plan_unittest.cc",
-      "payload_consumer/postinstall_runner_action_unittest.cc",
-      "payload_consumer/xz_extent_writer_unittest.cc",
-      "payload_generator/ab_generator_unittest.cc",
-      "payload_generator/blob_file_writer_unittest.cc",
-      "payload_generator/block_mapping_unittest.cc",
-      "payload_generator/deflate_utils_unittest.cc",
-      "payload_generator/delta_diff_utils_unittest.cc",
-      "payload_generator/ext2_filesystem_unittest.cc",
-      "payload_generator/extent_ranges_unittest.cc",
-      "payload_generator/extent_utils_unittest.cc",
-      "payload_generator/full_update_generator_unittest.cc",
-      "payload_generator/mapfile_filesystem_unittest.cc",
-      "payload_generator/merge_sequence_generator_unittest.cc",
-      "payload_generator/payload_file_unittest.cc",
-      "payload_generator/payload_generation_config_unittest.cc",
-      "payload_generator/payload_properties_unittest.cc",
-      "payload_generator/payload_signer_unittest.cc",
-      "payload_generator/squashfs_filesystem_unittest.cc",
-      "payload_generator/zip_unittest.cc",
-      "testrunner.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/enterprise_rollback_policy_impl_unittest.cc",
-      "update_manager/evaluation_context_unittest.cc",
-      "update_manager/generic_variables_unittest.cc",
-      "update_manager/minimum_version_policy_impl_unittest.cc",
-      "update_manager/prng_unittest.cc",
-      "update_manager/real_device_policy_provider_unittest.cc",
-      "update_manager/real_random_provider_unittest.cc",
-      "update_manager/real_shill_provider_unittest.cc",
-      "update_manager/real_system_provider_unittest.cc",
-      "update_manager/real_time_provider_unittest.cc",
-      "update_manager/real_updater_provider_unittest.cc",
-      "update_manager/staging_utils_unittest.cc",
-      "update_manager/update_manager_unittest.cc",
-      "update_manager/update_time_restrictions_monitor_unittest.cc",
-      "update_manager/update_time_restrictions_policy_impl_unittest.cc",
-      "update_manager/variable_unittest.cc",
-      "update_manager/weekly_time_unittest.cc",
-      "update_status_utils_unittest.cc",
-    ]
-    if (use.dlc) {
-      sources += [ "cros/excluder_chromeos_unittest.cc" ]
-    }
-
-    # //common-mk:test should be on the top.
-    # TODO(crbug.com/887845): Remove this after library odering issue is fixed.
-    configs += [
-      "//common-mk:test",
-      ":target_defaults",
-    ]
-    pkg_deps = [
-      "libbrillo-test",
-      "libchrome-test",
-      "libdebugd-client-test",
-      "libpower_manager-client-test",
-      "libsession_manager-client-test",
-      "libshill-client-test",
-    ]
-    deps = [
-      ":libpayload_generator",
-      ":libupdate_engine",
-      ":update_engine_test_libs",
-    ]
-  }
-}
-
-# Fuzzer target.
-if (use.fuzzer) {
-  executable("update_engine_delta_performer_fuzzer") {
-    sources = [ "payload_consumer/delta_performer_fuzzer.cc" ]
-    configs += [
-      "//common-mk/common_fuzzer",
-      ":target_defaults",
-    ]
-    pkg_deps = [
-      "libbrillo-test",
-      "libchrome-test",
-    ]
-    deps = [
-      ":libupdate_engine",
-      ":update_engine_test_libs",
-    ]
-  }
-  executable("update_engine_omaha_request_action_fuzzer") {
-    sources = [ "cros/omaha_request_action_fuzzer.cc" ]
-    configs += [
-      "//common-mk/common_fuzzer",
-      ":target_defaults",
-    ]
-    pkg_deps = [
-      "libbrillo-test",
-      "libchrome-test",
-    ]
-    deps = [
-      ":libupdate_engine",
-      ":update_engine_test_libs",
-    ]
-  }
-}
diff --git a/COMMIT-QUEUE.ini b/COMMIT-QUEUE.ini
new file mode 100644
index 0000000..ed99b9f
--- /dev/null
+++ b/COMMIT-QUEUE.ini
@@ -0,0 +1,11 @@
+# Copyright 2017 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Per-project Commit Queue settings.
+# Documentation: http://goo.gl/5J7oND
+
+[GENERAL]
+
+# Moblab testing is needed because of the udpate_payloads ebuild.
+pre-cq-configs: default guado_moblab-no-vmtest-pre-cq
diff --git a/Doxyfile b/Doxyfile
deleted file mode 100644
index db31f86..0000000
--- a/Doxyfile
+++ /dev/null
@@ -1,9 +0,0 @@
-CLANG_DATABASE_PATH=../../
-HAVE_DOT=YES
-CALL_GRAPH=YES
-CALLER_GRAPH=YES
-GENERATE_HTML=YES
-GENERATE_LATEX=NO
-INPUT=.
-RECURSIVE=YES
-
diff --git a/METADATA b/METADATA
deleted file mode 100644
index d97975c..0000000
--- a/METADATA
+++ /dev/null
@@ -1,3 +0,0 @@
-third_party {
-  license_type: NOTICE
-}
diff --git a/OWNERS b/OWNERS
index 938752f..07ee38e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,14 +1,10 @@
 set noparent
 
-# Android et. al. maintainers:
+# Current general maintainers:
 deymo@google.com
-elsk@google.com
 senj@google.com
-xunchang@google.com
 
 # Chromium OS maintainers:
+benchan@google.com
 ahassani@google.com
-kimjae@google.com
-
-# Chromium OS only:
-# COMPONENT: Internals>Installer
+xiaochu@google.com
diff --git a/PRESUBMIT.cfg b/PRESUBMIT.cfg
index 42156b3..f2c7831 100644
--- a/PRESUBMIT.cfg
+++ b/PRESUBMIT.cfg
@@ -1,6 +1,6 @@
 [Hook Scripts]
-cros lint = cros lint ${PRESUBMIT_FILES}
-gnlint = ../../../platform2/common-mk/gnlint.py ${PRESUBMIT_FILES}
+hook0=../../../../chromite/bin/cros lint ${PRESUBMIT_FILES}
+hook1=../../../platform2/common-mk/gyplint.py ${PRESUBMIT_FILES}
 
 [Hook Overrides]
 clang_format_check: true
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 85fd5ec..40ddcd1 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -2,6 +2,3 @@
 clang_format = true
 cpplint = true
 pylint = true
-
-[Hook Scripts]
-protobuflint = ./protobuflint.py ${PREUPLOAD_COMMIT} ${PREUPLOAD_FILES}
diff --git a/README.md b/README.md
deleted file mode 100644
index 71f271b..0000000
--- a/README.md
+++ /dev/null
@@ -1,642 +0,0 @@
-# Chrome OS Update Process
-
-[TOC]
-
-System updates in more modern operating systems like Chrome OS and Android are
-called A/B updates, over-the-air ([OTA]) updates, seamless updates, or simply
-auto updates. In contrast to more primitive system updates (like Windows or
-macOS) where the system is booted into a special mode to override the system
-partitions with newer updates and may take several minutes or hours, A/B updates
-have several advantages including but not limited to:
-
-*   Updates maintain a workable system that remains on the disk during and after
-    an update. Hence, reducing the likelihood of corrupting a device into a
-    non-usable state. And reducing the need for flashing devices manually or at
-    repair and warranty centers, etc.
-*   Updates can happen while the system is running (normally with minimum
-    overhead) without interrupting the user. The only downside for users is a
-    required reboot (or, in Chrome OS, a sign out which automatically causes a
-    reboot if an update was performed where the reboot duration is about 10
-    seconds and is no different than a normal reboot).
-*   The user does not need (although they can) to request for an update. The
-    update checks happen periodically in the background.
-*   If the update fails to apply, the user is not affected. The user will
-    continue on the old version of the system and the system will attempt to
-    apply the update again at a later time.
-*   If the update applies correctly but fails to boot, the system will rollback
-    to the old partition and the user can still use the system as usual.
-*   The user does not need to reserve enough space for the update. The system
-    has already reserved enough space in terms of two copies (A and B) of a
-    partition. The system doesn’t even need any cache space on the disk,
-    everything happens seamlessly from network to memory to the inactive
-    partitions.
-
-## Life of an A/B Update
-
-In A/B update capable systems, each partition, such as the kernel or root (or
-other artifacts like [DLC]), has two copies. We call these two copies active (A)
-and inactive (B). The system is booted into the active partition (depending on
-which copy has the higher priority at boot time) and when a new update is
-available, it is written into the inactive partition. After a successful reboot,
-the previously inactive partition becomes active and the old active partition
-becomes inactive.
-
-But everything starts with generating update payloads in (Google) servers for
-each new system image. Once the update payloads are generated, they are signed
-with specific keys and stored in a location known to an update server (Omaha).
-
-When the updater client initiates an update (either periodically or user
-initiated), it first consults different device policies to see if the update
-check is allowed. For example, device policies can prevent an update check
-during certain times of a day or they require the update check time to be
-scattered throughout the day randomly, etc.
-
-Once policies allow for the update check, the updater client sends a request to
-the update server (all this communication happens over HTTPS) and identifies its
-parameters like its Application ID, hardware ID, version, board, etc. Then if
-the update server decides to serve an update payload, it will respond with all
-the parameters needed to perform an update like the URLs to download the
-payloads, the metadata signatures, the payload size and hash, etc. The updater
-client continues communicating with the update server after different state
-changes, like reporting that it started to download the payload or it finished
-the update, or reports that the update failed with specific error codes, etc.
-
-Each payload consists of two main sections: metadata and extra data. The
-metadata is basically a list of operations that should be performed for an
-update. The extra data contains the data blobs needed by some or all of these
-operations. The updater client first downloads the metadata and
-cryptographically verifies it using the provided signatures from the update
-server’s response. Once the metadata is verified as valid, the rest of the
-payload can easily be verified cryptographically (mostly through SHA256 hashes).
-
-Next, the updater client marks the inactive partition as unbootable (because it
-needs to write the new updates into it). At this point the system cannot
-rollback to the inactive partition anymore.
-
-Then, the updater client performs the operations defined in the metadata (in the
-order they appear in the metadata) and the rest of the payload is gradually
-downloaded when these operations require their data. Once an operation is
-finished its data is discarded. This eliminates the need for caching the entire
-payload before applying it. During this process the updater client periodically
-checkpoints the last operation performed so in the event of failure or system
-shutdown, etc. it can continue from the point it missed without redoing all
-operations from the beginning.
-
-During the download, the updater client hashes the downloaded bytes and when the
-download finishes, it checks the payload signature (located at the end of the
-payload). If the signature cannot be verified, the update is rejected.
-
-After the inactive partition is updated, the entire partition is re-read, hashed
-and compared to a hash value passed in the metadata to make sure the update was
-successfully written into the partition.
-
-In the next step, the [Postinstall] process (if any) is called. The postinstall
-reconstructs the dm-verity tree hash of the ROOT partition and writes it at the
-end of the partition (after the last block of the file system). The postinstall
-can also perform any board specific or firmware update tasks necessary. If
-postinstall fails, the entire update is considered failed.
-
-Then the updater client goes into a state that identifies the update has
-completed and the user needs to reboot the system. At this point, until the user
-reboots (or signs out), the updater client will not do any more system updates
-even if newer updates are available. However, it does continue to perform
-periodic update checks so we can have statistics on the number of active devices
-in the field.
-
-After the update proved successful, the inactive partition is marked to have a
-higher priority (on a boot, a partition with higher priority is booted
-first). Once the user reboots the system, it will boot into the updated
-partition and it is marked as active. At this point, after the reboot, The
-updater client calls into the [`chromeos-setgoodkernel`] program. The program
-verifies the integrity of the system partitions using the dm-verity and marks
-the active partition as healthy. At this point the system is basically updated
-successfully.
-
-## Update Engine Daemon
-
-The `update_engine` is a single-threaded daemon process that runs all the
-times. This process is the heart of the auto updates. It runs with lower
-priorities in the background and is one of the last processes to start after a
-system boot. Different clients (like Chrome or other services) can send requests
-for update checks to the update engine. The details of how requests are passed
-to the update engine is system dependent, but in Chrome OS it is D-Bus.  Look at
-the [D-Bus interface] for a list of all available methods.
-
-There are many resiliency features embedded in the update engine that makes auto
-updates robust including but not limited to:
-
-*   If the update engine crashes, it will restart automatically.
-*   During an active update it periodically checkpoints the state of the update
-    and if it fails to continue the update or crashes in the middle, it will
-    continue from the last checkpoint.
-*   It retries failed network communication.
-*   If it fails to apply a delta payload (due to bit changes on the active
-    partition) for a few times, it switches to full payload.
-
-The updater clients writes its active preferences in
-`/var/lib/update_engine/prefs`. These preferences help with tracking changes
-during the lifetime of the updater client and allows properly continuing the
-update process after failed attempts or crashes.
-
-The core update engine code base in a Chromium OS checkout is located in
-`src/aosp/system/update_engine` fetching [this repository].
-
-### Policy Management
-
-In Chrome OS, devices are allowed to accept different policies from their
-managing organizations. Some of these policies affect how/when updates should be
-performed. For example, an organization may want to scatter the update checks
-during certain times of the day so as not to interfere with normal
-business. Within the update engine daemon, [UpdateManager] has the
-responsibility of loading such policies and making different decisions based on
-them. For example, some policies may allow the act of checking for updates to
-happen, while they prevent downloading the update payload. Or some policies
-don’t allow the update check within certain time frames, etc.  Anything that
-relates to the Chrome OS update policies should be contained within the
-[update_manager] directory in the source code.
-
-### Rollback vs. Enterprise Rollback
-
-Chrome OS defines a concept for Rollback: Whenever a newly updated system does
-not work as it is intended, under certain circumstances the device can be rolled
-back to a previously working version. There are two types of rollback supported
-in Chrome OS: A (legacy, original) rollback and an enterprise rollback (I know,
-naming is confusing).
-
-A normal rollback, which has existed for as long as Chrome OS had auto updater,
-is performed by switching the currently inactive partition into the active
-partition and rebooting into it. It is as simple as running a successful
-postinstall on the inactive partition, and rebooting the device. It is a feature
-used by Chrome that happens under certain circumstances. Of course rollback
-can’t happen if the inactive partition has been tampered with or has been nuked
-by the updater client to install an even newer update. Normally a rollback is
-followed by a Powerwash which clobbers the stateful partition.
-
-Enterprise rollback is a new feature added to allow enterprise users to
-downgrade the installed image to an older version. It is very similar to a
-normal system update, except that an older update payload is downloaded and
-installed. There is no direct API for entering into the enterprise rollback. It
-is managed by the enterprise device policies only.
-
-Developers should be careful when touching any rollback related feature and make
-sure they know exactly which of these two features they are trying to adapt.
-
-### Interactive vs Non-Interactive vs. Forced Updates
-
-Non-interactive updates are updates that are scheduled periodically by the
-update engine and happen in the background. Interactive updates, on the other
-hand, happen when a user specifically requests an update check (e.g. by clicking
-on “Check For Update” button in Chrome OS’s About page). Depending on the update
-server's policies, interactive updates have higher priority than non-interactive
-updates (by carrying marker hints). They may decide to not provide an update if
-they have busy server load, etc. There are other internal differences between
-these two types of updates too. For example, interactive updates try to install
-the update faster.
-
-Forced updates are similar to interactive updates (initiated by some kind of
-user action), but they can also be configured to act as non-interactive. Since
-non-interactive updates happen periodically, a forced-non-interactive update
-causes a non-interactive update at the moment of the request, not at a later
-time. We can call a forced non-interactive update with:
-
-```bash
-update_engine_client --interactive=false --check_for_update
-```
-
-### P2P Updates
-
-Many organizations might not have the external bandwidth requirements that
-system updates need for all their devices. To help with this, Chrome OS can act
-as a payload server to other client devices in the same network subnet. This is
-basically a peer-to-peer update system that allows the devices to download the
-update payloads from other devices in the network. This has to be enabled
-explicitly in the organization through device policies and specific network
-configurations to be enabled for P2P updates to work. Regardless of the location
-of update payloads, all update requests go through update servers in HTTPS.
-
-Check out the [P2P update related code] for both the server and the client side.
-
-### Network
-
-The updater client has the capability to download the payloads using Ethernet,
-WiFi, or Cellular networks depending on which one the device is connected
-to. Downloading over Cellular networks will prompt permission from the user as
-it can consume a considerable amount of data.
-
-### Logs
-
-In Chrome OS the `update_engine` logs are located in `/var/log/update_engine`
-directory. Whenever `update_engine` starts, it starts a new log file with the
-current data-time format in the log file’s name
-(`update_engine.log-DATE-TIME`). Many log files can be seen in
-`/var/log/update_engine` after a few restarts of the update engine or after the
-system reboots. The latest active log is symlinked to
-`/var/log/update_engine.log`.
-
-## Update Payload Generation
-
-The update payload generation is the process of converting a set of
-partitions/files into a format that is both understandable by the updater client
-(especially if it's a much older version) and is securely verifiable. This
-process involves breaking the input partitions into smaller components and
-compressing them in order to help with network bandwidth when downloading the
-payloads.
-
-For each generated payload, there is a corresponding properties file which
-contains the metadata information of the payload in JSON format. Normally the
-file is located in the same location as the generated payload and its file name
-is the same as the payload file name plus `.json`
-postfix. e.g. `/path/to/payload.bin` and `/path/to/payload.bin.json`. This
-properties file is necessary in order to do any kind of auto update in [`cros
-flash`], AU autotests, etc. Similarly the updater server uses this file to
-dispatch the payload properties to the updater clients.
-
-Once update payloads are generated, their original images cannot be changed
-anymore otherwise the update payloads may not be able to be applied.
-
-`delta_generator` is a tool with a wide range of options for generating
-different types of update payloads. Its code is located in
-`update_engine/payload_generator`. This directory contains all the source code
-related to mechanics of generating an update payload. None of the files in this
-directory should be included or used in any other library/executable other than
-the `delta_generator` which means this directory does not get compiled into the
-rest of the update engine tools.
-
-However, it is not recommended to use `delta_generator` directly. To manually
-generate payloads easier, [`cros_generate_update_payloads`] should be used. Most
-of the higher level policies and tools for generating payloads reside as a
-library in [`chromite/lib/paygen`]. Whenever calls to the update payload
-generation API are needed, this library should be used instead.
-
-### Update Payload File Specification
-
-Each update payload file has a specific structure defined in the table below:
-
-|Field|Size (bytes)|Type|Description|
-|-----|------------|----|-----------|
-|Magic Number|4|char[4]|Magic string "CrAU" identifying this is an update payload.|
-|Major Version|8|uint64|Payload major version number.|
-|Manifest Size|8|uint64|Manifest size in bytes.|
-|Manifest Signature Size|4|uint32|Manifest signature blob size in bytes (only in major version 2).|
-|Manifest|Varies|[DeltaArchiveManifest]|The list of operations to be performed.|
-|Manifest Signature|Varies|[Signatures]|The signature of the first five fields. There could be multiple signatures if the key has changed.|
-|Payload Data|Varies|List of raw or compressed data blobs|The list of binary blobs used by operations in the metadata.|
-|Payload Signature Size|Varies|uint64|The size of the payload signature.|
-|Payload Signature|Varies|[Signatures]|The signature of the entire payload except the metadata signature. There could be multiple signatures if the key has changed.|
-
-### Delta vs. Full Update Payloads
-
-There are two types of payload: Full and Delta. A full payload is generated
-solely from the target image (the image we want to update to) and has all the
-data necessary to update the inactive partition. Hence, full payloads can be
-quite large in size. A delta payload, on the other hand, is a differential
-update generated by comparing the source image (the active partitions) and the
-target image and producing the diffs between these two images. It is basically a
-differential update similar to applications like `diff` or `bsdiff`. Hence,
-updating the system using the delta payloads requires the system to read parts
-of the active partition in order to update the inactive partition (or
-reconstruct the target partition). The delta payloads are significantly smaller
-than the full payloads. The structure of the payload is equal for both types.
-
-Payload generation is quite resource intensive and its tools are implemented
-with high parallelism.
-
-#### Generating Full Payloads
-
-A full payload is generated by breaking the partition into 2MiB (configurable)
-chunks and either compressing them using bzip2 or XZ algorithms or keeping it as
-raw data depending on which produces smaller data. Full payloads are much larger
-in comparison to delta payloads hence require longer download time if the
-network bandwidth is limited. On the other hand, full payloads are a bit faster
-to apply because the system doesn’t need to read data from the source partition.
-
-#### Generating Delta Payloads
-
-Delta payloads are generated by looking at both the source and target images
-data on a file and metadata basis (more precisely, the file system level on each
-appropriate partition). The reason we can generate delta payloads is that Chrome
-OS partitions are read only. So with high certainty we can assume the active
-partitions on the client’s device is bit-by-bit equal to the original partitions
-generated in the image generation/signing phase. The process for generating a
-delta payload is roughly as follows:
-
-1.  Find all the zero-filled blocks on the target partition and produce `ZERO`
-    operation for them. `ZERO` operation basically discards the associated
-    blocks (depending on the implementation).
-2.  Find all the blocks that have not changed between the source and target
-    partitions by directly comparing one-to-one source and target blocks and
-    produce `SOURCE_COPY` operation.
-3.  List all the files (and their associated blocks) in the source and target
-    partitions and remove blocks (and files) which we have already generated
-    operations for in the last two steps. Assign the remaining metadata (inodes,
-    etc) of each partition as a file.
-4.  If a file is new, generate a `REPLACE`, `REPLACE_XZ`, or `REPLACE_BZ`
-    operation for its data blocks depending on which one generates a smaller
-    data blob.
-5.  For each other file, compare the source and target blocks and produce a
-    `SOURCE_BSDIFF` or `PUFFDIFF` operation depending on which one generates a
-    smaller data blob. These two operations produce binary diffs between a
-    source and target data blob. (Look at [bsdiff] and [puffin] for details of
-    such binary differential programs!)
-6.  Sort the operations based on their target partitions’ block offset.
-7.  Optionally merge same or similar operations next to each other into larger
-    operations for better efficiency and potentially smaller payloads.
-
-Full payloads can only contain `REPLACE`, `REPLACE_BZ`, and `REPLACE_XZ`
-operations. Delta payloads can contain any operations.
-
-### Major and Minor versions
-
-The major and minor versions specify the update payload file format and the
-capability of the updater client to accept certain types of update payloads
-respectively. These numbers are [hard coded] in the updater client.
-
-Major version is basically the update payload file version specified in the
-[update payload file specification] above (second field). Each updater client
-supports a range of major versions. Currently, there are only two major
-versions: 1, and 2. And both Chrome OS and Android are on major version 2 (major
-version 1 is being deprecated). Whenever there are new additions that cannot be
-fitted in the [Manifest protobuf], we need to uprev the major version. Upreving
-major version should be done with utmost care because older clients do not know
-how to handle the newer versions. Any major version uprev in Chrome OS should be
-associated with a GoldenEye stepping stone.
-
-Minor version defines the capability of the updater client to accept certain
-operations or perform certain actions. Each updater client supports a range of
-minor versions. For example, the updater client with minor version 4 (or less)
-does not know how to handle a `PUFFDIFF` operation. So when generating a delta
-payload for an image which has an updater client with minor version 4 (or less)
-we cannot produce PUFFDIFF operation for it. The payload generation process
-looks at the source image’s minor version to decide the type of operations it
-supports and only a payload that confirms to those restrictions. Similarly, if
-there is a bug in a client with a specific minor version, an uprev in the minor
-version helps with avoiding to generate payloads that cause that bug to
-manifest. However, upreving minor versions is quite expensive too in terms of
-maintainability and it can be error prone. So one should practice caution when
-making such a change.
-
-Minor versions are irrelevant in full payloads. Full payloads should always be
-able to be applied for very old clients. The reason is that the updater clients
-may not send their current version, so if we had different types of full
-payloads, we would not have known which version to serve to the client.
-
-### Signed vs Unsigned Payloads
-
-Update payloads can be signed (with private/public key pairs) for use in
-production or be kept unsigned for use in testing. Tools like `delta_generator`
-help with generating metadata and payload hashes or signing the payloads given
-private keys.
-
-## update_payload Scripts
-
-[update_payload] contains a set of python scripts used mostly to validate
-payload generation and application. We normally test the update payloads using
-an actual device (live tests). [`brillo_update_payload`] script can be used to
-generate and test applying of a payload on a host device machine. These tests
-can be viewed as dynamic tests without the need for an actual device. Other
-`update_payload` scripts (like [`check_update_payload`]) can be used to
-statically check that a payload is in the correct state and its application
-works correctly. These scripts actually apply the payload statically without
-running the code in payload_consumer.
-
-## Postinstall
-
-[Postinstall] is a process called after the updater client writes the new image
-artifacts to the inactive partitions. One of postinstall's main responsibilities
-is to recreate the dm-verity tree hash at the end of the root partition. Among
-other things, it installs new firmware updates or any board specific
-processes. Postinstall runs in separate chroot inside the newly installed
-partition. So it is quite separated from the rest of the active running
-system. Anything that needs to be done after an update and before the device is
-rebooted, should be implemented inside the postinstall.
-
-## Building Update Engine
-
-You can build `update_engine` the same as other platform applications:
-
-```bash
-(chroot) $ emerge-${BOARD} update_engine
-```
-or to build without the source copy:
-
-```bash
-(chroot) $ cros_workon_make --board=${BOARD} update_engine
-```
-
-After a change in the `update_engine` daemon, either build an image and install
-the image on the device using cros flash, etc. or use `cros deploy` to only
-install the `update_engine` service on the device:
-
-```bash
-(chroot) $ cros deploy update_engine
-```
-
-You need to restart the `update_engine` daemon in order to see the affected
-changes:
-
-```bash
-# SSH into the device.
-restart update-engine # with a dash not underscore.
-```
-
-Other payload generation tools like `delta_generator` are board agnostic and
-only available in the SDK. So in order to make any changes to the
-`delta_generator`, you should build the SDK:
-
-```bash
-# Do it only once to start building the 9999 ebuild from ToT.
-(chroot) $ cros_workon --host start update_engine
-
-(chroot) $ sudo emerge update_engine
-```
-
-If you make any changes to the D-Bus interface make sure `system_api`,
-`update_engine-client`, and `update_engine` packages are marked to build from
-9999 ebuild and then build both packages in that order:
-
-```bash
-(chroot) $ emerge-${BOARD} system_api update_engine-client update_engine
-```
-
-If you make any changes to [`update_engine` protobufs] in the `system_api`,
-build the `system_api` package first.
-
-## Running Unit Tests
-
-[Running unit tests similar to other platforms]:
-
-```bash
-(chroot) $ FEATURES=test emerge-<board> update_engine
-```
-
-or
-
-```bash
-(chroot) $ cros_workon_make --board=<board> --test update_engine
-```
-
-or
-
-```bash
-(chroot) $ cros_run_unit_tests --board ${BOARD} --packages update_engine
-```
-
-The above commands run all the unit tests, but `update_engine` package is quite
-large and it takes a long time to run all the unit tests. To run all unit tests
-in a test class run:
-
-```bash
-(chroot) $ FEATURES=test \
-    P2_TEST_FILTER="*OmahaRequestActionTest.*-*RunAsRoot*" \
-    emerge-amd64-generic update_engine
-```
-
-To run one exact unit test fixture (e.g. `MultiAppUpdateTest`), run:
-
-```bash
-(chroot) $ FEATURES=test \
-    P2_TEST_FILTER="*OmahaRequestActionTest.MultiAppUpdateTest-*RunAsRoot*" \
-    emerge-amd64-generic update_engine
-```
-
-To run `update_payload` unit tests enter `update_engine/scripts` directory and
-run the desired `unittest.p`y files.
-
-## Initiating a Configured Update
-
-There are different methods to initiate an update:
-
-*   Click on the “Check For Update” button in setting’s About page. There is no
-    way to configure this way of update check.
-*   Use the [`update_engine_client`] program. There are a few configurations you
-    can do.
-*   Call `autest` in the crosh. Mainly used by the QA team and is not intended
-    to be used by any other team.
-*   Use [`cros flash`]. It internally uses the update_engine to flash a device
-    with a given image.
-*   Run one of many auto update autotests.
-*   Start a [Dev Server] on your host machine and send a specific HTTP request
-    (look at `cros_au` API in the Dev Server code), that has the information
-    like the IP address of your Chromebook and where the update payloads are
-    located to the Dev Server to start an update on your device (**Warning:**
-    complicated to do, not recommended).
-
-`update_engine_client` is a client application that can help initiate an update
-or get more information about the status of the updater client. It has several
-options like initiating an interactive vs. non-interactive update, changing
-channels, getting the current status of update process, doing a rollback,
-changing the Omaha URL to download the payload (the most important one), etc.
-
-`update_engine` daemon reads the `/etc/lsb-release` file on the device to
-identify different update parameters like the updater server (Omaha) URL, the
-current channel, etc. However, to override any of these parameters, create the
-file `/mnt/stateful_partition/etc/lsb-release` with desired customized
-parameters. For example, this can be used to point to a developer version of the
-update server and allow the update_engine to schedule a periodic update from
-that specific server.
-
-If you have some changes in the protocol that communicates with Omaha, but you
-don’t have those changes in the update server, or you have some specific
-payloads that do not exist on the production update server you can use
-[Nebraska] to help with doing an update.
-
-## Note to Developers and Maintainers
-
-When changing the update engine source code be extra careful about these things:
-
-### Do NOT Break Backward Compatibility
-
-At each release cycle we should be able to generate full and delta payloads that
-can correctly be applied to older devices that run older versions of the update
-engine client. So for example, removing or not passing arguments in the metadata
-proto file might break older clients. Or passing operations that are not
-understood in older clients will break them. Whenever changing anything in the
-payload generation process, ask yourself this question: Would it work on older
-clients? If not, do I need to control it with minor versions or any other means.
-
-Especially regarding enterprise rollback, a newer updater client should be able
-to accept an older update payload. Normally this happens using a full payload,
-but care should be taken in order to not break this compatibility.
-
-### Think About The Future
-
-When creating a change in the update engine, think about 5 years from now:
-
-*   How can the change be implemented that five years from now older clients
-    don’t break?
-*   How is it going to be maintained five years from now?
-*   How can it make it easier for future changes without breaking older clients
-    or incurring heavy maintenance costs?
-
-### Prefer Not To Implement Your Feature In The Updater Client
-If a feature can be implemented from server side, Do NOT implement it in the
-client updater. Because the client updater can be fragile at points and small
-mistakes can have catastrophic consequences. For example, if a bug is introduced
-in the updater client that causes it to crash right before checking for update
-and we can't quite catch this bug early in the release process, then the
-production devices which have already moved to the new buggy system, may no
-longer receive automatic updates anymore. So, always think if the feature is
-being implemented can be done form the server side (with potentially minimal
-changes to the client updater)? Or can the feature be moved to another service
-with minimal interface to the updater client. Answering these questions will pay
-off greatly in the future.
-
-### Be Respectful Of Other Code Bases
-
-The current update engine code base is used in many projects like Android. We
-sync the code base among these two projects frequently. Try to not break Android
-or other systems that share the update engine code. Whenever landing a change,
-always think about whether Android needs that change:
-
-*   How will it affect Android?
-*   Can the change be moved to an interface and stubs implementations be
-    implemented so as not to affect Android?
-*   Can Chrome OS or Android specific code be guarded by macros?
-
-As a basic measure, if adding/removing/renaming code, make sure to change both
-`build.gn` and `Android.bp`. Do not bring Chrome OS specific code (for example
-other libraries that live in `system_api` or `dlcservice`) into the common code
-of update_engine. Try to separate these concerns using best software engineering
-practices.
-
-### Merging from Android (or other code bases)
-
-Chrome OS tracks the Android code as an [upstream branch]. To merge the Android
-code to Chrome OS (or vice versa) just do a `git merge` of that branch into
-Chrome OS, test it using whatever means and upload a merge commit.
-
-```bash
-repo start merge-aosp
-git merge --no-ff --strategy=recursive -X patience cros/upstream
-repo upload --cbr --no-verify .
-```
-
-[Postinstall]: #postinstall
-[update payload file specification]: #update-payload-file-specification
-[OTA]: https://source.android.com/devices/tech/ota
-[DLC]: https://chromium.googlesource.com/chromiumos/platform2/+/master/dlcservice
-[`chromeos-setgoodkernel`]: https://chromium.googlesource.com/chromiumos/platform2/+/master/installer/chromeos-setgoodkernel
-[D-Bus interface]: /dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
-[this repository]: /
-[UpdateManager]: /update_manager/update_manager.cc
-[update_manager]: /update_manager/
-[P2P update related code]: https://chromium.googlesource.com/chromiumos/platform2/+/master/p2p/
-[`cros_generate_update_payloads`]: https://chromium.googlesource.com/chromiumos/chromite/+/master/scripts/cros_generate_update_payload.py
-[`chromite/lib/paygen`]: https://chromium.googlesource.com/chromiumos/chromite/+/master/lib/paygen/
-[DeltaArchiveManifest]: /update_metadata.proto#302
-[Signatures]: /update_metadata.proto#122
-[hard coded]: /update_engine.conf
-[Manifest protobuf]: /update_metadata.proto
-[update_payload]: /scripts/
-[Postinstall]: https://chromium.googlesource.com/chromiumos/platform2/+/master/installer/chromeos-postinst
-[`update_engine` protobufs]: https://chromium.googlesource.com/chromiumos/platform2/+/master/system_api/dbus/update_engine/
-[Running unit tests similar to other platforms]: https://chromium.googlesource.com/chromiumos/docs/+/master/testing/running_unit_tests.md
-[Nebraska]: https://chromium.googlesource.com/chromiumos/platform/dev-util/+/master/nebraska/
-[upstream branch]: https://chromium.googlesource.com/aosp/platform/system/update_engine/+/upstream
-[`cros flash`]: https://chromium.googlesource.com/chromiumos/docs/+/master/cros_flash.md
-[bsdiff]: https://android.googlesource.com/platform/external/bsdiff/+/master
-[puffin]: https://android.googlesource.com/platform/external/puffin/+/master
-[`update_engine_client`]: /update_engine_client.cc
-[`brillo_update_payload`]: /scripts/brillo_update_payload
-[`check_update_payload`]: /scripts/paycheck.py
-[Dev Server]: https://chromium.googlesource.com/chromiumos/chromite/+/master/docs/devserver.md
diff --git a/UpdateEngine.conf b/UpdateEngine.conf
index f9a66dc..9490096 100644
--- a/UpdateEngine.conf
+++ b/UpdateEngine.conf
@@ -1,20 +1,5 @@
 <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
   "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
-<!--
-  Copyright (C) 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-!-->
 <busconfig>
   <policy user="root">
     <allow own="org.chromium.UpdateEngine" />
@@ -41,7 +26,7 @@
            send_member="ResetStatus"/>
     <allow send_destination="org.chromium.UpdateEngine"
            send_interface="org.chromium.UpdateEngineInterface"
-           send_member="GetStatusAdvanced"/>
+           send_member="GetStatus"/>
     <allow send_destination="org.chromium.UpdateEngine"
            send_interface="org.chromium.UpdateEngineInterface"
            send_member="RebootIfNeeded"/>
@@ -81,12 +66,15 @@
     <allow send_destination="org.chromium.UpdateEngine"
            send_interface="org.chromium.UpdateEngineInterface"
            send_member="GetLastAttemptError"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="GetEolStatus"/>
     <allow send_interface="org.chromium.UpdateEngineLibcrosProxyResolvedInterface" />
   </policy>
   <policy user="power">
     <allow send_destination="org.chromium.UpdateEngine"
            send_interface="org.chromium.UpdateEngineInterface"
-           send_member="GetStatusAdvanced"/>
+           send_member="GetStatus"/>
   </policy>
   <policy user="dlcservice">
     <allow send_destination="org.chromium.UpdateEngine"
@@ -94,12 +82,6 @@
            send_member="GetStatus"/>
     <allow send_destination="org.chromium.UpdateEngine"
            send_interface="org.chromium.UpdateEngineInterface"
-           send_member="GetStatusAdvanced"/>
-    <allow send_destination="org.chromium.UpdateEngine"
-           send_interface="org.chromium.UpdateEngineInterface"
            send_member="AttemptInstall"/>
-    <allow send_destination="org.chromium.UpdateEngine"
-           send_interface="org.chromium.UpdateEngineInterface"
-           send_member="SetDlcActiveValue"/>
   </policy>
 </busconfig>
diff --git a/aosp/apex_handler_android.cc b/aosp/apex_handler_android.cc
deleted file mode 100644
index 8beef96..0000000
--- a/aosp/apex_handler_android.cc
+++ /dev/null
@@ -1,110 +0,0 @@
-//
-// Copyright (C) 2021 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 <memory>
-#include <utility>
-
-#include <base/files/file_util.h>
-
-#include <ApexProperties.sysprop.h>
-
-#include "update_engine/aosp/apex_handler_android.h"
-#include "update_engine/common/utils.h"
-
-namespace chromeos_update_engine {
-
-namespace {
-
-android::apex::CompressedApexInfoList CreateCompressedApexInfoList(
-    const std::vector<ApexInfo>& apex_infos) {
-  android::apex::CompressedApexInfoList compressed_apex_info_list;
-  for (const auto& apex_info : apex_infos) {
-    if (!apex_info.is_compressed()) {
-      continue;
-    }
-    android::apex::CompressedApexInfo compressed_apex_info;
-    compressed_apex_info.moduleName = apex_info.package_name();
-    compressed_apex_info.versionCode = apex_info.version();
-    compressed_apex_info.decompressedSize = apex_info.decompressed_size();
-    compressed_apex_info_list.apexInfos.emplace_back(
-        std::move(compressed_apex_info));
-  }
-  return compressed_apex_info_list;
-}
-
-}  // namespace
-
-std::unique_ptr<ApexHandlerInterface> CreateApexHandler() {
-  if (android::sysprop::ApexProperties::updatable().value_or(false)) {
-    return std::make_unique<ApexHandlerAndroid>();
-  } else {
-    return std::make_unique<FlattenedApexHandlerAndroid>();
-  }
-}
-
-android::base::Result<uint64_t> ApexHandlerAndroid::CalculateSize(
-    const std::vector<ApexInfo>& apex_infos) const {
-  // We might not need to decompress every APEX. Communicate with apexd to get
-  // accurate requirement.
-  auto apex_service = GetApexService();
-  if (apex_service == nullptr) {
-    return android::base::Error() << "Failed to get hold of apexservice";
-  }
-
-  auto compressed_apex_info_list = CreateCompressedApexInfoList(apex_infos);
-  int64_t size_from_apexd;
-  auto result = apex_service->calculateSizeForCompressedApex(
-      compressed_apex_info_list, &size_from_apexd);
-  if (!result.isOk()) {
-    return android::base::Error()
-           << "Failed to get size required from apexservice";
-  }
-  return size_from_apexd;
-}
-
-bool ApexHandlerAndroid::AllocateSpace(
-    const std::vector<ApexInfo>& apex_infos) const {
-  auto apex_service = GetApexService();
-  if (apex_service == nullptr) {
-    return false;
-  }
-  auto compressed_apex_info_list = CreateCompressedApexInfoList(apex_infos);
-  auto result =
-      apex_service->reserveSpaceForCompressedApex(compressed_apex_info_list);
-  return result.isOk();
-}
-
-android::sp<android::apex::IApexService> ApexHandlerAndroid::GetApexService()
-    const {
-  auto binder = android::defaultServiceManager()->waitForService(
-      android::String16("apexservice"));
-  if (binder == nullptr) {
-    return nullptr;
-  }
-  return android::interface_cast<android::apex::IApexService>(binder);
-}
-
-android::base::Result<uint64_t> FlattenedApexHandlerAndroid::CalculateSize(
-    const std::vector<ApexInfo>& apex_infos) const {
-  return 0;
-}
-
-bool FlattenedApexHandlerAndroid::AllocateSpace(
-    const std::vector<ApexInfo>& apex_infos) const {
-  return true;
-}
-
-}  // namespace chromeos_update_engine
diff --git a/aosp/apex_handler_android.h b/aosp/apex_handler_android.h
deleted file mode 100644
index 767f561..0000000
--- a/aosp/apex_handler_android.h
+++ /dev/null
@@ -1,53 +0,0 @@
-//
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef SYSTEM_UPDATE_ENGINE_AOSP_APEX_HANDLER_ANDROID_H_
-#define SYSTEM_UPDATE_ENGINE_AOSP_APEX_HANDLER_ANDROID_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android/apex/IApexService.h>
-#include <binder/IServiceManager.h>
-
-#include "update_engine/aosp/apex_handler_interface.h"
-#include "update_engine/update_metadata.pb.h"
-
-namespace chromeos_update_engine {
-
-std::unique_ptr<ApexHandlerInterface> CreateApexHandler();
-
-class ApexHandlerAndroid : virtual public ApexHandlerInterface {
- public:
-  android::base::Result<uint64_t> CalculateSize(
-      const std::vector<ApexInfo>& apex_infos) const;
-  bool AllocateSpace(const std::vector<ApexInfo>& apex_infos) const;
-
- private:
-  android::sp<android::apex::IApexService> GetApexService() const;
-};
-
-class FlattenedApexHandlerAndroid : virtual public ApexHandlerInterface {
- public:
-  android::base::Result<uint64_t> CalculateSize(
-      const std::vector<ApexInfo>& apex_infos) const;
-  bool AllocateSpace(const std::vector<ApexInfo>& apex_infos) const;
-};
-
-}  // namespace chromeos_update_engine
-
-#endif  // SYSTEM_UPDATE_ENGINE_AOSP_APEX_HANDLER_ANDROID_H_
diff --git a/aosp/apex_handler_android_unittest.cc b/aosp/apex_handler_android_unittest.cc
deleted file mode 100644
index 847ccaa..0000000
--- a/aosp/apex_handler_android_unittest.cc
+++ /dev/null
@@ -1,102 +0,0 @@
-//
-// Copyright (C) 2021 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 <utility>
-#include <filesystem>
-
-#include "update_engine/aosp/apex_handler_android.h"
-
-#include <android-base/file.h>
-#include <android-base/strings.h>
-#include <gtest/gtest.h>
-
-using android::base::EndsWith;
-
-namespace chromeos_update_engine {
-
-namespace fs = std::filesystem;
-
-ApexInfo CreateApexInfo(const std::string& package_name,
-                        int version,
-                        bool is_compressed,
-                        int decompressed_size) {
-  ApexInfo result;
-  result.set_package_name(package_name);
-  result.set_version(version);
-  result.set_is_compressed(is_compressed);
-  result.set_decompressed_size(decompressed_size);
-  return std::move(result);
-}
-
-TEST(ApexHandlerAndroidTest, CalculateSizeUpdatableApex) {
-  ApexHandlerAndroid apex_handler;
-  std::vector<ApexInfo> apex_infos;
-  ApexInfo compressed_apex_1 = CreateApexInfo("sample1", 1, true, 1);
-  ApexInfo compressed_apex_2 = CreateApexInfo("sample2", 2, true, 2);
-  ApexInfo uncompressed_apex = CreateApexInfo("uncompressed", 1, false, 4);
-  apex_infos.push_back(compressed_apex_1);
-  apex_infos.push_back(compressed_apex_2);
-  apex_infos.push_back(uncompressed_apex);
-  auto result = apex_handler.CalculateSize(apex_infos);
-  ASSERT_TRUE(result.ok());
-  ASSERT_EQ(*result, 3u);
-}
-
-TEST(ApexHandlerAndroidTest, AllocateSpaceUpdatableApex) {
-  ApexHandlerAndroid apex_handler;
-  std::vector<ApexInfo> apex_infos;
-  ApexInfo compressed_apex_1 = CreateApexInfo("sample1", 1, true, 1);
-  ApexInfo compressed_apex_2 = CreateApexInfo("sample2", 2, true, 2);
-  ApexInfo uncompressed_apex = CreateApexInfo("uncompressed", 1, false, 4);
-  apex_infos.push_back(compressed_apex_1);
-  apex_infos.push_back(compressed_apex_2);
-  apex_infos.push_back(uncompressed_apex);
-  ASSERT_TRUE(apex_handler.AllocateSpace(apex_infos));
-
-  // Should be able to pass empty list
-  ASSERT_TRUE(apex_handler.AllocateSpace({}));
-}
-
-TEST(ApexHandlerAndroidTest, CalculateSizeFlattenedApex) {
-  FlattenedApexHandlerAndroid apex_handler;
-  std::vector<ApexInfo> apex_infos;
-  ApexInfo compressed_apex_1 = CreateApexInfo("sample1", 1, true, 1);
-  ApexInfo compressed_apex_2 = CreateApexInfo("sample2", 2, true, 2);
-  ApexInfo uncompressed_apex = CreateApexInfo("uncompressed", 1, false, 4);
-  apex_infos.push_back(compressed_apex_1);
-  apex_infos.push_back(compressed_apex_2);
-  apex_infos.push_back(uncompressed_apex);
-  auto result = apex_handler.CalculateSize(apex_infos);
-  ASSERT_TRUE(result.ok());
-  ASSERT_EQ(*result, 0u);
-}
-
-TEST(ApexHandlerAndroidTest, AllocateSpaceFlattenedApex) {
-  FlattenedApexHandlerAndroid apex_handler;
-  std::vector<ApexInfo> apex_infos;
-  ApexInfo compressed_apex_1 = CreateApexInfo("sample1", 1, true, 1);
-  ApexInfo compressed_apex_2 = CreateApexInfo("sample2", 2, true, 2);
-  ApexInfo uncompressed_apex = CreateApexInfo("uncompressed", 1, false, 4);
-  apex_infos.push_back(compressed_apex_1);
-  apex_infos.push_back(compressed_apex_2);
-  apex_infos.push_back(uncompressed_apex);
-  ASSERT_TRUE(apex_handler.AllocateSpace(apex_infos));
-
-  // Should be able to pass empty list
-  ASSERT_TRUE(apex_handler.AllocateSpace({}));
-}
-
-}  // namespace chromeos_update_engine
diff --git a/aosp/apex_handler_interface.h b/aosp/apex_handler_interface.h
deleted file mode 100644
index b9b6c96..0000000
--- a/aosp/apex_handler_interface.h
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef SYSTEM_UPDATE_ENGINE_AOSP_APEX_HANDLER_INTERFACE_H_
-#define SYSTEM_UPDATE_ENGINE_AOSP_APEX_HANDLER_INTERFACE_H_
-
-#include <vector>
-
-#include <android-base/result.h>
-
-#include "update_engine/update_metadata.pb.h"
-
-namespace chromeos_update_engine {
-
-class ApexHandlerInterface {
- public:
-  virtual ~ApexHandlerInterface() = default;
-  virtual android::base::Result<uint64_t> CalculateSize(
-      const std::vector<ApexInfo>& apex_infos) const = 0;
-  virtual bool AllocateSpace(const std::vector<ApexInfo>& apex_infos) const = 0;
-};
-
-}  // namespace chromeos_update_engine
-
-#endif  // SYSTEM_UPDATE_ENGINE_AOSP_APEX_HANDLER_INTERFACE_H_
diff --git a/aosp/binder_service_android_common.h b/aosp/binder_service_android_common.h
deleted file mode 100644
index 223b32e..0000000
--- a/aosp/binder_service_android_common.h
+++ /dev/null
@@ -1,45 +0,0 @@
-//
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_COMMON_H_
-#define UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_COMMON_H_
-
-#include <string>
-#include <vector>
-
-#include <binder/Status.h>
-
-namespace chromeos_update_engine {
-
-static inline android::binder::Status ErrorPtrToStatus(
-    const brillo::ErrorPtr& error) {
-  return android::binder::Status::fromServiceSpecificError(
-      1, android::String8{error->GetMessage().c_str()});
-}
-
-static inline std::vector<std::string> ToVecString(
-    const std::vector<android::String16>& inp) {
-  std::vector<std::string> out;
-  out.reserve(inp.size());
-  for (const auto& e : inp) {
-    out.emplace_back(android::String8{e}.string());
-  }
-  return out;
-}
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_COMMON_H_
diff --git a/aosp/binder_service_stable_android.cc b/aosp/binder_service_stable_android.cc
deleted file mode 100644
index 17b35ee..0000000
--- a/aosp/binder_service_stable_android.cc
+++ /dev/null
@@ -1,132 +0,0 @@
-//
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include "update_engine/aosp/binder_service_stable_android.h"
-
-#include <memory>
-
-#include <base/bind.h>
-#include <base/logging.h>
-#include <binderwrapper/binder_wrapper.h>
-#include <brillo/errors/error.h>
-#include <utils/String8.h>
-
-#include "update_engine/aosp/binder_service_android_common.h"
-
-using android::binder::Status;
-using android::os::IUpdateEngineStableCallback;
-using android::os::ParcelFileDescriptor;
-using std::string;
-using std::vector;
-using update_engine::UpdateEngineStatus;
-
-namespace chromeos_update_engine {
-
-BinderUpdateEngineAndroidStableService::BinderUpdateEngineAndroidStableService(
-    ServiceDelegateAndroidInterface* service_delegate)
-    : service_delegate_(service_delegate) {}
-
-void BinderUpdateEngineAndroidStableService::SendStatusUpdate(
-    const UpdateEngineStatus& update_engine_status) {
-  last_status_ = static_cast<int>(update_engine_status.status);
-  last_progress_ = update_engine_status.progress;
-  if (callback_) {
-    callback_->onStatusUpdate(last_status_, last_progress_);
-  }
-}
-
-void BinderUpdateEngineAndroidStableService::SendPayloadApplicationComplete(
-    ErrorCode error_code) {
-  if (callback_) {
-    callback_->onPayloadApplicationComplete(static_cast<int>(error_code));
-  }
-}
-
-Status BinderUpdateEngineAndroidStableService::bind(
-    const android::sp<IUpdateEngineStableCallback>& callback,
-    bool* return_value) {
-  // Reject binding if another callback is already bound.
-  if (callback_ != nullptr) {
-    LOG(ERROR) << "Another callback is already bound. Can't bind new callback.";
-    *return_value = false;
-    return Status::ok();
-  }
-
-  // See BinderUpdateEngineAndroidService::bind.
-  if (last_status_ != -1) {
-    auto status = callback->onStatusUpdate(last_status_, last_progress_);
-    if (!status.isOk()) {
-      LOG(ERROR) << "Failed to call onStatusUpdate() from callback: "
-                 << status.toString8();
-      *return_value = false;
-      return Status::ok();
-    }
-  }
-
-  callback_ = callback;
-
-  const android::sp<IBinder>& callback_binder =
-      IUpdateEngineStableCallback::asBinder(callback);
-  auto binder_wrapper = android::BinderWrapper::Get();
-  binder_wrapper->RegisterForDeathNotifications(
-      callback_binder,
-      base::Bind(base::IgnoreResult(
-                     &BinderUpdateEngineAndroidStableService::UnbindCallback),
-                 base::Unretained(this),
-                 base::Unretained(callback_binder.get())));
-
-  *return_value = true;
-  return Status::ok();
-}
-
-Status BinderUpdateEngineAndroidStableService::unbind(
-    const android::sp<IUpdateEngineStableCallback>& callback,
-    bool* return_value) {
-  const android::sp<IBinder>& callback_binder =
-      IUpdateEngineStableCallback::asBinder(callback);
-  auto binder_wrapper = android::BinderWrapper::Get();
-  binder_wrapper->UnregisterForDeathNotifications(callback_binder);
-
-  *return_value = UnbindCallback(callback_binder.get());
-  return Status::ok();
-}
-
-Status BinderUpdateEngineAndroidStableService::applyPayloadFd(
-    const ParcelFileDescriptor& pfd,
-    int64_t payload_offset,
-    int64_t payload_size,
-    const vector<android::String16>& header_kv_pairs) {
-  vector<string> str_headers = ToVecString(header_kv_pairs);
-
-  brillo::ErrorPtr error;
-  if (!service_delegate_->ApplyPayload(
-          pfd.get(), payload_offset, payload_size, str_headers, &error)) {
-    return ErrorPtrToStatus(error);
-  }
-  return Status::ok();
-}
-
-bool BinderUpdateEngineAndroidStableService::UnbindCallback(
-    const IBinder* callback) {
-  if (IUpdateEngineStableCallback::asBinder(callback_).get() != callback) {
-    LOG(ERROR) << "Unable to unbind unknown callback.";
-    return false;
-  }
-  callback_ = nullptr;
-  return true;
-}
-
-}  // namespace chromeos_update_engine
diff --git a/aosp/binder_service_stable_android.h b/aosp/binder_service_stable_android.h
deleted file mode 100644
index 212afaa..0000000
--- a/aosp/binder_service_stable_android.h
+++ /dev/null
@@ -1,85 +0,0 @@
-//
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef UPDATE_ENGINE_AOSP_BINDER_SERVICE_STABLE_ANDROID_H_
-#define UPDATE_ENGINE_AOSP_BINDER_SERVICE_STABLE_ANDROID_H_
-
-#include <stdint.h>
-
-#include <string>
-#include <vector>
-
-#include <utils/Errors.h>
-#include <utils/String16.h>
-#include <utils/StrongPointer.h>
-
-#include "android/os/BnUpdateEngineStable.h"
-#include "android/os/IUpdateEngineStableCallback.h"
-#include "update_engine/aosp/service_delegate_android_interface.h"
-#include "update_engine/common/service_observer_interface.h"
-
-namespace chromeos_update_engine {
-
-class BinderUpdateEngineAndroidStableService
-    : public android::os::BnUpdateEngineStable,
-      public ServiceObserverInterface {
- public:
-  explicit BinderUpdateEngineAndroidStableService(
-      ServiceDelegateAndroidInterface* service_delegate);
-  ~BinderUpdateEngineAndroidStableService() override = default;
-
-  const char* ServiceName() const {
-    return "android.os.UpdateEngineStableService";
-  }
-
-  // ServiceObserverInterface overrides.
-  void SendStatusUpdate(
-      const update_engine::UpdateEngineStatus& update_engine_status) override;
-  void SendPayloadApplicationComplete(ErrorCode error_code) override;
-
-  // android::os::BnUpdateEngineStable overrides.
-  android::binder::Status applyPayloadFd(
-      const ::android::os::ParcelFileDescriptor& pfd,
-      int64_t payload_offset,
-      int64_t payload_size,
-      const std::vector<android::String16>& header_kv_pairs) override;
-  android::binder::Status bind(
-      const android::sp<android::os::IUpdateEngineStableCallback>& callback,
-      bool* return_value) override;
-  android::binder::Status unbind(
-      const android::sp<android::os::IUpdateEngineStableCallback>& callback,
-      bool* return_value) override;
-
- private:
-  // Remove the passed |callback| from the list of registered callbacks. Called
-  // on unbind() or whenever the callback object is destroyed.
-  // Returns true on success.
-  bool UnbindCallback(const IBinder* callback);
-
-  // Bound callback. The stable interface only supports one callback at a time.
-  android::sp<android::os::IUpdateEngineStableCallback> callback_;
-
-  // Cached copy of the last status update sent. Used to send an initial
-  // notification when bind() is called from the client.
-  int last_status_{-1};
-  double last_progress_{0.0};
-
-  ServiceDelegateAndroidInterface* service_delegate_;
-};
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_AOSP_BINDER_SERVICE_STABLE_ANDROID_H_
diff --git a/aosp/cleanup_previous_update_action_unittest.cc b/aosp/cleanup_previous_update_action_unittest.cc
deleted file mode 100644
index 0d2b4e6..0000000
--- a/aosp/cleanup_previous_update_action_unittest.cc
+++ /dev/null
@@ -1,174 +0,0 @@
-//
-// Copyright (C) 2021 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 <brillo/message_loops/fake_message_loop.h>
-#include <gtest/gtest.h>
-#include <libsnapshot/snapshot.h>
-#include <libsnapshot/mock_snapshot.h>
-#include <libsnapshot/mock_snapshot_merge_stats.h>
-
-#include "update_engine/aosp/cleanup_previous_update_action.h"
-#include "update_engine/common/mock_boot_control.h"
-#include "update_engine/common/mock_dynamic_partition_control.h"
-#include "update_engine/common/mock_prefs.h"
-
-namespace chromeos_update_engine {
-
-using android::snapshot::AutoDevice;
-using android::snapshot::MockSnapshotManager;
-using android::snapshot::MockSnapshotMergeStats;
-using android::snapshot::UpdateState;
-using testing::_;
-using testing::AtLeast;
-using testing::Return;
-
-class MockCleanupPreviousUpdateActionDelegate final
-    : public CleanupPreviousUpdateActionDelegateInterface {
-  MOCK_METHOD(void, OnCleanupProgressUpdate, (double), (override));
-};
-
-class MockActionProcessor : public ActionProcessor {
- public:
-  MOCK_METHOD(void, ActionComplete, (AbstractAction*, ErrorCode), (override));
-};
-
-class MockAutoDevice : public AutoDevice {
- public:
-  explicit MockAutoDevice(std::string name) : AutoDevice(name) {}
-  ~MockAutoDevice() = default;
-};
-
-class CleanupPreviousUpdateActionTest : public ::testing::Test {
- public:
-  void SetUp() override {
-    ON_CALL(boot_control_, GetDynamicPartitionControl())
-        .WillByDefault(Return(&dynamic_control_));
-    ON_CALL(boot_control_, GetCurrentSlot()).WillByDefault(Return(0));
-    ON_CALL(mock_snapshot_, GetSnapshotMergeStatsInstance())
-        .WillByDefault(Return(&mock_stats_));
-    action_.SetProcessor(&mock_processor_);
-    loop_.SetAsCurrent();
-  }
-
-  constexpr static FeatureFlag LAUNCH{FeatureFlag::Value::LAUNCH};
-  constexpr static FeatureFlag NONE{FeatureFlag::Value::NONE};
-  MockSnapshotManager mock_snapshot_;
-  MockPrefs mock_prefs_;
-  MockBootControl boot_control_;
-  MockDynamicPartitionControl dynamic_control_{};
-  MockCleanupPreviousUpdateActionDelegate mock_delegate_;
-  MockSnapshotMergeStats mock_stats_;
-  MockActionProcessor mock_processor_;
-  brillo::FakeMessageLoop loop_{nullptr};
-  CleanupPreviousUpdateAction action_{
-      &mock_prefs_, &boot_control_, &mock_snapshot_, &mock_delegate_};
-};
-
-TEST_F(CleanupPreviousUpdateActionTest, NonVabTest) {
-  // Since VAB isn't even enabled, |GetSnapshotMergeStatsInstance| shouldn't be
-  // called at all
-  EXPECT_CALL(mock_snapshot_, GetSnapshotMergeStatsInstance()).Times(0);
-  EXPECT_CALL(dynamic_control_, GetVirtualAbFeatureFlag())
-      .Times(AtLeast(1))
-      .WillRepeatedly(Return(NONE));
-  action_.PerformAction();
-}
-
-TEST_F(CleanupPreviousUpdateActionTest, VABSlotSuccessful) {
-  // Expectaion: if VABC is enabled, Clenup action should call
-  // |SnapshotMergeStats::Start()| to start merge, and wait for it to finish
-  EXPECT_CALL(mock_snapshot_, GetSnapshotMergeStatsInstance())
-      .Times(AtLeast(1));
-  EXPECT_CALL(mock_snapshot_, EnsureMetadataMounted())
-      .Times(AtLeast(1))
-      .WillRepeatedly(
-          []() { return std::make_unique<MockAutoDevice>("mock_device"); });
-  EXPECT_CALL(dynamic_control_, GetVirtualAbFeatureFlag())
-      .Times(AtLeast(1))
-      .WillRepeatedly(Return(LAUNCH));
-  // CleanupPreviousUpdateAction should use whatever slot returned by
-  // |GetCurrentSlot()|
-  EXPECT_CALL(boot_control_, GetCurrentSlot())
-      .Times(AtLeast(1))
-      .WillRepeatedly(Return(1));
-  EXPECT_CALL(boot_control_, IsSlotMarkedSuccessful(1))
-      .Times(AtLeast(1))
-      .WillRepeatedly(Return(true));
-  EXPECT_CALL(mock_snapshot_, ProcessUpdateState(_, _))
-      .Times(AtLeast(2))
-      .WillOnce(Return(UpdateState::Merging))
-      .WillRepeatedly(Return(UpdateState::MergeCompleted));
-  EXPECT_CALL(mock_stats_, Start())
-      .Times(AtLeast(1))
-      .WillRepeatedly(Return(true));
-  EXPECT_CALL(mock_processor_, ActionComplete(&action_, ErrorCode::kSuccess))
-      .Times(1);
-  action_.PerformAction();
-  while (loop_.PendingTasks()) {
-    ASSERT_TRUE(loop_.RunOnce(true));
-  }
-}
-
-TEST_F(CleanupPreviousUpdateActionTest, VabSlotNotReady) {
-  // Cleanup action should repeatly query boot control until the slot is marked
-  // successful.
-  static constexpr auto MAX_TIMEPOINT =
-      std::chrono::steady_clock::time_point::max();
-  EXPECT_CALL(mock_snapshot_, GetSnapshotMergeStatsInstance())
-      .Times(AtLeast(1));
-  EXPECT_CALL(mock_snapshot_, EnsureMetadataMounted())
-      .Times(AtLeast(1))
-      .WillRepeatedly(
-          []() { return std::make_unique<MockAutoDevice>("mock_device"); });
-  EXPECT_CALL(dynamic_control_, GetVirtualAbFeatureFlag())
-      .Times(AtLeast(1))
-      .WillRepeatedly(Return(LAUNCH));
-  auto slot_success_time = MAX_TIMEPOINT;
-  auto merge_start_time = MAX_TIMEPOINT;
-  EXPECT_CALL(boot_control_, IsSlotMarkedSuccessful(_))
-      .Times(AtLeast(3))
-      .WillOnce(Return(false))
-      .WillOnce(Return(false))
-      .WillOnce([&slot_success_time]() {
-        slot_success_time =
-            std::min(slot_success_time, std::chrono::steady_clock::now());
-        return true;
-      });
-
-  EXPECT_CALL(mock_stats_, Start())
-      .Times(1)
-      .WillRepeatedly([&merge_start_time]() {
-        merge_start_time =
-            std::min(merge_start_time, std::chrono::steady_clock::now());
-        return true;
-      });
-
-  EXPECT_CALL(mock_snapshot_, ProcessUpdateState(_, _))
-      .Times(AtLeast(1))
-      .WillRepeatedly(Return(UpdateState::MergeCompleted));
-  EXPECT_CALL(mock_processor_, ActionComplete(&action_, ErrorCode::kSuccess))
-      .Times(1);
-  action_.PerformAction();
-  while (loop_.PendingTasks()) {
-    ASSERT_TRUE(loop_.RunOnce(true));
-  }
-  ASSERT_LT(slot_success_time, merge_start_time)
-      << "Merge should not be started until slot is marked successful";
-}
-
-}  // namespace chromeos_update_engine
diff --git a/aosp/cow_converter.cc b/aosp/cow_converter.cc
deleted file mode 100644
index 8c641b8..0000000
--- a/aosp/cow_converter.cc
+++ /dev/null
@@ -1,131 +0,0 @@
-//
-// Copyright (C) 2021 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 <stdio.h>
-#include <string.h>
-
-#include <cstdint>
-#include <cstdio>
-#include <memory>
-
-#include <sys/fcntl.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-
-#include <base/files/file_path.h>
-#include <libsnapshot/cow_writer.h>
-
-#include "update_engine/common/cow_operation_convert.h"
-#include "update_engine/common/utils.h"
-#include "update_engine/payload_consumer/file_descriptor.h"
-#include "update_engine/payload_consumer/payload_metadata.h"
-#include "update_engine/payload_generator/cow_size_estimator.h"
-#include "update_engine/update_metadata.pb.h"
-
-using android::snapshot::CowWriter;
-
-namespace chromeos_update_engine {
-
-bool ProcessPartition(const chromeos_update_engine::PartitionUpdate& partition,
-                      const char* image_dir,
-                      size_t block_size) {
-  base::FilePath img_dir{image_dir};
-  auto target_img = img_dir.Append(partition.partition_name() + ".img");
-  auto output_cow = img_dir.Append(partition.partition_name() + ".cow");
-  FileDescriptorPtr target_img_fd = std::make_shared<EintrSafeFileDescriptor>();
-  if (!target_img_fd->Open(target_img.value().c_str(), O_RDONLY)) {
-    PLOG(ERROR) << "Failed to open " << target_img.value();
-    return false;
-  }
-  android::base::unique_fd output_fd{
-      open(output_cow.value().c_str(), O_RDWR | O_CREAT, 0744)};
-  if (output_fd < 0) {
-    PLOG(ERROR) << "Failed to open " << output_cow.value();
-    return false;
-  }
-
-  android::snapshot::CowWriter cow_writer{
-      {.block_size = static_cast<uint32_t>(block_size), .compression = "gz"}};
-  TEST_AND_RETURN_FALSE(cow_writer.Initialize(output_fd));
-  TEST_AND_RETURN_FALSE(CowDryRun(target_img_fd,
-                                  partition.operations(),
-                                  partition.merge_operations(),
-                                  block_size,
-                                  &cow_writer));
-  TEST_AND_RETURN_FALSE(cow_writer.Finalize());
-  return true;
-}
-
-}  // namespace chromeos_update_engine
-
-using chromeos_update_engine::MetadataParseResult;
-using chromeos_update_engine::PayloadMetadata;
-
-int main(int argc, const char* argv[]) {
-  if (argc != 3) {
-    printf("Usage: %s <payload.bin> <extracted target_file>\n", argv[0]);
-    return -1;
-  }
-  const char* payload_path = argv[1];
-  const char* images_dir = argv[2];
-  int payload_fd = open(payload_path, O_RDONLY);
-  if (payload_fd < 0) {
-    PLOG(ERROR) << "Failed to open payload file:";
-    return 1;
-  }
-  chromeos_update_engine::ScopedFdCloser closer{&payload_fd};
-  auto payload_size = chromeos_update_engine::utils::FileSize(payload_fd);
-  if (payload_size <= 0) {
-    PLOG(ERROR)
-        << "Couldn't determine size of payload file, or payload file is empty";
-    return 2;
-  }
-
-  PayloadMetadata payload_metadata;
-  auto payload = static_cast<unsigned char*>(
-      mmap(nullptr, payload_size, PROT_READ, MAP_PRIVATE, payload_fd, 0));
-
-  // C++ dark magic to ensure that |payload| is properly deallocated once the
-  // program exits.
-  auto munmap_deleter = [payload_size](auto payload) {
-    munmap(payload, payload_size);
-  };
-  std::unique_ptr<unsigned char, decltype(munmap_deleter)> munmapper{
-      payload, munmap_deleter};
-
-  if (payload == nullptr) {
-    PLOG(ERROR) << "Failed to mmap() payload file";
-    return 3;
-  }
-  if (payload_metadata.ParsePayloadHeader(payload, payload_size, nullptr) !=
-      chromeos_update_engine::MetadataParseResult::kSuccess) {
-    LOG(ERROR) << "Payload header parse failed!";
-    return 4;
-  }
-  chromeos_update_engine::DeltaArchiveManifest manifest;
-  if (!payload_metadata.GetManifest(payload, payload_size, &manifest)) {
-    LOG(ERROR) << "Failed to parse manifest!";
-    return 5;
-  }
-
-  for (const auto& partition : manifest.partitions()) {
-    LOG(INFO) << partition.partition_name();
-    if (!ProcessPartition(partition, images_dir, manifest.block_size())) {
-      return 6;
-    }
-  }
-  return 0;
-}
diff --git a/aosp/daemon_android.cc b/aosp/daemon_android.cc
deleted file mode 100644
index c102e3b..0000000
--- a/aosp/daemon_android.cc
+++ /dev/null
@@ -1,74 +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/aosp/daemon_android.h"
-
-#include <sysexits.h>
-
-#include <binderwrapper/binder_wrapper.h>
-
-#include "update_engine/aosp/daemon_state_android.h"
-
-using std::unique_ptr;
-
-namespace chromeos_update_engine {
-
-unique_ptr<DaemonBase> DaemonBase::CreateInstance() {
-  return std::make_unique<DaemonAndroid>();
-}
-
-int DaemonAndroid::OnInit() {
-  // Register the |subprocess_| singleton with this Daemon as the signal
-  // handler.
-  subprocess_.Init(this);
-
-  int exit_code = brillo::Daemon::OnInit();
-  if (exit_code != EX_OK)
-    return exit_code;
-
-  android::BinderWrapper::Create();
-  binder_watcher_.Init();
-
-  DaemonStateAndroid* daemon_state_android = new DaemonStateAndroid();
-  daemon_state_.reset(daemon_state_android);
-  LOG_IF(ERROR, !daemon_state_android->Initialize())
-      << "Failed to initialize system state.";
-
-  auto binder_wrapper = android::BinderWrapper::Get();
-
-  // Create the Binder Service.
-  binder_service_ = new BinderUpdateEngineAndroidService{
-      daemon_state_android->service_delegate()};
-  if (!binder_wrapper->RegisterService(binder_service_->ServiceName(),
-                                       binder_service_)) {
-    LOG(ERROR) << "Failed to register binder service.";
-  }
-  daemon_state_->AddObserver(binder_service_.get());
-
-  // Create the stable binder service.
-  stable_binder_service_ = new BinderUpdateEngineAndroidStableService{
-      daemon_state_android->service_delegate()};
-  if (!binder_wrapper->RegisterService(stable_binder_service_->ServiceName(),
-                                       stable_binder_service_)) {
-    LOG(ERROR) << "Failed to register stable binder service.";
-  }
-  daemon_state_->AddObserver(stable_binder_service_.get());
-
-  daemon_state_->StartUpdater();
-  return EX_OK;
-}
-
-}  // namespace chromeos_update_engine
diff --git a/aosp/daemon_android.h b/aosp/daemon_android.h
deleted file mode 100644
index 38a8689..0000000
--- a/aosp/daemon_android.h
+++ /dev/null
@@ -1,58 +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.
-//
-
-#ifndef UPDATE_ENGINE_AOSP_DAEMON_ANDROID_H_
-#define UPDATE_ENGINE_AOSP_DAEMON_ANDROID_H_
-
-#include <memory>
-
-#include <brillo/binder_watcher.h>
-
-#include "update_engine/aosp/binder_service_android.h"
-#include "update_engine/aosp/binder_service_stable_android.h"
-#include "update_engine/common/daemon_base.h"
-#include "update_engine/common/daemon_state_interface.h"
-#include "update_engine/common/subprocess.h"
-
-namespace chromeos_update_engine {
-
-class DaemonAndroid : public DaemonBase {
- public:
-  DaemonAndroid() = default;
-
- protected:
-  int OnInit() override;
-
- private:
-  // The Subprocess singleton class requires a |brillo::MessageLoop| in the
-  // current thread, so we need to initialize it from this class instead of
-  // the main() function.
-  Subprocess subprocess_;
-
-  brillo::BinderWatcher binder_watcher_;
-  android::sp<BinderUpdateEngineAndroidService> binder_service_;
-  android::sp<BinderUpdateEngineAndroidStableService> stable_binder_service_;
-
-  // The daemon state with all the required daemon classes for the configured
-  // platform.
-  std::unique_ptr<DaemonStateInterface> daemon_state_;
-
-  DISALLOW_COPY_AND_ASSIGN(DaemonAndroid);
-};
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_AOSP_DAEMON_ANDROID_H_
diff --git a/aosp/hardware_android.cc b/aosp/hardware_android.cc
deleted file mode 100644
index 624cfc9..0000000
--- a/aosp/hardware_android.cc
+++ /dev/null
@@ -1,375 +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/aosp/hardware_android.h"
-
-#include <sys/types.h>
-
-#include <memory>
-#include <string>
-#include <string_view>
-
-#include <android/sysprop/GkiProperties.sysprop.h>
-#include <android-base/properties.h>
-#include <base/files/file_util.h>
-#include <base/strings/string_number_conversions.h>
-#include <base/strings/string_util.h>
-#include <bootloader_message/bootloader_message.h>
-#include <fstab/fstab.h>
-#include <libavb/libavb.h>
-#include <libavb_user/avb_ops_user.h>
-
-#include "update_engine/common/error_code_utils.h"
-#include "update_engine/common/hardware.h"
-#include "update_engine/common/platform_constants.h"
-#include "update_engine/common/utils.h"
-
-#ifndef __ANDROID_RECOVERY__
-#include <android/sysprop/OtaProperties.sysprop.h>
-#endif
-
-using android::base::GetBoolProperty;
-using android::base::GetIntProperty;
-using android::base::GetProperty;
-using std::string;
-
-namespace chromeos_update_engine {
-
-namespace {
-
-// Android properties that identify the hardware and potentially non-updatable
-// parts of the bootloader (such as the bootloader version and the baseband
-// version).
-const char kPropProductManufacturer[] = "ro.product.manufacturer";
-const char kPropBootHardwareSKU[] = "ro.boot.hardware.sku";
-const char kPropBootRevision[] = "ro.boot.revision";
-const char kPropBuildDateUTC[] = "ro.build.date.utc";
-
-string GetPartitionBuildDate(const string& partition_name) {
-  return android::base::GetProperty("ro." + partition_name + ".build.date.utc",
-                                    "");
-}
-
-ErrorCode IsTimestampNewerLogged(const std::string& partition_name,
-                                 const std::string& old_version,
-                                 const std::string& new_version) {
-  auto error_code = utils::IsTimestampNewer(old_version, new_version);
-  if (error_code != ErrorCode::kSuccess) {
-    LOG(WARNING) << "Timestamp check failed with "
-                 << utils::ErrorCodeToString(error_code) << ": "
-                 << partition_name << " Partition timestamp: " << old_version
-                 << " Update timestamp: " << new_version;
-  }
-  return error_code;
-}
-
-void SetVbmetaDigestProp(const std::string& value) {
-#ifndef __ANDROID_RECOVERY__
-  if (!android::sysprop::OtaProperties::other_vbmeta_digest(value)) {
-    LOG(WARNING) << "Failed to set other vbmeta digest to " << value;
-  }
-#endif
-}
-
-std::string CalculateVbmetaDigestForInactiveSlot() {
-  AvbSlotVerifyData* avb_slot_data;
-
-  auto suffix = fs_mgr_get_other_slot_suffix();
-  const char* requested_partitions[] = {nullptr};
-  auto avb_ops = avb_ops_user_new();
-  auto verify_result = avb_slot_verify(avb_ops,
-                                       requested_partitions,
-                                       suffix.c_str(),
-                                       AVB_SLOT_VERIFY_FLAGS_NONE,
-                                       AVB_HASHTREE_ERROR_MODE_EIO,
-                                       &avb_slot_data);
-  if (verify_result != AVB_SLOT_VERIFY_RESULT_OK) {
-    LOG(WARNING) << "Failed to verify avb slot data: " << verify_result;
-    return "";
-  }
-
-  uint8_t vbmeta_digest[AVB_SHA256_DIGEST_SIZE];
-  avb_slot_verify_data_calculate_vbmeta_digest(
-      avb_slot_data, AVB_DIGEST_TYPE_SHA256, vbmeta_digest);
-
-  std::string encoded_digest =
-      base::HexEncode(vbmeta_digest, AVB_SHA256_DIGEST_SIZE);
-  return base::ToLowerASCII(encoded_digest);
-}
-
-}  // namespace
-
-namespace hardware {
-
-// Factory defined in hardware.h.
-std::unique_ptr<HardwareInterface> CreateHardware() {
-  return std::make_unique<HardwareAndroid>();
-}
-
-}  // namespace hardware
-
-// In Android there are normally three kinds of builds: eng, userdebug and user.
-// These builds target respectively a developer build, a debuggable version of
-// the final product and the pristine final product the end user will run.
-// Apart from the ro.build.type property name, they differ in the following
-// properties that characterize the builds:
-// * eng builds: ro.secure=0 and ro.debuggable=1
-// * userdebug builds: ro.secure=1 and ro.debuggable=1
-// * user builds: ro.secure=1 and ro.debuggable=0
-//
-// See IsOfficialBuild() and IsNormalMode() for the meaning of these options in
-// Android.
-
-bool HardwareAndroid::IsOfficialBuild() const {
-  // We run an official build iff ro.secure == 1, because we expect the build to
-  // behave like the end user product and check for updates. Note that while
-  // developers are able to build "official builds" by just running "make user",
-  // that will only result in a more restrictive environment. The important part
-  // is that we don't produce and push "non-official" builds to the end user.
-  //
-  // In case of a non-bool value, we take the most restrictive option and
-  // assume we are in an official-build.
-  return GetBoolProperty("ro.secure", true);
-}
-
-bool HardwareAndroid::IsNormalBootMode() const {
-  // We are running in "dev-mode" iff ro.debuggable == 1. In dev-mode the
-  // update_engine will allow extra developers options, such as providing a
-  // different update URL. In case of error, we assume the build is in
-  // normal-mode.
-  return !GetBoolProperty("ro.debuggable", false);
-}
-
-bool HardwareAndroid::AreDevFeaturesEnabled() const {
-  return !IsNormalBootMode();
-}
-
-bool HardwareAndroid::IsOOBEEnabled() const {
-  // No OOBE flow blocking updates for Android-based boards.
-  return false;
-}
-
-bool HardwareAndroid::IsOOBEComplete(base::Time* out_time_of_oobe) const {
-  LOG(WARNING) << "OOBE is not enabled but IsOOBEComplete() called.";
-  if (out_time_of_oobe)
-    *out_time_of_oobe = base::Time();
-  return true;
-}
-
-string HardwareAndroid::GetHardwareClass() const {
-  auto manufacturer = GetProperty(kPropProductManufacturer, "");
-  auto sku = GetProperty(kPropBootHardwareSKU, "");
-  auto revision = GetProperty(kPropBootRevision, "");
-
-  return manufacturer + ":" + sku + ":" + revision;
-}
-
-string HardwareAndroid::GetDeviceRequisition() const {
-  LOG(WARNING) << "STUB: Getting requisition is not supported.";
-  return "";
-}
-
-int HardwareAndroid::GetMinKernelKeyVersion() const {
-  LOG(WARNING) << "STUB: No Kernel key version is available.";
-  return -1;
-}
-
-int HardwareAndroid::GetMinFirmwareKeyVersion() const {
-  LOG(WARNING) << "STUB: No Firmware key version is available.";
-  return -1;
-}
-
-int HardwareAndroid::GetMaxFirmwareKeyRollforward() const {
-  LOG(WARNING) << "STUB: Getting firmware_max_rollforward is not supported.";
-  return -1;
-}
-
-bool HardwareAndroid::SetMaxFirmwareKeyRollforward(
-    int firmware_max_rollforward) {
-  LOG(WARNING) << "STUB: Setting firmware_max_rollforward is not supported.";
-  return false;
-}
-
-bool HardwareAndroid::SetMaxKernelKeyRollforward(int kernel_max_rollforward) {
-  LOG(WARNING) << "STUB: Setting kernel_max_rollforward is not supported.";
-  return false;
-}
-
-int HardwareAndroid::GetPowerwashCount() const {
-  LOG(WARNING) << "STUB: Assuming no factory reset was performed.";
-  return 0;
-}
-
-bool HardwareAndroid::SchedulePowerwash(bool save_rollback_data) {
-  LOG(INFO) << "Scheduling a powerwash to BCB.";
-  LOG_IF(WARNING, save_rollback_data) << "save_rollback_data was true but "
-                                      << "isn't supported.";
-  string err;
-  if (!update_bootloader_message({"--wipe_data", "--reason=wipe_data_from_ota"},
-                                 &err)) {
-    LOG(ERROR) << "Failed to update bootloader message: " << err;
-    return false;
-  }
-  return true;
-}
-
-bool HardwareAndroid::CancelPowerwash() {
-  string err;
-  if (!clear_bootloader_message(&err)) {
-    LOG(ERROR) << "Failed to clear bootloader message: " << err;
-    return false;
-  }
-  return true;
-}
-
-bool HardwareAndroid::GetNonVolatileDirectory(base::FilePath* path) const {
-  base::FilePath local_path(constants::kNonVolatileDirectory);
-  if (!base::DirectoryExists(local_path)) {
-    LOG(ERROR) << "Non-volatile directory not found: " << local_path.value();
-    return false;
-  }
-  *path = local_path;
-  return true;
-}
-
-bool HardwareAndroid::GetPowerwashSafeDirectory(base::FilePath* path) const {
-  // On Android, we don't have a directory persisted across powerwash.
-  return false;
-}
-
-int64_t HardwareAndroid::GetBuildTimestamp() const {
-  return GetIntProperty<int64_t>(kPropBuildDateUTC, 0);
-}
-
-// Returns true if the device runs an userdebug build, and explicitly allows OTA
-// downgrade.
-bool HardwareAndroid::AllowDowngrade() const {
-  return GetBoolProperty("ro.ota.allow_downgrade", false) &&
-         GetBoolProperty("ro.debuggable", false);
-}
-
-bool HardwareAndroid::GetFirstActiveOmahaPingSent() const {
-  LOG(WARNING) << "STUB: Assuming first active omaha was never set.";
-  return false;
-}
-
-bool HardwareAndroid::SetFirstActiveOmahaPingSent() {
-  LOG(WARNING) << "STUB: Assuming first active omaha is set.";
-  // We will set it true, so its failure doesn't cause escalation.
-  return true;
-}
-
-void HardwareAndroid::SetWarmReset(bool warm_reset) {
-  if constexpr (!constants::kIsRecovery) {
-    constexpr char warm_reset_prop[] = "ota.warm_reset";
-    if (!android::base::SetProperty(warm_reset_prop, warm_reset ? "1" : "0")) {
-      LOG(WARNING) << "Failed to set prop " << warm_reset_prop;
-    }
-  }
-}
-
-void HardwareAndroid::SetVbmetaDigestForInactiveSlot(bool reset) {
-  if constexpr (constants::kIsRecovery) {
-    return;
-  }
-
-  if (android::base::GetProperty("ro.boot.avb_version", "").empty() &&
-      android::base::GetProperty("ro.boot.vbmeta.avb_version", "").empty()) {
-    LOG(INFO) << "Device doesn't use avb, skipping setting vbmeta digest";
-    return;
-  }
-
-  if (reset) {
-    SetVbmetaDigestProp("");
-    return;
-  }
-
-  std::string digest = CalculateVbmetaDigestForInactiveSlot();
-  if (digest.empty()) {
-    LOG(WARNING) << "Failed to calculate the vbmeta digest for the other slot";
-    return;
-  }
-  SetVbmetaDigestProp(digest);
-}
-
-string HardwareAndroid::GetVersionForLogging(
-    const string& partition_name) const {
-  if (partition_name == "boot") {
-    // ro.bootimage.build.date.utc
-    return GetPartitionBuildDate("bootimage");
-  }
-  return GetPartitionBuildDate(partition_name);
-}
-
-ErrorCode HardwareAndroid::IsPartitionUpdateValid(
-    const string& partition_name, const string& new_version) const {
-  if (partition_name == "boot") {
-    const auto old_version = GetPartitionBuildDate("bootimage");
-    auto error_code =
-        IsTimestampNewerLogged(partition_name, old_version, new_version);
-    if (error_code == ErrorCode::kPayloadTimestampError) {
-      bool prevent_downgrade =
-          android::sysprop::GkiProperties::prevent_downgrade_version().value_or(
-              false);
-      if (!prevent_downgrade) {
-        LOG(WARNING) << "Downgrade of boot image is detected, but permitting "
-                        "update because device does not prevent boot image "
-                        "downgrade";
-        // If prevent_downgrade_version sysprop is not explicitly set, permit
-        // downgrade in boot image version.
-        // Even though error_code is overridden here, always call
-        // IsTimestampNewerLogged to produce log messages.
-        error_code = ErrorCode::kSuccess;
-      }
-    }
-    return error_code;
-  }
-
-  const auto old_version = GetPartitionBuildDate(partition_name);
-  // TODO(zhangkelvin)  for some partitions, missing a current timestamp should
-  // be an error, e.g. system, vendor, product etc.
-  auto error_code =
-      IsTimestampNewerLogged(partition_name, old_version, new_version);
-  return error_code;
-}
-
-// Mount options for non-system partitions. This option causes selinux treat
-// every file in the mounted filesystem as having the 'postinstall_file'
-// context, regardless of what the filesystem itself records. See "SELinux
-// User's and Administrator's Guide" for more information on this option.
-constexpr const char* kDefaultPostinstallMountOptions =
-    "context=u:object_r:postinstall_file:s0";
-
-// Mount options for system partitions. This option causes selinux to use the
-// 'postinstall_file' context as a fallback if there are no other selinux
-// contexts associated with the file in the mounted partition. See "SELinux
-// User's and Administrator's Guide" for more information on this option.
-constexpr const char* kSystemPostinstallMountOptions =
-    "defcontext=u:object_r:postinstall_file:s0";
-
-// Name of the system-partition
-constexpr std::string_view kSystemPartitionName = "system";
-
-const char* HardwareAndroid::GetPartitionMountOptions(
-    const std::string& partition_name) const {
-  if (partition_name == kSystemPartitionName) {
-    return kSystemPostinstallMountOptions;
-  } else {
-    return kDefaultPostinstallMountOptions;
-  }
-}
-
-}  // namespace chromeos_update_engine
diff --git a/aosp/mock_dynamic_partition_control_android.h b/aosp/mock_dynamic_partition_control_android.h
deleted file mode 100644
index 428b6c7..0000000
--- a/aosp/mock_dynamic_partition_control_android.h
+++ /dev/null
@@ -1,146 +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 <stdint.h>
-
-#include <memory>
-#include <set>
-#include <string>
-
-#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"
-
-namespace chromeos_update_engine {
-
-class MockDynamicPartitionControlAndroid
-    : public DynamicPartitionControlAndroid {
- public:
-  MockDynamicPartitionControlAndroid()
-      : DynamicPartitionControlAndroid(0 /*source slot*/) {}
-  MOCK_METHOD(
-      bool,
-      MapPartitionOnDeviceMapper,
-      (const std::string&, const std::string&, uint32_t, bool, std::string*),
-      (override));
-  MOCK_METHOD(bool,
-              UnmapPartitionOnDeviceMapper,
-              (const std::string&),
-              (override));
-  MOCK_METHOD(void, Cleanup, (), (override));
-  MOCK_METHOD(bool, DeviceExists, (const std::string&), (override));
-  MOCK_METHOD(::android::dm::DmDeviceState,
-              GetState,
-              (const std::string&),
-              (override));
-  MOCK_METHOD(bool,
-              GetDmDevicePathByName,
-              (const std::string&, std::string*),
-              (override));
-  MOCK_METHOD(std::unique_ptr<::android::fs_mgr::MetadataBuilder>,
-              LoadMetadataBuilder,
-              (const std::string&, uint32_t),
-              (override));
-  MOCK_METHOD(std::unique_ptr<::android::fs_mgr::MetadataBuilder>,
-              LoadMetadataBuilder,
-              (const std::string&, uint32_t, uint32_t),
-              (override));
-  MOCK_METHOD(bool,
-              StoreMetadata,
-              (const std::string&, android::fs_mgr::MetadataBuilder*, uint32_t),
-              (override));
-  MOCK_METHOD(bool, GetDeviceDir, (std::string*), (override));
-  MOCK_METHOD(FeatureFlag, GetDynamicPartitionsFeatureFlag, (), (override));
-  MOCK_METHOD(std::string, GetSuperPartitionName, (uint32_t), (override));
-  MOCK_METHOD(FeatureFlag, GetVirtualAbFeatureFlag, (), (override));
-  MOCK_METHOD(FeatureFlag, GetVirtualAbCompressionFeatureFlag, (), (override));
-  MOCK_METHOD(bool, FinishUpdate, (bool), (override));
-  MOCK_METHOD(bool,
-              GetSystemOtherPath,
-              (uint32_t, uint32_t, const std::string&, std::string*, bool*),
-              (override));
-  MOCK_METHOD(bool,
-              EraseSystemOtherAvbFooter,
-              (uint32_t, uint32_t),
-              (override));
-  MOCK_METHOD(std::optional<bool>, IsAvbEnabledOnSystemOther, (), (override));
-  MOCK_METHOD(bool, IsRecovery, (), (override));
-  MOCK_METHOD(bool,
-              PrepareDynamicPartitionsForUpdate,
-              (uint32_t, uint32_t, const DeltaArchiveManifest&, bool),
-              (override));
-  MOCK_METHOD(std::unique_ptr<android::snapshot::ISnapshotWriter>,
-              OpenCowWriter,
-              (const std::string& unsuffixed_partition_name,
-               const std::optional<std::string>& source_path,
-               bool is_append),
-              (override));
-  MOCK_METHOD(FileDescriptorPtr,
-              OpenCowFd,
-              (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&, uint32_t slot),
-              (override));
-  MOCK_METHOD(bool, UpdateUsesSnapshotCompression, (), (override));
-
-  void set_fake_mapped_devices(const std::set<std::string>& fake) override {
-    DynamicPartitionControlAndroid::set_fake_mapped_devices(fake);
-  }
-
-  bool RealGetSystemOtherPath(uint32_t source_slot,
-                              uint32_t target_slot,
-                              const std::string& partition_name_suffix,
-                              std::string* path,
-                              bool* should_unmap) {
-    return DynamicPartitionControlAndroid::GetSystemOtherPath(
-        source_slot, target_slot, partition_name_suffix, path, should_unmap);
-  }
-
-  bool RealEraseSystemOtherAvbFooter(uint32_t source_slot,
-                                     uint32_t target_slot) {
-    return DynamicPartitionControlAndroid::EraseSystemOtherAvbFooter(
-        source_slot, target_slot);
-  }
-
-  std::optional<bool> RealIsAvbEnabledInFstab(const std::string& path) {
-    return DynamicPartitionControlAndroid::IsAvbEnabledInFstab(path);
-  }
-
-  bool RealPrepareDynamicPartitionsForUpdate(
-      uint32_t source_slot,
-      uint32_t target_slot,
-      const DeltaArchiveManifest& manifest,
-      bool delete_source) {
-    return DynamicPartitionControlAndroid::PrepareDynamicPartitionsForUpdate(
-        source_slot, target_slot, manifest, delete_source);
-  }
-  using DynamicPartitionControlAndroid::SetSourceSlot;
-  using DynamicPartitionControlAndroid::SetTargetSlot;
-};
-
-}  // namespace chromeos_update_engine
diff --git a/binder_bindings/android/brillo/IUpdateEngine.aidl b/binder_bindings/android/brillo/IUpdateEngine.aidl
new file mode 100644
index 0000000..56e1524
--- /dev/null
+++ b/binder_bindings/android/brillo/IUpdateEngine.aidl
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.brillo;
+
+import android.brillo.IUpdateEngineStatusCallback;
+import android.brillo.ParcelableUpdateEngineStatus;
+
+interface IUpdateEngine {
+  void SetUpdateAttemptFlags(in int flags);
+  boolean AttemptUpdate(in String app_version, in String omaha_url, in int flags);
+  void AttemptRollback(in boolean powerwash);
+  boolean CanRollback();
+  void ResetStatus();
+  ParcelableUpdateEngineStatus GetStatus();
+  void RebootIfNeeded();
+  void SetChannel(in String target_channel, in boolean powewash);
+  String GetChannel(in boolean get_current_channel);
+  void SetCohortHint(in String cohort_hint);
+  String GetCohortHint();
+  void SetP2PUpdatePermission(in boolean enabled);
+  boolean GetP2PUpdatePermission();
+  void SetUpdateOverCellularPermission(in boolean enabled);
+  void SetUpdateOverCellularTarget(in String target_version,
+                                   in long target_size);
+  boolean GetUpdateOverCellularPermission();
+  long GetDurationSinceUpdate();
+  String GetPrevVersion();
+  String GetRollbackPartition();
+  void RegisterStatusCallback(in IUpdateEngineStatusCallback callback);
+  int GetLastAttemptError();
+  int GetEolStatus();
+}
diff --git a/binder_bindings/android/brillo/IUpdateEngineStatusCallback.aidl b/binder_bindings/android/brillo/IUpdateEngineStatusCallback.aidl
new file mode 100644
index 0000000..837d44d
--- /dev/null
+++ b/binder_bindings/android/brillo/IUpdateEngineStatusCallback.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+package android.brillo;
+
+import android.brillo.ParcelableUpdateEngineStatus;
+
+interface IUpdateEngineStatusCallback {
+  oneway
+  void HandleStatusUpdate(in ParcelableUpdateEngineStatus status);
+}
diff --git a/binder_bindings/android/brillo/ParcelableUpdateEngineStatus.aidl b/binder_bindings/android/brillo/ParcelableUpdateEngineStatus.aidl
new file mode 100644
index 0000000..fc10505
--- /dev/null
+++ b/binder_bindings/android/brillo/ParcelableUpdateEngineStatus.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+package android.brillo;
+
+parcelable ParcelableUpdateEngineStatus cpp_header
+    "update_engine/parcelable_update_engine_status.h";
diff --git a/aosp/binder_service_android.cc b/binder_service_android.cc
similarity index 94%
rename from aosp/binder_service_android.cc
rename to binder_service_android.cc
index ed76c4a..6b8a552 100644
--- a/aosp/binder_service_android.cc
+++ b/binder_service_android.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/aosp/binder_service_android.h"
+#include "update_engine/binder_service_android.h"
 
 #include <memory>
 
@@ -24,8 +24,6 @@
 #include <brillo/errors/error.h>
 #include <utils/String8.h>
 
-#include "update_engine/aosp/binder_service_android_common.h"
-
 using android::binder::Status;
 using android::os::IUpdateEngineCallback;
 using android::os::ParcelFileDescriptor;
@@ -33,6 +31,23 @@
 using std::vector;
 using update_engine::UpdateEngineStatus;
 
+namespace {
+Status ErrorPtrToStatus(const brillo::ErrorPtr& error) {
+  return Status::fromServiceSpecificError(
+      1, android::String8{error->GetMessage().c_str()});
+}
+
+vector<string> ToVecString(const vector<android::String16>& inp) {
+  vector<string> out;
+  out.reserve(inp.size());
+  for (const auto& e : inp) {
+    out.emplace_back(android::String8{e}.string());
+  }
+  return out;
+}
+
+}  // namespace
+
 namespace chromeos_update_engine {
 
 BinderUpdateEngineAndroidService::BinderUpdateEngineAndroidService(
diff --git a/aosp/binder_service_android.h b/binder_service_android.h
similarity index 92%
rename from aosp/binder_service_android.h
rename to binder_service_android.h
index f41fbdf..5f28225 100644
--- a/aosp/binder_service_android.h
+++ b/binder_service_android.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_H_
-#define UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_H_
+#ifndef UPDATE_ENGINE_BINDER_SERVICE_ANDROID_H_
+#define UPDATE_ENGINE_BINDER_SERVICE_ANDROID_H_
 
 #include <stdint.h>
 
@@ -28,8 +28,8 @@
 
 #include "android/os/BnUpdateEngine.h"
 #include "android/os/IUpdateEngineCallback.h"
-#include "update_engine/aosp/service_delegate_android_interface.h"
-#include "update_engine/common/service_observer_interface.h"
+#include "update_engine/service_delegate_android_interface.h"
+#include "update_engine/service_observer_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -96,4 +96,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_H_
+#endif  // UPDATE_ENGINE_BINDER_SERVICE_ANDROID_H_
diff --git a/binder_service_brillo.cc b/binder_service_brillo.cc
new file mode 100644
index 0000000..cc74763
--- /dev/null
+++ b/binder_service_brillo.cc
@@ -0,0 +1,247 @@
+//
+// 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/binder_service_brillo.h"
+
+#include <base/bind.h>
+
+#include <binderwrapper/binder_wrapper.h>
+
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+
+#include "update_engine/update_status_utils.h"
+
+using android::sp;
+using android::String16;
+using android::String8;
+using android::binder::Status;
+using android::brillo::IUpdateEngineStatusCallback;
+using android::brillo::ParcelableUpdateEngineStatus;
+using brillo::ErrorPtr;
+using std::string;
+using update_engine::UpdateEngineStatus;
+
+namespace chromeos_update_engine {
+
+namespace {
+string NormalString(const String16& in) {
+  return string{String8{in}.string()};
+}
+
+Status ToStatus(ErrorPtr* error) {
+  return Status::fromServiceSpecificError(
+      1, String8{error->get()->GetMessage().c_str()});
+}
+}  // namespace
+
+template <typename... Parameters, typename... Arguments>
+Status BinderUpdateEngineBrilloService::CallCommonHandler(
+    bool (UpdateEngineService::*Handler)(ErrorPtr*, Parameters...),
+    Arguments... arguments) {
+  ErrorPtr error;
+  if (((common_.get())->*Handler)(&error, arguments...))
+    return Status::ok();
+  return ToStatus(&error);
+}
+
+Status BinderUpdateEngineBrilloService::SetUpdateAttemptFlags(int flags) {
+  return CallCommonHandler(&UpdateEngineService::SetUpdateAttemptFlags, flags);
+}
+
+Status BinderUpdateEngineBrilloService::AttemptUpdate(
+    const String16& app_version,
+    const String16& omaha_url,
+    int flags,
+    bool* out_result) {
+  return CallCommonHandler(&UpdateEngineService::AttemptUpdate,
+                           NormalString(app_version),
+                           NormalString(omaha_url),
+                           flags,
+                           out_result);
+}
+
+Status BinderUpdateEngineBrilloService::AttemptRollback(bool powerwash) {
+  return CallCommonHandler(&UpdateEngineService::AttemptRollback, powerwash);
+}
+
+Status BinderUpdateEngineBrilloService::CanRollback(bool* out_can_rollback) {
+  return CallCommonHandler(&UpdateEngineService::CanRollback, out_can_rollback);
+}
+
+Status BinderUpdateEngineBrilloService::ResetStatus() {
+  return CallCommonHandler(&UpdateEngineService::ResetStatus);
+}
+
+Status BinderUpdateEngineBrilloService::GetStatus(
+    ParcelableUpdateEngineStatus* status) {
+  UpdateEngineStatus update_engine_status;
+  auto ret =
+      CallCommonHandler(&UpdateEngineService::GetStatus, &update_engine_status);
+
+  if (ret.isOk()) {
+    *status = ParcelableUpdateEngineStatus(update_engine_status);
+  }
+
+  return ret;
+}
+
+Status BinderUpdateEngineBrilloService::RebootIfNeeded() {
+  return CallCommonHandler(&UpdateEngineService::RebootIfNeeded);
+}
+
+Status BinderUpdateEngineBrilloService::SetChannel(
+    const String16& target_channel, bool powerwash) {
+  return CallCommonHandler(&UpdateEngineService::SetChannel,
+                           NormalString(target_channel),
+                           powerwash);
+}
+
+Status BinderUpdateEngineBrilloService::GetChannel(bool get_current_channel,
+                                                   String16* out_channel) {
+  string channel_string;
+  auto ret = CallCommonHandler(
+      &UpdateEngineService::GetChannel, get_current_channel, &channel_string);
+
+  *out_channel = String16(channel_string.c_str());
+  return ret;
+}
+
+Status BinderUpdateEngineBrilloService::SetCohortHint(
+    const String16& in_cohort_hint) {
+  return CallCommonHandler(&UpdateEngineService::SetCohortHint,
+                           NormalString(in_cohort_hint));
+}
+
+Status BinderUpdateEngineBrilloService::GetCohortHint(
+    String16* out_cohort_hint) {
+  string cohort_hint;
+  auto ret =
+      CallCommonHandler(&UpdateEngineService::GetCohortHint, &cohort_hint);
+
+  *out_cohort_hint = String16(cohort_hint.c_str());
+  return ret;
+}
+
+Status BinderUpdateEngineBrilloService::SetP2PUpdatePermission(bool enabled) {
+  return CallCommonHandler(&UpdateEngineService::SetP2PUpdatePermission,
+                           enabled);
+}
+
+Status BinderUpdateEngineBrilloService::GetP2PUpdatePermission(
+    bool* out_p2p_permission) {
+  return CallCommonHandler(&UpdateEngineService::GetP2PUpdatePermission,
+                           out_p2p_permission);
+}
+
+Status BinderUpdateEngineBrilloService::SetUpdateOverCellularPermission(
+    bool enabled) {
+  return CallCommonHandler(
+      &UpdateEngineService::SetUpdateOverCellularPermission, enabled);
+}
+
+Status BinderUpdateEngineBrilloService::SetUpdateOverCellularTarget(
+    const String16& target_version, int64_t target_size) {
+  return CallCommonHandler(&UpdateEngineService::SetUpdateOverCellularTarget,
+                           NormalString(target_version),
+                           target_size);
+}
+
+Status BinderUpdateEngineBrilloService::GetUpdateOverCellularPermission(
+    bool* out_cellular_permission) {
+  return CallCommonHandler(
+      &UpdateEngineService::GetUpdateOverCellularPermission,
+      out_cellular_permission);
+}
+
+Status BinderUpdateEngineBrilloService::GetDurationSinceUpdate(
+    int64_t* out_duration) {
+  return CallCommonHandler(&UpdateEngineService::GetDurationSinceUpdate,
+                           out_duration);
+}
+
+Status BinderUpdateEngineBrilloService::GetPrevVersion(
+    String16* out_prev_version) {
+  string version_string;
+  auto ret =
+      CallCommonHandler(&UpdateEngineService::GetPrevVersion, &version_string);
+
+  *out_prev_version = String16(version_string.c_str());
+  return ret;
+}
+
+Status BinderUpdateEngineBrilloService::GetRollbackPartition(
+    String16* out_rollback_partition) {
+  string partition_string;
+  auto ret = CallCommonHandler(&UpdateEngineService::GetRollbackPartition,
+                               &partition_string);
+
+  if (ret.isOk()) {
+    *out_rollback_partition = String16(partition_string.c_str());
+  }
+
+  return ret;
+}
+
+Status BinderUpdateEngineBrilloService::RegisterStatusCallback(
+    const sp<IUpdateEngineStatusCallback>& callback) {
+  callbacks_.emplace_back(callback);
+
+  auto binder_wrapper = android::BinderWrapper::Get();
+
+  binder_wrapper->RegisterForDeathNotifications(
+      IUpdateEngineStatusCallback::asBinder(callback),
+      base::Bind(&BinderUpdateEngineBrilloService::UnregisterStatusCallback,
+                 base::Unretained(this),
+                 base::Unretained(callback.get())));
+
+  return Status::ok();
+}
+
+Status BinderUpdateEngineBrilloService::GetLastAttemptError(
+    int* out_last_attempt_error) {
+  return CallCommonHandler(&UpdateEngineService::GetLastAttemptError,
+                           out_last_attempt_error);
+}
+
+Status BinderUpdateEngineBrilloService::GetEolStatus(int* out_eol_status) {
+  return CallCommonHandler(&UpdateEngineService::GetEolStatus, out_eol_status);
+}
+
+void BinderUpdateEngineBrilloService::UnregisterStatusCallback(
+    IUpdateEngineStatusCallback* callback) {
+  auto it = callbacks_.begin();
+  while (it != callbacks_.end() && it->get() != callback)
+    it++;
+
+  if (it == callbacks_.end()) {
+    LOG(ERROR) << "Got death notification for unknown callback.";
+    return;
+  }
+
+  LOG(INFO) << "Erasing orphan callback";
+  callbacks_.erase(it);
+}
+
+void BinderUpdateEngineBrilloService::SendStatusUpdate(
+    const UpdateEngineStatus& update_engine_status) {
+  ParcelableUpdateEngineStatus parcelable_status(update_engine_status);
+  for (auto& callback : callbacks_) {
+    callback->HandleStatusUpdate(parcelable_status);
+  }
+}
+
+}  // namespace chromeos_update_engine
diff --git a/binder_service_brillo.h b/binder_service_brillo.h
new file mode 100644
index 0000000..d0d0dc9
--- /dev/null
+++ b/binder_service_brillo.h
@@ -0,0 +1,114 @@
+//
+// 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_BINDER_SERVICE_BRILLO_H_
+#define UPDATE_ENGINE_BINDER_SERVICE_BRILLO_H_
+
+#include <utils/Errors.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <utils/RefBase.h>
+
+#include "update_engine/common_service.h"
+#include "update_engine/parcelable_update_engine_status.h"
+#include "update_engine/service_observer_interface.h"
+
+#include "android/brillo/BnUpdateEngine.h"
+#include "android/brillo/IUpdateEngineStatusCallback.h"
+
+namespace chromeos_update_engine {
+
+class BinderUpdateEngineBrilloService : public android::brillo::BnUpdateEngine,
+                                        public ServiceObserverInterface {
+ public:
+  explicit BinderUpdateEngineBrilloService(SystemState* system_state)
+      : common_(new UpdateEngineService(system_state)) {}
+  virtual ~BinderUpdateEngineBrilloService() = default;
+
+  const char* ServiceName() const {
+    return "android.brillo.UpdateEngineService";
+  }
+
+  // ServiceObserverInterface overrides.
+  void SendStatusUpdate(
+      const update_engine::UpdateEngineStatus& update_engine_status) override;
+  void SendPayloadApplicationComplete(ErrorCode error_code) override {}
+
+  // android::brillo::BnUpdateEngine overrides.
+  android::binder::Status SetUpdateAttemptFlags(int flags) override;
+  android::binder::Status AttemptUpdate(const android::String16& app_version,
+                                        const android::String16& omaha_url,
+                                        int flags,
+                                        bool* out_result) override;
+  android::binder::Status AttemptRollback(bool powerwash) override;
+  android::binder::Status CanRollback(bool* out_can_rollback) override;
+  android::binder::Status ResetStatus() override;
+  android::binder::Status GetStatus(
+      android::brillo::ParcelableUpdateEngineStatus* status);
+  android::binder::Status RebootIfNeeded() override;
+  android::binder::Status SetChannel(const android::String16& target_channel,
+                                     bool powerwash) override;
+  android::binder::Status GetChannel(bool get_current_channel,
+                                     android::String16* out_channel) override;
+  android::binder::Status SetCohortHint(
+      const android::String16& cohort_hint) override;
+  android::binder::Status GetCohortHint(
+      android::String16* out_cohort_hint) override;
+  android::binder::Status SetP2PUpdatePermission(bool enabled) override;
+  android::binder::Status GetP2PUpdatePermission(
+      bool* out_p2p_permission) override;
+  android::binder::Status SetUpdateOverCellularPermission(
+      bool enabled) override;
+  android::binder::Status SetUpdateOverCellularTarget(
+      const android::String16& target_version, int64_t target_size) override;
+  android::binder::Status GetUpdateOverCellularPermission(
+      bool* out_cellular_permission) override;
+  android::binder::Status GetDurationSinceUpdate(
+      int64_t* out_duration) override;
+  android::binder::Status GetPrevVersion(
+      android::String16* out_prev_version) override;
+  android::binder::Status GetRollbackPartition(
+      android::String16* out_rollback_partition) override;
+  android::binder::Status RegisterStatusCallback(
+      const android::sp<android::brillo::IUpdateEngineStatusCallback>& callback)
+      override;
+  android::binder::Status GetLastAttemptError(
+      int* out_last_attempt_error) override;
+  android::binder::Status GetEolStatus(int* out_eol_status) override;
+
+ private:
+  // Generic function for dispatching to the common service.
+  template <typename... Parameters, typename... Arguments>
+  android::binder::Status CallCommonHandler(
+      bool (UpdateEngineService::*Handler)(brillo::ErrorPtr*, Parameters...),
+      Arguments... arguments);
+
+  // To be used as a death notification handler only.
+  void UnregisterStatusCallback(
+      android::brillo::IUpdateEngineStatusCallback* callback);
+
+  std::unique_ptr<UpdateEngineService> common_;
+
+  std::vector<android::sp<android::brillo::IUpdateEngineStatusCallback>>
+      callbacks_;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_BINDER_SERVICE_BRILLO_H_
diff --git a/aosp/boot_control_android.cc b/boot_control_android.cc
similarity index 78%
rename from aosp/boot_control_android.cc
rename to boot_control_android.cc
index c1ac0d4..ec2ca0f 100644
--- a/aosp/boot_control_android.cc
+++ b/boot_control_android.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/aosp/boot_control_android.h"
+#include "update_engine/boot_control_android.h"
 
 #include <memory>
 #include <utility>
@@ -25,11 +25,13 @@
 #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;
 
+using android::dm::DmDeviceState;
+using android::hardware::hidl_string;
 using android::hardware::Return;
 using android::hardware::boot::V1_0::BoolResult;
 using android::hardware::boot::V1_0::CommandResult;
@@ -67,8 +69,7 @@
 
   LOG(INFO) << "Loaded boot control hidl hal.";
 
-  dynamic_control_ =
-      std::make_unique<DynamicPartitionControlAndroid>(GetCurrentSlot());
+  dynamic_control_ = std::make_unique<DynamicPartitionControlAndroid>();
 
   return true;
 }
@@ -81,24 +82,12 @@
   return module_->getCurrentSlot();
 }
 
-bool BootControlAndroid::GetPartitionDevice(const std::string& partition_name,
-                                            BootControlInterface::Slot slot,
-                                            bool not_in_payload,
-                                            std::string* device,
-                                            bool* is_dynamic) const {
-  return dynamic_control_->GetPartitionDevice(partition_name,
-                                              slot,
-                                              GetCurrentSlot(),
-                                              not_in_payload,
-                                              device,
-                                              is_dynamic);
-}
 
 bool BootControlAndroid::GetPartitionDevice(const string& partition_name,
-                                            BootControlInterface::Slot slot,
+                                            Slot slot,
                                             string* device) const {
-  return GetPartitionDevice(
-      partition_name, slot, false /* not_in_payload */, device, nullptr);
+  return dynamic_control_->GetPartitionDevice(
+      partition_name, slot, GetCurrentSlot(), device);
 }
 
 bool BootControlAndroid::IsSlotBootable(Slot slot) const {
@@ -182,12 +171,4 @@
   return dynamic_control_.get();
 }
 
-std::optional<PartitionDevice> BootControlAndroid::GetPartitionDevice(
-    const std::string& partition_name,
-    uint32_t slot,
-    uint32_t current_slot,
-    bool not_in_payload) const {
-  return dynamic_control_->GetPartitionDevice(
-      partition_name, slot, current_slot, not_in_payload);
-}
 }  // namespace chromeos_update_engine
diff --git a/aosp/boot_control_android.h b/boot_control_android.h
similarity index 77%
rename from aosp/boot_control_android.h
rename to boot_control_android.h
index 926023a..0b042e3 100644
--- a/aosp/boot_control_android.h
+++ b/boot_control_android.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_AOSP_BOOT_CONTROL_ANDROID_H_
-#define UPDATE_ENGINE_AOSP_BOOT_CONTROL_ANDROID_H_
+#ifndef UPDATE_ENGINE_BOOT_CONTROL_ANDROID_H_
+#define UPDATE_ENGINE_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 {
 
@@ -44,16 +44,6 @@
   // BootControlInterface overrides.
   unsigned int GetNumSlots() const override;
   BootControlInterface::Slot GetCurrentSlot() const override;
-  std::optional<PartitionDevice> GetPartitionDevice(
-      const std::string& partition_name,
-      uint32_t slot,
-      uint32_t current_slot,
-      bool not_in_payload = false) const override;
-  bool GetPartitionDevice(const std::string& partition_name,
-                          BootControlInterface::Slot slot,
-                          bool not_in_payload,
-                          std::string* device,
-                          bool* is_dynamic) const override;
   bool GetPartitionDevice(const std::string& partition_name,
                           BootControlInterface::Slot slot,
                           std::string* device) const override;
@@ -75,4 +65,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_AOSP_BOOT_CONTROL_ANDROID_H_
+#endif  // UPDATE_ENGINE_BOOT_CONTROL_ANDROID_H_
diff --git a/cros/boot_control_chromeos.cc b/boot_control_chromeos.cc
similarity index 83%
rename from cros/boot_control_chromeos.cc
rename to boot_control_chromeos.cc
index 17659ae..0f47169 100644
--- a/cros/boot_control_chromeos.cc
+++ b/boot_control_chromeos.cc
@@ -14,19 +14,16 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/boot_control_chromeos.h"
+#include "update_engine/boot_control_chromeos.h"
 
 #include <memory>
 #include <string>
 #include <utility>
-#include <vector>
 
 #include <base/bind.h>
 #include <base/files/file_path.h>
 #include <base/files/file_util.h>
-#include <base/strings/string_split.h>
 #include <base/strings/string_util.h>
-#include <chromeos/constants/imageloader.h>
 #include <rootdev/rootdev.h>
 
 extern "C" {
@@ -39,7 +36,6 @@
 #include "update_engine/common/utils.h"
 
 using std::string;
-using std::vector;
 
 namespace {
 
@@ -48,7 +44,8 @@
 const char* kAndroidPartitionNameKernel = "boot";
 const char* kAndroidPartitionNameRoot = "system";
 
-const char kPartitionNamePrefixDlc[] = "dlc";
+const char kDlcInstallRootDirectoryEncrypted[] = "/home/chronos/dlc";
+const char kPartitionNamePrefixDlc[] = "dlc_";
 const char kPartitionNameDlcA[] = "dlc_a";
 const char kPartitionNameDlcB[] = "dlc_b";
 const char kPartitionNameDlcImage[] = "dlc.img";
@@ -128,9 +125,8 @@
   }
   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;
@@ -152,47 +148,24 @@
   return current_slot_;
 }
 
-bool BootControlChromeOS::ParseDlcPartitionName(
-    const std::string partition_name,
-    std::string* dlc_id,
-    std::string* dlc_package) const {
-  CHECK_NE(dlc_id, nullptr);
-  CHECK_NE(dlc_package, nullptr);
-
-  vector<string> tokens = base::SplitString(
-      partition_name, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-  if (tokens.size() != 3 || tokens[0] != kPartitionNamePrefixDlc) {
-    LOG(ERROR) << "DLC partition name (" << partition_name
-               << ") is not well formatted.";
-    return false;
-  }
-  if (tokens[1].empty() || tokens[2].empty()) {
-    LOG(ERROR) << " partition name does not contain valid DLC ID (" << tokens[1]
-               << ") or package (" << tokens[2] << ")";
-    return false;
-  }
-
-  *dlc_id = tokens[1];
-  *dlc_package = tokens[2];
-  return true;
-}
-
-bool BootControlChromeOS::GetPartitionDevice(const std::string& partition_name,
-                                             BootControlInterface::Slot slot,
-                                             bool not_in_payload,
-                                             std::string* device,
-                                             bool* is_dynamic) const {
+bool BootControlChromeOS::GetPartitionDevice(const string& partition_name,
+                                             unsigned int slot,
+                                             string* device) const {
   // Partition name prefixed with |kPartitionNamePrefixDlc| is a DLC module.
   if (base::StartsWith(partition_name,
                        kPartitionNamePrefixDlc,
                        base::CompareCase::SENSITIVE)) {
-    string dlc_id, dlc_package;
-    if (!ParseDlcPartitionName(partition_name, &dlc_id, &dlc_package))
+    // Extract DLC module ID from partition_name (DLC module ID is the string
+    // after |kPartitionNamePrefixDlc| in partition_name).
+    const auto dlc_module_id =
+        partition_name.substr(strlen(kPartitionNamePrefixDlc));
+    if (dlc_module_id.empty()) {
+      LOG(ERROR) << " partition name does not contain DLC module ID:"
+                 << partition_name;
       return false;
-
-    *device = base::FilePath(imageloader::kDlcImageRootpath)
-                  .Append(dlc_id)
-                  .Append(dlc_package)
+    }
+    *device = base::FilePath(kDlcInstallRootDirectoryEncrypted)
+                  .Append(dlc_module_id)
                   .Append(slot == 0 ? kPartitionNameDlcA : kPartitionNameDlcB)
                   .Append(kPartitionNameDlcImage)
                   .value();
@@ -207,18 +180,9 @@
     return false;
 
   *device = part_device;
-  if (is_dynamic) {
-    *is_dynamic = false;
-  }
   return true;
 }
 
-bool BootControlChromeOS::GetPartitionDevice(const string& partition_name,
-                                             BootControlInterface::Slot slot,
-                                             string* device) const {
-  return GetPartitionDevice(partition_name, slot, false, device, nullptr);
-}
-
 bool BootControlChromeOS::IsSlotBootable(Slot slot) const {
   int partition_num = GetPartitionNumber(kChromeOSPartitionNameKernel, slot);
   if (partition_num < 0)
diff --git a/cros/boot_control_chromeos.h b/boot_control_chromeos.h
similarity index 80%
rename from cros/boot_control_chromeos.h
rename to boot_control_chromeos.h
index 0dff2c0..0209052 100644
--- a/cros/boot_control_chromeos.h
+++ b/boot_control_chromeos.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_BOOT_CONTROL_CHROMEOS_H_
-#define UPDATE_ENGINE_CROS_BOOT_CONTROL_CHROMEOS_H_
+#ifndef UPDATE_ENGINE_BOOT_CONTROL_CHROMEOS_H_
+#define UPDATE_ENGINE_BOOT_CONTROL_CHROMEOS_H_
 
 #include <memory>
 #include <string>
@@ -47,11 +47,6 @@
   BootControlInterface::Slot GetCurrentSlot() const override;
   bool GetPartitionDevice(const std::string& partition_name,
                           BootControlInterface::Slot slot,
-                          bool not_in_payload,
-                          std::string* device,
-                          bool* is_dynamic) const override;
-  bool GetPartitionDevice(const std::string& partition_name,
-                          BootControlInterface::Slot slot,
                           std::string* device) const override;
   bool IsSlotBootable(BootControlInterface::Slot slot) const override;
   bool MarkSlotUnbootable(BootControlInterface::Slot slot) override;
@@ -64,7 +59,6 @@
   friend class BootControlChromeOSTest;
   FRIEND_TEST(BootControlChromeOSTest, SysfsBlockDeviceTest);
   FRIEND_TEST(BootControlChromeOSTest, GetPartitionNumberTest);
-  FRIEND_TEST(BootControlChromeOSTest, ParseDlcPartitionNameTest);
 
   // Returns the sysfs block device for a root block device. For example,
   // SysfsBlockDevice("/dev/sda") returns "/sys/block/sda". Returns an empty
@@ -80,13 +74,6 @@
   int GetPartitionNumber(const std::string partition_name,
                          BootControlInterface::Slot slot) const;
 
-  // Extracts DLC module ID and package ID from partition name. The structure of
-  // the partition name is dlc/<dlc-id>/<dlc-package>. For example:
-  // dlc/fake-dlc/fake-package
-  bool ParseDlcPartitionName(const std::string partition_name,
-                             std::string* dlc_id,
-                             std::string* dlc_package) const;
-
   // Cached values for GetNumSlots() and GetCurrentSlot().
   BootControlInterface::Slot num_slots_{1};
   BootControlInterface::Slot current_slot_{BootControlInterface::kInvalidSlot};
@@ -101,4 +88,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_BOOT_CONTROL_CHROMEOS_H_
+#endif  // UPDATE_ENGINE_BOOT_CONTROL_CHROMEOS_H_
diff --git a/cros/boot_control_chromeos_unittest.cc b/boot_control_chromeos_unittest.cc
similarity index 74%
rename from cros/boot_control_chromeos_unittest.cc
rename to boot_control_chromeos_unittest.cc
index fc1dd1e..6a60009 100644
--- a/cros/boot_control_chromeos_unittest.cc
+++ b/boot_control_chromeos_unittest.cc
@@ -14,12 +14,10 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/boot_control_chromeos.h"
+#include "update_engine/boot_control_chromeos.h"
 
 #include <gtest/gtest.h>
 
-using std::string;
-
 namespace chromeos_update_engine {
 
 class BootControlChromeOSTest : public ::testing::Test {
@@ -69,22 +67,4 @@
   EXPECT_EQ(-1, bootctl_.GetPartitionNumber("A little panda", 0));
 }
 
-TEST_F(BootControlChromeOSTest, ParseDlcPartitionNameTest) {
-  string id, package;
-
-  EXPECT_TRUE(bootctl_.ParseDlcPartitionName("dlc/id/package", &id, &package));
-  EXPECT_EQ(id, "id");
-  EXPECT_EQ(package, "package");
-
-  EXPECT_FALSE(
-      bootctl_.ParseDlcPartitionName("dlc-foo/id/package", &id, &package));
-  EXPECT_FALSE(
-      bootctl_.ParseDlcPartitionName("dlc-foo/id/package/", &id, &package));
-  EXPECT_FALSE(bootctl_.ParseDlcPartitionName("dlc/id", &id, &package));
-  EXPECT_FALSE(bootctl_.ParseDlcPartitionName("dlc/id/", &id, &package));
-  EXPECT_FALSE(bootctl_.ParseDlcPartitionName("dlc//package", &id, &package));
-  EXPECT_FALSE(bootctl_.ParseDlcPartitionName("dlc", &id, &package));
-  EXPECT_FALSE(bootctl_.ParseDlcPartitionName("foo", &id, &package));
-}
-
 }  // namespace chromeos_update_engine
diff --git a/cros/chrome_browser_proxy_resolver.cc b/chrome_browser_proxy_resolver.cc
similarity index 94%
rename from cros/chrome_browser_proxy_resolver.cc
rename to chrome_browser_proxy_resolver.cc
index 3ea8a9b..bfb58f7 100644
--- a/cros/chrome_browser_proxy_resolver.cc
+++ b/chrome_browser_proxy_resolver.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/chrome_browser_proxy_resolver.h"
+#include "update_engine/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/cros/dbus_connection.h"
+#include "update_engine/dbus_connection.h"
 
 namespace chromeos_update_engine {
 
diff --git a/cros/chrome_browser_proxy_resolver.h b/chrome_browser_proxy_resolver.h
similarity index 91%
rename from cros/chrome_browser_proxy_resolver.h
rename to chrome_browser_proxy_resolver.h
index 76848ef..10a55fb 100644
--- a/cros/chrome_browser_proxy_resolver.h
+++ b/chrome_browser_proxy_resolver.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_CHROME_BROWSER_PROXY_RESOLVER_H_
-#define UPDATE_ENGINE_CROS_CHROME_BROWSER_PROXY_RESOLVER_H_
+#ifndef UPDATE_ENGINE_CHROME_BROWSER_PROXY_RESOLVER_H_
+#define UPDATE_ENGINE_CHROME_BROWSER_PROXY_RESOLVER_H_
 
 #include <deque>
 #include <map>
@@ -63,4 +63,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_CHROME_BROWSER_PROXY_RESOLVER_H_
+#endif  // UPDATE_ENGINE_CHROME_BROWSER_PROXY_RESOLVER_H_
diff --git a/aosp/cleanup_previous_update_action.cc b/cleanup_previous_update_action.cc
similarity index 73%
rename from aosp/cleanup_previous_update_action.cc
rename to cleanup_previous_update_action.cc
index 53cb993..88dbc57 100644
--- a/aosp/cleanup_previous_update_action.cc
+++ b/cleanup_previous_update_action.cc
@@ -13,14 +13,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //
-#include "update_engine/aosp/cleanup_previous_update_action.h"
+#include "update_engine/cleanup_previous_update_action.h"
 
 #include <chrono>  // NOLINT(build/c++11) -- for merge times
 #include <functional>
 #include <string>
 #include <type_traits>
 
-#include <android-base/chrono_utils.h>
 #include <android-base/properties.h>
 #include <base/bind.h>
 
@@ -32,7 +31,7 @@
 #include "update_engine/payload_consumer/delta_performer.h"
 
 using android::base::GetBoolProperty;
-using android::snapshot::ISnapshotManager;
+using android::snapshot::SnapshotManager;
 using android::snapshot::SnapshotMergeStats;
 using android::snapshot::UpdateState;
 using brillo::MessageLoop;
@@ -57,7 +56,7 @@
 CleanupPreviousUpdateAction::CleanupPreviousUpdateAction(
     PrefsInterface* prefs,
     BootControlInterface* boot_control,
-    android::snapshot::ISnapshotManager* snapshot,
+    android::snapshot::SnapshotManager* snapshot,
     CleanupPreviousUpdateActionDelegateInterface* delegate)
     : prefs_(prefs),
       boot_control_(boot_control),
@@ -66,30 +65,32 @@
       running_(false),
       cancel_failed_(false),
       last_percentage_(0),
-      merge_stats_(nullptr) {}
-
-CleanupPreviousUpdateAction::~CleanupPreviousUpdateAction() {
-  StopActionInternal();
-}
+      merge_stats_(SnapshotMergeStats::GetInstance(*snapshot)) {}
 
 void CleanupPreviousUpdateAction::PerformAction() {
-  StartActionInternal();
+  ResumeAction();
 }
 
 void CleanupPreviousUpdateAction::TerminateProcessing() {
-  StopActionInternal();
+  SuspendAction();
 }
 
 void CleanupPreviousUpdateAction::ResumeAction() {
+  CHECK(prefs_);
+  CHECK(boot_control_);
+
+  LOG(INFO) << "Starting/resuming CleanupPreviousUpdateAction";
+  running_ = true;
   StartActionInternal();
 }
 
 void CleanupPreviousUpdateAction::SuspendAction() {
-  StopActionInternal();
+  LOG(INFO) << "Stopping/suspending CleanupPreviousUpdateAction";
+  running_ = false;
 }
 
 void CleanupPreviousUpdateAction::ActionCompleted(ErrorCode error_code) {
-  StopActionInternal();
+  running_ = false;
   ReportMergeStats();
   metadata_device_ = nullptr;
 }
@@ -102,52 +103,7 @@
   return "CleanupPreviousUpdateAction";
 }
 
-// This function is called at the beginning of all delayed functions. By
-// resetting |scheduled_task_|, the delayed function acknowledges that the task
-// has already been executed, therefore there's no need to cancel it in the
-// future. This avoids StopActionInternal() from resetting task IDs in an
-// unexpected way because task IDs could be reused.
-void CleanupPreviousUpdateAction::AcknowledgeTaskExecuted() {
-  if (scheduled_task_ != MessageLoop::kTaskIdNull) {
-    LOG(INFO) << "Executing task " << scheduled_task_;
-  }
-  scheduled_task_ = MessageLoop::kTaskIdNull;
-}
-
-// Check that scheduled_task_ is a valid task ID. Otherwise, terminate the
-// action.
-void CleanupPreviousUpdateAction::CheckTaskScheduled(std::string_view name) {
-  if (scheduled_task_ == MessageLoop::kTaskIdNull) {
-    LOG(ERROR) << "Unable to schedule " << name;
-    processor_->ActionComplete(this, ErrorCode::kError);
-  } else {
-    LOG(INFO) << "CleanupPreviousUpdateAction scheduled task ID "
-              << scheduled_task_ << " for " << name;
-  }
-}
-
-void CleanupPreviousUpdateAction::StopActionInternal() {
-  LOG(INFO) << "Stopping/suspending/completing CleanupPreviousUpdateAction";
-  running_ = false;
-
-  if (scheduled_task_ != MessageLoop::kTaskIdNull) {
-    if (MessageLoop::current()->CancelTask(scheduled_task_)) {
-      LOG(INFO) << "CleanupPreviousUpdateAction cancelled pending task ID "
-                << scheduled_task_;
-    } else {
-      LOG(ERROR) << "CleanupPreviousUpdateAction unable to cancel task ID "
-                 << scheduled_task_;
-    }
-  }
-  scheduled_task_ = MessageLoop::kTaskIdNull;
-}
-
 void CleanupPreviousUpdateAction::StartActionInternal() {
-  CHECK(prefs_);
-  CHECK(boot_control_);
-
-  LOG(INFO) << "Starting/resuming CleanupPreviousUpdateAction";
-  running_ = true;
   // Do nothing on non-VAB device.
   if (!boot_control_->GetDynamicPartitionControl()
            ->GetVirtualAbFeatureFlag()
@@ -155,25 +111,21 @@
     processor_->ActionComplete(this, ErrorCode::kSuccess);
     return;
   }
-  // SnapshotManager must be available on VAB devices.
-  CHECK(snapshot_ != nullptr);
-  merge_stats_ = snapshot_->GetSnapshotMergeStatsInstance();
-  CHECK(merge_stats_ != nullptr);
+  // SnapshotManager is only available on VAB devices.
+  CHECK(snapshot_);
   WaitBootCompletedOrSchedule();
 }
 
 void CleanupPreviousUpdateAction::ScheduleWaitBootCompleted() {
   TEST_AND_RETURN(running_);
-  scheduled_task_ = MessageLoop::current()->PostDelayedTask(
+  MessageLoop::current()->PostDelayedTask(
       FROM_HERE,
       base::Bind(&CleanupPreviousUpdateAction::WaitBootCompletedOrSchedule,
                  base::Unretained(this)),
       kCheckBootCompletedInterval);
-  CheckTaskScheduled("WaitBootCompleted");
 }
 
 void CleanupPreviousUpdateAction::WaitBootCompletedOrSchedule() {
-  AcknowledgeTaskExecuted();
   TEST_AND_RETURN(running_);
   if (!kIsRecovery &&
       !android::base::GetBoolProperty(kBootCompletedProp, false)) {
@@ -182,32 +134,25 @@
     return;
   }
 
-  auto boot_time = std::chrono::duration_cast<std::chrono::milliseconds>(
-      android::base::boot_clock::now().time_since_epoch());
-  merge_stats_->set_boot_complete_time_ms(boot_time.count());
-
   LOG(INFO) << "Boot completed, waiting on markBootSuccessful()";
   CheckSlotMarkedSuccessfulOrSchedule();
 }
 
 void CleanupPreviousUpdateAction::ScheduleWaitMarkBootSuccessful() {
   TEST_AND_RETURN(running_);
-  scheduled_task_ = MessageLoop::current()->PostDelayedTask(
+  MessageLoop::current()->PostDelayedTask(
       FROM_HERE,
       base::Bind(
           &CleanupPreviousUpdateAction::CheckSlotMarkedSuccessfulOrSchedule,
           base::Unretained(this)),
       kCheckSlotMarkedSuccessfulInterval);
-  CheckTaskScheduled("WaitMarkBootSuccessful");
 }
 
 void CleanupPreviousUpdateAction::CheckSlotMarkedSuccessfulOrSchedule() {
-  AcknowledgeTaskExecuted();
   TEST_AND_RETURN(running_);
   if (!kIsRecovery &&
       !boot_control_->IsSlotMarkedSuccessful(boot_control_->GetCurrentSlot())) {
     ScheduleWaitMarkBootSuccessful();
-    return;
   }
 
   if (metadata_device_ == nullptr) {
@@ -265,22 +210,19 @@
 
 void CleanupPreviousUpdateAction::ScheduleWaitForMerge() {
   TEST_AND_RETURN(running_);
-  scheduled_task_ = MessageLoop::current()->PostDelayedTask(
+  MessageLoop::current()->PostDelayedTask(
       FROM_HERE,
       base::Bind(&CleanupPreviousUpdateAction::WaitForMergeOrSchedule,
                  base::Unretained(this)),
       kWaitForMergeInterval);
-  CheckTaskScheduled("WaitForMerge");
 }
 
 void CleanupPreviousUpdateAction::WaitForMergeOrSchedule() {
-  AcknowledgeTaskExecuted();
   TEST_AND_RETURN(running_);
-  auto update_uses_compression = snapshot_->UpdateUsesCompression();
   auto state = snapshot_->ProcessUpdateState(
       std::bind(&CleanupPreviousUpdateAction::OnMergePercentageUpdate, this),
       std::bind(&CleanupPreviousUpdateAction::BeforeCancel, this));
-  merge_stats_->set_state(state, update_uses_compression);
+  merge_stats_->set_state(state);
 
   switch (state) {
     case UpdateState::None: {
@@ -324,7 +266,6 @@
 
     case UpdateState::MergeFailed: {
       LOG(ERROR) << "Merge failed. Device may be corrupted.";
-      merge_stats_->set_merge_failure_code(snapshot_->ReadMergeFailureCode());
       processor_->ActionComplete(this, ErrorCode::kDeviceCorrupted);
       return;
     }
@@ -402,29 +343,16 @@
     return;
   }
 
-  snapshot_->UpdateCowStats(merge_stats_);
-
-  auto merge_start_time = std::chrono::duration_cast<std::chrono::milliseconds>(
-      android::base::boot_clock::now().time_since_epoch());
-  merge_stats_->set_boot_complete_to_merge_start_time_ms(
-      merge_start_time.count() - merge_stats_->boot_complete_time_ms());
-
-  auto source_build_fingerprint = snapshot_->ReadSourceBuildFingerprint();
-  merge_stats_->set_source_build_fingerprint(source_build_fingerprint);
-
-  if (!merge_stats_->WriteState()) {
-    LOG(ERROR) << "Failed to write merge stats; record may be unreliable if "
-                  "merge is interrupted.";
-  }
-
-  if (snapshot_->InitiateMerge()) {
+  uint64_t cow_file_size;
+  if (snapshot_->InitiateMerge(&cow_file_size)) {
+    merge_stats_->set_cow_file_size(cow_file_size);
     WaitForMergeOrSchedule();
     return;
   }
 
   LOG(WARNING) << "InitiateMerge failed.";
   auto state = snapshot_->GetUpdateState();
-  merge_stats_->set_state(state, snapshot_->UpdateUsesCompression());
+  merge_stats_->set_state(state);
   if (state == UpdateState::Unverified) {
     // We are stuck at unverified state. This can happen if the update has
     // been applied, but it has not even been attempted yet (in libsnapshot,
@@ -477,16 +405,6 @@
   bool vab_retrofit = boot_control_->GetDynamicPartitionControl()
                           ->GetVirtualAbFeatureFlag()
                           .IsRetrofit();
-  bool vab_compression_enabled = boot_control_->GetDynamicPartitionControl()
-                                     ->GetVirtualAbCompressionFeatureFlag()
-                                     .IsEnabled();
-  // The snapshot has been merged, so we can no longer call
-  // DynamicPartitionControlInterface::UpdateUsesSnapshotCompression.
-  // However, we have saved the flag in the snapshot report.
-  bool vab_compression_used = report.compression_enabled();
-
-  auto target_build_fingerprint =
-      android::base::GetProperty("ro.build.fingerprint", "");
 
   LOG(INFO) << "Reporting merge stats: "
             << android::snapshot::UpdateState_Name(report.state()) << " in "
@@ -498,16 +416,7 @@
                              static_cast<int64_t>(passed_ms.count()),
                              static_cast<int32_t>(report.resume_count()),
                              vab_retrofit,
-                             static_cast<int64_t>(report.cow_file_size()),
-                             vab_compression_enabled,
-                             vab_compression_used,
-                             report.total_cow_size_bytes(),
-                             report.estimated_cow_size_bytes(),
-                             report.boot_complete_time_ms(),
-                             report.boot_complete_to_merge_start_time_ms(),
-                             static_cast<int32_t>(report.merge_failure_code()),
-                             report.source_build_fingerprint().c_str(),
-                             target_build_fingerprint.c_str());
+                             static_cast<int64_t>(report.cow_file_size()));
 #endif
 }
 
diff --git a/aosp/cleanup_previous_update_action.h b/cleanup_previous_update_action.h
similarity index 81%
rename from aosp/cleanup_previous_update_action.h
rename to cleanup_previous_update_action.h
index b93c557..91e08b0 100644
--- a/aosp/cleanup_previous_update_action.h
+++ b/cleanup_previous_update_action.h
@@ -14,13 +14,12 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_AOSP_CLEANUP_PREVIOUS_UPDATE_ACTION_H_
-#define UPDATE_ENGINE_AOSP_CLEANUP_PREVIOUS_UPDATE_ACTION_H_
+#ifndef UPDATE_ENGINE_CLEANUP_PREVIOUS_UPDATE_ACTION_H_
+#define UPDATE_ENGINE_CLEANUP_PREVIOUS_UPDATE_ACTION_H_
 
 #include <chrono>  // NOLINT(build/c++11) -- for merge times
 #include <memory>
 #include <string>
-#include <string_view>
 
 #include <brillo/message_loops/message_loop.h>
 #include <libsnapshot/snapshot.h>
@@ -50,9 +49,8 @@
   CleanupPreviousUpdateAction(
       PrefsInterface* prefs,
       BootControlInterface* boot_control,
-      android::snapshot::ISnapshotManager* snapshot,
+      android::snapshot::SnapshotManager* snapshot,
       CleanupPreviousUpdateActionDelegateInterface* delegate);
-  ~CleanupPreviousUpdateAction();
 
   void PerformAction() override;
   void SuspendAction() override;
@@ -69,20 +67,14 @@
  private:
   PrefsInterface* prefs_;
   BootControlInterface* boot_control_;
-  android::snapshot::ISnapshotManager* snapshot_;
+  android::snapshot::SnapshotManager* snapshot_;
   CleanupPreviousUpdateActionDelegateInterface* delegate_;
   std::unique_ptr<android::snapshot::AutoDevice> metadata_device_;
   bool running_{false};
   bool cancel_failed_{false};
   unsigned int last_percentage_{0};
-  android::snapshot::ISnapshotMergeStats* merge_stats_;
-  brillo::MessageLoop::TaskId scheduled_task_{brillo::MessageLoop::kTaskIdNull};
+  android::snapshot::SnapshotMergeStats* merge_stats_;
 
-  // Helpers for task management.
-  void AcknowledgeTaskExecuted();
-  void CheckTaskScheduled(std::string_view name);
-
-  void StopActionInternal();
   void StartActionInternal();
   void ScheduleWaitBootCompleted();
   void WaitBootCompletedOrSchedule();
@@ -100,4 +92,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_AOSP_CLEANUP_PREVIOUS_UPDATE_ACTION_H_
+#endif  // UPDATE_ENGINE_CLEANUP_PREVIOUS_UPDATE_ACTION_H_
diff --git a/client-headers/BUILD.gn b/client-headers/BUILD.gn
deleted file mode 100644
index 8c1a17e..0000000
--- a/client-headers/BUILD.gn
+++ /dev/null
@@ -1,31 +0,0 @@
-#
-# Copyright (C) 2019 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import("//common-mk/generate-dbus-proxies.gni")
-
-group("all") {
-  deps = [ ":libupdate_engine-client-headers" ]
-}
-
-# update_engine client library generated headers. Used by other daemons and
-# by the update_engine_client console program to interact with update_engine.
-generate_dbus_proxies("libupdate_engine-client-headers") {
-  sources = [ "../dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml" ]
-  dbus_service_config = "../dbus_bindings/dbus-service-config.json"
-  mock_output_file = "include/update_engine/dbus-proxy-mocks.h"
-  proxy_output_file = "include/update_engine/dbus-proxies.h"
-  proxy_path_in_mocks = "update_engine/dbus-proxies.h"
-}
diff --git a/client_library/client.cc b/client_library/client.cc
new file mode 100644
index 0000000..b05df90
--- /dev/null
+++ b/client_library/client.cc
@@ -0,0 +1,46 @@
+//
+// 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/client_library/include/update_engine/client.h"
+
+#include <memory>
+
+#if USE_BINDER
+#include "update_engine/client_library/client_binder.h"
+#else  // !USE_BINDER
+#include "update_engine/client_library/client_dbus.h"
+#endif  // USE_BINDER
+
+using std::unique_ptr;
+
+namespace update_engine {
+
+unique_ptr<UpdateEngineClient> UpdateEngineClient::CreateInstance() {
+#if USE_BINDER
+  auto update_engine_client_impl = new internal::BinderUpdateEngineClient{};
+#else   // !USE_BINDER
+  auto update_engine_client_impl = new internal::DBusUpdateEngineClient{};
+#endif  // USE_BINDER
+  auto ret = unique_ptr<UpdateEngineClient>{update_engine_client_impl};
+
+  if (!update_engine_client_impl->Init()) {
+    ret.reset();
+  }
+
+  return ret;
+}
+
+}  // namespace update_engine
diff --git a/client_library/client_binder.cc b/client_library/client_binder.cc
new file mode 100644
index 0000000..588bc64
--- /dev/null
+++ b/client_library/client_binder.cc
@@ -0,0 +1,264 @@
+//
+// 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/client_library/client_binder.h"
+
+#include <binder/IServiceManager.h>
+
+#include <base/message_loop/message_loop.h>
+#include <utils/String8.h>
+
+#include "update_engine/common_service.h"
+#include "update_engine/parcelable_update_engine_status.h"
+#include "update_engine/update_status_utils.h"
+
+using android::getService;
+using android::OK;
+using android::String16;
+using android::String8;
+using android::binder::Status;
+using android::brillo::ParcelableUpdateEngineStatus;
+using chromeos_update_engine::StringToUpdateStatus;
+using std::string;
+using update_engine::UpdateAttemptFlags;
+
+namespace update_engine {
+namespace internal {
+
+bool BinderUpdateEngineClient::Init() {
+  if (!binder_watcher_.Init())
+    return false;
+
+  return getService(String16{"android.brillo.UpdateEngineService"},
+                    &service_) == OK;
+}
+
+bool BinderUpdateEngineClient::AttemptUpdate(const string& in_app_version,
+                                             const string& in_omaha_url,
+                                             bool at_user_request) {
+  bool started;
+  return service_
+      ->AttemptUpdate(
+          String16{in_app_version.c_str()},
+          String16{in_omaha_url.c_str()},
+          at_user_request ? 0 : UpdateAttemptFlags::kFlagNonInteractive,
+          &started)
+      .isOk();
+}
+
+bool BinderUpdateEngineClient::AttemptInstall(
+    const string& omaha_url, const std::vector<string>& dlc_module_ids) {
+  return false;
+}
+
+bool BinderUpdateEngineClient::GetStatus(int64_t* out_last_checked_time,
+                                         double* out_progress,
+                                         UpdateStatus* out_update_status,
+                                         string* out_new_version,
+                                         int64_t* out_new_size) const {
+  ParcelableUpdateEngineStatus status;
+
+  if (!service_->GetStatus(&status).isOk())
+    return false;
+
+  *out_last_checked_time = status.last_checked_time_;
+  *out_progress = status.progress_;
+  StringToUpdateStatus(String8{status.current_operation_}.string(),
+                       out_update_status);
+  *out_new_version = String8{status.new_version_}.string();
+  *out_new_size = status.new_size_;
+  return true;
+}
+
+bool BinderUpdateEngineClient::SetCohortHint(const string& in_cohort_hint) {
+  return service_->SetCohortHint(String16{in_cohort_hint.c_str()}).isOk();
+}
+
+bool BinderUpdateEngineClient::GetCohortHint(string* out_cohort_hint) const {
+  String16 out_as_string16;
+
+  if (!service_->GetCohortHint(&out_as_string16).isOk())
+    return false;
+
+  *out_cohort_hint = String8{out_as_string16}.string();
+  return true;
+}
+
+bool BinderUpdateEngineClient::SetUpdateOverCellularPermission(bool allowed) {
+  return service_->SetUpdateOverCellularPermission(allowed).isOk();
+}
+
+bool BinderUpdateEngineClient::GetUpdateOverCellularPermission(
+    bool* allowed) const {
+  return service_->GetUpdateOverCellularPermission(allowed).isOk();
+}
+
+bool BinderUpdateEngineClient::SetP2PUpdatePermission(bool enabled) {
+  return service_->SetP2PUpdatePermission(enabled).isOk();
+}
+
+bool BinderUpdateEngineClient::GetP2PUpdatePermission(bool* enabled) const {
+  return service_->GetP2PUpdatePermission(enabled).isOk();
+}
+
+bool BinderUpdateEngineClient::Rollback(bool powerwash) {
+  return service_->AttemptRollback(powerwash).isOk();
+}
+
+bool BinderUpdateEngineClient::GetRollbackPartition(
+    string* rollback_partition) const {
+  String16 out_as_string16;
+
+  if (!service_->GetRollbackPartition(&out_as_string16).isOk())
+    return false;
+
+  *rollback_partition = String8{out_as_string16}.string();
+  return true;
+}
+
+bool BinderUpdateEngineClient::GetPrevVersion(string* prev_version) const {
+  String16 out_as_string16;
+
+  if (!service_->GetPrevVersion(&out_as_string16).isOk())
+    return false;
+
+  *prev_version = String8{out_as_string16}.string();
+  return true;
+}
+
+void BinderUpdateEngineClient::RebootIfNeeded() {
+  if (!service_->RebootIfNeeded().isOk()) {
+    // Reboot error code doesn't necessarily mean that a reboot
+    // failed. For example, D-Bus may be shutdown before we receive the
+    // result.
+    LOG(INFO) << "RebootIfNeeded() failure ignored.";
+  }
+}
+
+bool BinderUpdateEngineClient::ResetStatus() {
+  return service_->ResetStatus().isOk();
+}
+
+Status BinderUpdateEngineClient::StatusUpdateCallback::HandleStatusUpdate(
+    const ParcelableUpdateEngineStatus& status) {
+  UpdateStatus update_status;
+
+  StringToUpdateStatus(String8{status.current_operation_}.string(),
+                       &update_status);
+
+  for (auto& handler : client_->handlers_) {
+    handler->HandleStatusUpdate(status.last_checked_time_,
+                                status.progress_,
+                                update_status,
+                                String8{status.new_version_}.string(),
+                                status.new_size_);
+  }
+
+  return Status::ok();
+}
+
+bool BinderUpdateEngineClient::RegisterStatusUpdateHandler(
+    StatusUpdateHandler* handler) {
+  if (!status_callback_.get()) {
+    status_callback_ = new BinderUpdateEngineClient::StatusUpdateCallback(this);
+    if (!service_->RegisterStatusCallback(status_callback_).isOk()) {
+      return false;
+    }
+  }
+
+  handlers_.push_back(handler);
+
+  int64_t last_checked_time;
+  double progress;
+  UpdateStatus update_status;
+  string new_version;
+  int64_t new_size;
+
+  if (!GetStatus(&last_checked_time,
+                 &progress,
+                 &update_status,
+                 &new_version,
+                 &new_size)) {
+    handler->IPCError("Could not get status from binder service");
+  }
+
+  handler->HandleStatusUpdate(
+      last_checked_time, progress, update_status, new_version, new_size);
+
+  return true;
+}
+
+bool BinderUpdateEngineClient::UnregisterStatusUpdateHandler(
+    StatusUpdateHandler* handler) {
+  auto it = std::find(handlers_.begin(), handlers_.end(), handler);
+  if (it != handlers_.end()) {
+    handlers_.erase(it);
+    return true;
+  }
+
+  return false;
+}
+
+bool BinderUpdateEngineClient::SetTargetChannel(const string& in_target_channel,
+                                                bool allow_powerwash) {
+  return service_
+      ->SetChannel(String16{in_target_channel.c_str()}, allow_powerwash)
+      .isOk();
+}
+
+bool BinderUpdateEngineClient::GetTargetChannel(string* out_channel) const {
+  String16 out_as_string16;
+
+  if (!service_->GetChannel(false, &out_as_string16).isOk())
+    return false;
+
+  *out_channel = String8{out_as_string16}.string();
+  return true;
+}
+
+bool BinderUpdateEngineClient::GetChannel(string* out_channel) const {
+  String16 out_as_string16;
+
+  if (!service_->GetChannel(true, &out_as_string16).isOk())
+    return false;
+
+  *out_channel = String8{out_as_string16}.string();
+  return true;
+}
+
+bool BinderUpdateEngineClient::GetLastAttemptError(
+    int32_t* last_attempt_error) const {
+  int out_as_int;
+
+  if (!service_->GetLastAttemptError(&out_as_int).isOk())
+    return false;
+
+  *last_attempt_error = out_as_int;
+  return true;
+}
+
+bool BinderUpdateEngineClient::GetEolStatus(int32_t* eol_status) const {
+  int out_as_int;
+
+  if (!service_->GetEolStatus(&out_as_int).isOk())
+    return false;
+
+  *eol_status = out_as_int;
+  return true;
+}
+
+}  // namespace internal
+}  // namespace update_engine
diff --git a/client_library/client_binder.h b/client_library/client_binder.h
new file mode 100644
index 0000000..f3e4102
--- /dev/null
+++ b/client_library/client_binder.h
@@ -0,0 +1,117 @@
+//
+// 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_CLIENT_LIBRARY_CLIENT_BINDER_H_
+#define UPDATE_ENGINE_CLIENT_LIBRARY_CLIENT_BINDER_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+
+#include <brillo/binder_watcher.h>
+
+#include "android/brillo/BnUpdateEngineStatusCallback.h"
+#include "android/brillo/IUpdateEngine.h"
+
+#include "update_engine/client_library/include/update_engine/client.h"
+
+namespace update_engine {
+namespace internal {
+
+class BinderUpdateEngineClient : public UpdateEngineClient {
+ public:
+  BinderUpdateEngineClient() = default;
+  bool Init();
+
+  virtual ~BinderUpdateEngineClient() = default;
+
+  bool AttemptUpdate(const std::string& app_version,
+                     const std::string& omaha_url,
+                     bool at_user_request) override;
+
+  bool AttemptInstall(const std::string& omaha_url,
+                      const std::vector<std::string>& dlc_module_ids) override;
+
+  bool GetStatus(int64_t* out_last_checked_time,
+                 double* out_progress,
+                 UpdateStatus* out_update_status,
+                 std::string* out_new_version,
+                 int64_t* out_new_size) const override;
+
+  bool SetCohortHint(const std::string& in_cohort_hint) override;
+  bool GetCohortHint(std::string* out_cohort_hint) const override;
+
+  bool SetUpdateOverCellularPermission(bool allowed) override;
+  bool GetUpdateOverCellularPermission(bool* allowed) const override;
+
+  bool SetP2PUpdatePermission(bool enabled) override;
+  bool GetP2PUpdatePermission(bool* enabled) const override;
+
+  bool Rollback(bool powerwash) override;
+
+  bool GetRollbackPartition(std::string* rollback_partition) const override;
+
+  void RebootIfNeeded() override;
+
+  bool GetPrevVersion(std::string* prev_version) const override;
+
+  bool ResetStatus() override;
+
+  bool SetTargetChannel(const std::string& target_channel,
+                        bool allow_powerwash) override;
+
+  bool GetTargetChannel(std::string* out_channel) const override;
+
+  bool GetChannel(std::string* out_channel) const override;
+
+  bool RegisterStatusUpdateHandler(StatusUpdateHandler* handler) override;
+  bool UnregisterStatusUpdateHandler(StatusUpdateHandler* handler) override;
+
+  bool GetLastAttemptError(int32_t* last_attempt_error) const override;
+
+  bool GetEolStatus(int32_t* eol_status) const override;
+
+ private:
+  class StatusUpdateCallback
+      : public android::brillo::BnUpdateEngineStatusCallback {
+   public:
+    explicit StatusUpdateCallback(BinderUpdateEngineClient* client)
+        : client_(client) {}
+
+    android::binder::Status HandleStatusUpdate(
+        const android::brillo::ParcelableUpdateEngineStatus& status) override;
+
+   private:
+    BinderUpdateEngineClient* client_;
+  };
+
+  android::sp<android::brillo::IUpdateEngine> service_;
+  android::sp<android::brillo::IUpdateEngineStatusCallback> status_callback_;
+  std::vector<update_engine::StatusUpdateHandler*> handlers_;
+  brillo::BinderWatcher binder_watcher_;
+
+  DISALLOW_COPY_AND_ASSIGN(BinderUpdateEngineClient);
+};  // class BinderUpdateEngineClient
+
+}  // namespace internal
+}  // namespace update_engine
+
+#endif  // UPDATE_ENGINE_CLIENT_LIBRARY_CLIENT_BINDER_H_
diff --git a/client_library/client_dbus.cc b/client_library/client_dbus.cc
index 30ad78c..7ca6307 100644
--- a/client_library/client_dbus.cc
+++ b/client_library/client_dbus.cc
@@ -16,51 +16,23 @@
 
 #include "update_engine/client_library/client_dbus.h"
 
-#include <base/message_loop/message_loop_current.h>
-
-#include <memory>
+#include <base/message_loop/message_loop.h>
 
 #include <dbus/bus.h>
 #include <update_engine/dbus-constants.h>
+#include <update_engine/proto_bindings/update_engine.pb.h>
 
 #include "update_engine/update_status_utils.h"
 
+using chromeos_update_engine::StringToUpdateStatus;
 using dbus::Bus;
 using org::chromium::UpdateEngineInterfaceProxy;
 using std::string;
-using std::unique_ptr;
 using std::vector;
 
 namespace update_engine {
-
-unique_ptr<UpdateEngineClient> UpdateEngineClient::CreateInstance() {
-  auto ret = std::make_unique<internal::DBusUpdateEngineClient>();
-  if (!ret->Init()) {
-    ret.reset();
-  }
-  return ret;
-}
-
 namespace internal {
 
-namespace {
-// This converts the status from Protobuf |StatusResult| to The internal
-// |UpdateEngineStatus| struct.
-void ConvertToUpdateEngineStatus(const StatusResult& status,
-                                 UpdateEngineStatus* out_status) {
-  out_status->last_checked_time = status.last_checked_time();
-  out_status->progress = status.progress();
-  out_status->new_version = status.new_version();
-  out_status->new_size_bytes = status.new_size();
-  out_status->status = static_cast<UpdateStatus>(status.current_operation());
-  out_status->is_enterprise_rollback = status.is_enterprise_rollback();
-  out_status->is_install = status.is_install();
-  out_status->eol_date = status.eol_date();
-  out_status->will_powerwash_after_reboot =
-      status.will_powerwash_after_reboot();
-}
-}  // namespace
-
 bool DBusUpdateEngineClient::Init() {
   Bus::Options options;
   options.bus_type = Bus::SYSTEM;
@@ -85,24 +57,41 @@
       nullptr);
 }
 
-bool DBusUpdateEngineClient::AttemptInstall(const string& omaha_url,
-                                            const vector<string>& dlc_ids) {
-  return proxy_->AttemptInstall(omaha_url, dlc_ids, nullptr);
+bool DBusUpdateEngineClient::AttemptInstall(
+    const string& omaha_url, const vector<string>& dlc_module_ids) {
+  // Convert parameters into protobuf.
+  chromeos_update_engine::DlcParameters dlc_parameters;
+  dlc_parameters.set_omaha_url(omaha_url);
+  for (const auto& dlc_module_id : dlc_module_ids) {
+    chromeos_update_engine::DlcInfo* dlc_info = dlc_parameters.add_dlc_infos();
+    dlc_info->set_dlc_id(dlc_module_id);
+  }
+  string dlc_request;
+  if (dlc_parameters.SerializeToString(&dlc_request)) {
+    return proxy_->AttemptInstall(dlc_request, nullptr /* brillo::ErrorPtr* */);
+  } else {
+    LOG(ERROR) << "Fail to serialize a protobuf to a string.";
+    return false;
+  }
 }
 
-bool DBusUpdateEngineClient::SetDlcActiveValue(bool is_active,
-                                               const std::string& dlc_id) {
-  return proxy_->SetDlcActiveValue(is_active, dlc_id, /*error=*/nullptr);
-}
-
-bool DBusUpdateEngineClient::GetStatus(UpdateEngineStatus* out_status) const {
-  StatusResult status;
-  if (!proxy_->GetStatusAdvanced(&status, nullptr)) {
+bool DBusUpdateEngineClient::GetStatus(int64_t* out_last_checked_time,
+                                       double* out_progress,
+                                       UpdateStatus* out_update_status,
+                                       string* out_new_version,
+                                       int64_t* out_new_size) const {
+  string status_as_string;
+  const bool success = proxy_->GetStatus(out_last_checked_time,
+                                         out_progress,
+                                         &status_as_string,
+                                         out_new_version,
+                                         out_new_size,
+                                         nullptr);
+  if (!success) {
     return false;
   }
 
-  ConvertToUpdateEngineStatus(status, out_status);
-  return true;
+  return StringToUpdateStatus(status_as_string, out_update_status);
 }
 
 bool DBusUpdateEngineClient::SetCohortHint(const string& cohort_hint) {
@@ -171,25 +160,40 @@
 
 void DBusUpdateEngineClient::StatusUpdateHandlersRegistered(
     StatusUpdateHandler* handler) const {
-  UpdateEngineStatus status;
-  if (!GetStatus(&status)) {
+  int64_t last_checked_time;
+  double progress;
+  UpdateStatus update_status;
+  string new_version;
+  int64_t new_size;
+
+  if (!GetStatus(&last_checked_time,
+                 &progress,
+                 &update_status,
+                 &new_version,
+                 &new_size)) {
     handler->IPCError("Could not query current status");
     return;
   }
 
   std::vector<update_engine::StatusUpdateHandler*> just_handler = {handler};
   for (auto h : handler ? just_handler : handlers_) {
-    h->HandleStatusUpdate(status);
+    h->HandleStatusUpdate(
+        last_checked_time, progress, update_status, new_version, new_size);
   }
 }
 
 void DBusUpdateEngineClient::RunStatusUpdateHandlers(
-    const StatusResult& status) {
-  UpdateEngineStatus ue_status;
-  ConvertToUpdateEngineStatus(status, &ue_status);
+    int64_t last_checked_time,
+    double progress,
+    const string& current_operation,
+    const string& new_version,
+    int64_t new_size) {
+  UpdateStatus status;
+  StringToUpdateStatus(current_operation, &status);
 
   for (auto handler : handlers_) {
-    handler->HandleStatusUpdate(ue_status);
+    handler->HandleStatusUpdate(
+        last_checked_time, progress, status, new_version, new_size);
   }
 }
 
@@ -206,7 +210,7 @@
 
 bool DBusUpdateEngineClient::RegisterStatusUpdateHandler(
     StatusUpdateHandler* handler) {
-  if (!base::MessageLoopCurrent::IsSet()) {
+  if (!base::MessageLoopForIO::current()) {
     LOG(FATAL) << "Cannot get UpdateEngineClient outside of message loop.";
     return false;
   }
@@ -218,7 +222,7 @@
     return true;
   }
 
-  proxy_->RegisterStatusUpdateAdvancedSignalHandler(
+  proxy_->RegisterStatusUpdateSignalHandler(
       base::Bind(&DBusUpdateEngineClient::RunStatusUpdateHandlers,
                  base::Unretained(this)),
       base::Bind(&DBusUpdateEngineClient::DBusStatusHandlersRegistered,
@@ -251,5 +255,9 @@
   return proxy_->GetLastAttemptError(last_attempt_error, nullptr);
 }
 
+bool DBusUpdateEngineClient::GetEolStatus(int32_t* eol_status) const {
+  return proxy_->GetEolStatus(eol_status, nullptr);
+}
+
 }  // namespace internal
 }  // namespace update_engine
diff --git a/client_library/client_dbus.h b/client_library/client_dbus.h
index f19555f..a186d45 100644
--- a/client_library/client_dbus.h
+++ b/client_library/client_dbus.h
@@ -23,7 +23,6 @@
 #include <vector>
 
 #include <base/macros.h>
-#include <update_engine/proto_bindings/update_engine.pb.h>
 
 #include "update_engine/client_library/include/update_engine/client.h"
 #include "update_engine/dbus-proxies.h"
@@ -43,11 +42,13 @@
                      bool at_user_request) override;
 
   bool AttemptInstall(const std::string& omaha_url,
-                      const std::vector<std::string>& dlc_ids) override;
+                      const std::vector<std::string>& dlc_module_ids) override;
 
-  bool SetDlcActiveValue(bool is_active, const std::string& dlc_id) override;
-
-  bool GetStatus(UpdateEngineStatus* out_status) const override;
+  bool GetStatus(int64_t* out_last_checked_time,
+                 double* out_progress,
+                 UpdateStatus* out_update_status,
+                 std::string* out_new_version,
+                 int64_t* out_new_size) const override;
 
   bool SetCohortHint(const std::string& cohort_hint) override;
   bool GetCohortHint(std::string* cohort_hint) const override;
@@ -80,6 +81,8 @@
 
   bool GetLastAttemptError(int32_t* last_attempt_error) const override;
 
+  bool GetEolStatus(int32_t* eol_status) const override;
+
  private:
   void DBusStatusHandlersRegistered(const std::string& interface,
                                     const std::string& signal_name,
@@ -90,7 +93,11 @@
   // registered handlers receive the event.
   void StatusUpdateHandlersRegistered(StatusUpdateHandler* handler) const;
 
-  void RunStatusUpdateHandlers(const StatusResult& status);
+  void RunStatusUpdateHandlers(int64_t last_checked_time,
+                               double progress,
+                               const std::string& current_operation,
+                               const std::string& new_version,
+                               int64_t new_size);
 
   std::unique_ptr<org::chromium::UpdateEngineInterfaceProxy> proxy_;
   std::vector<update_engine::StatusUpdateHandler*> handlers_;
diff --git a/client_library/include/update_engine/client.h b/client_library/include/update_engine/client.h
index f734733..1bc6111 100644
--- a/client_library/include/update_engine/client.h
+++ b/client_library/include/update_engine/client.h
@@ -54,18 +54,31 @@
   //     empty indicates update_engine should use its default value. Note that
   //     update_engine will ignore this parameter in production mode to avoid
   //     pulling untrusted updates.
-  // |dlc_ids|
+  // |dlc_module_ids|
   //     A list of DLC module IDs.
-  virtual bool AttemptInstall(const std::string& omaha_url,
-                              const std::vector<std::string>& dlc_ids) = 0;
+  virtual bool AttemptInstall(
+      const std::string& omaha_url,
+      const std::vector<std::string>& dlc_module_ids) = 0;
 
-  // Same as above but return the entire struct instead.
-  virtual bool GetStatus(UpdateEngineStatus* out_status) const = 0;
-
-  // Sets the DLC as active or inactive. When set to active, the ping metadata
-  // for the DLC is updated accordingly. When set to inactive, the metadata
-  // for the DLC is deleted.
-  virtual bool SetDlcActiveValue(bool is_active, const std::string& dlc_id) = 0;
+  // Returns the current status of the Update Engine.
+  //
+  // |out_last_checked_time|
+  //     the last time the update engine checked for an update in seconds since
+  //     the epoc.
+  // |out_progress|
+  //     when downloading an update, this is calculated as
+  //     (number of bytes received) / (total bytes).
+  // |out_update_status|
+  //     See update_status.h.
+  // |out_new_version|
+  //     string version of the new system image.
+  // |out_new_size|
+  //     number of bytes in the new system image.
+  virtual bool GetStatus(int64_t* out_last_checked_time,
+                         double* out_progress,
+                         UpdateStatus* out_update_status,
+                         std::string* out_new_version,
+                         int64_t* out_new_size) const = 0;
 
   // Getter and setter for the cohort hint.
   virtual bool SetCohortHint(const std::string& cohort_hint) = 0;
@@ -119,6 +132,9 @@
   // Get the last UpdateAttempt error code.
   virtual bool GetLastAttemptError(int32_t* last_attempt_error) const = 0;
 
+  // Get the current end-of-life status code. See EolStatus enum for details.
+  virtual bool GetEolStatus(int32_t* eol_status) const = 0;
+
  protected:
   // Use CreateInstance().
   UpdateEngineClient() = default;
diff --git a/client_library/include/update_engine/status_update_handler.h b/client_library/include/update_engine/status_update_handler.h
index 238f6bd..d2fad34 100644
--- a/client_library/include/update_engine/status_update_handler.h
+++ b/client_library/include/update_engine/status_update_handler.h
@@ -14,9 +14,7 @@
 // limitations under the License.
 //
 
-// NOLINTNEXTLINE(whitespace/line_length)
 #ifndef UPDATE_ENGINE_CLIENT_LIBRARY_INCLUDE_UPDATE_ENGINE_STATUS_UPDATE_HANDLER_H_
-// NOLINTNEXTLINE(whitespace/line_length)
 #define UPDATE_ENGINE_CLIENT_LIBRARY_INCLUDE_UPDATE_ENGINE_STATUS_UPDATE_HANDLER_H_
 
 #include <string>
@@ -37,10 +35,13 @@
   virtual void IPCError(const std::string& error) = 0;
 
   // Runs every time update_engine reports a status change.
-  virtual void HandleStatusUpdate(const UpdateEngineStatus& status) = 0;
+  virtual void HandleStatusUpdate(int64_t last_checked_time,
+                                  double progress,
+                                  UpdateStatus current_operation,
+                                  const std::string& new_version,
+                                  int64_t new_size) = 0;
 };
 
 }  // namespace update_engine
 
-// NOLINTNEXTLINE(whitespace/line_length)
 #endif  // UPDATE_ENGINE_CLIENT_LIBRARY_INCLUDE_UPDATE_ENGINE_STATUS_UPDATE_HANDLER_H_
diff --git a/client_library/include/update_engine/update_status.h b/client_library/include/update_engine/update_status.h
index 043a36e..6490e27 100644
--- a/client_library/include/update_engine/update_status.h
+++ b/client_library/include/update_engine/update_status.h
@@ -21,19 +21,12 @@
 
 #include <brillo/enum_flags.h>
 
-// NOTE: Keep this file in sync with
-// platform2/system_api/dbus/update_engine/update_engine.proto especially:
-// - |UpdateStatus| <-> |Operation|
-// - |UpdateEngineStatus| <-> |StatusResult|
-
 namespace update_engine {
 
-// ATTENTION:
-// When adding a new enum value:
-// - always append at the end with proper adjustments in |ActionCompleted()|.
-// - always update |kNonIdleUpdateStatues| in update_attempter_unittest.cc.
-// When deprecating an old enum value:
-// - other enum values should not change their old values. See b/62842358.
+// ATTENTION: When adding a new enum value here, always append at the end and
+// make sure to make proper adjustments in UpdateAttempter:ActionCompleted(). If
+// any enum memeber is deprecated, the assigned value of other members should
+// not change. See b/62842358.
 enum class UpdateStatus {
   IDLE = 0,
   CHECKING_FOR_UPDATE = 1,
@@ -49,13 +42,6 @@
   // allow updates, e.g. over cellular network.
   NEED_PERMISSION_TO_UPDATE = 10,
   CLEANUP_PREVIOUS_UPDATE = 11,
-
-  // This value is exclusively used in Chrome. DO NOT define nor use it.
-  // TODO(crbug.com/977320): Remove this value from chrome by refactoring the
-  // Chrome code and evantually from here. This is not really an operation or
-  // state that the update_engine stays on. This is the result of an internal
-  // failure and should be reflected differently.
-  // ERROR = -1,
 };
 
 // Enum of bit-wise flags for controlling how updates are attempted.
@@ -72,27 +58,23 @@
 DECLARE_FLAGS_ENUM(UpdateAttemptFlags);
 
 struct UpdateEngineStatus {
-  // Update engine last checked update (time_t: seconds from unix epoch).
+  // When the update_engine last checked for updates (time_t: seconds from unix
+  // epoch)
   int64_t last_checked_time;
-  // Current status/operation of the update_engine.
+  // the current status/operation of the update_engine
   UpdateStatus status;
-  // Current product version (oem bundle id).
+  // the current product version (oem bundle id)
   std::string current_version;
-  // Current progress (0.0f-1.0f).
+  // the current system version
+  std::string current_system_version;
+  // The current progress (0.0f-1.0f).
   double progress;
-  // Size of the update in bytes.
+  // the size of the update (bytes)
   uint64_t new_size_bytes;
-  // New product version.
+  // the new product version
   std::string new_version;
-  // Wether the update is an enterprise rollback. The value is valid only if the
-  // current operation is passed CHECKING_FOR_UPDATE.
-  bool is_enterprise_rollback;
-  // Indication of install for DLC(s).
-  bool is_install;
-  // The end-of-life date of the device in the number of days since Unix Epoch.
-  int64_t eol_date;
-  // The system will powerwash once the update is applied.
-  bool will_powerwash_after_reboot;
+  // the new system version, if there is one (empty, otherwise)
+  std::string new_system_version;
 };
 
 }  // namespace update_engine
diff --git a/common/action_pipe.h b/common/action_pipe.h
index 4c56812..0c98ee1 100644
--- a/common/action_pipe.h
+++ b/common/action_pipe.h
@@ -79,8 +79,6 @@
 
  private:
   ObjectType contents_;
-  // Give unit test access
-  friend class DownloadActionTest;
 
   // The ctor is private. This is because this class should construct itself
   // via the static Bond() method.
diff --git a/common/action_processor.h b/common/action_processor.h
index ad98cc9..735a106 100644
--- a/common/action_processor.h
+++ b/common/action_processor.h
@@ -89,7 +89,7 @@
   // But this call deletes the action if there no other object has a reference
   // to it, so in that case, the caller should not try to access any of its
   // member variables after this call.
-  virtual void ActionComplete(AbstractAction* actionptr, ErrorCode code);
+  void ActionComplete(AbstractAction* actionptr, ErrorCode code);
 
  private:
   FRIEND_TEST(ActionProcessorTest, ChainActionsTest);
diff --git a/common/boot_control_interface.h b/common/boot_control_interface.h
index 321174e..3906e2f 100644
--- a/common/boot_control_interface.h
+++ b/common/boot_control_interface.h
@@ -59,27 +59,12 @@
   // every slot. In order to access the dynamic partitions in the target slot,
   // GetDynamicPartitionControl()->PreparePartitionsForUpdate() must be called
   // (with |update| == true for the first time for a payload, and |false| for
-  // for the rest of the times) prior to calling this function.
-  // The handling may be different based on whether the partition is included
-  // in the update payload. On success, returns true; and stores the block
-  // device in |device|, if the partition is dynamic in |is_dynamic|.
-  virtual bool GetPartitionDevice(const std::string& partition_name,
-                                  Slot slot,
-                                  bool not_in_payload,
-                                  std::string* device,
-                                  bool* is_dynamic) const = 0;
-
-  // Overload of the above function. We assume the partition is always included
-  // in the payload.
+  // for the rest of the times) prior to calling this function. On success,
+  // returns true and stores the block device in |device|.
   virtual bool GetPartitionDevice(const std::string& partition_name,
                                   Slot slot,
                                   std::string* device) const = 0;
 
-  virtual std::optional<PartitionDevice> GetPartitionDevice(
-      const std::string& partition_name,
-      uint32_t slot,
-      uint32_t current_slot,
-      bool not_in_payload = false) const = 0;
   // Returns whether the passed |slot| is marked as bootable. Returns false if
   // the slot is invalid.
   virtual bool IsSlotBootable(Slot slot) const = 0;
@@ -103,7 +88,7 @@
   // Check if |slot| is marked boot successfully.
   virtual bool IsSlotMarkedSuccessful(Slot slot) const = 0;
 
-  // Return the dynamic partition control interface. Never null.
+  // Return the dynamic partition control interface.
   virtual DynamicPartitionControlInterface* GetDynamicPartitionControl() = 0;
 
   // Return a human-readable slot name used for logging.
diff --git a/common/boot_control_stub.cc b/common/boot_control_stub.cc
index a1cc055..2eb9211 100644
--- a/common/boot_control_stub.cc
+++ b/common/boot_control_stub.cc
@@ -35,24 +35,6 @@
   return 0;
 }
 
-bool BootControlStub::GetPartitionDevice(const std::string& partition_name,
-                                         BootControlInterface::Slot slot,
-                                         bool not_in_payload,
-                                         std::string* device,
-                                         bool* is_dynamic) const {
-  LOG(ERROR) << __FUNCTION__ << " should never be called.";
-  return false;
-}
-
-std::optional<PartitionDevice> BootControlStub::GetPartitionDevice(
-    const std::string& partition_name,
-    uint32_t slot,
-    uint32_t current_slot,
-    bool not_in_payload) const {
-  LOG(ERROR) << __FUNCTION__ << " should never be called.";
-  return {};
-}
-
 bool BootControlStub::GetPartitionDevice(const string& partition_name,
                                          Slot slot,
                                          string* device) const {
diff --git a/common/boot_control_stub.h b/common/boot_control_stub.h
index dcddbae..cc16190 100644
--- a/common/boot_control_stub.h
+++ b/common/boot_control_stub.h
@@ -41,18 +41,8 @@
   unsigned int GetNumSlots() const override;
   BootControlInterface::Slot GetCurrentSlot() const override;
   bool GetPartitionDevice(const std::string& partition_name,
-                          Slot slot,
-                          bool not_in_payload,
-                          std::string* device,
-                          bool* is_dynamic) const override;
-  bool GetPartitionDevice(const std::string& partition_name,
                           BootControlInterface::Slot slot,
                           std::string* device) const override;
-  std::optional<PartitionDevice> GetPartitionDevice(
-      const std::string& partition_name,
-      uint32_t slot,
-      uint32_t current_slot,
-      bool not_in_payload = false) const override;
   bool IsSlotBootable(BootControlInterface::Slot slot) const override;
   bool MarkSlotUnbootable(BootControlInterface::Slot slot) override;
   bool SetActiveBootSlot(BootControlInterface::Slot slot) override;
diff --git a/common/cleanup_previous_update_action_delegate.h b/common/cleanup_previous_update_action_delegate.h
index 8daf860..7dad9c5 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_COMMON_CLEANUP_PREVIOUS_UPDATE_ACTION_DELEGETE_H_
-#define UPDATE_ENGINE_COMMON_CLEANUP_PREVIOUS_UPDATE_ACTION_DELEGETE_H_
+#ifndef UPDATE_ENGINE_CLEANUP_PREVIOUS_UPDATE_ACTION_DELEGETE_H_
+#define UPDATE_ENGINE_CLEANUP_PREVIOUS_UPDATE_ACTION_DELEGETE_H_
 
 namespace chromeos_update_engine {
 
@@ -29,4 +29,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_COMMON_CLEANUP_PREVIOUS_UPDATE_ACTION_DELEGETE_H_
+#endif  // UPDATE_ENGINE_CLEANUP_PREVIOUS_UPDATE_ACTION_DELEGETE_H_
diff --git a/common/clock.cc b/common/clock.cc
index 0821a56..05c495c 100644
--- a/common/clock.cc
+++ b/common/clock.cc
@@ -20,11 +20,11 @@
 
 namespace chromeos_update_engine {
 
-base::Time Clock::GetWallclockTime() const {
+base::Time Clock::GetWallclockTime() {
   return base::Time::Now();
 }
 
-base::Time Clock::GetMonotonicTime() const {
+base::Time Clock::GetMonotonicTime() {
   struct timespec now_ts;
   if (clock_gettime(CLOCK_MONOTONIC_RAW, &now_ts) != 0) {
     // Avoid logging this as an error as call-sites may call this very
@@ -40,7 +40,7 @@
   return base::Time::FromTimeVal(now_tv);
 }
 
-base::Time Clock::GetBootTime() const {
+base::Time Clock::GetBootTime() {
   struct timespec now_ts;
   if (clock_gettime(CLOCK_BOOTTIME, &now_ts) != 0) {
     // Avoid logging this as an error as call-sites may call this very
diff --git a/common/clock.h b/common/clock.h
index 4021fa1..2f373a7 100644
--- a/common/clock.h
+++ b/common/clock.h
@@ -24,11 +24,13 @@
 // Implements a clock.
 class Clock : public ClockInterface {
  public:
-  Clock() = default;
+  Clock() {}
 
-  base::Time GetWallclockTime() const override;
-  base::Time GetMonotonicTime() const override;
-  base::Time GetBootTime() const override;
+  base::Time GetWallclockTime() override;
+
+  base::Time GetMonotonicTime() override;
+
+  base::Time GetBootTime() override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(Clock);
diff --git a/common/clock_interface.h b/common/clock_interface.h
index 176505d..2228983 100644
--- a/common/clock_interface.h
+++ b/common/clock_interface.h
@@ -32,21 +32,21 @@
   virtual ~ClockInterface() = default;
 
   // Gets the current time e.g. similar to base::Time::Now().
-  virtual base::Time GetWallclockTime() const = 0;
+  virtual base::Time GetWallclockTime() = 0;
 
   // Returns monotonic time since some unspecified starting point. It
   // is not increased when the system is sleeping nor is it affected
   // by NTP or the user changing the time.
   //
   // (This is a simple wrapper around clock_gettime(2) / CLOCK_MONOTONIC_RAW.)
-  virtual base::Time GetMonotonicTime() const = 0;
+  virtual base::Time GetMonotonicTime() = 0;
 
   // Returns monotonic time since some unspecified starting point. It
   // is increased when the system is sleeping but it's not affected
   // by NTP or the user changing the time.
   //
   // (This is a simple wrapper around clock_gettime(2) / CLOCK_BOOTTIME.)
-  virtual base::Time GetBootTime() const = 0;
+  virtual base::Time GetBootTime() = 0;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/common/constants.cc b/common/constants.cc
index 0677e66..5bfb2b6 100644
--- a/common/constants.cc
+++ b/common/constants.cc
@@ -18,10 +18,6 @@
 
 namespace chromeos_update_engine {
 
-const char kExclusionPrefsSubDir[] = "exclusion";
-
-const char kDlcPrefsSubDir[] = "dlc";
-
 const char kPowerwashSafePrefsSubDirectory[] = "update_engine/prefs";
 
 const char kPrefsSubDirectory[] = "prefs";
@@ -59,19 +55,11 @@
 const char kPrefsOmahaCohort[] = "omaha-cohort";
 const char kPrefsOmahaCohortHint[] = "omaha-cohort-hint";
 const char kPrefsOmahaCohortName[] = "omaha-cohort-name";
-const char kPrefsOmahaEolDate[] = "omaha-eol-date";
+const char kPrefsOmahaEolStatus[] = "omaha-eol-status";
 const char kPrefsP2PEnabled[] = "p2p-enabled";
 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";
-const char kPrefsPingLastActive[] = "date_last_active";
-const char kPrefsPingLastRollcall[] = "date_last_rollcall";
-const char kPrefsLastFp[] = "last-fp";
 const char kPrefsPostInstallSucceeded[] = "post-install-succeeded";
 const char kPrefsPreviousVersion[] = "previous-version";
 const char kPrefsResumedUpdateFailures[] = "resumed-update-failures";
@@ -111,8 +99,6 @@
 const char kPrefsWallClockScatteringWaitPeriod[] = "wall-clock-wait-period";
 const char kPrefsWallClockStagingWaitPeriod[] =
     "wall-clock-staging-wait-period";
-const char kPrefsManifestBytes[] = "manifest-bytes";
-const char kPrefsPreviousSlot[] = "previous-slot";
 
 // These four fields are generated by scripts/brillo_update_payload.
 const char kPayloadPropertyFileSize[] = "FILE_SIZE";
@@ -137,12 +123,4 @@
 // The default is 1 (always run post install).
 const char kPayloadPropertyRunPostInstall[] = "RUN_POST_INSTALL";
 
-const char kOmahaUpdaterVersion[] = "0.1.0.0";
-
-// X-Goog-Update headers.
-const char kXGoogleUpdateInteractivity[] = "X-Goog-Update-Interactivity";
-const char kXGoogleUpdateAppId[] = "X-Goog-Update-AppId";
-const char kXGoogleUpdateUpdater[] = "X-Goog-Update-Updater";
-const char kXGoogleUpdateSessionId[] = "X-Goog-SessionId";
-
 }  // namespace chromeos_update_engine
diff --git a/common/constants.h b/common/constants.h
index 68f720d..af1c0ab 100644
--- a/common/constants.h
+++ b/common/constants.h
@@ -17,16 +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.
-extern const char kExclusionPrefsSubDir[];
-
-// The root path of all DLC metadata.
-extern const char kDlcPrefsSubDir[];
-
 // Directory for AU prefs that are preserved across powerwash.
 extern const char kPowerwashSafePrefsSubDirectory[];
 
@@ -64,19 +56,13 @@
 extern const char kPrefsOmahaCohort[];
 extern const char kPrefsOmahaCohortHint[];
 extern const char kPrefsOmahaCohortName[];
-extern const char kPrefsOmahaEolDate[];
+extern const char kPrefsOmahaEolStatus[];
 extern const char kPrefsP2PEnabled[];
 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[];
-extern const char kPrefsLastFp[];
 extern const char kPrefsPostInstallSucceeded[];
 extern const char kPrefsPreviousVersion[];
-extern const char kPrefsPreviousSlot[];
 extern const char kPrefsResumedUpdateFailures[];
 extern const char kPrefsRollbackHappened[];
 extern const char kPrefsRollbackVersion[];
@@ -109,7 +95,6 @@
 extern const char kPrefsVerityWritten[];
 extern const char kPrefsWallClockScatteringWaitPeriod[];
 extern const char kPrefsWallClockStagingWaitPeriod[];
-extern const char kPrefsManifestBytes[];
 
 // Keys used when storing and loading payload properties.
 extern const char kPayloadPropertyFileSize[];
@@ -123,14 +108,6 @@
 extern const char kPayloadPropertySwitchSlotOnReboot[];
 extern const char kPayloadPropertyRunPostInstall[];
 
-extern const char kOmahaUpdaterVersion[];
-
-// X-Goog-Update headers.
-extern const char kXGoogleUpdateInteractivity[];
-extern const char kXGoogleUpdateAppId[];
-extern const char kXGoogleUpdateUpdater[];
-extern const char kXGoogleUpdateSessionId[];
-
 // A download source is any combination of protocol and server (that's of
 // interest to us when looking at UMA metrics) using which we may download
 // the payload.
@@ -157,30 +134,30 @@
 } PayloadType;
 
 // Maximum number of times we'll allow using p2p for the same update payload.
-constexpr int kMaxP2PAttempts = 10;
+const int kMaxP2PAttempts = 10;
 
 // Maximum wallclock time we allow attempting to update using p2p for
 // the same update payload - five days.
-constexpr int kMaxP2PAttemptTimeSeconds = 5 * 24 * 60 * 60;
+const 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.
-constexpr int kMaxP2PNetworkWaitTimeSeconds = 6 * 60 * 60;
+const int kMaxP2PNetworkWaitTimeSeconds = 6 * 60 * 60;
 
 // The maximum number of payload files to keep in /var/cache/p2p.
-constexpr int kMaxP2PFilesToKeep = 3;
+const int kMaxP2PFilesToKeep = 3;
 
 // The maximum number of days to keep a p2p file;
-constexpr int kMaxP2PFileAgeDays = 5;
+const int kMaxP2PFileAgeDays = 5;
 
 // The default number of UMA buckets for metrics.
-constexpr int kNumDefaultUmaBuckets = 50;
+const int kNumDefaultUmaBuckets = 50;
 
-// General constexprants
-constexpr int kNumBytesInOneMiB = 1024 * 1024;
+// General constants
+const int kNumBytesInOneMiB = 1024 * 1024;
 
 // Number of redirects allowed when downloading.
-constexpr int kDownloadMaxRedirects = 10;
+const int kDownloadMaxRedirects = 10;
 
 // The minimum average speed that downloads must sustain...
 //
@@ -188,8 +165,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.
-constexpr int kDownloadLowSpeedLimitBps = 1;
-constexpr int kDownloadP2PLowSpeedLimitBps = 25 * 1000;
+const int kDownloadLowSpeedLimitBps = 1;
+const int kDownloadP2PLowSpeedLimitBps = 25 * 1000;
 
 // ... measured over this period.
 //
@@ -198,18 +175,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.
-constexpr int kDownloadLowSpeedTimeSeconds = 30;
-constexpr int kDownloadDevModeLowSpeedTimeSeconds = 180;
-constexpr int kDownloadP2PLowSpeedTimeSeconds = 60;
+const int kDownloadLowSpeedTimeSeconds = 30;
+const int kDownloadDevModeLowSpeedTimeSeconds = 180;
+const 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.
-constexpr int kDownloadMaxRetryCount = 20;
-constexpr int kDownloadMaxRetryCountOobeNotComplete = 3;
-constexpr int kDownloadMaxRetryCountInteractive = 3;
-constexpr int kDownloadP2PMaxRetryCount = 5;
+const int kDownloadMaxRetryCount = 20;
+const int kDownloadMaxRetryCountOobeNotComplete = 3;
+const int kDownloadMaxRetryCountInteractive = 3;
+const int kDownloadP2PMaxRetryCount = 5;
 
 // The connect timeout, in seconds.
 //
@@ -217,19 +194,11 @@
 // 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.
-constexpr int kDownloadConnectTimeoutSeconds = 30;
-constexpr int kDownloadP2PConnectTimeoutSeconds = 5;
+const int kDownloadConnectTimeoutSeconds = 30;
+const int kDownloadP2PConnectTimeoutSeconds = 5;
 
 // Size in bytes of SHA256 hash.
-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);
+const int kSHA256Size = 32;
 
 }  // namespace chromeos_update_engine
 
diff --git a/common/cow_operation_convert.cc b/common/cow_operation_convert.cc
deleted file mode 100644
index 2564abf..0000000
--- a/common/cow_operation_convert.cc
+++ /dev/null
@@ -1,82 +0,0 @@
-//
-// 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"
-#include "update_engine/update_metadata.pb.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) {
-    if (merge_op.type() != CowMergeOperation::COW_COPY) {
-      continue;
-    }
-    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, because snapused specifically prefers this
-    // ordering. Since we already eliminated all self-overlapping SOURCE_COPY
-    // during delta generation, this should be safe to do.
-    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;
-      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
deleted file mode 100644
index c0543f7..0000000
--- a/common/cow_operation_convert.h
+++ /dev/null
@@ -1,56 +0,0 @@
-//
-// 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
deleted file mode 100644
index 93173fe..0000000
--- a/common/cow_operation_convert_unittest.cc
+++ /dev/null
@@ -1,236 +0,0 @@
-//
-// 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/common/cpu_limiter.cc b/common/cpu_limiter.cc
index 5f1ae6f..1d14764 100644
--- a/common/cpu_limiter.cc
+++ b/common/cpu_limiter.cc
@@ -67,7 +67,7 @@
   if (shares_ == shares)
     return true;
 
-  std::string string_shares = base::NumberToString(static_cast<int>(shares));
+  std::string string_shares = base::IntToString(static_cast<int>(shares));
   LOG(INFO) << "Setting cgroup cpu shares to  " << string_shares;
   if (!utils::WriteFile(
           kCGroupSharesPath, string_shares.c_str(), string_shares.size())) {
diff --git a/common/cpu_limiter.h b/common/cpu_limiter.h
index e6d7331..c7add89 100644
--- a/common/cpu_limiter.h
+++ b/common/cpu_limiter.h
@@ -30,6 +30,10 @@
   kLow = 2,
 };
 
+// Sets the current process shares to |shares|. Returns true on
+// success, false otherwise.
+bool SetCpuShares(CpuShares shares);
+
 class CPULimiter {
  public:
   CPULimiter() = default;
diff --git a/common/daemon_base.h b/common/daemon_base.h
deleted file mode 100644
index 4bc5ef7..0000000
--- a/common/daemon_base.h
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef UPDATE_ENGINE_COMMON_DAEMON_BASE_H_
-#define UPDATE_ENGINE_COMMON_DAEMON_BASE_H_
-
-#include <memory>
-
-#include <brillo/daemons/daemon.h>
-
-namespace chromeos_update_engine {
-
-class DaemonBase : public brillo::Daemon {
- public:
-  DaemonBase() = default;
-  virtual ~DaemonBase() = default;
-
-  // Creates an instance of the daemon.
-  static std::unique_ptr<DaemonBase> CreateInstance();
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(DaemonBase);
-};
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_COMMON_DAEMON_BASE_H_
diff --git a/common/metrics_reporter_stub.cc b/common/dlcservice.h
similarity index 62%
copy from common/metrics_reporter_stub.cc
copy to common/dlcservice.h
index 61559d9..9dae560 100644
--- a/common/metrics_reporter_stub.cc
+++ b/common/dlcservice.h
@@ -1,5 +1,5 @@
 //
-// Copyright (C) 2017 The Android Open Source Project
+// 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.
@@ -14,20 +14,19 @@
 // limitations under the License.
 //
 
-#include "update_engine/common/metrics_reporter_stub.h"
+#ifndef UPDATE_ENGINE_COMMON_DLCSERVICE_H_
+#define UPDATE_ENGINE_COMMON_DLCSERVICE_H_
 
 #include <memory>
 
+#include "update_engine/common/dlcservice_interface.h"
+
 namespace chromeos_update_engine {
 
-namespace metrics {
-
-std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter(
-    DynamicPartitionControlInterface* dynamic_partition_control,
-    const InstallPlan* install_plan) {
-  return std::make_unique<MetricsReporterStub>();
-}
-
-}  // namespace metrics
+// This factory function creates a new DlcServiceInterface instance for the
+// current platform.
+std::unique_ptr<DlcServiceInterface> CreateDlcService();
 
 }  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_DLCSERVICE_H_
diff --git a/common/dlcservice_interface.h b/common/dlcservice_interface.h
index 7b57710..aa24105 100644
--- a/common/dlcservice_interface.h
+++ b/common/dlcservice_interface.h
@@ -17,7 +17,6 @@
 #ifndef UPDATE_ENGINE_COMMON_DLCSERVICE_INTERFACE_H_
 #define UPDATE_ENGINE_COMMON_DLCSERVICE_INTERFACE_H_
 
-#include <memory>
 #include <string>
 #include <vector>
 
@@ -31,17 +30,9 @@
  public:
   virtual ~DlcServiceInterface() = default;
 
-  // Returns true and a list of installed DLC ids in |dlc_ids|.
+  // Returns true and a list of installed DLC module ids in |dlc_module_ids|.
   // On failure it returns false.
-  virtual bool GetDlcsToUpdate(std::vector<std::string>* dlc_ids) = 0;
-
-  // Returns true if dlcservice successfully handled the install completion
-  // method call, otherwise false.
-  virtual bool InstallCompleted(const std::vector<std::string>& dlc_ids) = 0;
-
-  // Returns true if dlcservice successfully handled the update completion
-  // method call, otherwise false.
-  virtual bool UpdateCompleted(const std::vector<std::string>& dlc_ids) = 0;
+  virtual bool GetInstalled(std::vector<std::string>* dlc_module_ids) = 0;
 
  protected:
   DlcServiceInterface() = default;
@@ -50,10 +41,6 @@
   DISALLOW_COPY_AND_ASSIGN(DlcServiceInterface);
 };
 
-// This factory function creates a new DlcServiceInterface instance for the
-// current platform.
-std::unique_ptr<DlcServiceInterface> CreateDlcService();
-
 }  // namespace chromeos_update_engine
 
 #endif  // UPDATE_ENGINE_COMMON_DLCSERVICE_INTERFACE_H_
diff --git a/common/dlcservice_stub.cc b/common/dlcservice_stub.cc
index 2447147..c5f9306 100644
--- a/common/dlcservice_stub.cc
+++ b/common/dlcservice_stub.cc
@@ -27,16 +27,9 @@
   return std::make_unique<DlcServiceStub>();
 }
 
-bool DlcServiceStub::GetDlcsToUpdate(vector<string>* dlc_ids) {
-  if (dlc_ids)
-    dlc_ids->clear();
-  return true;
-}
-
-bool DlcServiceStub::InstallCompleted(const vector<string>& dlc_ids) {
-  return true;
-}
-bool DlcServiceStub::UpdateCompleted(const vector<string>& dlc_ids) {
+bool DlcServiceStub::GetInstalled(std::vector<std::string>* dlc_module_ids) {
+  if (dlc_module_ids)
+    dlc_module_ids->clear();
   return true;
 }
 
diff --git a/common/dlcservice_stub.h b/common/dlcservice_stub.h
index bc803e8..4e12c11 100644
--- a/common/dlcservice_stub.h
+++ b/common/dlcservice_stub.h
@@ -31,9 +31,7 @@
   ~DlcServiceStub() = default;
 
   // BootControlInterface overrides.
-  bool GetDlcsToUpdate(std::vector<std::string>* dlc_ids) override;
-  bool InstallCompleted(const std::vector<std::string>& dlc_ids) override;
-  bool UpdateCompleted(const std::vector<std::string>& dlc_ids) override;
+  bool GetInstalled(std::vector<std::string>* dlc_module_ids) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(DlcServiceStub);
diff --git a/common/dynamic_partition_control_interface.h b/common/dynamic_partition_control_interface.h
index a5be6e1..58ebfe4 100644
--- a/common/dynamic_partition_control_interface.h
+++ b/common/dynamic_partition_control_interface.h
@@ -21,28 +21,14 @@
 
 #include <memory>
 #include <string>
-#include <vector>
 
 #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/common/prefs_interface.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 PartitionDevice {
-  std::string rw_device_path;
-  std::string readonly_device_path;
-  bool is_dynamic;
-};
-
 struct FeatureFlag {
   enum class Value { NONE = 0, RETROFIT, LAUNCH };
   constexpr explicit FeatureFlag(Value value) : value_(value) {}
@@ -55,6 +41,7 @@
 };
 
 class BootControlInterface;
+class PrefsInterface;
 
 class DynamicPartitionControlInterface {
  public:
@@ -68,11 +55,6 @@
 
   // Return the feature flags of Virtual A/B on this device.
   virtual FeatureFlag GetVirtualAbFeatureFlag() = 0;
-  // Return the feature flags of Virtual A/B Compression on this device.
-  // This function will tell you if current device supports VABC. However, it
-  // DOES NOT tell you if VABC is used for current OTA update. For that, use
-  // UpdateUsesSnapshotCompression.
-  virtual FeatureFlag GetVirtualAbCompressionFeatureFlag() = 0;
 
   // Attempt to optimize |operation|.
   // If successful, |optimized| contains an operation with extents that
@@ -136,59 +118,6 @@
   // progress, while ResetUpdate() forcefully free previously
   // allocated space for snapshot updates.
   virtual bool ResetUpdate(PrefsInterface* prefs) = 0;
-
-  // Reads the dynamic partitions metadata from the given slot, and puts the
-  // name of the dynamic partitions with the current suffix to |partitions|.
-  // Returns true on success.
-  virtual bool ListDynamicPartitionsForSlot(
-      uint32_t slot,
-      uint32_t current_slot,
-      std::vector<std::string>* partitions) = 0;
-
-  // Finds a possible location that list all block devices by name; and puts
-  // the result in |path|. Returns true on success.
-  // Sample result: /dev/block/by-name/
-  virtual bool GetDeviceDir(std::string* path) = 0;
-
-  // Verifies that the untouched dynamic partitions in the target metadata have
-  // the same extents as the source metadata.
-  virtual bool VerifyExtentsForUntouchedPartitions(
-      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;
-  // Open a general purpose FD capable to reading and writing to COW. Note that
-  // writes must be block aligned.
-  virtual FileDescriptorPtr OpenCowFd(
-      const std::string& unsuffixed_partition_name,
-      const std::optional<std::string>&,
-      bool is_append = false) = 0;
-
-  virtual bool IsDynamicPartition(const std::string& part_name,
-                                  uint32_t slot) = 0;
-
-  // Create virtual block devices for all partitions.
-  virtual bool MapAllPartitions() = 0;
-  // Unmap virtual block devices for all partitions.
-  virtual bool UnmapAllPartitions() = 0;
-
-  // Return if snapshot compression is enabled for this update.
-  // This function should only be called after preparing for an update
-  // (PreparePartitionsForUpdate), and before merging
-  // (see GetCleanupPreviousUpdateAction and CleanupPreviousUpdateAction) or
-  // resetting it (ResetUpdate).
-  //
-  // To know if the device supports snapshot compression by itself, use
-  // GetVirtualAbCompressionFeatureFlag
-  virtual bool UpdateUsesSnapshotCompression() = 0;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/common/dynamic_partition_control_stub.cc b/common/dynamic_partition_control_stub.cc
index dd30a8b..903b7ee 100644
--- a/common/dynamic_partition_control_stub.cc
+++ b/common/dynamic_partition_control_stub.cc
@@ -20,7 +20,6 @@
 #include <string>
 
 #include <base/logging.h>
-#include <libsnapshot/cow_writer.h>
 
 #include "update_engine/common/dynamic_partition_control_stub.h"
 
@@ -34,10 +33,6 @@
   return FeatureFlag(FeatureFlag::Value::NONE);
 }
 
-FeatureFlag DynamicPartitionControlStub::GetVirtualAbCompressionFeatureFlag() {
-  return FeatureFlag(FeatureFlag::Value::NONE);
-}
-
 bool DynamicPartitionControlStub::OptimizeOperation(
     const std::string& partition_name,
     const InstallOperation& operation,
@@ -72,47 +67,4 @@
   return false;
 }
 
-bool DynamicPartitionControlStub::ListDynamicPartitionsForSlot(
-    uint32_t slot,
-    uint32_t current_slot,
-    std::vector<std::string>* partitions) {
-  return true;
-}
-
-bool DynamicPartitionControlStub::GetDeviceDir(std::string* path) {
-  return true;
-}
-
-bool DynamicPartitionControlStub::VerifyExtentsForUntouchedPartitions(
-    uint32_t source_slot,
-    uint32_t target_slot,
-    const std::vector<std::string>& partitions) {
-  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;
-}
-
-bool DynamicPartitionControlStub::MapAllPartitions() {
-  return false;
-}
-
-bool DynamicPartitionControlStub::UnmapAllPartitions() {
-  return false;
-}
-
-bool DynamicPartitionControlStub::IsDynamicPartition(
-    const std::string& part_name, uint32_t slot) {
-  return false;
-}
-
-bool DynamicPartitionControlStub::UpdateUsesSnapshotCompression() {
-  return false;
-}
-
 }  // namespace chromeos_update_engine
diff --git a/common/dynamic_partition_control_stub.h b/common/dynamic_partition_control_stub.h
index 515ec7c..d8e254e 100644
--- a/common/dynamic_partition_control_stub.h
+++ b/common/dynamic_partition_control_stub.h
@@ -21,7 +21,6 @@
 
 #include <memory>
 #include <string>
-#include <vector>
 
 #include "update_engine/common/dynamic_partition_control_interface.h"
 
@@ -31,7 +30,6 @@
  public:
   FeatureFlag GetDynamicPartitionsFeatureFlag() override;
   FeatureFlag GetVirtualAbFeatureFlag() override;
-  FeatureFlag GetVirtualAbCompressionFeatureFlag() override;
   bool OptimizeOperation(const std::string& partition_name,
                          const InstallOperation& operation,
                          InstallOperation* optimized) override;
@@ -48,35 +46,8 @@
       PrefsInterface* prefs,
       CleanupPreviousUpdateActionDelegateInterface* delegate) override;
   bool ResetUpdate(PrefsInterface* prefs) override;
-
-  bool ListDynamicPartitionsForSlot(
-      uint32_t slot,
-      uint32_t current_slot,
-      std::vector<std::string>* partitions) override;
-  bool GetDeviceDir(std::string* path) override;
-
-  bool VerifyExtentsForUntouchedPartitions(
-      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 OpenCowFd(const std::string& unsuffixed_partition_name,
-                              const std::optional<std::string>&,
-                              bool is_append = false) override {
-    return nullptr;
-  }
-
-  bool MapAllPartitions() override;
-  bool UnmapAllPartitions() override;
-
-  bool IsDynamicPartition(const std::string& part_name, uint32_t slot) override;
-  bool UpdateUsesSnapshotCompression() 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 a889888..e473a05 100644
--- a/common/error_code.h
+++ b/common/error_code.h
@@ -85,8 +85,6 @@
   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 421544a..3fbf0fe 100644
--- a/common/error_code_utils.cc
+++ b/common/error_code_utils.cc
@@ -171,15 +171,11 @@
       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.
   }
 
-  return "Unknown error: " + base::NumberToString(static_cast<unsigned>(code));
+  return "Unknown error: " + base::UintToString(static_cast<unsigned>(code));
 }
 
 }  // namespace utils
diff --git a/common/excluder_interface.h b/common/excluder_interface.h
deleted file mode 100644
index 1dfd227..0000000
--- a/common/excluder_interface.h
+++ /dev/null
@@ -1,62 +0,0 @@
-//
-// 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_COMMON_EXCLUDER_INTERFACE_H_
-#define UPDATE_ENGINE_COMMON_EXCLUDER_INTERFACE_H_
-
-#include <memory>
-#include <string>
-
-#include <base/macros.h>
-
-namespace chromeos_update_engine {
-
-class PrefsInterface;
-
-// TODO(b/171829801): Move this interface to 'cros' directory. 'aosp' in no way
-// is using this. Not even the stub implementation.
-class ExcluderInterface {
- public:
-  virtual ~ExcluderInterface() = default;
-
-  // Returns true on successfuly excluding |name|, otherwise false. On a
-  // successful |Exclude()| the passed in |name| will be considered excluded
-  // and calls to |IsExcluded()| will return true. The exclusions are persisted.
-  virtual bool Exclude(const std::string& name) = 0;
-
-  // Returns true if |name| reached the exclusion limit, otherwise false.
-  virtual bool IsExcluded(const std::string& name) = 0;
-
-  // Returns true on sucessfully reseting the entire exclusion state, otherwise
-  // false. On a successful |Reset()| there will be no excluded |name| in the
-  // exclusion state.
-  virtual bool Reset() = 0;
-
-  // Not copyable or movable
-  ExcluderInterface(const ExcluderInterface&) = delete;
-  ExcluderInterface& operator=(const ExcluderInterface&) = delete;
-  ExcluderInterface(ExcluderInterface&&) = delete;
-  ExcluderInterface& operator=(ExcluderInterface&&) = delete;
-
- protected:
-  ExcluderInterface() = default;
-};
-
-std::unique_ptr<ExcluderInterface> CreateExcluder();
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_COMMON_EXCLUDER_INTERFACE_H_
diff --git a/common/excluder_stub.cc b/common/excluder_stub.cc
deleted file mode 100644
index 2b987fd..0000000
--- a/common/excluder_stub.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-// 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/excluder_stub.h"
-
-#include <memory>
-
-#include "update_engine/common/prefs_interface.h"
-
-using std::string;
-
-namespace chromeos_update_engine {
-
-std::unique_ptr<ExcluderInterface> CreateExcluder() {
-  return std::make_unique<ExcluderStub>();
-}
-
-bool ExcluderStub::Exclude(const string& name) {
-  return true;
-}
-
-bool ExcluderStub::IsExcluded(const string& name) {
-  return false;
-}
-
-bool ExcluderStub::Reset() {
-  return true;
-}
-
-}  // namespace chromeos_update_engine
diff --git a/common/excluder_stub.h b/common/excluder_stub.h
deleted file mode 100644
index 2d5372a..0000000
--- a/common/excluder_stub.h
+++ /dev/null
@@ -1,46 +0,0 @@
-//
-// 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_COMMON_EXCLUDER_STUB_H_
-#define UPDATE_ENGINE_COMMON_EXCLUDER_STUB_H_
-
-#include <string>
-
-#include "update_engine/common/excluder_interface.h"
-
-namespace chromeos_update_engine {
-
-// An implementation of the |ExcluderInterface| that does nothing.
-class ExcluderStub : public ExcluderInterface {
- public:
-  ExcluderStub() = default;
-  ~ExcluderStub() = default;
-
-  // |ExcluderInterface| overrides.
-  bool Exclude(const std::string& name) override;
-  bool IsExcluded(const std::string& name) override;
-  bool Reset() override;
-
-  // Not copyable or movable.
-  ExcluderStub(const ExcluderStub&) = delete;
-  ExcluderStub& operator=(const ExcluderStub&) = delete;
-  ExcluderStub(ExcluderStub&&) = delete;
-  ExcluderStub& operator=(ExcluderStub&&) = delete;
-};
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_COMMON_EXCLUDER_STUB_H_
diff --git a/common/fake_boot_control.h b/common/fake_boot_control.h
index 79e2139..bd9d9ca 100644
--- a/common/fake_boot_control.h
+++ b/common/fake_boot_control.h
@@ -48,27 +48,14 @@
 
   bool GetPartitionDevice(const std::string& partition_name,
                           BootControlInterface::Slot slot,
-                          bool not_in_payload,
-                          std::string* device,
-                          bool* is_dynamic) const override {
-    auto dev =
-        GetPartitionDevice(partition_name, slot, current_slot_, not_in_payload);
-    if (!dev.has_value()) {
-      return false;
-    }
-    if (is_dynamic) {
-      *is_dynamic = dev->is_dynamic;
-    }
-    if (device) {
-      *device = dev->rw_device_path;
-    }
-    return true;
-  }
-
-  bool GetPartitionDevice(const std::string& partition_name,
-                          BootControlInterface::Slot slot,
                           std::string* device) const override {
-    return GetPartitionDevice(partition_name, slot, false, device, nullptr);
+    if (slot >= num_slots_)
+      return false;
+    auto part_it = devices_[slot].find(partition_name);
+    if (part_it == devices_[slot].end())
+      return false;
+    *device = part_it->second;
+    return true;
   }
 
   bool IsSlotBootable(BootControlInterface::Slot slot) const override {
@@ -118,29 +105,10 @@
     is_bootable_[slot] = bootable;
   }
 
-  DynamicPartitionControlInterface* GetDynamicPartitionControl() override {
+  DynamicPartitionControlInterface* GetDynamicPartitionControl() {
     return dynamic_partition_control_.get();
   }
 
-  std::optional<PartitionDevice> GetPartitionDevice(
-      const std::string& partition_name,
-      uint32_t slot,
-      uint32_t current_slot,
-      bool not_in_payload = false) const override {
-    if (slot >= devices_.size()) {
-      return {};
-    }
-    auto device_path = devices_[slot].find(partition_name);
-    if (device_path == devices_[slot].end()) {
-      return {};
-    }
-    PartitionDevice device;
-    device.is_dynamic = false;
-    device.rw_device_path = device_path->second;
-    device.readonly_device_path = device.rw_device_path;
-    return device;
-  }
-
  private:
   BootControlInterface::Slot num_slots_{2};
   BootControlInterface::Slot current_slot_{0};
diff --git a/common/fake_clock.h b/common/fake_clock.h
index 9c47b57..165ec4d 100644
--- a/common/fake_clock.h
+++ b/common/fake_clock.h
@@ -26,11 +26,11 @@
  public:
   FakeClock() {}
 
-  base::Time GetWallclockTime() const override { return wallclock_time_; }
+  base::Time GetWallclockTime() override { return wallclock_time_; }
 
-  base::Time GetMonotonicTime() const override { return monotonic_time_; }
+  base::Time GetMonotonicTime() override { return monotonic_time_; }
 
-  base::Time GetBootTime() const override { return boot_time_; }
+  base::Time GetBootTime() override { return boot_time_; }
 
   void SetWallclockTime(const base::Time& time) { wallclock_time_ = time; }
 
diff --git a/common/fake_hardware.h b/common/fake_hardware.h
index 6c25183..0b232da 100644
--- a/common/fake_hardware.h
+++ b/common/fake_hardware.h
@@ -19,13 +19,10 @@
 
 #include <map>
 #include <string>
-#include <utility>
 
 #include <base/time/time.h>
 
-#include "update_engine/common/error_code.h"
 #include "update_engine/common/hardware_interface.h"
-#include "update_engine/common/utils.h"
 
 namespace chromeos_update_engine {
 
@@ -76,9 +73,9 @@
 
   std::string GetHardwareClass() const override { return hardware_class_; }
 
-  std::string GetDeviceRequisition() const override {
-    return device_requisition_;
-  }
+  std::string GetFirmwareVersion() const override { return firmware_version_; }
+
+  std::string GetECVersion() const override { return ec_version_; }
 
   int GetMinKernelKeyVersion() const override {
     return min_kernel_key_version_;
@@ -107,15 +104,15 @@
 
   int GetPowerwashCount() const override { return powerwash_count_; }
 
-  bool SchedulePowerwash(bool save_rollback_data) override {
+  bool SchedulePowerwash(bool is_rollback) override {
     powerwash_scheduled_ = true;
-    save_rollback_data_ = save_rollback_data;
+    is_rollback_powerwash_ = is_rollback;
     return true;
   }
 
   bool CancelPowerwash() override {
     powerwash_scheduled_ = false;
-    save_rollback_data_ = false;
+    is_rollback_powerwash_ = false;
     return true;
   }
 
@@ -172,10 +169,12 @@
     hardware_class_ = hardware_class;
   }
 
-  void SetDeviceRequisition(const std::string& requisition) {
-    device_requisition_ = requisition;
+  void SetFirmwareVersion(const std::string& firmware_version) {
+    firmware_version_ = firmware_version;
   }
 
+  void SetECVersion(const std::string& ec_version) { ec_version_ = ec_version; }
+
   void SetMinKernelKeyVersion(int min_kernel_key_version) {
     min_kernel_key_version_ = min_kernel_key_version;
   }
@@ -192,39 +191,13 @@
     build_timestamp_ = build_timestamp;
   }
 
-  void SetWarmReset(bool warm_reset) override { warm_reset_ = warm_reset; }
-
-  void SetVbmetaDigestForInactiveSlot(bool reset) override {}
+  void SetWarmReset(bool warm_reset) { warm_reset_ = warm_reset; }
 
   // Getters to verify state.
   int GetMaxKernelKeyRollforward() const { return kernel_max_rollforward_; }
 
   bool GetIsRollbackPowerwashScheduled() const {
-    return powerwash_scheduled_ && save_rollback_data_;
-  }
-  std::string GetVersionForLogging(
-      const std::string& partition_name) const override {
-    return partition_timestamps_[partition_name];
-  }
-  void SetVersion(const std::string& partition_name, std::string timestamp) {
-    partition_timestamps_[partition_name] = std::move(timestamp);
-  }
-  ErrorCode IsPartitionUpdateValid(
-      const std::string& partition_name,
-      const std::string& new_version) const override {
-    const auto old_version = GetVersionForLogging(partition_name);
-    return utils::IsTimestampNewer(old_version, new_version);
-  }
-
-  const char* GetPartitionMountOptions(
-      const std::string& partition_name) const override {
-#ifdef __ANDROID__
-    // TODO(allight): This matches the declaration in hardware_android.cc but
-    // ideally shouldn't be duplicated.
-    return "defcontext=u:object_r:postinstall_file:s0";
-#else
-    return "";
-#endif
+    return powerwash_scheduled_ && is_rollback_powerwash_;
   }
 
  private:
@@ -236,18 +209,18 @@
   // Jan 20, 2007
   base::Time oobe_timestamp_{base::Time::FromTimeT(1169280000)};
   std::string hardware_class_{"Fake HWID BLAH-1234"};
-  std::string device_requisition_{"fake_requisition"};
+  std::string firmware_version_{"Fake Firmware v1.0.1"};
+  std::string ec_version_{"Fake EC v1.0a"};
   int min_kernel_key_version_{kMinKernelKeyVersion};
   int min_firmware_key_version_{kMinFirmwareKeyVersion};
   int kernel_max_rollforward_{kKernelMaxRollforward};
   int firmware_max_rollforward_{kFirmwareMaxRollforward};
   int powerwash_count_{kPowerwashCountNotSet};
   bool powerwash_scheduled_{false};
-  bool save_rollback_data_{false};
+  bool is_rollback_powerwash_{false};
   int64_t build_timestamp_{0};
   bool first_active_omaha_ping_sent_{false};
   bool warm_reset_{false};
-  mutable std::map<std::string, std::string> partition_timestamps_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeHardware);
 };
diff --git a/common/fake_prefs.cc b/common/fake_prefs.cc
index ea6ea60..c446e06 100644
--- a/common/fake_prefs.cc
+++ b/common/fake_prefs.cc
@@ -17,12 +17,10 @@
 #include "update_engine/common/fake_prefs.h"
 
 #include <algorithm>
-#include <utility>
 
 #include <gtest/gtest.h>
 
 using std::string;
-using std::vector;
 
 using chromeos_update_engine::FakePrefs;
 
@@ -67,8 +65,8 @@
   return GetValue(key, value);
 }
 
-bool FakePrefs::SetString(const string& key, std::string_view value) {
-  SetValue(key, std::string(value));
+bool FakePrefs::SetString(const string& key, const string& value) {
+  SetValue(key, value);
   return true;
 }
 
@@ -107,29 +105,6 @@
   return true;
 }
 
-bool FakePrefs::Delete(const string& key, const vector<string>& nss) {
-  bool success = Delete(key);
-  for (const auto& ns : nss) {
-    vector<string> ns_keys;
-    success = GetSubKeys(ns, &ns_keys) && success;
-    for (const auto& sub_key : ns_keys) {
-      auto last_key_seperator = sub_key.find_last_of(kKeySeparator);
-      if (last_key_seperator != string::npos &&
-          key == sub_key.substr(last_key_seperator + 1)) {
-        success = Delete(sub_key) && success;
-      }
-    }
-  }
-  return success;
-}
-
-bool FakePrefs::GetSubKeys(const string& ns, vector<string>* keys) const {
-  for (const auto& pr : values_)
-    if (pr.first.compare(0, ns.length(), ns) == 0)
-      keys->push_back(pr.first);
-  return true;
-}
-
 string FakePrefs::GetTypeName(PrefType type) {
   switch (type) {
     case PrefType::kString:
@@ -150,10 +125,10 @@
 }
 
 template <typename T>
-void FakePrefs::SetValue(const string& key, T value) {
+void FakePrefs::SetValue(const string& key, const T& value) {
   CheckKeyType(key, PrefConsts<T>::type);
   values_[key].type = PrefConsts<T>::type;
-  values_[key].value.*(PrefConsts<T>::member) = std::move(value);
+  values_[key].value.*(PrefConsts<T>::member) = value;
   const auto observers_for_key = observers_.find(key);
   if (observers_for_key != observers_.end()) {
     std::vector<ObserverInterface*> copy_observers(observers_for_key->second);
diff --git a/common/fake_prefs.h b/common/fake_prefs.h
index 430c291..b1c5b71 100644
--- a/common/fake_prefs.h
+++ b/common/fake_prefs.h
@@ -19,7 +19,6 @@
 
 #include <map>
 #include <string>
-#include <string_view>
 #include <vector>
 
 #include <base/macros.h>
@@ -41,7 +40,7 @@
 
   // PrefsInterface methods.
   bool GetString(const std::string& key, std::string* value) const override;
-  bool SetString(const std::string& key, std::string_view value) override;
+  bool SetString(const std::string& key, const std::string& value) override;
   bool GetInt64(const std::string& key, int64_t* value) const override;
   bool SetInt64(const std::string& key, const int64_t value) override;
   bool GetBoolean(const std::string& key, bool* value) const override;
@@ -49,11 +48,6 @@
 
   bool Exists(const std::string& key) const override;
   bool Delete(const std::string& key) override;
-  bool Delete(const std::string& key,
-              const std::vector<std::string>& nss) override;
-
-  bool GetSubKeys(const std::string& ns,
-                  std::vector<std::string>* keys) const override;
 
   void AddObserver(const std::string& key,
                    ObserverInterface* observer) override;
@@ -97,7 +91,7 @@
   // Helper function to set a value of the passed |key|. It sets the type based
   // on the template parameter T.
   template <typename T>
-  void SetValue(const std::string& key, T value);
+  void SetValue(const std::string& key, const T& value);
 
   // Helper function to get a value from the map checking for invalid calls.
   // The function fails the test if you attempt to read a value  defined as a
diff --git a/common/file_fetcher.h b/common/file_fetcher.h
index bd39007..fbdfc32 100644
--- a/common/file_fetcher.h
+++ b/common/file_fetcher.h
@@ -59,12 +59,6 @@
   void SetHeader(const std::string& header_name,
                  const std::string& header_value) override {}
 
-  bool GetHeader(const std::string& header_name,
-                 std::string* header_value) const override {
-    header_value->clear();
-    return false;
-  }
-
   // Suspend the asynchronous file read.
   void Pause() override;
 
diff --git a/common/hardware_interface.h b/common/hardware_interface.h
index 4e820f1..d92a6fc 100644
--- a/common/hardware_interface.h
+++ b/common/hardware_interface.h
@@ -25,8 +25,6 @@
 #include <base/files/file_path.h>
 #include <base/time/time.h>
 
-#include "update_engine/common/error_code.h"
-
 namespace chromeos_update_engine {
 
 // The hardware interface allows access to the crossystem exposed properties,
@@ -64,9 +62,13 @@
   // Returns the HWID or an empty string on error.
   virtual std::string GetHardwareClass() 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;
+  // 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 minimum kernel key version that verified boot on Chrome OS
   // will allow to boot. This is the value of crossystem tpm_kernver. Returns
@@ -100,9 +102,9 @@
   virtual int GetPowerwashCount() const = 0;
 
   // Signals that a powerwash (stateful partition wipe) should be performed
-  // after reboot. If |save_rollback_data| is true additional state is
-  // preserved during shutdown that can be restored after the powerwash.
-  virtual bool SchedulePowerwash(bool save_rollback_data) = 0;
+  // after reboot. If |is_rollback| is true additional state is preserved
+  // during shutdown that can be restored after the powerwash.
+  virtual bool SchedulePowerwash(bool is_rollback) = 0;
 
   // Cancel the powerwash operation scheduled to be performed on next boot.
   virtual bool CancelPowerwash() = 0;
@@ -136,33 +138,6 @@
   // If |warm_reset| is true, sets the warm reset to indicate a warm reset is
   // needed on the next reboot. Otherwise, clears the flag.
   virtual void SetWarmReset(bool warm_reset) = 0;
-
-  // If not reset, sets the vbmeta digest of the inactive slot as a sysprop.
-  // Otherwise, clears the sysprop.
-  virtual void SetVbmetaDigestForInactiveSlot(bool reset) = 0;
-
-  // Return the version/timestamp for partition `partition_name`.
-  // Don't make any assumption about the formatting of returned string.
-  // Only used for logging/debugging purposes.
-  virtual std::string GetVersionForLogging(
-      const std::string& partition_name) const = 0;
-
-  // Return true if and only if `new_version` is "newer" than the
-  // version number of partition `partition_name`. The notion of
-  // "newer" is defined by this function. Caller should not make
-  // any assumption about the underlying logic.
-  // Return:
-  // - kSuccess if update is valid.
-  // - kPayloadTimestampError if downgrade is detected
-  // - kDownloadManifestParseError if |new_version| has an incorrect format
-  // - Other error values if the source of error is known, or kError for
-  //   a generic error on the device.
-  virtual ErrorCode IsPartitionUpdateValid(
-      const std::string& partition_name,
-      const std::string& new_version) const = 0;
-
-  virtual const char* GetPartitionMountOptions(
-      const std::string& partition_name) const = 0;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/common/hash_calculator.cc b/common/hash_calculator.cc
index 60812d5..d010a53 100644
--- a/common/hash_calculator.cc
+++ b/common/hash_calculator.cc
@@ -95,11 +95,6 @@
   return RawHashOfBytes(data.data(), data.size(), out_hash);
 }
 
-bool HashCalculator::RawHashOfFile(const string& name, brillo::Blob* out_hash) {
-  const auto file_size = utils::FileSize(name);
-  return RawHashOfFile(name, file_size, out_hash) == file_size;
-}
-
 off_t HashCalculator::RawHashOfFile(const string& name,
                                     off_t length,
                                     brillo::Blob* out_hash) {
diff --git a/common/hash_calculator.h b/common/hash_calculator.h
index 4426128..b7e4d86 100644
--- a/common/hash_calculator.h
+++ b/common/hash_calculator.h
@@ -75,7 +75,6 @@
   static off_t RawHashOfFile(const std::string& name,
                              off_t length,
                              brillo::Blob* out_hash);
-  static bool RawHashOfFile(const std::string& name, brillo::Blob* out_hash);
 
  private:
   // If non-empty, the final raw hash. Will only be set to non-empty when
diff --git a/common/hash_calculator_unittest.cc b/common/hash_calculator_unittest.cc
index fe7d543..e8f73d5 100644
--- a/common/hash_calculator_unittest.cc
+++ b/common/hash_calculator_unittest.cc
@@ -104,7 +104,7 @@
 }
 
 TEST_F(HashCalculatorTest, UpdateFileSimpleTest) {
-  ScopedTempFile data_file("data.XXXXXX");
+  test_utils::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) {
-  ScopedTempFile data_file("data.XXXXXX");
+  test_utils::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_common.cc b/common/http_common.cc
index c8bac47..5f234b0 100644
--- a/common/http_common.cc
+++ b/common/http_common.cc
@@ -21,7 +21,6 @@
 #include <cstdlib>
 
 #include <base/macros.h>
-#include <base/stl_util.h>
 
 namespace chromeos_update_engine {
 
@@ -57,7 +56,7 @@
 
   bool is_found = false;
   size_t i;
-  for (i = 0; i < base::size(http_response_table); i++)
+  for (i = 0; i < arraysize(http_response_table); i++)
     if ((is_found = (http_response_table[i].code == code)))
       break;
 
@@ -78,7 +77,7 @@
 
   bool is_found = false;
   size_t i;
-  for (i = 0; i < base::size(http_content_type_table); i++)
+  for (i = 0; i < arraysize(http_content_type_table); i++)
     if ((is_found = (http_content_type_table[i].type == type)))
       break;
 
diff --git a/common/http_fetcher.h b/common/http_fetcher.h
index 7fa5f09..2b4fc83 100644
--- a/common/http_fetcher.h
+++ b/common/http_fetcher.h
@@ -28,7 +28,6 @@
 #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"
 
 // This class is a simple wrapper around an HTTP library (libcurl). We can
@@ -59,12 +58,6 @@
   HttpFetcherDelegate* delegate() const { return delegate_; }
   int http_response_code() const { return http_response_code_; }
 
-  // Returns additional error code that can't be expressed in terms of an HTTP
-  // response code. For example, if there was a specific internal error code in
-  // the objects used in the implementation of this class (like libcurl) that we
-  // are interested about, we can communicate it through this value.
-  ErrorCode GetAuxiliaryErrorCode() const { return auxiliary_error_code_; }
-
   // Optional: Post data to the server. The HttpFetcher should make a copy
   // of this data and upload it via HTTP POST during the transfer. The type of
   // the data is necessary for properly setting the Content-Type HTTP header.
@@ -106,14 +99,6 @@
   virtual void SetHeader(const std::string& header_name,
                          const std::string& header_value) = 0;
 
-  // Only used for testing.
-  // If |header_name| is set, the value will be set into |header_value|.
-  // On success the boolean true will be returned, hoewever on failture to find
-  // the |header_name| in the header the return value will be false. The state
-  // in which |header_value| is left in for failures is an empty string.
-  virtual bool GetHeader(const std::string& header_name,
-                         std::string* header_value) const = 0;
-
   // If data is coming in too quickly, you can call Pause() to pause the
   // transfer. The delegate will not have ReceivedBytes() called while
   // an HttpFetcher is paused.
@@ -165,10 +150,6 @@
   // set to the response code when the transfer is complete.
   int http_response_code_;
 
-  // Set when there is an error that can't be expressed in the form of
-  // |http_response_code_|.
-  ErrorCode auxiliary_error_code_{ErrorCode::kSuccess};
-
   // The delegate; may be null.
   HttpFetcherDelegate* delegate_;
 
diff --git a/common/http_fetcher_unittest.cc b/common/http_fetcher_unittest.cc
index 99ea99b..237ea20 100644
--- a/common/http_fetcher_unittest.cc
+++ b/common/http_fetcher_unittest.cc
@@ -28,25 +28,15 @@
 #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>
 #include <brillo/message_loops/message_loop_utils.h>
-#ifdef __CHROMEOS__
-#include <brillo/process/process.h>
-#else
 #include <brillo/process.h>
-#endif  // __CHROMEOS__
 #include <brillo/streams/file_stream.h>
 #include <brillo/streams/stream.h>
 #include <gtest/gtest.h>
@@ -369,7 +359,7 @@
   HttpServer* CreateServer() override { return new NullHttpServer; }
 
  private:
-  ScopedTempFile temp_file_{"ue_file_fetcher.XXXXXX"};
+  test_utils::ScopedTempFile temp_file_{"ue_file_fetcher.XXXXXX"};
 };
 
 class MultiRangeHttpFetcherOverFileFetcherTest : public FileFetcherTest {
@@ -408,13 +398,8 @@
 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_;
 
@@ -1064,7 +1049,7 @@
   unique_ptr<HttpServer> server(this->test_.CreateServer());
   ASSERT_TRUE(server->started_);
 
-  for (size_t c = 0; c < base::size(kRedirectCodes); ++c) {
+  for (size_t c = 0; c < arraysize(kRedirectCodes); ++c) {
     const string url = base::StringPrintf(
         "/redirect/%d/download/%d", kRedirectCodes[c], kMediumLength);
     RedirectTest(server.get(), true, url, this->test_.NewLargeFetcher());
@@ -1081,7 +1066,7 @@
   string url;
   for (int r = 0; r < kDownloadMaxRedirects; r++) {
     url += base::StringPrintf("/redirect/%d",
-                              kRedirectCodes[r % base::size(kRedirectCodes)]);
+                              kRedirectCodes[r % arraysize(kRedirectCodes)]);
   }
   url += base::StringPrintf("/download/%d", kMediumLength);
   RedirectTest(server.get(), true, url, this->test_.NewLargeFetcher());
@@ -1097,7 +1082,7 @@
   string url;
   for (int r = 0; r < kDownloadMaxRedirects + 1; r++) {
     url += base::StringPrintf("/redirect/%d",
-                              kRedirectCodes[r % base::size(kRedirectCodes)]);
+                              kRedirectCodes[r % arraysize(kRedirectCodes)]);
   }
   url += base::StringPrintf("/download/%d", kMediumLength);
   RedirectTest(server.get(), false, url, this->test_.NewLargeFetcher());
diff --git a/common/hwid_override.cc b/common/hwid_override.cc
index 1bb0f8f..8800e94 100644
--- a/common/hwid_override.cc
+++ b/common/hwid_override.cc
@@ -16,12 +16,14 @@
 
 #include "update_engine/common/hwid_override.h"
 
+#include <map>
 #include <string>
 
 #include <base/files/file_path.h>
 #include <base/files/file_util.h>
 #include <brillo/key_value_store.h>
 
+using std::map;
 using std::string;
 
 namespace chromeos_update_engine {
diff --git a/common/mock_action_processor.h b/common/mock_action_processor.h
index 9785776..4c62109 100644
--- a/common/mock_action_processor.h
+++ b/common/mock_action_processor.h
@@ -32,8 +32,6 @@
   MOCK_METHOD0(StartProcessing, void());
   MOCK_METHOD1(EnqueueAction, void(AbstractAction* action));
 
-  MOCK_METHOD2(ActionComplete, void(AbstractAction*, ErrorCode));
-
   // This is a legacy workaround described in:
   // https://github.com/google/googletest/blob/master/googlemock/docs/CookBook.md#legacy-workarounds-for-move-only-types-legacymoveonly
   void EnqueueAction(std::unique_ptr<AbstractAction> action) override {
diff --git a/common/mock_boot_control.h b/common/mock_boot_control.h
deleted file mode 100644
index f75ce5e..0000000
--- a/common/mock_boot_control.h
+++ /dev/null
@@ -1,74 +0,0 @@
-//
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef UPDATE_ENGINE_COMMON_MOCK_BOOT_CONTROL_H_
-#define UPDATE_ENGINE_COMMON_MOCK_BOOT_CONTROL_H_
-
-#include <memory>
-#include <string>
-
-#include <gmock/gmock.h>
-
-#include "update_engine/common/boot_control_stub.h"
-
-namespace chromeos_update_engine {
-
-class MockBootControl final : public BootControlStub {
- public:
-  MOCK_METHOD(bool,
-              IsSlotMarkedSuccessful,
-              (BootControlInterface::Slot),
-              (const override));
-  MOCK_METHOD(unsigned int, GetNumSlots, (), (const override));
-  MOCK_METHOD(BootControlInterface::Slot, GetCurrentSlot, (), (const override));
-  MOCK_METHOD(bool,
-              GetPartitionDevice,
-              (const std::string&, Slot, bool, std::string*, bool*),
-              (const override));
-  MOCK_METHOD(bool,
-              GetPartitionDevice,
-              (const std::string&, BootControlInterface::Slot, std::string*),
-              (const override));
-  MOCK_METHOD(std::optional<PartitionDevice>,
-              GetPartitionDevice,
-              (const std::string&, uint32_t, uint32_t, bool),
-              (const override));
-
-  MOCK_METHOD(bool,
-              IsSlotBootable,
-              (BootControlInterface::Slot),
-              (const override));
-  MOCK_METHOD(bool,
-              MarkSlotUnbootable,
-              (BootControlInterface::Slot),
-              (override));
-  MOCK_METHOD(bool,
-              SetActiveBootSlot,
-              (BootControlInterface::Slot),
-              (override));
-  MOCK_METHOD(bool,
-              MarkBootSuccessfulAsync,
-              (base::Callback<void(bool)>),
-              (override));
-  MOCK_METHOD(DynamicPartitionControlInterface*,
-              GetDynamicPartitionControl,
-              (),
-              (override));
-};
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_COMMON_MOCK_BOOT_CONTROL_H_
diff --git a/common/mock_dynamic_partition_control.h b/common/mock_dynamic_partition_control.h
deleted file mode 100644
index bfd1b0c..0000000
--- a/common/mock_dynamic_partition_control.h
+++ /dev/null
@@ -1,89 +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 <stdint.h>
-
-#include <memory>
-#include <set>
-#include <string>
-#include <vector>
-
-#include <gmock/gmock.h>
-
-#include "update_engine/common/dynamic_partition_control_interface.h"
-#include "update_engine/payload_consumer/file_descriptor.h"
-
-namespace chromeos_update_engine {
-
-class MockDynamicPartitionControl : public DynamicPartitionControlInterface {
- public:
-  MOCK_METHOD(void, Cleanup, (), (override));
-  MOCK_METHOD(bool, GetDeviceDir, (std::string*), (override));
-  MOCK_METHOD(FeatureFlag, GetDynamicPartitionsFeatureFlag, (), (override));
-  MOCK_METHOD(FeatureFlag, GetVirtualAbCompressionFeatureFlag, (), (override));
-  MOCK_METHOD(FeatureFlag, GetVirtualAbFeatureFlag, (), (override));
-  MOCK_METHOD(bool, FinishUpdate, (bool), (override));
-  MOCK_METHOD(FileDescriptorPtr,
-              OpenCowFd,
-              (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,
-              OptimizeOperation,
-              (const std::string&, const InstallOperation&, InstallOperation*),
-              (override));
-
-  std::unique_ptr<android::snapshot::ISnapshotWriter> OpenCowWriter(
-      const std::string& unsuffixed_partition_name,
-      const std::optional<std::string>&,
-      bool is_append = false) override {
-    return nullptr;
-  }
-
-  MOCK_METHOD(
-      bool,
-      PreparePartitionsForUpdate,
-      (uint32_t, uint32_t, const DeltaArchiveManifest&, bool, uint64_t*),
-      (override));
-
-  MOCK_METHOD(bool, ResetUpdate, (PrefsInterface*), (override));
-  MOCK_METHOD(std::unique_ptr<AbstractAction>,
-              GetCleanupPreviousUpdateAction,
-              (BootControlInterface*,
-               PrefsInterface*,
-               CleanupPreviousUpdateActionDelegateInterface*),
-              (override));
-  MOCK_METHOD(bool,
-              ListDynamicPartitionsForSlot,
-              (uint32_t, uint32_t, std::vector<std::string>*),
-              (override));
-
-  MOCK_METHOD(bool,
-              VerifyExtentsForUntouchedPartitions,
-              (uint32_t, uint32_t, const std::vector<std::string>&),
-              (override));
-  MOCK_METHOD(bool,
-              IsDynamicPartition,
-              (const std::string&, uint32_t slot),
-              (override));
-  MOCK_METHOD(bool, UpdateUsesSnapshotCompression, (), (override));
-};
-
-}  // namespace chromeos_update_engine
diff --git a/common/mock_excluder.h b/common/mock_excluder.h
deleted file mode 100644
index 560ba0d..0000000
--- a/common/mock_excluder.h
+++ /dev/null
@@ -1,37 +0,0 @@
-//
-// 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_COMMON_MOCK_APP_EXCLUDER_H_
-#define UPDATE_ENGINE_COMMON_MOCK_APP_EXCLUDER_H_
-
-#include "update_engine/common/excluder_interface.h"
-
-#include <string>
-
-#include <gmock/gmock.h>
-
-namespace chromeos_update_engine {
-
-class MockExcluder : public ExcluderInterface {
- public:
-  MOCK_METHOD(bool, Exclude, (const std::string&), (override));
-  MOCK_METHOD(bool, IsExcluded, (const std::string&), (override));
-  MOCK_METHOD(bool, Reset, (), (override));
-};
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_COMMON_MOCK_APP_EXCLUDER_H_
diff --git a/common/mock_hardware.h b/common/mock_hardware.h
index 071906b..84c0c5b 100644
--- a/common/mock_hardware.h
+++ b/common/mock_hardware.h
@@ -45,6 +45,11 @@
     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));
@@ -85,6 +90,8 @@
   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/common/mock_http_fetcher.cc b/common/mock_http_fetcher.cc
index 1b3cd7d..10e3b9e 100644
--- a/common/mock_http_fetcher.cc
+++ b/common/mock_http_fetcher.cc
@@ -22,7 +22,6 @@
 #include <base/logging.h>
 #include <base/strings/string_util.h>
 #include <base/time/time.h>
-#include <brillo/message_loops/message_loop.h>
 #include <gtest/gtest.h>
 
 // This is a mock implementation of HttpFetcher which is useful for testing.
@@ -44,12 +43,12 @@
     SignalTransferComplete();
     return;
   }
-  if (sent_offset_ < data_.size())
+  if (sent_size_ < data_.size())
     SendData(true);
 }
 
 void MockHttpFetcher::SendData(bool skip_delivery) {
-  if (fail_transfer_ || sent_offset_ == data_.size()) {
+  if (fail_transfer_ || sent_size_ == data_.size()) {
     SignalTransferComplete();
     return;
   }
@@ -61,22 +60,19 @@
 
   // Setup timeout callback even if the transfer is about to be completed in
   // order to get a call to |TransferComplete|.
-  if (timeout_id_ == MessageLoop::kTaskIdNull && delay_) {
-    CHECK(MessageLoop::current());
+  if (timeout_id_ == MessageLoop::kTaskIdNull) {
     timeout_id_ = MessageLoop::current()->PostDelayedTask(
         FROM_HERE,
         base::Bind(&MockHttpFetcher::TimeoutCallback, base::Unretained(this)),
         base::TimeDelta::FromMilliseconds(10));
   }
 
-  if (!skip_delivery || !delay_) {
+  if (!skip_delivery) {
     const size_t chunk_size =
-        min(kMockHttpFetcherChunkSize, data_.size() - sent_offset_);
-    sent_offset_ += chunk_size;
-    bytes_sent_ += chunk_size;
+        min(kMockHttpFetcherChunkSize, data_.size() - sent_size_);
+    sent_size_ += chunk_size;
     CHECK(delegate_);
-    delegate_->ReceivedBytes(
-        this, &data_[sent_offset_ - chunk_size], chunk_size);
+    delegate_->ReceivedBytes(this, &data_[sent_size_ - chunk_size], chunk_size);
   }
   // We may get terminated and deleted right after |ReceivedBytes| call, so we
   // should not access any class member variable after this call.
@@ -85,7 +81,7 @@
 void MockHttpFetcher::TimeoutCallback() {
   CHECK(!paused_);
   timeout_id_ = MessageLoop::kTaskIdNull;
-  CHECK_LE(sent_offset_, data_.size());
+  CHECK_LE(sent_size_, data_.size());
   // Same here, we should not access any member variable after this call.
   SendData(false);
 }
@@ -94,15 +90,10 @@
 // The transfer cannot be resumed.
 void MockHttpFetcher::TerminateTransfer() {
   LOG(INFO) << "Terminating transfer.";
-  // During testing, MessageLoop may or may not be available.
-  // So don't call CancelTask() unless necessary.
-  if (timeout_id_ != MessageLoop::kTaskIdNull) {
-    MessageLoop::current()->CancelTask(timeout_id_);
-    timeout_id_ = MessageLoop::kTaskIdNull;
-  }
-  if (delegate_) {
-    delegate_->TransferTerminated(this);
-  }
+  // Kill any timeout, it is ok to call with kTaskIdNull.
+  MessageLoop::current()->CancelTask(timeout_id_);
+  timeout_id_ = MessageLoop::kTaskIdNull;
+  delegate_->TransferTerminated(this);
 }
 
 void MockHttpFetcher::SetHeader(const std::string& header_name,
diff --git a/common/mock_http_fetcher.h b/common/mock_http_fetcher.h
index ea5b83d..492e6ce 100644
--- a/common/mock_http_fetcher.h
+++ b/common/mock_http_fetcher.h
@@ -46,7 +46,7 @@
                   size_t size,
                   ProxyResolver* proxy_resolver)
       : HttpFetcher(proxy_resolver),
-        sent_offset_(0),
+        sent_size_(0),
         timeout_id_(brillo::MessageLoop::kTaskIdNull),
         paused_(false),
         fail_transfer_(false),
@@ -64,7 +64,7 @@
 
   // Ignores this.
   void SetOffset(off_t offset) override {
-    sent_offset_ = offset;
+    sent_size_ = offset;
     if (delegate_)
       delegate_->SeekToOffset(offset);
   }
@@ -76,8 +76,8 @@
   void set_connect_timeout(int connect_timeout_seconds) override {}
   void set_max_retry_count(int max_retry_count) override {}
 
-  // No bytes were downloaded in the mock class.
-  size_t GetBytesDownloaded() override { return bytes_sent_; }
+  // Dummy: no bytes were downloaded.
+  size_t GetBytesDownloaded() override { return sent_size_; }
 
   // Begins the transfer if it hasn't already begun.
   void BeginTransfer(const std::string& url) override;
@@ -89,12 +89,6 @@
   void SetHeader(const std::string& header_name,
                  const std::string& header_value) override;
 
-  bool GetHeader(const std::string& header_name,
-                 std::string* header_value) const override {
-    header_value->clear();
-    return false;
-  }
-
   // Return the value of the header |header_name| or the empty string if not
   // set.
   std::string GetHeader(const std::string& header_name) const;
@@ -113,8 +107,6 @@
 
   const brillo::Blob& post_data() const { return post_data_; }
 
-  void set_delay(bool delay) { delay_ = delay; }
-
  private:
   // Sends data to the delegate and sets up a timeout callback if needed. There
   // must be a delegate. If |skip_delivery| is true, no bytes will be delivered,
@@ -131,11 +123,8 @@
   // A full copy of the data we'll return to the delegate
   brillo::Blob data_;
 
-  // The current offset, marks the first byte that will be sent next
-  size_t sent_offset_{0};
-
-  // Total number of bytes transferred
-  size_t bytes_sent_{0};
+  // The number of bytes we've sent so far
+  size_t sent_size_;
 
   // The extra headers set.
   std::map<std::string, std::string> extra_headers_;
@@ -145,16 +134,13 @@
   brillo::MessageLoop::TaskId timeout_id_;
 
   // True iff the fetcher is paused.
-  bool paused_{false};
+  bool paused_;
 
   // Set to true if the transfer should fail.
-  bool fail_transfer_{false};
+  bool fail_transfer_;
 
   // Set to true if BeginTransfer should EXPECT fail.
-  bool never_use_{false};
-
-  // Whether it should wait for 10ms before sending data to delegates
-  bool delay_{true};
+  bool never_use_;
 
   DISALLOW_COPY_AND_ASSIGN(MockHttpFetcher);
 };
diff --git a/common/mock_prefs.h b/common/mock_prefs.h
index 49431fb..2582e19 100644
--- a/common/mock_prefs.h
+++ b/common/mock_prefs.h
@@ -18,7 +18,6 @@
 #define UPDATE_ENGINE_COMMON_MOCK_PREFS_H_
 
 #include <string>
-#include <vector>
 
 #include <gmock/gmock.h>
 
@@ -31,7 +30,8 @@
  public:
   MOCK_CONST_METHOD2(GetString,
                      bool(const std::string& key, std::string* value));
-  MOCK_METHOD2(SetString, bool(const std::string& key, std::string_view value));
+  MOCK_METHOD2(SetString,
+               bool(const std::string& key, const std::string& value));
   MOCK_CONST_METHOD2(GetInt64, bool(const std::string& key, int64_t* value));
   MOCK_METHOD2(SetInt64, bool(const std::string& key, const int64_t value));
 
@@ -40,12 +40,6 @@
 
   MOCK_CONST_METHOD1(Exists, bool(const std::string& key));
   MOCK_METHOD1(Delete, bool(const std::string& key));
-  MOCK_METHOD2(Delete,
-               bool(const std::string& key,
-                    const std::vector<std::string>& nss));
-
-  MOCK_CONST_METHOD2(GetSubKeys,
-                     bool(const std::string&, std::vector<std::string>*));
 
   MOCK_METHOD2(AddObserver, void(const std::string& key, ObserverInterface*));
   MOCK_METHOD2(RemoveObserver,
diff --git a/common/multi_range_http_fetcher.h b/common/multi_range_http_fetcher.h
index ef32f0d..f57ea7f 100644
--- a/common/multi_range_http_fetcher.h
+++ b/common/multi_range_http_fetcher.h
@@ -83,11 +83,6 @@
     base_fetcher_->SetHeader(header_name, header_value);
   }
 
-  bool GetHeader(const std::string& header_name,
-                 std::string* header_value) const override {
-    return base_fetcher_->GetHeader(header_name, header_value);
-  }
-
   void Pause() override { base_fetcher_->Pause(); }
 
   void Unpause() override { base_fetcher_->Unpause(); }
diff --git a/common/platform_constants.h b/common/platform_constants.h
index 06399e5..243af69 100644
--- a/common/platform_constants.h
+++ b/common/platform_constants.h
@@ -54,11 +54,9 @@
 // The stateful directory used by update_engine.
 extern const char kNonVolatileDirectory[];
 
-#ifdef __ANDROID_RECOVERY__
-constexpr bool kIsRecovery = true;
-#else
-constexpr bool kIsRecovery = false;
-#endif
+// Options passed to the filesystem when mounting the new partition during
+// postinstall.
+extern const char kPostinstallMountOptions[];
 
 }  // namespace constants
 }  // namespace chromeos_update_engine
diff --git a/aosp/platform_constants_android.cc b/common/platform_constants_android.cc
similarity index 93%
rename from aosp/platform_constants_android.cc
rename to common/platform_constants_android.cc
index a0a2a5e..f468c3b 100644
--- a/aosp/platform_constants_android.cc
+++ b/common/platform_constants_android.cc
@@ -31,6 +31,8 @@
 // No deadline file API support on Android.
 const char kOmahaResponseDeadlineFile[] = "";
 const char kNonVolatileDirectory[] = "/data/misc/update_engine";
+const char kPostinstallMountOptions[] =
+    "context=u:object_r:postinstall_file:s0";
 
 }  // namespace constants
 }  // namespace chromeos_update_engine
diff --git a/cros/platform_constants_chromeos.cc b/common/platform_constants_chromeos.cc
similarity index 96%
rename from cros/platform_constants_chromeos.cc
rename to common/platform_constants_chromeos.cc
index 5a5a521..fe94a45 100644
--- a/cros/platform_constants_chromeos.cc
+++ b/common/platform_constants_chromeos.cc
@@ -32,6 +32,7 @@
 const char kOmahaResponseDeadlineFile[] = "/tmp/update-check-response-deadline";
 // This directory is wiped during powerwash.
 const char kNonVolatileDirectory[] = "/var/lib/update_engine";
+const char kPostinstallMountOptions[] = "";
 
 }  // namespace constants
 }  // namespace chromeos_update_engine
diff --git a/common/prefs.cc b/common/prefs.cc
index 1e06be4..12d06c0 100644
--- a/common/prefs.cc
+++ b/common/prefs.cc
@@ -18,44 +18,22 @@
 
 #include <algorithm>
 
-#include <base/files/file_enumerator.h>
 #include <base/files/file_util.h>
 #include <base/logging.h>
 #include <base/strings/string_number_conversions.h>
-#include <base/strings/string_split.h>
 #include <base/strings/string_util.h>
 
 #include "update_engine/common/utils.h"
 
 using std::string;
-using std::vector;
 
 namespace chromeos_update_engine {
 
-namespace {
-
-void DeleteEmptyDirectories(const base::FilePath& path) {
-  base::FileEnumerator path_enum(
-      path, false /* recursive */, base::FileEnumerator::DIRECTORIES);
-  for (base::FilePath dir_path = path_enum.Next(); !dir_path.empty();
-       dir_path = path_enum.Next()) {
-    DeleteEmptyDirectories(dir_path);
-    if (base::IsDirectoryEmpty(dir_path))
-#if BASE_VER < 800000
-      base::DeleteFile(dir_path, false);
-#else
-      base::DeleteFile(dir_path);
-#endif
-  }
-}
-
-}  // namespace
-
 bool PrefsBase::GetString(const string& key, string* value) const {
   return storage_->GetKey(key, value);
 }
 
-bool PrefsBase::SetString(const string& key, std::string_view value) {
+bool PrefsBase::SetString(const string& key, const string& value) {
   TEST_AND_RETURN_FALSE(storage_->SetKey(key, value));
   const auto observers_for_key = observers_.find(key);
   if (observers_for_key != observers_.end()) {
@@ -76,7 +54,7 @@
 }
 
 bool PrefsBase::SetInt64(const string& key, const int64_t value) {
-  return SetString(key, base::NumberToString(value));
+  return SetString(key, base::Int64ToString(value));
 }
 
 bool PrefsBase::GetBoolean(const string& key, bool* value) const {
@@ -114,28 +92,6 @@
   return true;
 }
 
-bool PrefsBase::Delete(const string& pref_key, const vector<string>& nss) {
-  // Delete pref key for platform.
-  bool success = Delete(pref_key);
-  // Delete pref key in each namespace.
-  for (const auto& ns : nss) {
-    vector<string> namespace_keys;
-    success = GetSubKeys(ns, &namespace_keys) && success;
-    for (const auto& key : namespace_keys) {
-      auto last_key_seperator = key.find_last_of(kKeySeparator);
-      if (last_key_seperator != string::npos &&
-          pref_key == key.substr(last_key_seperator + 1)) {
-        success = Delete(key) && success;
-      }
-    }
-  }
-  return success;
-}
-
-bool PrefsBase::GetSubKeys(const string& ns, vector<string>* keys) const {
-  return storage_->GetSubKeys(ns, keys);
-}
-
 void PrefsBase::AddObserver(const string& key, ObserverInterface* observer) {
   observers_[key].push_back(observer);
 }
@@ -148,10 +104,6 @@
     observers_for_key.erase(observer_it);
 }
 
-string PrefsInterface::CreateSubKey(const vector<string>& ns_and_key) {
-  return base::JoinString(ns_and_key, string(1, kKeySeparator));
-}
-
 // Prefs
 
 bool Prefs::Init(const base::FilePath& prefs_dir) {
@@ -160,8 +112,6 @@
 
 bool Prefs::FileStorage::Init(const base::FilePath& prefs_dir) {
   prefs_dir_ = prefs_dir;
-  // Delete empty directories. Ignore errors when deleting empty directories.
-  DeleteEmptyDirectories(prefs_dir_);
   return true;
 }
 
@@ -169,30 +119,13 @@
   base::FilePath filename;
   TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
   if (!base::ReadFileToString(filename, value)) {
+    LOG(INFO) << key << " not present in " << prefs_dir_.value();
     return false;
   }
   return true;
 }
 
-bool Prefs::FileStorage::GetSubKeys(const string& ns,
-                                    vector<string>* keys) const {
-  base::FilePath filename;
-  TEST_AND_RETURN_FALSE(GetFileNameForKey(ns, &filename));
-  base::FileEnumerator namespace_enum(
-      prefs_dir_, true, base::FileEnumerator::FILES);
-  for (base::FilePath f = namespace_enum.Next(); !f.empty();
-       f = namespace_enum.Next()) {
-    auto filename_str = filename.value();
-    if (f.value().compare(0, filename_str.length(), filename_str) == 0) {
-      // Only return the key portion excluding the |prefs_dir_| with slash.
-      keys->push_back(f.value().substr(
-          prefs_dir_.AsEndingWithSeparator().value().length()));
-    }
-  }
-  return true;
-}
-
-bool Prefs::FileStorage::SetKey(const string& key, std::string_view value) {
+bool Prefs::FileStorage::SetKey(const string& key, const string& value) {
   base::FilePath filename;
   TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
   if (!base::DirectoryExists(filename.DirName())) {
@@ -214,21 +147,19 @@
 bool Prefs::FileStorage::DeleteKey(const string& key) {
   base::FilePath filename;
   TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
-#if BASE_VER < 800000
   TEST_AND_RETURN_FALSE(base::DeleteFile(filename, false));
-#else
-  TEST_AND_RETURN_FALSE(base::DeleteFile(filename));
-#endif
   return true;
 }
 
 bool Prefs::FileStorage::GetFileNameForKey(const string& key,
                                            base::FilePath* filename) const {
-  // Allows only non-empty keys containing [A-Za-z0-9_-/].
+  // Allows only non-empty keys containing [A-Za-z0-9_-].
   TEST_AND_RETURN_FALSE(!key.empty());
-  for (char c : key)
+  for (size_t i = 0; i < key.size(); ++i) {
+    char c = key.at(i);
     TEST_AND_RETURN_FALSE(base::IsAsciiAlpha(c) || base::IsAsciiDigit(c) ||
-                          c == '_' || c == '-' || c == kKeySeparator);
+                          c == '_' || c == '-');
+  }
   *filename = prefs_dir_.Append(key);
   return true;
 }
@@ -244,26 +175,8 @@
   return true;
 }
 
-bool MemoryPrefs::MemoryStorage::GetSubKeys(const string& ns,
-                                            vector<string>* keys) const {
-  using value_type = decltype(values_)::value_type;
-  using key_type = decltype(values_)::key_type;
-  auto lower_comp = [](const value_type& pr, const key_type& ns) {
-    return pr.first.substr(0, ns.length()) < ns;
-  };
-  auto upper_comp = [](const key_type& ns, const value_type& pr) {
-    return ns < pr.first.substr(0, ns.length());
-  };
-  auto lower_it =
-      std::lower_bound(begin(values_), end(values_), ns, lower_comp);
-  auto upper_it = std::upper_bound(lower_it, end(values_), ns, upper_comp);
-  while (lower_it != upper_it)
-    keys->push_back((lower_it++)->first);
-  return true;
-}
-
 bool MemoryPrefs::MemoryStorage::SetKey(const string& key,
-                                        std::string_view value) {
+                                        const string& value) {
   values_[key] = value;
   return true;
 }
@@ -274,8 +187,9 @@
 
 bool MemoryPrefs::MemoryStorage::DeleteKey(const string& key) {
   auto it = values_.find(key);
-  if (it != values_.end())
-    values_.erase(it);
+  if (it == values_.end())
+    return false;
+  values_.erase(it);
   return true;
 }
 
diff --git a/common/prefs.h b/common/prefs.h
index 93477dd..0116454 100644
--- a/common/prefs.h
+++ b/common/prefs.h
@@ -19,7 +19,6 @@
 
 #include <map>
 #include <string>
-#include <string_view>
 #include <vector>
 
 #include <base/files/file_path.h>
@@ -43,14 +42,9 @@
     // Returns whether the operation succeeded.
     virtual bool GetKey(const std::string& key, std::string* value) const = 0;
 
-    // Get the keys stored within the namespace. If there are no keys in the
-    // namespace, |keys| will be empty. Returns whether the operation succeeded.
-    virtual bool GetSubKeys(const std::string& ns,
-                            std::vector<std::string>* keys) const = 0;
-
     // Set the value of the key named |key| to |value| regardless of the
     // previous value. Returns whether the operation succeeded.
-    virtual bool SetKey(const std::string& key, std::string_view value) = 0;
+    virtual bool SetKey(const std::string& key, const std::string& value) = 0;
 
     // Returns whether the key named |key| exists.
     virtual bool KeyExists(const std::string& key) const = 0;
@@ -67,7 +61,7 @@
 
   // PrefsInterface methods.
   bool GetString(const std::string& key, std::string* value) const override;
-  bool SetString(const std::string& key, std::string_view value) override;
+  bool SetString(const std::string& key, const std::string& value) override;
   bool GetInt64(const std::string& key, int64_t* value) const override;
   bool SetInt64(const std::string& key, const int64_t value) override;
   bool GetBoolean(const std::string& key, bool* value) const override;
@@ -75,11 +69,6 @@
 
   bool Exists(const std::string& key) const override;
   bool Delete(const std::string& key) override;
-  bool Delete(const std::string& pref_key,
-              const std::vector<std::string>& nss) override;
-
-  bool GetSubKeys(const std::string& ns,
-                  std::vector<std::string>* keys) const override;
 
   void AddObserver(const std::string& key,
                    ObserverInterface* observer) override;
@@ -122,9 +111,7 @@
 
     // PrefsBase::StorageInterface overrides.
     bool GetKey(const std::string& key, std::string* value) const override;
-    bool GetSubKeys(const std::string& ns,
-                    std::vector<std::string>* keys) const override;
-    bool SetKey(const std::string& key, std::string_view value) override;
+    bool SetKey(const std::string& key, const std::string& value) override;
     bool KeyExists(const std::string& key) const override;
     bool DeleteKey(const std::string& key) override;
 
@@ -162,9 +149,7 @@
 
     // PrefsBase::StorageInterface overrides.
     bool GetKey(const std::string& key, std::string* value) const override;
-    bool GetSubKeys(const std::string& ns,
-                    std::vector<std::string>* keys) const override;
-    bool SetKey(const std::string& key, std::string_view value) override;
+    bool SetKey(const std::string& key, const std::string& value) override;
     bool KeyExists(const std::string& key) const override;
     bool DeleteKey(const std::string& key) override;
 
diff --git a/common/prefs_interface.h b/common/prefs_interface.h
index e773a35..03ae3ec 100644
--- a/common/prefs_interface.h
+++ b/common/prefs_interface.h
@@ -20,7 +20,6 @@
 #include <stdint.h>
 
 #include <string>
-#include <vector>
 
 namespace chromeos_update_engine {
 
@@ -52,7 +51,7 @@
 
   // Associates |key| with a string |value|. Returns true on success,
   // false otherwise.
-  virtual bool SetString(const std::string& key, std::string_view value) = 0;
+  virtual bool SetString(const std::string& key, const std::string& value) = 0;
 
   // Gets an int64_t |value| associated with |key|. Returns true on
   // success, false on failure (including when the |key| is not
@@ -80,19 +79,6 @@
   // this key. Calling with non-existent keys does nothing.
   virtual bool Delete(const std::string& key) = 0;
 
-  // Deletes the pref key from platform and given namespace subdirectories.
-  // Keys are matched against end of pref keys in each namespace.
-  // Returns true if all deletes were successful.
-  virtual bool Delete(const std::string& pref_key,
-                      const std::vector<std::string>& nss) = 0;
-
-  // Creates a key which is part of a sub preference.
-  static std::string CreateSubKey(const std::vector<std::string>& ns_with_key);
-
-  // Returns a list of keys within the namespace.
-  virtual bool GetSubKeys(const std::string& ns,
-                          std::vector<std::string>* keys) const = 0;
-
   // Add an observer to watch whenever the given |key| is modified. The
   // OnPrefSet() and OnPrefDelete() methods will be called whenever any of the
   // Set*() methods or the Delete() method are called on the given key,
@@ -104,10 +90,6 @@
   // anymore for future Set*() and Delete() method calls.
   virtual void RemoveObserver(const std::string& key,
                               ObserverInterface* observer) = 0;
-
- protected:
-  // Key separator used to create sub key and get file names,
-  static const char kKeySeparator = '/';
 };
 
 }  // namespace chromeos_update_engine
diff --git a/common/prefs_unittest.cc b/common/prefs_unittest.cc
index a5f46e5..cb6fc70 100644
--- a/common/prefs_unittest.cc
+++ b/common/prefs_unittest.cc
@@ -20,7 +20,6 @@
 
 #include <limits>
 #include <string>
-#include <vector>
 
 #include <base/files/file_util.h>
 #include <base/files/scoped_temp_dir.h>
@@ -31,11 +30,8 @@
 #include <gtest/gtest.h>
 
 using std::string;
-using std::vector;
 using testing::_;
-using testing::ElementsAre;
 using testing::Eq;
-using testing::UnorderedElementsAre;
 
 namespace {
 // Test key used along the tests.
@@ -44,109 +40,12 @@
 
 namespace chromeos_update_engine {
 
-class BasePrefsTest : public ::testing::Test {
- protected:
-  void MultiNamespaceKeyTest() {
-    ASSERT_TRUE(common_prefs_);
-    auto key0 = common_prefs_->CreateSubKey({"ns1", "key"});
-    // Corner case for "ns1".
-    auto key0corner = common_prefs_->CreateSubKey({"ns11", "key"});
-    auto key1A = common_prefs_->CreateSubKey({"ns1", "nsA", "keyA"});
-    auto key1B = common_prefs_->CreateSubKey({"ns1", "nsA", "keyB"});
-    auto key2 = common_prefs_->CreateSubKey({"ns1", "nsB", "key"});
-    // Corner case for "ns1/nsB".
-    auto key2corner = common_prefs_->CreateSubKey({"ns1", "nsB1", "key"});
-    EXPECT_FALSE(common_prefs_->Exists(key0));
-    EXPECT_FALSE(common_prefs_->Exists(key1A));
-    EXPECT_FALSE(common_prefs_->Exists(key1B));
-    EXPECT_FALSE(common_prefs_->Exists(key2));
-
-    EXPECT_TRUE(common_prefs_->SetString(key0, ""));
-    EXPECT_TRUE(common_prefs_->SetString(key0corner, ""));
-    EXPECT_TRUE(common_prefs_->SetString(key1A, ""));
-    EXPECT_TRUE(common_prefs_->SetString(key1B, ""));
-    EXPECT_TRUE(common_prefs_->SetString(key2, ""));
-    EXPECT_TRUE(common_prefs_->SetString(key2corner, ""));
-
-    EXPECT_TRUE(common_prefs_->Exists(key0));
-    EXPECT_TRUE(common_prefs_->Exists(key0corner));
-    EXPECT_TRUE(common_prefs_->Exists(key1A));
-    EXPECT_TRUE(common_prefs_->Exists(key1B));
-    EXPECT_TRUE(common_prefs_->Exists(key2));
-    EXPECT_TRUE(common_prefs_->Exists(key2corner));
-
-    vector<string> keys2;
-    EXPECT_TRUE(common_prefs_->GetSubKeys("ns1/nsB/", &keys2));
-    EXPECT_THAT(keys2, ElementsAre(key2));
-    for (const auto& key : keys2)
-      EXPECT_TRUE(common_prefs_->Delete(key));
-    EXPECT_TRUE(common_prefs_->Exists(key0));
-    EXPECT_TRUE(common_prefs_->Exists(key0corner));
-    EXPECT_TRUE(common_prefs_->Exists(key1A));
-    EXPECT_TRUE(common_prefs_->Exists(key1B));
-    EXPECT_FALSE(common_prefs_->Exists(key2));
-    EXPECT_TRUE(common_prefs_->Exists(key2corner));
-
-    vector<string> keys2corner;
-    EXPECT_TRUE(common_prefs_->GetSubKeys("ns1/nsB", &keys2corner));
-    EXPECT_THAT(keys2corner, ElementsAre(key2corner));
-    for (const auto& key : keys2corner)
-      EXPECT_TRUE(common_prefs_->Delete(key));
-    EXPECT_FALSE(common_prefs_->Exists(key2corner));
-
-    vector<string> keys1;
-    EXPECT_TRUE(common_prefs_->GetSubKeys("ns1/nsA/", &keys1));
-    EXPECT_THAT(keys1, UnorderedElementsAre(key1A, key1B));
-    for (const auto& key : keys1)
-      EXPECT_TRUE(common_prefs_->Delete(key));
-    EXPECT_TRUE(common_prefs_->Exists(key0));
-    EXPECT_TRUE(common_prefs_->Exists(key0corner));
-    EXPECT_FALSE(common_prefs_->Exists(key1A));
-    EXPECT_FALSE(common_prefs_->Exists(key1B));
-
-    vector<string> keys0;
-    EXPECT_TRUE(common_prefs_->GetSubKeys("ns1/", &keys0));
-    EXPECT_THAT(keys0, ElementsAre(key0));
-    for (const auto& key : keys0)
-      EXPECT_TRUE(common_prefs_->Delete(key));
-    EXPECT_FALSE(common_prefs_->Exists(key0));
-    EXPECT_TRUE(common_prefs_->Exists(key0corner));
-
-    vector<string> keys0corner;
-    EXPECT_TRUE(common_prefs_->GetSubKeys("ns1", &keys0corner));
-    EXPECT_THAT(keys0corner, ElementsAre(key0corner));
-    for (const auto& key : keys0corner)
-      EXPECT_TRUE(common_prefs_->Delete(key));
-    EXPECT_FALSE(common_prefs_->Exists(key0corner));
-
-    // Test sub directory namespace.
-    const string kDlcPrefsSubDir = "foo-dir";
-    key1A = common_prefs_->CreateSubKey({kDlcPrefsSubDir, "dlc1", "keyA"});
-    EXPECT_TRUE(common_prefs_->SetString(key1A, "fp_1A"));
-    key1B = common_prefs_->CreateSubKey({kDlcPrefsSubDir, "dlc1", "keyB"});
-    EXPECT_TRUE(common_prefs_->SetString(key1B, "fp_1B"));
-    auto key2A = common_prefs_->CreateSubKey({kDlcPrefsSubDir, "dlc2", "keyA"});
-    EXPECT_TRUE(common_prefs_->SetString(key2A, "fp_A2"));
-
-    vector<string> fpKeys;
-    EXPECT_TRUE(common_prefs_->GetSubKeys(kDlcPrefsSubDir, &fpKeys));
-    EXPECT_EQ(fpKeys.size(), 3UL);
-    EXPECT_TRUE(common_prefs_->Delete(fpKeys[0]));
-    EXPECT_TRUE(common_prefs_->Delete(fpKeys[1]));
-    EXPECT_TRUE(common_prefs_->Delete(fpKeys[2]));
-    EXPECT_FALSE(common_prefs_->Exists(key1A));
-  }
-
-  PrefsInterface* common_prefs_;
-};
-
-class PrefsTest : public BasePrefsTest {
+class PrefsTest : public ::testing::Test {
  protected:
   void SetUp() override {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     prefs_dir_ = temp_dir_.GetPath();
     ASSERT_TRUE(prefs_.Init(prefs_dir_));
-    common_prefs_ = &prefs_;
   }
 
   bool SetValue(const string& key, const string& value) {
@@ -160,31 +59,6 @@
   Prefs prefs_;
 };
 
-TEST(Prefs, Init) {
-  Prefs prefs;
-  const string ns1 = "ns1";
-  const string ns2A = "ns2A";
-  const string ns2B = "ns2B";
-  const string sub_pref = "sp";
-
-  base::ScopedTempDir temp_dir;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-  auto ns1_path = temp_dir.GetPath().Append(ns1);
-  auto ns2A_path = ns1_path.Append(ns2A);
-  auto ns2B_path = ns1_path.Append(ns2B);
-  auto sub_pref_path = ns2A_path.Append(sub_pref);
-
-  EXPECT_TRUE(base::CreateDirectory(ns2B_path));
-  EXPECT_TRUE(base::PathExists(ns2B_path));
-
-  EXPECT_TRUE(base::CreateDirectory(sub_pref_path));
-  EXPECT_TRUE(base::PathExists(sub_pref_path));
-
-  EXPECT_TRUE(base::PathExists(ns1_path));
-  ASSERT_TRUE(prefs.Init(temp_dir.GetPath()));
-  EXPECT_FALSE(base::PathExists(ns1_path));
-}
-
 TEST_F(PrefsTest, GetFileNameForKey) {
   const char kAllvalidCharsKey[] =
       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-";
@@ -203,18 +77,6 @@
   EXPECT_FALSE(prefs_.file_storage_.GetFileNameForKey("", &path));
 }
 
-TEST_F(PrefsTest, CreateSubKey) {
-  const string name_space = "ns";
-  const string sub_pref1 = "sp1";
-  const string sub_pref2 = "sp2";
-  const string sub_key = "sk";
-
-  EXPECT_EQ(PrefsInterface::CreateSubKey({name_space, sub_pref1, sub_key}),
-            "ns/sp1/sk");
-  EXPECT_EQ(PrefsInterface::CreateSubKey({name_space, sub_pref2, sub_key}),
-            "ns/sp2/sk");
-}
-
 TEST_F(PrefsTest, GetString) {
   const string test_data = "test data";
   ASSERT_TRUE(SetValue(kKey, test_data));
@@ -417,94 +279,6 @@
   EXPECT_FALSE(prefs_.Exists(kKey));
 }
 
-TEST_F(PrefsTest, SetDeleteSubKey) {
-  const string name_space = "ns";
-  const string sub_pref = "sp";
-  const string sub_key1 = "sk1";
-  const string sub_key2 = "sk2";
-  auto key1 = prefs_.CreateSubKey({name_space, sub_pref, sub_key1});
-  auto key2 = prefs_.CreateSubKey({name_space, sub_pref, sub_key2});
-  base::FilePath sub_pref_path = prefs_dir_.Append(name_space).Append(sub_pref);
-
-  ASSERT_TRUE(prefs_.SetInt64(key1, 0));
-  ASSERT_TRUE(prefs_.SetInt64(key2, 0));
-  EXPECT_TRUE(base::PathExists(sub_pref_path.Append(sub_key1)));
-  EXPECT_TRUE(base::PathExists(sub_pref_path.Append(sub_key2)));
-
-  ASSERT_TRUE(prefs_.Delete(key1));
-  EXPECT_FALSE(base::PathExists(sub_pref_path.Append(sub_key1)));
-  EXPECT_TRUE(base::PathExists(sub_pref_path.Append(sub_key2)));
-  ASSERT_TRUE(prefs_.Delete(key2));
-  EXPECT_FALSE(base::PathExists(sub_pref_path.Append(sub_key2)));
-  prefs_.Init(prefs_dir_);
-  EXPECT_FALSE(base::PathExists(prefs_dir_.Append(name_space)));
-}
-
-TEST_F(PrefsTest, DeletePrefs) {
-  const string kPrefsSubDir = "foo-dir";
-  const string kFpKey = "kPrefFp";
-  const string kNotFpKey = "NotkPrefFp";
-  const string kOtherKey = "kPrefNotFp";
-
-  EXPECT_TRUE(prefs_.SetString(kFpKey, "3.000"));
-  EXPECT_TRUE(prefs_.SetString(kOtherKey, "not_fp_val"));
-
-  auto key1_fp = prefs_.CreateSubKey({kPrefsSubDir, "id-1", kFpKey});
-  EXPECT_TRUE(prefs_.SetString(key1_fp, "3.7"));
-  auto key_not_fp = prefs_.CreateSubKey({kPrefsSubDir, "id-1", kOtherKey});
-  EXPECT_TRUE(prefs_.SetString(key_not_fp, "not_fp_val"));
-  auto key2_fp = prefs_.CreateSubKey({kPrefsSubDir, "id-2", kFpKey});
-  EXPECT_TRUE(prefs_.SetString(key2_fp, "3.9"));
-  auto key3_fp = prefs_.CreateSubKey({kPrefsSubDir, "id-3", kFpKey});
-  EXPECT_TRUE(prefs_.SetString(key3_fp, "3.45"));
-
-  // Pref key does not match full subkey at end, should not delete.
-  auto key_middle_fp = prefs_.CreateSubKey({kPrefsSubDir, kFpKey, kOtherKey});
-  EXPECT_TRUE(prefs_.SetString(key_middle_fp, "not_fp_val"));
-  auto key_end_not_fp = prefs_.CreateSubKey({kPrefsSubDir, "id-1", kNotFpKey});
-  EXPECT_TRUE(prefs_.SetString(key_end_not_fp, "not_fp_val"));
-
-  // Delete key in platform and one namespace.
-  prefs_.Delete(kFpKey, {kPrefsSubDir});
-
-  EXPECT_FALSE(prefs_.Exists(kFpKey));
-  EXPECT_FALSE(prefs_.Exists(key1_fp));
-  EXPECT_FALSE(prefs_.Exists(key2_fp));
-  EXPECT_FALSE(prefs_.Exists(key3_fp));
-
-  // Check other keys are not deleted.
-  EXPECT_TRUE(prefs_.Exists(kOtherKey));
-  EXPECT_TRUE(prefs_.Exists(key_not_fp));
-  EXPECT_TRUE(prefs_.Exists(key_middle_fp));
-  EXPECT_TRUE(prefs_.Exists(key_end_not_fp));
-}
-
-TEST_F(PrefsTest, DeleteMultipleNamespaces) {
-  const string kFirstSubDir = "foo-dir";
-  const string kSecondarySubDir = "bar-dir";
-  const string kTertiarySubDir = "ter-dir";
-  const string kFpKey = "kPrefFp";
-
-  EXPECT_TRUE(prefs_.SetString(kFpKey, "3.000"));
-  // Set pref key in different namespaces.
-  auto key1_fp = prefs_.CreateSubKey({kFirstSubDir, "id-1", kFpKey});
-  EXPECT_TRUE(prefs_.SetString(key1_fp, "3.7"));
-  auto key2_fp = prefs_.CreateSubKey({kSecondarySubDir, "id-3", kFpKey});
-  EXPECT_TRUE(prefs_.SetString(key2_fp, "7.45"));
-  auto key3_fp = prefs_.CreateSubKey({kTertiarySubDir, "id-3", kFpKey});
-  EXPECT_TRUE(prefs_.SetString(key3_fp, "7.45"));
-
-  // Delete key in platform and given namespaces.
-  prefs_.Delete(kFpKey, {kFirstSubDir, kSecondarySubDir});
-
-  EXPECT_FALSE(prefs_.Exists(kFpKey));
-  EXPECT_FALSE(prefs_.Exists(key1_fp));
-  EXPECT_FALSE(prefs_.Exists(key2_fp));
-
-  // Tertiary namespace not given to delete. Key should still exist.
-  EXPECT_TRUE(prefs_.Exists(key3_fp));
-}
-
 class MockPrefsObserver : public PrefsInterface::ObserverInterface {
  public:
   MOCK_METHOD1(OnPrefSet, void(const string&));
@@ -525,19 +299,6 @@
   prefs_.Delete(kKey);
   testing::Mock::VerifyAndClearExpectations(&mock_obserser);
 
-  auto key1 = prefs_.CreateSubKey({"ns", "sp1", "key1"});
-  prefs_.AddObserver(key1, &mock_obserser);
-
-  EXPECT_CALL(mock_obserser, OnPrefSet(key1));
-  EXPECT_CALL(mock_obserser, OnPrefDeleted(_)).Times(0);
-  prefs_.SetString(key1, "value");
-  testing::Mock::VerifyAndClearExpectations(&mock_obserser);
-
-  EXPECT_CALL(mock_obserser, OnPrefSet(_)).Times(0);
-  EXPECT_CALL(mock_obserser, OnPrefDeleted(Eq(key1)));
-  prefs_.Delete(key1);
-  testing::Mock::VerifyAndClearExpectations(&mock_obserser);
-
   prefs_.RemoveObserver(kKey, &mock_obserser);
 }
 
@@ -580,14 +341,8 @@
   prefs_.RemoveObserver(kInvalidKey, &mock_obserser);
 }
 
-TEST_F(PrefsTest, MultiNamespaceKeyTest) {
-  MultiNamespaceKeyTest();
-}
-
-class MemoryPrefsTest : public BasePrefsTest {
+class MemoryPrefsTest : public ::testing::Test {
  protected:
-  void SetUp() override { common_prefs_ = &prefs_; }
-
   MemoryPrefs prefs_;
 };
 
@@ -603,16 +358,7 @@
 
   EXPECT_TRUE(prefs_.Delete(kKey));
   EXPECT_FALSE(prefs_.Exists(kKey));
-  EXPECT_TRUE(prefs_.Delete(kKey));
-
-  auto key = prefs_.CreateSubKey({"ns", "sp", "sk"});
-  ASSERT_TRUE(prefs_.SetInt64(key, 0));
-  EXPECT_TRUE(prefs_.Exists(key));
-  EXPECT_TRUE(prefs_.Delete(kKey));
-}
-
-TEST_F(MemoryPrefsTest, MultiNamespaceKeyTest) {
-  MultiNamespaceKeyTest();
+  EXPECT_FALSE(prefs_.Delete(kKey));
 }
 
 }  // namespace chromeos_update_engine
diff --git a/common/scoped_task_id.h b/common/scoped_task_id.h
deleted file mode 100644
index 91a2986..0000000
--- a/common/scoped_task_id.h
+++ /dev/null
@@ -1,123 +0,0 @@
-//
-// Copyright (C) 2021 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_SCOPED_TASK_ID_H_
-#define UPDATE_ENGINE_SCOPED_TASK_ID_H_
-
-#include <type_traits>
-#include <utility>
-
-#include <base/bind.h>
-#include <brillo/message_loops/message_loop.h>
-
-namespace chromeos_update_engine {
-
-// This class provides unique_ptr like semantic for |MessageLoop::TaskId|, when
-// instance of this class goes out of scope, underlying task will be cancelled.
-class ScopedTaskId {
-  using MessageLoop = brillo::MessageLoop;
-
- public:
-  // Move only type similar to unique_ptr.
-  ScopedTaskId(const ScopedTaskId&) = delete;
-  ScopedTaskId& operator=(const ScopedTaskId&) = delete;
-
-  constexpr ScopedTaskId() = default;
-
-  constexpr ScopedTaskId(ScopedTaskId&& other) noexcept {
-    *this = std::move(other);
-  }
-
-  constexpr ScopedTaskId& operator=(ScopedTaskId&& other) noexcept {
-    std::swap(task_id_, other.task_id_);
-    return *this;
-  }
-
-  // Post a callback on current message loop, return true if succeeded, false if
-  // the previous callback hasn't run yet, or scheduling failed at MessageLoop
-  // side.
-  [[nodiscard]] bool PostTask(const base::Location& from_here,
-                              base::OnceClosure&& callback,
-                              base::TimeDelta delay = {}) noexcept {
-    return PostTask<decltype(callback)>(from_here, std::move(callback), delay);
-  }
-  [[nodiscard]] bool PostTask(const base::Location& from_here,
-                              std::function<void()>&& callback,
-                              base::TimeDelta delay = {}) noexcept {
-    return PostTask<decltype(callback)>(from_here, std::move(callback), delay);
-  }
-
-  ~ScopedTaskId() noexcept { Cancel(); }
-
-  // Cancel the underlying managed task, true if cancel successful. False if no
-  // task scheduled or task cancellation failed
-  bool Cancel() noexcept {
-    if (task_id_ != MessageLoop::kTaskIdNull) {
-      if (MessageLoop::current()->CancelTask(task_id_)) {
-        LOG(INFO) << "Cancelled task id " << task_id_;
-        task_id_ = MessageLoop::kTaskIdNull;
-        return true;
-      }
-    }
-    return false;
-  }
-
-  [[nodiscard]] constexpr bool IsScheduled() const noexcept {
-    return task_id_ != MessageLoop::kTaskIdNull;
-  }
-
-  [[nodiscard]] constexpr bool operator==(const ScopedTaskId& other) const
-      noexcept {
-    return other.task_id_ == task_id_;
-  }
-
-  [[nodiscard]] constexpr bool operator<(const ScopedTaskId& other) const
-      noexcept {
-    return task_id_ < other.task_id_;
-  }
-
- private:
-  template <typename Callable>
-  [[nodiscard]] bool PostTask(const base::Location& from_here,
-                              Callable&& callback,
-                              base::TimeDelta delay) noexcept {
-    if (task_id_ != MessageLoop::kTaskIdNull) {
-      LOG(ERROR) << "Scheduling another task but task id " << task_id_
-                 << " isn't executed yet! This can cause the old task to leak.";
-      return false;
-    }
-    task_id_ = MessageLoop::current()->PostDelayedTask(
-        from_here,
-        base::BindOnce(&ScopedTaskId::ExecuteTask<decltype(callback)>,
-                       base::Unretained(this),
-                       std::move(callback)),
-        delay);
-    return task_id_ != MessageLoop::kTaskIdNull;
-  }
-  template <typename Callable>
-  void ExecuteTask(Callable&& callback) {
-    task_id_ = MessageLoop::kTaskIdNull;
-    if constexpr (std::is_same_v<Callable&&, base::OnceClosure&&>) {
-      std::move(callback).Run();
-    } else {
-      std::move(callback)();
-    }
-  }
-  MessageLoop::TaskId task_id_{MessageLoop::kTaskIdNull};
-};
-}  // namespace chromeos_update_engine
-
-#endif
diff --git a/common/subprocess.cc b/common/subprocess.cc
index 023017b..0131f10 100644
--- a/common/subprocess.cc
+++ b/common/subprocess.cc
@@ -29,9 +29,9 @@
 #include <base/bind.h>
 #include <base/logging.h>
 #include <base/posix/eintr_wrapper.h>
-#include <base/stl_util.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
+#include <brillo/process.h>
 #include <brillo/secure_blob.h>
 
 #include "update_engine/common/utils.h"
@@ -95,7 +95,6 @@
   proc->RedirectUsingPipe(STDOUT_FILENO, false);
   proc->SetPreExecCallback(base::Bind(&SetupChild, env, flags));
 
-  LOG(INFO) << "Running \"" << base::JoinString(cmd, " ") << "\"";
   return proc->Start();
 }
 
@@ -123,12 +122,13 @@
     bytes_read = 0;
     bool eof;
     bool ok = utils::ReadAll(
-        record->stdout_fd, buf, base::size(buf), &bytes_read, &eof);
+        record->stdout_fd, buf, arraysize(buf), &bytes_read, &eof);
     record->stdout.append(buf, bytes_read);
     if (!ok || eof) {
       // There was either an error or an EOF condition, so we are done watching
       // the file descriptor.
-      record->stdout_controller.reset();
+      MessageLoop::current()->CancelTask(record->stdout_task_id);
+      record->stdout_task_id = MessageLoop::kTaskIdNull;
       return;
     }
   } while (bytes_read);
@@ -143,7 +143,8 @@
   // Make sure we read any remaining process output and then close the pipe.
   OnStdoutReady(record);
 
-  record->stdout_controller.reset();
+  MessageLoop::current()->CancelTask(record->stdout_task_id);
+  record->stdout_task_id = MessageLoop::kTaskIdNull;
 
   // Don't print any log if the subprocess exited with exit code 0.
   if (info.si_code != CLD_EXITED) {
@@ -198,9 +199,12 @@
                << record->stdout_fd << ".";
   }
 
-  record->stdout_controller = base::FileDescriptorWatcher::WatchReadable(
+  record->stdout_task_id = MessageLoop::current()->WatchFileDescriptor(
+      FROM_HERE,
       record->stdout_fd,
-      base::BindRepeating(&Subprocess::OnStdoutReady, record.get()));
+      MessageLoop::WatchMode::kWatchRead,
+      true,
+      base::Bind(&Subprocess::OnStdoutReady, record.get()));
 
   subprocess_records_[pid] = std::move(record);
   return pid;
@@ -230,20 +234,22 @@
 
 bool Subprocess::SynchronousExec(const vector<string>& cmd,
                                  int* return_code,
-                                 string* stdout,
-                                 string* stderr) {
-  // The default for |SynchronousExec| is to use |kSearchPath| since the code
-  // relies on that.
-  return SynchronousExecFlags(cmd, kSearchPath, return_code, stdout, stderr);
+                                 string* stdout) {
+  // The default for SynchronousExec is to use kSearchPath since the code relies
+  // on that.
+  return SynchronousExecFlags(
+      cmd, kRedirectStderrToStdout | kSearchPath, return_code, stdout);
 }
 
 bool Subprocess::SynchronousExecFlags(const vector<string>& cmd,
                                       uint32_t flags,
                                       int* return_code,
-                                      string* stdout,
-                                      string* stderr) {
+                                      string* stdout) {
   brillo::ProcessImpl proc;
-  if (!LaunchProcess(cmd, flags, {STDERR_FILENO}, &proc)) {
+  // It doesn't make sense to redirect some pipes in the synchronous case
+  // because we won't be reading on our end, so we don't expose the output_pipes
+  // in this case.
+  if (!LaunchProcess(cmd, flags, {}, &proc)) {
     LOG(ERROR) << "Failed to launch subprocess";
     return false;
   }
@@ -251,39 +257,21 @@
   if (stdout) {
     stdout->clear();
   }
-  if (stderr) {
-    stderr->clear();
-  }
 
-  // Read from both stdout and stderr individually.
-  int stdout_fd = proc.GetPipe(STDOUT_FILENO);
-  int stderr_fd = proc.GetPipe(STDERR_FILENO);
+  int fd = proc.GetPipe(STDOUT_FILENO);
   vector<char> buffer(32 * 1024);
-  bool stdout_closed = false, stderr_closed = false;
-  while (!stdout_closed || !stderr_closed) {
-    if (!stdout_closed) {
-      int rc = HANDLE_EINTR(read(stdout_fd, buffer.data(), buffer.size()));
-      if (rc <= 0) {
-        stdout_closed = true;
-        if (rc < 0)
-          PLOG(ERROR) << "Reading from child's stdout";
-      } else if (stdout != nullptr) {
+  while (true) {
+    int rc = HANDLE_EINTR(read(fd, buffer.data(), buffer.size()));
+    if (rc < 0) {
+      PLOG(ERROR) << "Reading from child's output";
+      break;
+    } else if (rc == 0) {
+      break;
+    } else {
+      if (stdout)
         stdout->append(buffer.data(), rc);
-      }
-    }
-
-    if (!stderr_closed) {
-      int rc = HANDLE_EINTR(read(stderr_fd, buffer.data(), buffer.size()));
-      if (rc <= 0) {
-        stderr_closed = true;
-        if (rc < 0)
-          PLOG(ERROR) << "Reading from child's stderr";
-      } else if (stderr != nullptr) {
-        stderr->append(buffer.data(), rc);
-      }
     }
   }
-
   // At this point, the subprocess already closed the output, so we only need to
   // wait for it to finish.
   int proc_return_code = proc.Wait();
diff --git a/common/subprocess.h b/common/subprocess.h
index 2ed8b81..bc19d16 100644
--- a/common/subprocess.h
+++ b/common/subprocess.h
@@ -25,19 +25,13 @@
 #include <vector>
 
 #include <base/callback.h>
-#include <base/files/file_descriptor_watcher_posix.h>
 #include <base/logging.h>
 #include <base/macros.h>
 #include <brillo/asynchronous_signal_handler_interface.h>
 #include <brillo/message_loops/message_loop.h>
-#ifdef __CHROMEOS__
-#include <brillo/process/process.h>
-#include <brillo/process/process_reaper.h>
-#else
 #include <brillo/process.h>
 #include <brillo/process_reaper.h>
-#endif  // __CHROMEOS__
-#include <gtest/gtest_prod.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
 // 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
@@ -93,16 +87,14 @@
 
   // Executes a command synchronously. Returns true on success. If |stdout| is
   // non-null, the process output is stored in it, otherwise the output is
-  // logged.
+  // logged. Note that stderr is redirected to stdout.
   static bool SynchronousExec(const std::vector<std::string>& cmd,
                               int* return_code,
-                              std::string* stdout,
-                              std::string* stderr);
+                              std::string* stdout);
   static bool SynchronousExecFlags(const std::vector<std::string>& cmd,
                                    uint32_t flags,
                                    int* return_code,
-                                   std::string* stdout,
-                                   std::string* stderr);
+                                   std::string* stdout);
 
   // Gets the one instance.
   static Subprocess& Get() { return *subprocess_singleton_; }
@@ -128,8 +120,8 @@
 
     // These are used to monitor the stdout of the running process, including
     // the stderr if it was redirected.
-    std::unique_ptr<base::FileDescriptorWatcher::Controller> stdout_controller;
-
+    brillo::MessageLoop::TaskId stdout_task_id{
+        brillo::MessageLoop::kTaskIdNull};
     int stdout_fd{-1};
     std::string stdout;
   };
diff --git a/common/subprocess_unittest.cc b/common/subprocess_unittest.cc
index ff4158e..104ef41 100644
--- a/common/subprocess_unittest.cc
+++ b/common/subprocess_unittest.cc
@@ -28,14 +28,9 @@
 #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>
@@ -50,7 +45,6 @@
 using base::TimeDelta;
 using brillo::MessageLoop;
 using std::string;
-using std::unique_ptr;
 using std::vector;
 
 namespace {
@@ -75,16 +69,10 @@
     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_;
 };
 
 namespace {
@@ -205,7 +193,7 @@
 TEST_F(SubprocessTest, SynchronousTrueSearchsOnPath) {
   int rc = -1;
   EXPECT_TRUE(Subprocess::SynchronousExecFlags(
-      {"true"}, Subprocess::kSearchPath, &rc, nullptr, nullptr));
+      {"true"}, Subprocess::kSearchPath, &rc, nullptr));
   EXPECT_EQ(0, rc);
 }
 
@@ -213,17 +201,16 @@
   vector<string> cmd = {
       kBinPath "/sh", "-c", "echo -n stdout-here; echo -n stderr-there >&2"};
   int rc = -1;
-  string stdout, stderr;
-  ASSERT_TRUE(Subprocess::SynchronousExec(cmd, &rc, &stdout, &stderr));
+  string stdout;
+  ASSERT_TRUE(Subprocess::SynchronousExec(cmd, &rc, &stdout));
   EXPECT_EQ(0, rc);
-  EXPECT_EQ("stdout-here", stdout);
-  EXPECT_EQ("stderr-there", stderr);
+  EXPECT_EQ("stdout-herestderr-there", stdout);
 }
 
 TEST_F(SubprocessTest, SynchronousEchoNoOutputTest) {
   int rc = -1;
   ASSERT_TRUE(Subprocess::SynchronousExec(
-      {kBinPath "/sh", "-c", "echo test"}, &rc, nullptr, nullptr));
+      {kBinPath "/sh", "-c", "echo test"}, &rc, nullptr));
   EXPECT_EQ(0, rc);
 }
 
@@ -268,28 +255,26 @@
   int fifo_fd = HANDLE_EINTR(open(fifo_path.c_str(), O_RDONLY));
   EXPECT_GE(fifo_fd, 0);
 
-  watcher_ = base::FileDescriptorWatcher::WatchReadable(
-      fifo_fd,
-      base::Bind(
-          [](unique_ptr<base::FileDescriptorWatcher::Controller>* watcher,
-             int fifo_fd,
-             uint32_t tag) {
-            char c;
-            EXPECT_EQ(1, HANDLE_EINTR(read(fifo_fd, &c, 1)));
-            EXPECT_EQ('X', c);
-            LOG(INFO) << "Killing tag " << tag;
-            Subprocess::Get().KillExec(tag);
-            *watcher = nullptr;
-          },
-          // watcher_ is no longer used outside the clousure.
-          base::Unretained(&watcher_),
-          fifo_fd,
-          tag));
+  loop_.WatchFileDescriptor(FROM_HERE,
+                            fifo_fd,
+                            MessageLoop::WatchMode::kWatchRead,
+                            false,
+                            base::Bind(
+                                [](int fifo_fd, uint32_t tag) {
+                                  char c;
+                                  EXPECT_EQ(1,
+                                            HANDLE_EINTR(read(fifo_fd, &c, 1)));
+                                  EXPECT_EQ('X', c);
+                                  LOG(INFO) << "Killing tag " << tag;
+                                  Subprocess::Get().KillExec(tag);
+                                },
+                                fifo_fd,
+                                tag));
 
   // This test would leak a callback that runs when the child process exits
   // unless we wait for it to run.
   brillo::MessageLoopRunUntil(
-      &loop_, TimeDelta::FromSeconds(20), base::Bind([] {
+      &loop_, TimeDelta::FromSeconds(120), base::Bind([] {
         return Subprocess::Get().subprocess_records_.empty();
       }));
   EXPECT_TRUE(Subprocess::Get().subprocess_records_.empty());
diff --git a/common/test_utils.h b/common/test_utils.h
index bb5a678..44b7aa1 100644
--- a/common/test_utils.h
+++ b/common/test_utils.h
@@ -78,7 +78,7 @@
 
 void FillWithData(brillo::Blob* buffer);
 
-// Compare the value of builtin array for download source parameter.
+// Compare the value of native array for download source parameter.
 MATCHER_P(DownloadSourceMatcher, source_array, "") {
   return std::equal(source_array, source_array + kNumDownloadSources, arg);
 }
@@ -138,6 +138,22 @@
   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 5dbb445..fc89040 100644
--- a/common/utils.cc
+++ b/common/utils.cc
@@ -28,7 +28,6 @@
 #include <string.h>
 #include <sys/mount.h>
 #include <sys/resource.h>
-#include <sys/sendfile.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <time.h>
@@ -53,6 +52,7 @@
 #include <base/strings/stringprintf.h>
 #include <brillo/data_encoding.h>
 
+#include "update_engine/common/clock_interface.h"
 #include "update_engine/common/constants.h"
 #include "update_engine/common/platform_constants.h"
 #include "update_engine/common/prefs_interface.h"
@@ -84,6 +84,49 @@
 // The path to the kernel's boot_id.
 const char kBootIdPath[] = "/proc/sys/kernel/random/boot_id";
 
+// Return true if |disk_name| is an MTD or a UBI device. Note that this test is
+// simply based on the name of the device.
+bool IsMtdDeviceName(const string& disk_name) {
+  return base::StartsWith(
+             disk_name, "/dev/ubi", base::CompareCase::SENSITIVE) ||
+         base::StartsWith(disk_name, "/dev/mtd", base::CompareCase::SENSITIVE);
+}
+
+// Return the device name for the corresponding partition on a NAND device.
+// WARNING: This function returns device names that are not mountable.
+string MakeNandPartitionName(int partition_num) {
+  switch (partition_num) {
+    case 2:
+    case 4:
+    case 6: {
+      return base::StringPrintf("/dev/mtd%d", partition_num);
+    }
+    default: {
+      return base::StringPrintf("/dev/ubi%d_0", partition_num);
+    }
+  }
+}
+
+// Return the device name for the corresponding partition on a NAND device that
+// may be mountable (but may not be writable).
+string MakeNandPartitionNameForMount(int partition_num) {
+  switch (partition_num) {
+    case 2:
+    case 4:
+    case 6: {
+      return base::StringPrintf("/dev/mtd%d", partition_num);
+    }
+    case 3:
+    case 5:
+    case 7: {
+      return base::StringPrintf("/dev/ubiblock%d_0", partition_num);
+    }
+    default: {
+      return base::StringPrintf("/dev/ubi%d_0", partition_num);
+    }
+  }
+}
+
 // If |path| is absolute, or explicit relative to the current working directory,
 // leaves it as is. Otherwise, uses the system's temp directory, as defined by
 // base::GetTempDir() and prepends it to |path|. On success stores the full
@@ -112,6 +155,27 @@
 
 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);
@@ -192,10 +256,10 @@
   return true;
 }
 
-bool WriteAll(const FileDescriptorPtr& fd,
-              const void* buf,
-              size_t count,
-              off_t offset) {
+bool PWriteAll(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);
@@ -218,11 +282,11 @@
   return true;
 }
 
-bool ReadAll(const FileDescriptorPtr& fd,
-             void* buf,
-             size_t count,
-             off_t offset,
-             ssize_t* out_bytes_read) {
+bool PReadAll(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);
@@ -239,31 +303,6 @@
   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,
@@ -435,6 +474,22 @@
     return false;
   }
 
+  size_t partition_name_len = string::npos;
+  if (partition_name[last_nondigit_pos] == '_') {
+    // NAND block devices have weird naming which could be something
+    // like "/dev/ubiblock2_0". We discard "_0" in such a case.
+    size_t prev_nondigit_pos =
+        partition_name.find_last_not_of("0123456789", last_nondigit_pos - 1);
+    if (prev_nondigit_pos == string::npos ||
+        (prev_nondigit_pos + 1) == last_nondigit_pos) {
+      LOG(ERROR) << "Unable to parse partition device name: " << partition_name;
+      return false;
+    }
+
+    partition_name_len = last_nondigit_pos - prev_nondigit_pos;
+    last_nondigit_pos = prev_nondigit_pos;
+  }
+
   if (out_disk_name) {
     // Special case for MMC devices which have the following naming scheme:
     // mmcblk0p2
@@ -447,7 +502,8 @@
   }
 
   if (out_partition_num) {
-    string partition_str = partition_name.substr(last_nondigit_pos + 1);
+    string partition_str =
+        partition_name.substr(last_nondigit_pos + 1, partition_name_len);
     *out_partition_num = atoi(partition_str.c_str());
   }
   return true;
@@ -464,6 +520,13 @@
     return string();
   }
 
+  if (IsMtdDeviceName(disk_name)) {
+    // Special case for UBI block devices.
+    //   1. ubiblock is not writable, we need to use plain "ubi".
+    //   2. There is a "_0" suffix.
+    return MakeNandPartitionName(partition_num);
+  }
+
   string partition_name = disk_name;
   if (isdigit(partition_name.back())) {
     // Special case for devices with names ending with a digit.
@@ -477,6 +540,17 @@
   return partition_name;
 }
 
+string MakePartitionNameForMount(const string& part_name) {
+  if (IsMtdDeviceName(part_name)) {
+    int partition_num;
+    if (!SplitPartitionName(part_name, nullptr, &partition_num)) {
+      return "";
+    }
+    return MakeNandPartitionNameForMount(partition_num);
+  }
+  return part_name;
+}
+
 string ErrnoNumberAsString(int err) {
   char buf[100];
   buf[0] = '\0';
@@ -493,9 +567,31 @@
   return lstat(path, &stbuf) == 0 && S_ISLNK(stbuf.st_mode) != 0;
 }
 
-bool IsRegFile(const char* path) {
-  struct stat stbuf;
-  return lstat(path, &stbuf) == 0 && S_ISREG(stbuf.st_mode) != 0;
+bool TryAttachingUbiVolume(int volume_num, int timeout) {
+  const string volume_path = base::StringPrintf("/dev/ubi%d_0", volume_num);
+  if (FileExists(volume_path.c_str())) {
+    return true;
+  }
+
+  int exit_code;
+  vector<string> cmd = {"ubiattach",
+                        "-m",
+                        base::StringPrintf("%d", volume_num),
+                        "-d",
+                        base::StringPrintf("%d", volume_num)};
+  TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &exit_code, nullptr));
+  TEST_AND_RETURN_FALSE(exit_code == 0);
+
+  cmd = {"ubiblock", "--create", volume_path};
+  TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &exit_code, nullptr));
+  TEST_AND_RETURN_FALSE(exit_code == 0);
+
+  while (timeout > 0 && !FileExists(volume_path.c_str())) {
+    sleep(1);
+    timeout--;
+  }
+
+  return FileExists(volume_path.c_str());
 }
 
 bool MakeTempFile(const string& base_filename_template,
@@ -829,7 +925,7 @@
   return base_code;
 }
 
-string StringVectorToString(const vector<string>& vec_str) {
+string StringVectorToString(const vector<string> &vec_str) {
   string str = "[";
   for (vector<string>::const_iterator i = vec_str.begin(); i != vec_str.end();
        ++i) {
@@ -858,7 +954,7 @@
                             encoded_hash.c_str());
 }
 
-bool ConvertToOmahaInstallDate(Time time, int* out_num_days) {
+bool ConvertToOmahaInstallDate(Time time, int *out_num_days) {
   time_t unix_time = time.ToTimeT();
   // Output of: date +"%s" --date="Jan 1, 2007 0:00 PST".
   const time_t kOmahaEpoch = 1167638400;
@@ -920,25 +1016,6 @@
   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));
@@ -1006,40 +1083,6 @@
   return str;
 }
 
-string GetExclusionName(const string& str_to_convert) {
-  return base::NumberToString(base::StringPieceHash()(str_to_convert));
-}
-
-static bool ParseTimestamp(const std::string& str, int64_t* out) {
-  if (!base::StringToInt64(str, out)) {
-    LOG(WARNING) << "Invalid timestamp: " << str;
-    return false;
-  }
-  return true;
-}
-
-ErrorCode IsTimestampNewer(const std::string& old_version,
-                           const std::string& new_version) {
-  if (old_version.empty() || new_version.empty()) {
-    LOG(WARNING)
-        << "One of old/new timestamp is empty, permit update anyway. Old: "
-        << old_version << " New: " << new_version;
-    return ErrorCode::kSuccess;
-  }
-  int64_t old_ver = 0;
-  if (!ParseTimestamp(old_version, &old_ver)) {
-    return ErrorCode::kError;
-  }
-  int64_t new_ver = 0;
-  if (!ParseTimestamp(new_version, &new_ver)) {
-    return ErrorCode::kDownloadManifestParseError;
-  }
-  if (old_ver > new_ver) {
-    return ErrorCode::kPayloadTimestampError;
-  }
-  return ErrorCode::kSuccess;
-}
-
 }  // namespace utils
 
 }  // namespace chromeos_update_engine
diff --git a/common/utils.h b/common/utils.h
index 59f236e..c6c34f4 100644
--- a/common/utils.h
+++ b/common/utils.h
@@ -18,7 +18,6 @@
 #define UPDATE_ENGINE_COMMON_UTILS_H_
 
 #include <errno.h>
-#include <sys/types.h>
 #include <time.h>
 #include <unistd.h>
 
@@ -54,6 +53,10 @@
 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);
@@ -64,15 +67,6 @@
 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,
@@ -91,16 +85,6 @@
 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,
@@ -144,8 +128,10 @@
 // Returns true if |path| exists and is a symbolic link.
 bool IsSymlink(const char* path);
 
-// Return true iff |path| exists and is a regular file
-bool IsRegFile(const char* path);
+// Try attaching UBI |volume_num|. If there is any error executing required
+// commands to attach the volume, this function returns false. This function
+// only returns true if "/dev/ubi%d_0" becomes available in |timeout| seconds.
+bool TryAttachingUbiVolume(int volume_num, int timeout);
 
 // If |base_filename_template| is neither absolute (starts with "/") nor
 // explicitly relative to the current working directory (starts with "./" or
@@ -177,6 +163,14 @@
 // Returns empty string when invalid parameters are passed in
 std::string MakePartitionName(const std::string& disk_name, int partition_num);
 
+// Similar to "MakePartitionName" but returns a name that is suitable for
+// mounting. On NAND system we can write to "/dev/ubiX_0", which is what
+// MakePartitionName returns, but we cannot mount that device. To mount, we
+// have to use "/dev/ubiblockX_0" for rootfs. Stateful and OEM partitions are
+// mountable with "/dev/ubiX_0". The input is a partition device such as
+// /dev/sda3. Return empty string on error.
+std::string MakePartitionNameForMount(const std::string& part_name);
+
 // Set the read-only attribute on the block device |device| to the value passed
 // in |read_only|. Return whether the operation succeeded.
 bool SetBlockDeviceReadOnly(const std::string& device, bool read_only);
@@ -311,10 +305,6 @@
 // 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);
 
@@ -342,20 +332,6 @@
 
 // Return a string representation of |utime| for log file names.
 std::string GetTimeAsString(time_t utime);
-// Returns the string format of the hashed |str_to_convert| that can be used
-// with |Excluder| as the exclusion name.
-std::string GetExclusionName(const std::string& str_to_convert);
-
-// Parse `old_version` and `new_version` as integer timestamps and
-// Return kSuccess if `new_version` is larger/newer.
-// Return kSuccess if either one is empty.
-// Return kError if |old_version| is not empty and not an integer.
-// Return kDownloadManifestParseError if |new_version| is not empty and not an
-// integer.
-// Return kPayloadTimestampError if both are integers but |new_version| <
-// |old_version|.
-ErrorCode IsTimestampNewer(const std::string& old_version,
-                           const std::string& new_version);
 
 }  // namespace utils
 
@@ -393,48 +369,6 @@
   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.
-  // If |truncate_size| is non-zero, truncate file to that size on creation.
-  explicit ScopedTempFile(const std::string& pattern,
-                          bool open_fd = false,
-                          size_t truncate_size = 0) {
-    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_));
-    }
-    if (truncate_size > 0) {
-      CHECK_EQ(0, truncate(path_.c_str(), truncate_size));
-    }
-  }
-  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 20c6b84..b4ac2f5 100644
--- a/common/utils_unittest.cc
+++ b/common/utils_unittest.cc
@@ -41,12 +41,25 @@
 
 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) {
-  ScopedTempFile file;
+  test_utils::ScopedTempFile file;
   EXPECT_TRUE(utils::WriteFile(file.path().c_str(), "hello", 5));
 
   brillo::Blob readback;
@@ -60,7 +73,7 @@
 }
 
 TEST(UtilsTest, ReadFileChunk) {
-  ScopedTempFile file;
+  test_utils::ScopedTempFile file;
   brillo::Blob data;
   const size_t kSize = 1024 * 1024;
   for (size_t i = 0; i < kSize; i++) {
@@ -110,6 +123,10 @@
   EXPECT_EQ("/dev/mmcblk0", disk);
   EXPECT_EQ(3, part_num);
 
+  EXPECT_TRUE(utils::SplitPartitionName("/dev/ubiblock3_2", &disk, &part_num));
+  EXPECT_EQ("/dev/ubiblock", disk);
+  EXPECT_EQ(3, part_num);
+
   EXPECT_TRUE(utils::SplitPartitionName("/dev/loop10", &disk, &part_num));
   EXPECT_EQ("/dev/loop", disk);
   EXPECT_EQ(10, part_num);
@@ -118,6 +135,14 @@
   EXPECT_EQ("/dev/loop28", disk);
   EXPECT_EQ(11, part_num);
 
+  EXPECT_TRUE(utils::SplitPartitionName("/dev/loop10_0", &disk, &part_num));
+  EXPECT_EQ("/dev/loop", disk);
+  EXPECT_EQ(10, part_num);
+
+  EXPECT_TRUE(utils::SplitPartitionName("/dev/loop28p11_0", &disk, &part_num));
+  EXPECT_EQ("/dev/loop28", disk);
+  EXPECT_EQ(11, part_num);
+
   EXPECT_FALSE(utils::SplitPartitionName("/dev/mmcblk0p", &disk, &part_num));
   EXPECT_FALSE(utils::SplitPartitionName("/dev/sda", &disk, &part_num));
   EXPECT_FALSE(utils::SplitPartitionName("/dev/foo/bar", &disk, &part_num));
@@ -132,6 +157,29 @@
   EXPECT_EQ("/dev/mmcblk0p2", utils::MakePartitionName("/dev/mmcblk0", 2));
   EXPECT_EQ("/dev/loop8", utils::MakePartitionName("/dev/loop", 8));
   EXPECT_EQ("/dev/loop12p2", utils::MakePartitionName("/dev/loop12", 2));
+  EXPECT_EQ("/dev/ubi5_0", utils::MakePartitionName("/dev/ubiblock", 5));
+  EXPECT_EQ("/dev/mtd4", utils::MakePartitionName("/dev/ubiblock", 4));
+  EXPECT_EQ("/dev/ubi3_0", utils::MakePartitionName("/dev/ubiblock", 3));
+  EXPECT_EQ("/dev/mtd2", utils::MakePartitionName("/dev/ubiblock", 2));
+  EXPECT_EQ("/dev/ubi1_0", utils::MakePartitionName("/dev/ubiblock", 1));
+}
+
+TEST(UtilsTest, MakePartitionNameForMountTest) {
+  EXPECT_EQ("/dev/sda4", utils::MakePartitionNameForMount("/dev/sda4"));
+  EXPECT_EQ("/dev/sda123", utils::MakePartitionNameForMount("/dev/sda123"));
+  EXPECT_EQ("/dev/mmcblk2", utils::MakePartitionNameForMount("/dev/mmcblk2"));
+  EXPECT_EQ("/dev/mmcblk0p2",
+            utils::MakePartitionNameForMount("/dev/mmcblk0p2"));
+  EXPECT_EQ("/dev/loop0", utils::MakePartitionNameForMount("/dev/loop0"));
+  EXPECT_EQ("/dev/loop8", utils::MakePartitionNameForMount("/dev/loop8"));
+  EXPECT_EQ("/dev/loop12p2", utils::MakePartitionNameForMount("/dev/loop12p2"));
+  EXPECT_EQ("/dev/ubiblock5_0",
+            utils::MakePartitionNameForMount("/dev/ubiblock5_0"));
+  EXPECT_EQ("/dev/mtd4", utils::MakePartitionNameForMount("/dev/ubi4_0"));
+  EXPECT_EQ("/dev/ubiblock3_0",
+            utils::MakePartitionNameForMount("/dev/ubiblock3"));
+  EXPECT_EQ("/dev/mtd2", utils::MakePartitionNameForMount("/dev/ubi2"));
+  EXPECT_EQ("/dev/ubi1_0", utils::MakePartitionNameForMount("/dev/ubiblock1"));
 }
 
 TEST(UtilsTest, FuzzIntTest) {
@@ -149,7 +197,7 @@
 namespace {
 void GetFileFormatTester(const string& expected,
                          const vector<uint8_t>& contents) {
-  ScopedTempFile file;
+  test_utils::ScopedTempFile file;
   ASSERT_TRUE(utils::WriteFile(file.path().c_str(),
                                reinterpret_cast<const char*>(contents.data()),
                                contents.size()));
@@ -378,7 +426,7 @@
 }
 
 TEST(UtilsTest, RunAsRootUnmountFilesystemBusyFailureTest) {
-  ScopedTempFile tmp_image("img.XXXXXX");
+  test_utils::ScopedTempFile tmp_image("img.XXXXXX");
 
   EXPECT_TRUE(base::CopyFile(
       test_utils::GetBuildArtifactsPath().Append("gen/disk_ext2_4k.img"),
@@ -418,7 +466,7 @@
   EXPECT_TRUE(mnt_dir.CreateUniqueTempDir());
   EXPECT_FALSE(utils::IsMountpoint(mnt_dir.GetPath().value()));
 
-  ScopedTempFile file;
+  test_utils::ScopedTempFile file;
   EXPECT_FALSE(utils::IsMountpoint(file.path()));
 }
 
@@ -460,7 +508,7 @@
 }
 
 TEST(UtilsTest, GetFilePathTest) {
-  ScopedTempFile file;
+  test_utils::ScopedTempFile file;
   int fd = HANDLE_EINTR(open(file.path().c_str(), O_RDONLY));
   EXPECT_GE(fd, 0);
   EXPECT_EQ(file.path(), utils::GetFilePath(fd));
@@ -468,14 +516,4 @@
   IGNORE_EINTR(close(fd));
 }
 
-TEST(UtilsTest, ValidatePerPartitionTimestamp) {
-  ASSERT_EQ(ErrorCode::kPayloadTimestampError,
-            utils::IsTimestampNewer("10", "5"));
-  ASSERT_EQ(ErrorCode::kSuccess, utils::IsTimestampNewer("10", "11"));
-  ASSERT_EQ(ErrorCode::kDownloadManifestParseError,
-            utils::IsTimestampNewer("10", "lol"));
-  ASSERT_EQ(ErrorCode::kError, utils::IsTimestampNewer("lol", "ZZZ"));
-  ASSERT_EQ(ErrorCode::kSuccess, utils::IsTimestampNewer("10", ""));
-}
-
 }  // namespace chromeos_update_engine
diff --git a/cros/common_service.cc b/common_service.cc
similarity index 78%
rename from cros/common_service.cc
rename to common_service.cc
index e5ee828..0d5ee6d 100644
--- a/cros/common_service.cc
+++ b/common_service.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/common_service.h"
+#include "update_engine/common_service.h"
 
 #include <string>
 
@@ -26,16 +26,16 @@
 #include <brillo/strings/string_utils.h>
 #include <policy/device_policy.h>
 
+#include "update_engine/common/clock_interface.h"
 #include "update_engine/common/hardware_interface.h"
 #include "update_engine/common/prefs.h"
-#include "update_engine/common/system_state.h"
 #include "update_engine/common/utils.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"
+#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"
 
 using base::StringPrintf;
 using brillo::ErrorPtr;
@@ -50,7 +50,11 @@
 namespace {
 // Log and set the error on the passed ErrorPtr.
 void LogAndSetError(ErrorPtr* error,
+#if BASE_VER < 576279
+                    const tracked_objects::Location& location,
+#else
                     const base::Location& location,
+#endif
                     const string& reason) {
   brillo::Error::AddTo(error,
                        location,
@@ -66,7 +70,8 @@
 const char* const UpdateEngineService::kErrorFailed =
     "org.chromium.UpdateEngine.Error.Failed";
 
-UpdateEngineService::UpdateEngineService() = default;
+UpdateEngineService::UpdateEngineService(SystemState* system_state)
+    : system_state_(system_state) {}
 
 // org::chromium::UpdateEngineInterfaceInterface methods implementation.
 
@@ -78,7 +83,7 @@
             << "RestrictDownload="
             << ((flags & UpdateAttemptFlags::kFlagRestrictDownload) ? "yes"
                                                                     : "no");
-  SystemState::Get()->update_attempter()->SetUpdateAttemptFlags(flags);
+  system_state_->update_attempter()->SetUpdateAttemptFlags(flags);
   return true;
 }
 
@@ -97,16 +102,16 @@
             << "interactive=" << (interactive ? "yes " : "no ")
             << "RestrictDownload=" << (restrict_downloads ? "yes " : "no ");
 
-  *out_result = SystemState::Get()->update_attempter()->CheckForUpdate(
+  *out_result = system_state_->update_attempter()->CheckForUpdate(
       in_app_version, in_omaha_url, flags);
   return true;
 }
 
 bool UpdateEngineService::AttemptInstall(brillo::ErrorPtr* error,
                                          const string& omaha_url,
-                                         const vector<string>& dlc_ids) {
-  if (!SystemState::Get()->update_attempter()->CheckForInstall(dlc_ids,
-                                                               omaha_url)) {
+                                         const vector<string>& dlc_module_ids) {
+  if (!system_state_->update_attempter()->CheckForInstall(dlc_module_ids,
+                                                          omaha_url)) {
     // TODO(xiaochu): support more detailed error messages.
     LogAndSetError(error, FROM_HERE, "Could not schedule install operation.");
     return false;
@@ -117,7 +122,7 @@
 bool UpdateEngineService::AttemptRollback(ErrorPtr* error, bool in_powerwash) {
   LOG(INFO) << "Attempting rollback to non-active partitions.";
 
-  if (!SystemState::Get()->update_attempter()->Rollback(in_powerwash)) {
+  if (!system_state_->update_attempter()->Rollback(in_powerwash)) {
     // TODO(dgarrett): Give a more specific error code/reason.
     LogAndSetError(error, FROM_HERE, "Rollback attempt failed.");
     return false;
@@ -127,14 +132,14 @@
 
 bool UpdateEngineService::CanRollback(ErrorPtr* /* error */,
                                       bool* out_can_rollback) {
-  bool can_rollback = SystemState::Get()->update_attempter()->CanRollback();
+  bool can_rollback = system_state_->update_attempter()->CanRollback();
   LOG(INFO) << "Checking to see if we can rollback . Result: " << can_rollback;
   *out_can_rollback = can_rollback;
   return true;
 }
 
 bool UpdateEngineService::ResetStatus(ErrorPtr* error) {
-  if (!SystemState::Get()->update_attempter()->ResetStatus()) {
+  if (!system_state_->update_attempter()->ResetStatus()) {
     // TODO(dgarrett): Give a more specific error code/reason.
     LogAndSetError(error, FROM_HERE, "ResetStatus failed.");
     return false;
@@ -142,20 +147,9 @@
   return true;
 }
 
-bool UpdateEngineService::SetDlcActiveValue(brillo::ErrorPtr* error,
-                                            bool is_active,
-                                            const string& dlc_id) {
-  if (!SystemState::Get()->update_attempter()->SetDlcActiveValue(is_active,
-                                                                 dlc_id)) {
-    LogAndSetError(error, FROM_HERE, "SetDlcActiveValue failed.");
-    return false;
-  }
-  return true;
-}
-
 bool UpdateEngineService::GetStatus(ErrorPtr* error,
                                     UpdateEngineStatus* out_status) {
-  if (!SystemState::Get()->update_attempter()->GetStatus(out_status)) {
+  if (!system_state_->update_attempter()->GetStatus(out_status)) {
     LogAndSetError(error, FROM_HERE, "GetStatus failed.");
     return false;
   }
@@ -163,7 +157,7 @@
 }
 
 bool UpdateEngineService::RebootIfNeeded(ErrorPtr* error) {
-  if (!SystemState::Get()->update_attempter()->RebootIfNeeded()) {
+  if (!system_state_->update_attempter()->RebootIfNeeded()) {
     // TODO(dgarrett): Give a more specific error code/reason.
     LogAndSetError(error, FROM_HERE, "Reboot not needed, or attempt failed.");
     return false;
@@ -174,16 +168,15 @@
 bool UpdateEngineService::SetChannel(ErrorPtr* error,
                                      const string& in_target_channel,
                                      bool in_is_powerwash_allowed) {
-  const policy::DevicePolicy* device_policy =
-      SystemState::Get()->device_policy();
+  const policy::DevicePolicy* device_policy = system_state_->device_policy();
 
   // The device_policy is loaded in a lazy way before an update check. Load it
   // now from the libbrillo cache if it wasn't already loaded.
   if (!device_policy) {
-    UpdateAttempter* update_attempter = SystemState::Get()->update_attempter();
+    UpdateAttempter* update_attempter = system_state_->update_attempter();
     if (update_attempter) {
       update_attempter->RefreshDevicePolicy();
-      device_policy = SystemState::Get()->device_policy();
+      device_policy = system_state_->device_policy();
     }
   }
 
@@ -199,7 +192,7 @@
 
   LOG(INFO) << "Setting destination channel to: " << in_target_channel;
   string error_message;
-  if (!SystemState::Get()->request_params()->SetTargetChannel(
+  if (!system_state_->request_params()->SetTargetChannel(
           in_target_channel, in_is_powerwash_allowed, &error_message)) {
     LogAndSetError(error, FROM_HERE, error_message);
     return false;
@@ -210,19 +203,20 @@
 bool UpdateEngineService::GetChannel(ErrorPtr* /* error */,
                                      bool in_get_current_channel,
                                      string* out_channel) {
-  OmahaRequestParams* rp = SystemState::Get()->request_params();
+  OmahaRequestParams* rp = system_state_->request_params();
   *out_channel =
       (in_get_current_channel ? rp->current_channel() : rp->target_channel());
   return true;
 }
 
 bool UpdateEngineService::SetCohortHint(ErrorPtr* error,
-                                        const string& in_cohort_hint) {
+                                        string in_cohort_hint) {
+  PrefsInterface* prefs = system_state_->prefs();
+
   // It is ok to override the cohort hint with an invalid value since it is
   // stored in stateful partition. The code reading it should sanitize it
   // anyway.
-  if (!SystemState::Get()->prefs()->SetString(kPrefsOmahaCohortHint,
-                                              in_cohort_hint)) {
+  if (!prefs->SetString(kPrefsOmahaCohortHint, in_cohort_hint)) {
     LogAndSetError(
         error,
         FROM_HERE,
@@ -235,7 +229,8 @@
 
 bool UpdateEngineService::GetCohortHint(ErrorPtr* error,
                                         string* out_cohort_hint) {
-  const auto* prefs = SystemState::Get()->prefs();
+  PrefsInterface* prefs = system_state_->prefs();
+
   *out_cohort_hint = "";
   if (prefs->Exists(kPrefsOmahaCohortHint) &&
       !prefs->GetString(kPrefsOmahaCohortHint, out_cohort_hint)) {
@@ -247,7 +242,9 @@
 
 bool UpdateEngineService::SetP2PUpdatePermission(ErrorPtr* error,
                                                  bool in_enabled) {
-  if (!SystemState::Get()->prefs()->SetBoolean(kPrefsP2PEnabled, in_enabled)) {
+  PrefsInterface* prefs = system_state_->prefs();
+
+  if (!prefs->SetBoolean(kPrefsP2PEnabled, in_enabled)) {
     LogAndSetError(
         error,
         FROM_HERE,
@@ -260,7 +257,8 @@
 
 bool UpdateEngineService::GetP2PUpdatePermission(ErrorPtr* error,
                                                  bool* out_enabled) {
-  const auto* prefs = SystemState::Get()->prefs();
+  PrefsInterface* prefs = system_state_->prefs();
+
   bool p2p_pref = false;  // Default if no setting is present.
   if (prefs->Exists(kPrefsP2PEnabled) &&
       !prefs->GetBoolean(kPrefsP2PEnabled, &p2p_pref)) {
@@ -275,7 +273,7 @@
 bool UpdateEngineService::SetUpdateOverCellularPermission(ErrorPtr* error,
                                                           bool in_allowed) {
   ConnectionManagerInterface* connection_manager =
-      SystemState::Get()->connection_manager();
+      system_state_->connection_manager();
 
   // Check if this setting is allowed by the device policy.
   if (connection_manager->IsAllowedConnectionTypesForUpdateSet()) {
@@ -288,8 +286,11 @@
 
   // If the policy wasn't loaded yet, then it is still OK to change the local
   // setting because the policy will be checked again during the update check.
-  if (!SystemState::Get()->prefs()->SetBoolean(
-          kPrefsUpdateOverCellularPermission, in_allowed)) {
+
+  PrefsInterface* prefs = system_state_->prefs();
+
+  if (!prefs ||
+      !prefs->SetBoolean(kPrefsUpdateOverCellularPermission, in_allowed)) {
     LogAndSetError(error,
                    FROM_HERE,
                    string("Error setting the update over cellular to ") +
@@ -304,7 +305,7 @@
     const std::string& target_version,
     int64_t target_size) {
   ConnectionManagerInterface* connection_manager =
-      SystemState::Get()->connection_manager();
+      system_state_->connection_manager();
 
   // Check if this setting is allowed by the device policy.
   if (connection_manager->IsAllowedConnectionTypesForUpdateSet()) {
@@ -318,8 +319,10 @@
   // If the policy wasn't loaded yet, then it is still OK to change the local
   // setting because the policy will be checked again during the update check.
 
-  auto* prefs = SystemState::Get()->prefs();
-  if (!prefs->SetString(kPrefsUpdateOverCellularTargetVersion,
+  PrefsInterface* prefs = system_state_->prefs();
+
+  if (!prefs ||
+      !prefs->SetString(kPrefsUpdateOverCellularTargetVersion,
                         target_version) ||
       !prefs->SetInt64(kPrefsUpdateOverCellularTargetSize, target_size)) {
     LogAndSetError(
@@ -332,15 +335,16 @@
 bool UpdateEngineService::GetUpdateOverCellularPermission(ErrorPtr* error,
                                                           bool* out_allowed) {
   ConnectionManagerInterface* connection_manager =
-      SystemState::Get()->connection_manager();
+      system_state_->connection_manager();
 
   if (connection_manager->IsAllowedConnectionTypesForUpdateSet()) {
     // We have device policy, so ignore the user preferences.
     *out_allowed = connection_manager->IsUpdateAllowedOver(
         ConnectionType::kCellular, ConnectionTethering::kUnknown);
   } else {
-    const auto* prefs = SystemState::Get()->prefs();
-    if (!prefs->Exists(kPrefsUpdateOverCellularPermission)) {
+    PrefsInterface* prefs = system_state_->prefs();
+
+    if (!prefs || !prefs->Exists(kPrefsUpdateOverCellularPermission)) {
       // Update is not allowed as user preference is not set or not available.
       *out_allowed = false;
       return true;
@@ -362,26 +366,26 @@
 bool UpdateEngineService::GetDurationSinceUpdate(ErrorPtr* error,
                                                  int64_t* out_usec_wallclock) {
   base::Time time;
-  if (!SystemState::Get()->update_attempter()->GetBootTimeAtUpdate(&time)) {
+  if (!system_state_->update_attempter()->GetBootTimeAtUpdate(&time)) {
     LogAndSetError(error, FROM_HERE, "No pending update.");
     return false;
   }
 
-  const auto* clock = SystemState::Get()->clock();
+  ClockInterface* clock = system_state_->clock();
   *out_usec_wallclock = (clock->GetBootTime() - time).InMicroseconds();
   return true;
 }
 
 bool UpdateEngineService::GetPrevVersion(ErrorPtr* /* error */,
                                          string* out_prev_version) {
-  *out_prev_version = SystemState::Get()->update_attempter()->GetPrevVersion();
+  *out_prev_version = system_state_->update_attempter()->GetPrevVersion();
   return true;
 }
 
 bool UpdateEngineService::GetRollbackPartition(
     ErrorPtr* /* error */, string* out_rollback_partition_name) {
   BootControlInterface::Slot rollback_slot =
-      SystemState::Get()->update_attempter()->GetRollbackSlot();
+      system_state_->update_attempter()->GetRollbackSlot();
 
   if (rollback_slot == BootControlInterface::kInvalidSlot) {
     out_rollback_partition_name->clear();
@@ -389,7 +393,7 @@
   }
 
   string name;
-  if (!SystemState::Get()->boot_control()->GetPartitionDevice(
+  if (!system_state_->boot_control()->GetPartitionDevice(
           "KERNEL", rollback_slot, &name)) {
     LOG(ERROR) << "Invalid rollback device";
     return false;
@@ -403,9 +407,25 @@
 bool UpdateEngineService::GetLastAttemptError(ErrorPtr* /* error */,
                                               int32_t* out_last_attempt_error) {
   ErrorCode error_code =
-      SystemState::Get()->update_attempter()->GetAttemptErrorCode();
+      system_state_->update_attempter()->GetAttemptErrorCode();
   *out_last_attempt_error = static_cast<int>(error_code);
   return true;
 }
 
+bool UpdateEngineService::GetEolStatus(ErrorPtr* error,
+                                       int32_t* out_eol_status) {
+  PrefsInterface* prefs = system_state_->prefs();
+
+  string str_eol_status;
+  if (prefs->Exists(kPrefsOmahaEolStatus) &&
+      !prefs->GetString(kPrefsOmahaEolStatus, &str_eol_status)) {
+    LogAndSetError(error, FROM_HERE, "Error getting the end-of-life status.");
+    return false;
+  }
+
+  // StringToEolStatus will return kSupported for invalid values.
+  *out_eol_status = static_cast<int32_t>(StringToEolStatus(str_eol_status));
+  return true;
+}
+
 }  // namespace chromeos_update_engine
diff --git a/cros/common_service.h b/common_service.h
similarity index 90%
rename from cros/common_service.h
rename to common_service.h
index 2c176c5..f93855d 100644
--- a/cros/common_service.h
+++ b/common_service.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_SERVICE_H_
-#define UPDATE_ENGINE_CROS_SERVICE_H_
+#ifndef UPDATE_ENGINE_COMMON_SERVICE_H_
+#define UPDATE_ENGINE_COMMON_SERVICE_H_
 
 #include <inttypes.h>
 
@@ -26,6 +26,7 @@
 #include <brillo/errors/error.h>
 
 #include "update_engine/client_library/include/update_engine/update_status.h"
+#include "update_engine/system_state.h"
 
 namespace chromeos_update_engine {
 
@@ -37,7 +38,7 @@
   // Generic service error.
   static const char* const kErrorFailed;
 
-  UpdateEngineService();
+  explicit UpdateEngineService(SystemState* system_state);
   virtual ~UpdateEngineService() = default;
 
   // Set flags that influence how updates and checks are performed.  These
@@ -54,10 +55,10 @@
 
   // Attempts a DLC module install operation.
   // |omaha_url|: the URL to query for update.
-  // |dlc_ids|: a list of DLC module IDs.
+  // |dlc_module_ids|: a list of DLC module IDs.
   bool AttemptInstall(brillo::ErrorPtr* error,
                       const std::string& omaha_url,
-                      const std::vector<std::string>& dlc_ids);
+                      const std::vector<std::string>& dlc_module_ids);
 
   bool AttemptRollback(brillo::ErrorPtr* error, bool in_powerwash);
 
@@ -69,13 +70,6 @@
   // update. This is used for development only.
   bool ResetStatus(brillo::ErrorPtr* error);
 
-  // Sets the DLC as active or inactive. When set to active, the ping metadata
-  // for the DLC is updated accordingly. When set to inactive, the metadata
-  // for the DLC is deleted.
-  bool SetDlcActiveValue(brillo::ErrorPtr* error,
-                         bool is_active,
-                         const std::string& dlc_id);
-
   // Returns the current status of the Update Engine. If an update is in
   // progress, the number of operations, size to download and overall progress
   // is reported.
@@ -108,8 +102,7 @@
   // Sets the current "cohort hint" value to |in_cohort_hint|. The cohort hint
   // is sent back to Omaha on every request and can be used as a hint of what
   // cohort should we be put on.
-  bool SetCohortHint(brillo::ErrorPtr* error,
-                     const std::string& in_cohort_hint);
+  bool SetCohortHint(brillo::ErrorPtr* error, std::string in_cohort_hint);
 
   // Return the current cohort hint. This value can be set with SetCohortHint()
   // and can also be updated from Omaha on every update check request.
@@ -159,8 +152,15 @@
   // Returns the last UpdateAttempt error.
   bool GetLastAttemptError(brillo::ErrorPtr* error,
                            int32_t* out_last_attempt_error);
+
+  // Returns the current end-of-life status of the device. This value is updated
+  // on every update check and persisted on disk across reboots.
+  bool GetEolStatus(brillo::ErrorPtr* error, int32_t* out_eol_status);
+
+ private:
+  SystemState* system_state_;
 };
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_SERVICE_H_
+#endif  // UPDATE_ENGINE_COMMON_SERVICE_H_
diff --git a/cros/common_service_unittest.cc b/common_service_unittest.cc
similarity index 76%
rename from cros/common_service_unittest.cc
rename to common_service_unittest.cc
index 0644643..65202a0 100644
--- a/cros/common_service_unittest.cc
+++ b/common_service_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/common_service.h"
+#include "update_engine/common_service.h"
 
 #include <gtest/gtest.h>
 #include <string>
@@ -24,8 +24,9 @@
 #include <policy/libpolicy.h>
 #include <policy/mock_device_policy.h>
 
-#include "update_engine/cros/fake_system_state.h"
-#include "update_engine/cros/omaha_utils.h"
+#include "update_engine/common/fake_prefs.h"
+#include "update_engine/fake_system_state.h"
+#include "update_engine/omaha_utils.h"
 
 using std::string;
 using std::vector;
@@ -38,14 +39,17 @@
 
 class UpdateEngineServiceTest : public ::testing::Test {
  protected:
-  UpdateEngineServiceTest() = default;
+  UpdateEngineServiceTest()
+      : mock_update_attempter_(fake_system_state_.mock_update_attempter()),
+        common_service_(&fake_system_state_) {}
 
-  void SetUp() override {
-    FakeSystemState::CreateInstance();
-    FakeSystemState::Get()->set_device_policy(nullptr);
-    mock_update_attempter_ = FakeSystemState::Get()->mock_update_attempter();
-  }
+  void SetUp() override { fake_system_state_.set_device_policy(nullptr); }
 
+  // Fake/mock infrastructure.
+  FakeSystemState fake_system_state_;
+  policy::MockDevicePolicy mock_device_policy_;
+
+  // Shortcut for fake_system_state_.mock_update_attempter().
   MockUpdateAttempter* mock_update_attempter_;
 
   brillo::ErrorPtr error_;
@@ -96,26 +100,12 @@
   EXPECT_FALSE(common_service_.AttemptInstall(&error_, "", {}));
 }
 
-TEST_F(UpdateEngineServiceTest, SetDlcActiveValue) {
-  EXPECT_CALL(*mock_update_attempter_, SetDlcActiveValue(_, _))
-      .WillOnce(Return(true));
-
-  EXPECT_TRUE(common_service_.SetDlcActiveValue(&error_, true, "dlc0"));
-}
-
-TEST_F(UpdateEngineServiceTest, SetDlcActiveValueReturnsFalse) {
-  EXPECT_CALL(*mock_update_attempter_, SetDlcActiveValue(_, _))
-      .WillOnce(Return(false));
-
-  EXPECT_FALSE(common_service_.SetDlcActiveValue(&error_, true, "dlc0"));
-}
-
 // SetChannel is allowed when there's no device policy (the device is not
 // enterprise enrolled).
 TEST_F(UpdateEngineServiceTest, SetChannelWithNoPolicy) {
   EXPECT_CALL(*mock_update_attempter_, RefreshDevicePolicy());
   // If SetTargetChannel is called it means the policy check passed.
-  EXPECT_CALL(*FakeSystemState::Get()->mock_request_params(),
+  EXPECT_CALL(*fake_system_state_.mock_request_params(),
               SetTargetChannel("stable-channel", true, _))
       .WillOnce(Return(true));
   EXPECT_TRUE(common_service_.SetChannel(&error_, "stable-channel", true));
@@ -125,10 +115,10 @@
 // When the policy is present, the delegated value should be checked.
 TEST_F(UpdateEngineServiceTest, SetChannelWithDelegatedPolicy) {
   policy::MockDevicePolicy mock_device_policy;
-  FakeSystemState::Get()->set_device_policy(&mock_device_policy);
+  fake_system_state_.set_device_policy(&mock_device_policy);
   EXPECT_CALL(mock_device_policy, GetReleaseChannelDelegated(_))
       .WillOnce(DoAll(SetArgPointee<0>(true), Return(true)));
-  EXPECT_CALL(*FakeSystemState::Get()->mock_request_params(),
+  EXPECT_CALL(*fake_system_state_.mock_request_params(),
               SetTargetChannel("beta-channel", true, _))
       .WillOnce(Return(true));
 
@@ -140,7 +130,7 @@
 // raised.
 TEST_F(UpdateEngineServiceTest, SetChannelWithInvalidChannel) {
   EXPECT_CALL(*mock_update_attempter_, RefreshDevicePolicy());
-  EXPECT_CALL(*FakeSystemState::Get()->mock_request_params(),
+  EXPECT_CALL(*fake_system_state_.mock_request_params(),
               SetTargetChannel("foo-channel", true, _))
       .WillOnce(Return(false));
 
@@ -151,8 +141,8 @@
 }
 
 TEST_F(UpdateEngineServiceTest, GetChannel) {
-  FakeSystemState::Get()->mock_request_params()->set_current_channel("current");
-  FakeSystemState::Get()->mock_request_params()->set_target_channel("target");
+  fake_system_state_.mock_request_params()->set_current_channel("current");
+  fake_system_state_.mock_request_params()->set_target_channel("target");
   string channel;
   EXPECT_TRUE(common_service_.GetChannel(
       &error_, true /* get_current_channel */, &channel));
@@ -179,4 +169,19 @@
                                UpdateEngineService::kErrorFailed));
 }
 
+TEST_F(UpdateEngineServiceTest, GetEolStatusTest) {
+  FakePrefs fake_prefs;
+  fake_system_state_.set_prefs(&fake_prefs);
+  // The default value should be "supported".
+  int32_t eol_status = static_cast<int32_t>(EolStatus::kEol);
+  EXPECT_TRUE(common_service_.GetEolStatus(&error_, &eol_status));
+  EXPECT_EQ(nullptr, error_);
+  EXPECT_EQ(EolStatus::kSupported, static_cast<EolStatus>(eol_status));
+
+  fake_prefs.SetString(kPrefsOmahaEolStatus, "security-only");
+  EXPECT_TRUE(common_service_.GetEolStatus(&error_, &eol_status));
+  EXPECT_EQ(nullptr, error_);
+  EXPECT_EQ(EolStatus::kSecurityOnly, static_cast<EolStatus>(eol_status));
+}
+
 }  // namespace chromeos_update_engine
diff --git a/cros/connection_manager.cc b/connection_manager.cc
similarity index 64%
rename from cros/connection_manager.cc
rename to connection_manager.cc
index 6a5c63b..7263a74 100644
--- a/cros/connection_manager.cc
+++ b/connection_manager.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/connection_manager.h"
+#include "update_engine/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/cros/shill_proxy.h"
-#include "update_engine/cros/update_attempter.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"
 
 using org::chromium::flimflam::ManagerProxyInterface;
 using org::chromium::flimflam::ServiceProxyInterface;
@@ -41,75 +41,83 @@
 namespace chromeos_update_engine {
 
 namespace connection_manager {
-std::unique_ptr<ConnectionManagerInterface> CreateConnectionManager() {
+std::unique_ptr<ConnectionManagerInterface> CreateConnectionManager(
+    SystemState* system_state) {
   return std::unique_ptr<ConnectionManagerInterface>(
-      new ConnectionManager(new ShillProxy()));
+      new ConnectionManager(new ShillProxy(), system_state));
 }
 }  // namespace connection_manager
 
-ConnectionManager::ConnectionManager(ShillProxyInterface* shill_proxy)
-    : shill_proxy_(shill_proxy) {}
+ConnectionManager::ConnectionManager(ShillProxyInterface* shill_proxy,
+                                     SystemState* system_state)
+    : shill_proxy_(shill_proxy), system_state_(system_state) {}
 
 bool ConnectionManager::IsUpdateAllowedOver(
     ConnectionType type, ConnectionTethering tethering) const {
-  if (type != ConnectionType::kCellular) {
-    if (tethering != ConnectionTethering::kConfirmed) {
+  switch (type) {
+    case ConnectionType::kBluetooth:
+      return false;
+
+    case ConnectionType::kCellular: {
+      set<string> allowed_types;
+
+      const policy::DevicePolicy* device_policy =
+          system_state_->device_policy();
+
+      // The device_policy is loaded in a lazy way before an update check. Load
+      // it now from the libbrillo cache if it wasn't already loaded.
+      if (!device_policy) {
+        UpdateAttempter* update_attempter = system_state_->update_attempter();
+        if (update_attempter) {
+          update_attempter->RefreshDevicePolicy();
+          device_policy = system_state_->device_policy();
+        }
+      }
+
+      if (!device_policy) {
+        // Device policy fails to be loaded (possibly due to guest account). We
+        // do not check the local user setting here, which should be checked by
+        // |OmahaRequestAction| during checking for update.
+        LOG(INFO) << "Allowing updates over cellular as device policy "
+                     "fails to be loaded.";
+        return true;
+      }
+
+      if (device_policy->GetAllowedConnectionTypesForUpdate(&allowed_types)) {
+        // The update setting is enforced by the device policy.
+
+        if (!base::ContainsKey(allowed_types, shill::kTypeCellular)) {
+          LOG(INFO) << "Disabling updates over cellular connection as it's not "
+                       "allowed in the device policy.";
+          return false;
+        }
+
+        LOG(INFO) << "Allowing updates over cellular per device policy.";
+        return true;
+      }
+
+      // If there's no update setting in the device policy, we do not check
+      // the local user setting here, which should be checked by
+      // |OmahaRequestAction| during checking for update.
+      LOG(INFO) << "Allowing updates over cellular as device policy does "
+                   "not include update setting.";
       return true;
     }
 
-    // Treat this connection as if it is a cellular connection.
-    LOG(INFO)
-        << "Current connection is confirmed tethered, using Cellular setting.";
+    default:
+      if (tethering == ConnectionTethering::kConfirmed) {
+        // Treat this connection as if it is a cellular connection.
+        LOG(INFO) << "Current connection is confirmed tethered, using Cellular "
+                     "setting.";
+        return IsUpdateAllowedOver(ConnectionType::kCellular,
+                                   ConnectionTethering::kUnknown);
+      }
+      return true;
   }
-
-  const policy::DevicePolicy* device_policy =
-      SystemState::Get()->device_policy();
-
-  // The device_policy is loaded in a lazy way before an update check. Load
-  // it now from the libbrillo cache if it wasn't already loaded.
-  if (!device_policy) {
-    UpdateAttempter* update_attempter = SystemState::Get()->update_attempter();
-    if (update_attempter) {
-      update_attempter->RefreshDevicePolicy();
-      device_policy = SystemState::Get()->device_policy();
-    }
-  }
-
-  if (!device_policy) {
-    // Device policy fails to be loaded (possibly due to guest account). We
-    // do not check the local user setting here, which should be checked by
-    // |OmahaRequestAction| during checking for update.
-    LOG(INFO) << "Allowing updates over cellular as device policy fails to be "
-                 "loaded.";
-    return true;
-  }
-
-  set<string> allowed_types;
-  if (device_policy->GetAllowedConnectionTypesForUpdate(&allowed_types)) {
-    // The update setting is enforced by the device policy.
-
-    // TODO(crbug.com/1054279): Use base::Contains after uprev to r680000.
-    if (allowed_types.find(shill::kTypeCellular) == allowed_types.end()) {
-      LOG(INFO) << "Disabling updates over cellular connection as it's not "
-                   "allowed in the device policy.";
-      return false;
-    }
-
-    LOG(INFO) << "Allowing updates over cellular per device policy.";
-    return true;
-  }
-
-  // If there's no update setting in the device policy, we do not check
-  // the local user setting here, which should be checked by
-  // |OmahaRequestAction| during checking for update.
-  LOG(INFO) << "Allowing updates over cellular as device policy does "
-               "not include update setting.";
-  return true;
 }
 
 bool ConnectionManager::IsAllowedConnectionTypesForUpdateSet() const {
-  const policy::DevicePolicy* device_policy =
-      SystemState::Get()->device_policy();
+  const policy::DevicePolicy* device_policy = system_state_->device_policy();
   if (!device_policy) {
     LOG(INFO) << "There's no device policy loaded yet.";
     return false;
diff --git a/cros/connection_manager.h b/connection_manager.h
similarity index 83%
rename from cros/connection_manager.h
rename to connection_manager.h
index bb54ff7..d8527a3 100644
--- a/cros/connection_manager.h
+++ b/connection_manager.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_CONNECTION_MANAGER_H_
-#define UPDATE_ENGINE_CROS_CONNECTION_MANAGER_H_
+#ifndef UPDATE_ENGINE_CONNECTION_MANAGER_H_
+#define UPDATE_ENGINE_CONNECTION_MANAGER_H_
 
 #include <memory>
 #include <string>
@@ -23,8 +23,8 @@
 #include <base/macros.h>
 #include <dbus/object_path.h>
 
-#include "update_engine/cros/connection_manager_interface.h"
-#include "update_engine/cros/shill_proxy_interface.h"
+#include "update_engine/connection_manager_interface.h"
+#include "update_engine/shill_proxy_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -35,7 +35,8 @@
  public:
   // Constructs a new ConnectionManager object initialized with the
   // given system state.
-  explicit ConnectionManager(ShillProxyInterface* shill_proxy);
+  ConnectionManager(ShillProxyInterface* shill_proxy,
+                    SystemState* system_state);
   ~ConnectionManager() override = default;
 
   // ConnectionManagerInterface overrides.
@@ -57,9 +58,12 @@
   // The mockable interface to access the shill DBus proxies.
   std::unique_ptr<ShillProxyInterface> shill_proxy_;
 
+  // The global context for update_engine.
+  SystemState* system_state_;
+
   DISALLOW_COPY_AND_ASSIGN(ConnectionManager);
 };
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_CONNECTION_MANAGER_H_
+#endif  // UPDATE_ENGINE_CONNECTION_MANAGER_H_
diff --git a/connection_manager_android.cc b/connection_manager_android.cc
new file mode 100644
index 0000000..9d0c57b
--- /dev/null
+++ b/connection_manager_android.cc
@@ -0,0 +1,43 @@
+//
+// 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
new file mode 100644
index 0000000..006f4ea
--- /dev/null
+++ b/connection_manager_android.h
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#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/cros/connection_manager_interface.h b/connection_manager_interface.h
similarity index 88%
rename from cros/connection_manager_interface.h
rename to connection_manager_interface.h
index dc6c983..9f77989 100644
--- a/cros/connection_manager_interface.h
+++ b/connection_manager_interface.h
@@ -14,17 +14,19 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_CONNECTION_MANAGER_INTERFACE_H_
-#define UPDATE_ENGINE_CROS_CONNECTION_MANAGER_INTERFACE_H_
+#ifndef UPDATE_ENGINE_CONNECTION_MANAGER_INTERFACE_H_
+#define UPDATE_ENGINE_CONNECTION_MANAGER_INTERFACE_H_
 
 #include <memory>
 
 #include <base/macros.h>
 
-#include "update_engine/common/connection_utils.h"
+#include "update_engine/connection_utils.h"
 
 namespace chromeos_update_engine {
 
+class SystemState;
+
 // This class exposes a generic interface to the connection manager
 // (e.g FlimFlam, Shill, etc.) to consolidate all connection-related
 // logic in update_engine.
@@ -57,9 +59,10 @@
 
 namespace connection_manager {
 // Factory function which creates a ConnectionManager.
-std::unique_ptr<ConnectionManagerInterface> CreateConnectionManager();
+std::unique_ptr<ConnectionManagerInterface> CreateConnectionManager(
+    SystemState* system_state);
 }  // namespace connection_manager
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_CONNECTION_MANAGER_INTERFACE_H_
+#endif  // UPDATE_ENGINE_CONNECTION_MANAGER_INTERFACE_H_
diff --git a/cros/connection_manager_unittest.cc b/connection_manager_unittest.cc
similarity index 86%
rename from cros/connection_manager_unittest.cc
rename to connection_manager_unittest.cc
index 46da8cc..3cdaf4c 100644
--- a/cros/connection_manager_unittest.cc
+++ b/connection_manager_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/connection_manager.h"
+#include "update_engine/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/cros/fake_shill_proxy.h"
-#include "update_engine/cros/fake_system_state.h"
+#include "update_engine/fake_shill_proxy.h"
+#include "update_engine/fake_system_state.h"
 
 using chromeos_update_engine::connection_utils::StringForConnectionType;
 using org::chromium::flimflam::ManagerProxyMock;
@@ -52,8 +52,7 @@
 
   void SetUp() override {
     loop_.SetAsCurrent();
-    FakeSystemState::CreateInstance();
-    FakeSystemState::Get()->set_connection_manager(&cmut_);
+    fake_system_state_.set_connection_manager(&cmut_);
   }
 
   void TearDown() override { EXPECT_FALSE(loop_.PendingTasks()); }
@@ -82,10 +81,11 @@
                                 ConnectionTethering expected_tethering);
 
   brillo::FakeMessageLoop loop_{nullptr};
+  FakeSystemState fake_system_state_;
   FakeShillProxy* fake_shill_proxy_;
 
   // ConnectionManager under test.
-  ConnectionManager cmut_{fake_shill_proxy_};
+  ConnectionManager cmut_{fake_shill_proxy_, &fake_system_state_};
 };
 
 void ConnectionManagerTest::SetManagerReply(const char* default_service,
@@ -184,6 +184,9 @@
 TEST_F(ConnectionManagerTest, SimpleTest) {
   TestWithServiceType(shill::kTypeEthernet, nullptr, ConnectionType::kEthernet);
   TestWithServiceType(shill::kTypeWifi, nullptr, ConnectionType::kWifi);
+  TestWithServiceType(shill::kTypeWimax, nullptr, ConnectionType::kWimax);
+  TestWithServiceType(
+      shill::kTypeBluetooth, nullptr, ConnectionType::kBluetooth);
   TestWithServiceType(shill::kTypeCellular, nullptr, ConnectionType::kCellular);
 }
 
@@ -192,6 +195,8 @@
   TestWithServiceType(
       shill::kTypeVPN, shill::kTypeVPN, ConnectionType::kUnknown);
   TestWithServiceType(shill::kTypeVPN, shill::kTypeWifi, ConnectionType::kWifi);
+  TestWithServiceType(
+      shill::kTypeVPN, shill::kTypeWimax, ConnectionType::kWimax);
 }
 
 TEST_F(ConnectionManagerTest, TetheringTest) {
@@ -224,10 +229,20 @@
                                         ConnectionTethering::kUnknown));
 }
 
+TEST_F(ConnectionManagerTest, AllowUpdatesOverWimaxTest) {
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(ConnectionType::kWimax,
+                                        ConnectionTethering::kUnknown));
+}
+
+TEST_F(ConnectionManagerTest, BlockUpdatesOverBluetoothTest) {
+  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(ConnectionType::kBluetooth,
+                                         ConnectionTethering::kUnknown));
+}
+
 TEST_F(ConnectionManagerTest, AllowUpdatesOnlyOver3GPerPolicyTest) {
   policy::MockDevicePolicy allow_3g_policy;
 
-  FakeSystemState::Get()->set_device_policy(&allow_3g_policy);
+  fake_system_state_.set_device_policy(&allow_3g_policy);
 
   // This test tests cellular (3G) being the only connection type being allowed.
   set<string> allowed_set;
@@ -244,13 +259,14 @@
 TEST_F(ConnectionManagerTest, AllowUpdatesOver3GAndOtherTypesPerPolicyTest) {
   policy::MockDevicePolicy allow_3g_policy;
 
-  FakeSystemState::Get()->set_device_policy(&allow_3g_policy);
+  fake_system_state_.set_device_policy(&allow_3g_policy);
 
   // This test tests multiple connection types being allowed, with
   // 3G one among them. Only Cellular is currently enforced by the policy
-  // setting.
+  // setting, the others are ignored (see Bluetooth for example).
   set<string> allowed_set;
   allowed_set.insert(StringForConnectionType(ConnectionType::kCellular));
+  allowed_set.insert(StringForConnectionType(ConnectionType::kBluetooth));
 
   EXPECT_CALL(allow_3g_policy, GetAllowedConnectionTypesForUpdate(_))
       .Times(3)
@@ -264,6 +280,10 @@
                                         ConnectionTethering::kUnknown));
   EXPECT_TRUE(cmut_.IsUpdateAllowedOver(ConnectionType::kWifi,
                                         ConnectionTethering::kUnknown));
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(ConnectionType::kWimax,
+                                        ConnectionTethering::kUnknown));
+  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(ConnectionType::kBluetooth,
+                                         ConnectionTethering::kUnknown));
 
   // Tethered networks are treated in the same way as Cellular networks and
   // thus allowed.
@@ -276,7 +296,7 @@
 TEST_F(ConnectionManagerTest, AllowUpdatesOverCellularByDefaultTest) {
   policy::MockDevicePolicy device_policy;
   // Set an empty device policy.
-  FakeSystemState::Get()->set_device_policy(&device_policy);
+  fake_system_state_.set_device_policy(&device_policy);
 
   EXPECT_TRUE(cmut_.IsUpdateAllowedOver(ConnectionType::kCellular,
                                         ConnectionTethering::kUnknown));
@@ -285,7 +305,7 @@
 TEST_F(ConnectionManagerTest, AllowUpdatesOverTetheredNetworkByDefaultTest) {
   policy::MockDevicePolicy device_policy;
   // Set an empty device policy.
-  FakeSystemState::Get()->set_device_policy(&device_policy);
+  fake_system_state_.set_device_policy(&device_policy);
 
   EXPECT_TRUE(cmut_.IsUpdateAllowedOver(ConnectionType::kWifi,
                                         ConnectionTethering::kConfirmed));
@@ -298,13 +318,14 @@
 TEST_F(ConnectionManagerTest, BlockUpdatesOver3GPerPolicyTest) {
   policy::MockDevicePolicy block_3g_policy;
 
-  FakeSystemState::Get()->set_device_policy(&block_3g_policy);
+  fake_system_state_.set_device_policy(&block_3g_policy);
 
   // Test that updates for 3G are blocked while updates are allowed
   // over several other types.
   set<string> allowed_set;
   allowed_set.insert(StringForConnectionType(ConnectionType::kEthernet));
   allowed_set.insert(StringForConnectionType(ConnectionType::kWifi));
+  allowed_set.insert(StringForConnectionType(ConnectionType::kWimax));
 
   EXPECT_CALL(block_3g_policy, GetAllowedConnectionTypesForUpdate(_))
       .Times(1)
@@ -317,7 +338,7 @@
 TEST_F(ConnectionManagerTest, AllowUpdatesOver3GIfPolicyIsNotSet) {
   policy::MockDevicePolicy device_policy;
 
-  FakeSystemState::Get()->set_device_policy(&device_policy);
+  fake_system_state_.set_device_policy(&device_policy);
 
   // Return false for GetAllowedConnectionTypesForUpdate and see
   // that updates are allowed as device policy is not set. Further
@@ -331,7 +352,7 @@
 }
 
 TEST_F(ConnectionManagerTest, AllowUpdatesOverCellularIfPolicyFailsToBeLoaded) {
-  FakeSystemState::Get()->set_device_policy(nullptr);
+  fake_system_state_.set_device_policy(nullptr);
 
   EXPECT_TRUE(cmut_.IsUpdateAllowedOver(ConnectionType::kCellular,
                                         ConnectionTethering::kUnknown));
@@ -342,6 +363,10 @@
                StringForConnectionType(ConnectionType::kEthernet));
   EXPECT_STREQ(shill::kTypeWifi,
                StringForConnectionType(ConnectionType::kWifi));
+  EXPECT_STREQ(shill::kTypeWimax,
+               StringForConnectionType(ConnectionType::kWimax));
+  EXPECT_STREQ(shill::kTypeBluetooth,
+               StringForConnectionType(ConnectionType::kBluetooth));
   EXPECT_STREQ(shill::kTypeCellular,
                StringForConnectionType(ConnectionType::kCellular));
   EXPECT_STREQ("Unknown", StringForConnectionType(ConnectionType::kUnknown));
diff --git a/common/connection_utils.cc b/connection_utils.cc
similarity index 86%
rename from common/connection_utils.cc
rename to connection_utils.cc
index 44e5128..aeb0163 100644
--- a/common/connection_utils.cc
+++ b/connection_utils.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/common/connection_utils.h"
+#include "update_engine/connection_utils.h"
 
 #include <shill/dbus-constants.h>
 
@@ -32,6 +32,10 @@
     return ConnectionType::kEthernet;
   } else if (type_str == shill::kTypeWifi) {
     return ConnectionType::kWifi;
+  } else if (type_str == shill::kTypeWimax) {
+    return ConnectionType::kWimax;
+  } else if (type_str == shill::kTypeBluetooth) {
+    return ConnectionType::kBluetooth;
   } else if (type_str == shill::kTypeCellular) {
     return ConnectionType::kCellular;
   } else if (type_str == kTypeDisconnected) {
@@ -57,6 +61,10 @@
       return shill::kTypeEthernet;
     case ConnectionType::kWifi:
       return shill::kTypeWifi;
+    case ConnectionType::kWimax:
+      return shill::kTypeWimax;
+    case ConnectionType::kBluetooth:
+      return shill::kTypeBluetooth;
     case ConnectionType::kCellular:
       return shill::kTypeCellular;
     case ConnectionType::kDisconnected:
diff --git a/common/connection_utils.h b/connection_utils.h
similarity index 89%
rename from common/connection_utils.h
rename to connection_utils.h
index 5d63fb2..d5133a1 100644
--- a/common/connection_utils.h
+++ b/connection_utils.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_COMMON_CONNECTION_UTILS_H_
-#define UPDATE_ENGINE_COMMON_CONNECTION_UTILS_H_
+#ifndef UPDATE_ENGINE_CONNECTION_UTILS_H_
+#define UPDATE_ENGINE_CONNECTION_UTILS_H_
 
 #include <string>
 
@@ -25,6 +25,8 @@
   kDisconnected,
   kEthernet,
   kWifi,
+  kWimax,
+  kBluetooth,
   kCellular,
   kUnknown
 };
@@ -47,4 +49,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_COMMON_CONNECTION_UTILS_H_
+#endif  // UPDATE_ENGINE_CONNECTION_UTILS_H_
diff --git a/cros/daemon_chromeos.cc b/cros/daemon_chromeos.cc
deleted file mode 100644
index 366fb9a..0000000
--- a/cros/daemon_chromeos.cc
+++ /dev/null
@@ -1,79 +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/cros/daemon_chromeos.h"
-
-#include <sysexits.h>
-
-#include <base/bind.h>
-#include <base/location.h>
-
-#include "update_engine/cros/real_system_state.h"
-
-using brillo::Daemon;
-using std::unique_ptr;
-
-namespace chromeos_update_engine {
-
-unique_ptr<DaemonBase> DaemonBase::CreateInstance() {
-  return std::make_unique<DaemonChromeOS>();
-}
-
-int DaemonChromeOS::OnInit() {
-  // Register the |subprocess_| singleton with this Daemon as the signal
-  // handler.
-  subprocess_.Init(this);
-
-  int exit_code = Daemon::OnInit();
-  if (exit_code != EX_OK)
-    return exit_code;
-
-  // Initialize update engine global state.
-  // TODO(deymo): Move the initialization to a factory method avoiding the
-  // explicit re-usage of the |bus| instance, shared between D-Bus service and
-  // D-Bus client calls.
-  RealSystemState::SetInstance(&system_state_);
-
-  // Create the DBus service.
-  dbus_adaptor_.reset(new UpdateEngineAdaptor());
-  SystemState::Get()->update_attempter()->AddObserver(dbus_adaptor_.get());
-
-  dbus_adaptor_->RegisterAsync(
-      base::Bind(&DaemonChromeOS::OnDBusRegistered, base::Unretained(this)));
-  LOG(INFO) << "Waiting for DBus object to be registered.";
-  return EX_OK;
-}
-
-void DaemonChromeOS::OnDBusRegistered(bool succeeded) {
-  if (!succeeded) {
-    LOG(ERROR) << "Registering the UpdateEngineAdaptor";
-    QuitWithExitCode(1);
-    return;
-  }
-
-  // Take ownership of the service now that everything is initialized. We need
-  // to this now and not before to avoid exposing a well known DBus service
-  // path that doesn't have the service it is supposed to implement.
-  if (!dbus_adaptor_->RequestOwnership()) {
-    LOG(ERROR) << "Unable to take ownership of the DBus service, is there "
-               << "other update_engine daemon running?";
-    QuitWithExitCode(1);
-    return;
-  }
-  SystemState::Get()->update_attempter()->StartUpdater();
-}
-
-}  // namespace chromeos_update_engine
diff --git a/cros/daemon_chromeos.h b/cros/daemon_chromeos.h
deleted file mode 100644
index b23c2a6..0000000
--- a/cros/daemon_chromeos.h
+++ /dev/null
@@ -1,63 +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.
-//
-
-#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/cros/dbus_service.h"
-#include "update_engine/cros/real_system_state.h"
-
-namespace chromeos_update_engine {
-
-class DaemonChromeOS : public DaemonBase {
- public:
-  DaemonChromeOS() = default;
-
- protected:
-  int OnInit() override;
-
- private:
-  // Run from the main loop when the |dbus_adaptor_| object is registered. At
-  // this point we can request ownership of the DBus service name and continue
-  // initialization.
-  void OnDBusRegistered(bool succeeded);
-
-  // |SystemState| is a global context, but we can't have a static singleton of
-  // its object because the style guide does not allow that (it has non-trivial
-  // dtor). We need an instance of |SystemState| in this class instead and have
-  // a global pointer to it. This is better to be defined as the first variable
-  // of this class so it is initialized first and destructed last.
-  RealSystemState system_state_;
-
-  // Main D-Bus service adaptor.
-  std::unique_ptr<UpdateEngineAdaptor> dbus_adaptor_;
-
-  // The Subprocess singleton class requires a brillo::MessageLoop in the
-  // current thread, so we need to initialize it from this class instead of
-  // the main() function.
-  Subprocess subprocess_;
-
-  DISALLOW_COPY_AND_ASSIGN(DaemonChromeOS);
-};
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_CROS_DAEMON_CHROMEOS_H_
diff --git a/cros/dlcservice_chromeos.cc b/cros/dlcservice_chromeos.cc
deleted file mode 100644
index e510c1d..0000000
--- a/cros/dlcservice_chromeos.cc
+++ /dev/null
@@ -1,77 +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 "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/cros/dbus_connection.h"
-
-using std::string;
-using std::vector;
-
-namespace chromeos_update_engine {
-
-namespace {
-org::chromium::DlcServiceInterfaceProxy GetDlcServiceProxy() {
-  return {DBusConnection::Get()->GetDBus()};
-}
-}  // namespace
-
-std::unique_ptr<DlcServiceInterface> CreateDlcService() {
-  return std::make_unique<DlcServiceChromeOS>();
-}
-
-bool DlcServiceChromeOS::GetDlcsToUpdate(vector<string>* dlc_ids) {
-  if (!dlc_ids)
-    return false;
-  dlc_ids->clear();
-
-  brillo::ErrorPtr err;
-  if (!GetDlcServiceProxy().GetDlcsToUpdate(dlc_ids, &err)) {
-    LOG(ERROR) << "dlcservice failed to return DLCs that need to be updated. "
-               << "ErrorCode=" << err->GetCode()
-               << ", ErrMsg=" << err->GetMessage();
-    dlc_ids->clear();
-    return false;
-  }
-  return true;
-}
-
-bool DlcServiceChromeOS::InstallCompleted(const vector<string>& dlc_ids) {
-  brillo::ErrorPtr err;
-  if (!GetDlcServiceProxy().InstallCompleted(dlc_ids, &err)) {
-    LOG(ERROR) << "dlcservice failed to complete install. ErrCode="
-               << err->GetCode() << ", ErrMsg=" << err->GetMessage();
-    return false;
-  }
-  return true;
-}
-
-bool DlcServiceChromeOS::UpdateCompleted(const vector<string>& dlc_ids) {
-  brillo::ErrorPtr err;
-  if (!GetDlcServiceProxy().UpdateCompleted(dlc_ids, &err)) {
-    LOG(ERROR) << "dlcservice failed to complete updated. ErrCode="
-               << err->GetCode() << ", ErrMsg=" << err->GetMessage();
-    return false;
-  }
-  return true;
-}
-
-}  // namespace chromeos_update_engine
diff --git a/cros/dlcservice_chromeos.h b/cros/dlcservice_chromeos.h
deleted file mode 100644
index 3f11b12..0000000
--- a/cros/dlcservice_chromeos.h
+++ /dev/null
@@ -1,55 +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.
-//
-
-#ifndef UPDATE_ENGINE_CROS_DLCSERVICE_CHROMEOS_H_
-#define UPDATE_ENGINE_CROS_DLCSERVICE_CHROMEOS_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "update_engine/common/dlcservice_interface.h"
-
-namespace chromeos_update_engine {
-
-// The Chrome OS implementation of the DlcServiceInterface. This interface
-// interacts with dlcservice via D-Bus.
-class DlcServiceChromeOS : public DlcServiceInterface {
- public:
-  DlcServiceChromeOS() = default;
-  ~DlcServiceChromeOS() = default;
-
-  // DlcServiceInterface overrides.
-
-  // Will clear the |dlc_ids|, passed to be modified. Clearing by default has
-  // the added benefit of avoiding indeterminate behavior in the case that
-  // |dlc_ids| wasn't empty to begin which would lead to possible duplicates and
-  // cases when error was not checked it's still safe.
-  bool GetDlcsToUpdate(std::vector<std::string>* dlc_ids) override;
-
-  // Call into dlcservice for it to mark the DLC IDs as being installed.
-  bool InstallCompleted(const std::vector<std::string>& dlc_ids) override;
-
-  // Call into dlcservice for it to mark the DLC IDs as being updated.
-  bool UpdateCompleted(const std::vector<std::string>& dlc_ids) override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(DlcServiceChromeOS);
-};
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_CROS_DLCSERVICE_CHROMEOS_H_
diff --git a/cros/download_action_chromeos.h b/cros/download_action_chromeos.h
deleted file mode 100644
index 068946a..0000000
--- a/cros/download_action_chromeos.h
+++ /dev/null
@@ -1,174 +0,0 @@
-//
-// 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_COMMON_DOWNLOAD_ACTION_CHROMEOS_H_
-#define UPDATE_ENGINE_COMMON_DOWNLOAD_ACTION_CHROMEOS_H_
-
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <memory>
-#include <string>
-
-#include "update_engine/common/action.h"
-#include "update_engine/common/boot_control_interface.h"
-#include "update_engine/common/download_action.h"
-#include "update_engine/common/http_fetcher.h"
-#include "update_engine/common/multi_range_http_fetcher.h"
-#include "update_engine/payload_consumer/delta_performer.h"
-#include "update_engine/payload_consumer/install_plan.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
-// DeltaPerformer that will apply the delta to the disk.
-
-namespace chromeos_update_engine {
-
-class PrefsInterface;
-
-class DownloadActionChromeos : public InstallPlanAction,
-                               public HttpFetcherDelegate {
- public:
-  static std::string StaticType() { return "DownloadActionChromeos"; }
-
-  // Takes ownership of the passed in HttpFetcher. Useful for testing.
-  // A good calling pattern is:
-  // DownloadActionChromeos(prefs, boot_contol, hardware, system_state,
-  //                new WhateverHttpFetcher, false);
-  DownloadActionChromeos(PrefsInterface* prefs,
-                         BootControlInterface* boot_control,
-                         HardwareInterface* hardware,
-                         HttpFetcher* http_fetcher,
-                         bool interactive);
-  ~DownloadActionChromeos() override;
-
-  // InstallPlanAction overrides.
-  void PerformAction() override;
-  void SuspendAction() override;
-  void ResumeAction() override;
-  void TerminateProcessing() override;
-  std::string Type() const override { return StaticType(); }
-
-  // Testing
-  void SetTestFileWriter(FileWriter* writer) { writer_ = writer; }
-
-  int GetHTTPResponseCode() { return http_fetcher_->http_response_code(); }
-
-  // HttpFetcherDelegate methods (see http_fetcher.h)
-  bool ReceivedBytes(HttpFetcher* fetcher,
-                     const void* bytes,
-                     size_t length) override;
-  void SeekToOffset(off_t offset) override;
-  void TransferComplete(HttpFetcher* fetcher, bool successful) override;
-  void TransferTerminated(HttpFetcher* fetcher) override;
-
-  DownloadActionDelegate* delegate() const { return delegate_; }
-  void set_delegate(DownloadActionDelegate* delegate) { delegate_ = delegate; }
-
-  void set_base_offset(int64_t base_offset) { base_offset_ = base_offset; }
-
-  HttpFetcher* http_fetcher() { return http_fetcher_.get(); }
-
-  // Returns the p2p file id for the file being written or the empty
-  // string if we're not writing to a p2p file.
-  std::string p2p_file_id() { return p2p_file_id_; }
-
- private:
-  // Closes the file descriptor for the p2p file being written and
-  // clears |p2p_file_id_| to indicate that we're no longer sharing
-  // the file. If |delete_p2p_file| is True, also deletes the file.
-  // If there is no p2p file descriptor, this method does nothing.
-  void CloseP2PSharingFd(bool delete_p2p_file);
-
-  // Starts sharing the p2p file. Must be called before
-  // WriteToP2PFile(). Returns True if this worked.
-  bool SetupP2PSharingFd();
-
-  // Writes |length| bytes of payload from |data| into |file_offset|
-  // of the p2p file. Also does validation checks; for example ensures we
-  // don't end up with a file with holes in it.
-  //
-  // This method does nothing if SetupP2PSharingFd() hasn't been
-  // called or if CloseP2PSharingFd() has been called.
-  void WriteToP2PFile(const void* data, size_t length, off_t file_offset);
-
-  // Attempt to load cached manifest data from prefs
-  // return true on success, false otherwise.
-  bool LoadCachedManifest(int64_t manifest_size);
-
-  // Start downloading the current payload using delta_performer.
-  void StartDownloading();
-
-  // Pointer to the current payload in install_plan_.payloads.
-  InstallPlan::Payload* payload_{nullptr};
-
-  PrefsInterface* prefs_;
-  BootControlInterface* boot_control_;
-  HardwareInterface* hardware_;
-
-  // Pointer to the MultiRangeHttpFetcher that does the http work.
-  std::unique_ptr<MultiRangeHttpFetcher> http_fetcher_;
-
-  // If |true|, the update is user initiated (vs. periodic update checks). Hence
-  // the |delta_performer_| can decide not to use O_DSYNC flag for faster
-  // update.
-  bool interactive_;
-
-  // The FileWriter that downloaded data should be written to. It will
-  // either point to *decompressing_file_writer_ or *delta_performer_.
-  FileWriter* writer_;
-
-  std::unique_ptr<DeltaPerformer> delta_performer_;
-
-  // Used by TransferTerminated to figure if this action terminated itself or
-  // was terminated by the action processor.
-  ErrorCode code_;
-
-  // For reporting status to outsiders
-  DownloadActionDelegate* delegate_;
-  uint64_t bytes_received_{0};  // per file/range
-  uint64_t bytes_received_previous_payloads_{0};
-  uint64_t bytes_total_{0};
-  bool download_active_{false};
-
-  // The file-id for the file we're sharing or the empty string
-  // if we're not using p2p to share.
-  std::string p2p_file_id_;
-
-  // The file descriptor for the p2p file used for caching the payload or -1
-  // if we're not using p2p to share.
-  int p2p_sharing_fd_;
-
-  // Set to |false| if p2p file is not visible.
-  bool p2p_visible_;
-
-  // Loaded from prefs before downloading any payload.
-  size_t resume_payload_index_{0};
-
-  // Offset of the payload in the download URL, used by UpdateAttempterAndroid.
-  int64_t base_offset_{0};
-
-  DISALLOW_COPY_AND_ASSIGN(DownloadActionChromeos);
-};
-
-// We want to be sure that we're compiled with large file support on linux,
-// just in case we find ourselves downloading large images.
-static_assert(8 == sizeof(off_t), "off_t not 64 bit");
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_COMMON_DOWNLOAD_ACTION_CHROMEOS_H_
diff --git a/cros/excluder_chromeos.cc b/cros/excluder_chromeos.cc
deleted file mode 100644
index 35154d6..0000000
--- a/cros/excluder_chromeos.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-//
-// 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/excluder_chromeos.h"
-
-#include <memory>
-#include <vector>
-
-#include <base/logging.h>
-#include <base/strings/string_piece.h>
-#include <base/strings/string_number_conversions.h>
-#include <base/strings/string_split.h>
-
-#include "update_engine/common/constants.h"
-#include "update_engine/common/system_state.h"
-
-using std::string;
-using std::vector;
-
-namespace chromeos_update_engine {
-
-std::unique_ptr<ExcluderInterface> CreateExcluder() {
-  return std::make_unique<ExcluderChromeOS>();
-}
-
-bool ExcluderChromeOS::Exclude(const string& name) {
-  auto* prefs = SystemState::Get()->prefs();
-  auto key = prefs->CreateSubKey({kExclusionPrefsSubDir, name});
-  return prefs->SetString(key, "");
-}
-
-bool ExcluderChromeOS::IsExcluded(const string& name) {
-  auto* prefs = SystemState::Get()->prefs();
-  auto key = prefs->CreateSubKey({kExclusionPrefsSubDir, name});
-  return prefs->Exists(key);
-}
-
-bool ExcluderChromeOS::Reset() {
-  auto* prefs = SystemState::Get()->prefs();
-  bool ret = true;
-  vector<string> keys;
-  if (!prefs->GetSubKeys(kExclusionPrefsSubDir, &keys))
-    return false;
-  for (const auto& key : keys)
-    if (!(ret &= prefs->Delete(key)))
-      LOG(ERROR) << "Failed to delete exclusion pref for " << key;
-  return ret;
-}
-
-}  // namespace chromeos_update_engine
diff --git a/cros/excluder_chromeos.h b/cros/excluder_chromeos.h
deleted file mode 100644
index 7d3efc9..0000000
--- a/cros/excluder_chromeos.h
+++ /dev/null
@@ -1,49 +0,0 @@
-//
-// 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_EXCLUDER_CHROMEOS_H_
-#define UPDATE_ENGINE_CROS_EXCLUDER_CHROMEOS_H_
-
-#include <string>
-
-#include "update_engine/common/excluder_interface.h"
-#include "update_engine/common/prefs_interface.h"
-
-namespace chromeos_update_engine {
-
-class SystemState;
-
-// The Chrome OS implementation of the |ExcluderInterface|.
-class ExcluderChromeOS : public ExcluderInterface {
- public:
-  ExcluderChromeOS() = default;
-  ~ExcluderChromeOS() = default;
-
-  // |ExcluderInterface| overrides.
-  bool Exclude(const std::string& name) override;
-  bool IsExcluded(const std::string& name) override;
-  bool Reset() override;
-
-  // Not copyable or movable.
-  ExcluderChromeOS(const ExcluderChromeOS&) = delete;
-  ExcluderChromeOS& operator=(const ExcluderChromeOS&) = delete;
-  ExcluderChromeOS(ExcluderChromeOS&&) = delete;
-  ExcluderChromeOS& operator=(ExcluderChromeOS&&) = delete;
-};
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_CROS_EXCLUDER_CHROMEOS_H_
diff --git a/cros/excluder_chromeos_unittest.cc b/cros/excluder_chromeos_unittest.cc
deleted file mode 100644
index fd70818..0000000
--- a/cros/excluder_chromeos_unittest.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-//
-// 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/excluder_chromeos.h"
-
-#include <gtest/gtest.h>
-
-#include "update_engine/cros/fake_system_state.h"
-
-namespace chromeos_update_engine {
-
-namespace {
-constexpr char kFakeHash[] =
-    "71ff43d76e2488e394e46872f5b066cc25e394c2c3e3790dd319517883b33db1";
-}  // namespace
-
-class ExcluderChromeOSTest : public ::testing::Test {
- protected:
-  void SetUp() override { FakeSystemState::CreateInstance(); }
-
-  ExcluderChromeOS excluder_;
-};
-
-TEST_F(ExcluderChromeOSTest, ExclusionCheck) {
-  EXPECT_FALSE(excluder_.IsExcluded(kFakeHash));
-  EXPECT_TRUE(excluder_.Exclude(kFakeHash));
-  EXPECT_TRUE(excluder_.IsExcluded(kFakeHash));
-}
-
-TEST_F(ExcluderChromeOSTest, ResetFlow) {
-  EXPECT_TRUE(excluder_.Exclude("abc"));
-  EXPECT_TRUE(excluder_.Exclude(kFakeHash));
-  EXPECT_TRUE(excluder_.IsExcluded("abc"));
-  EXPECT_TRUE(excluder_.IsExcluded(kFakeHash));
-
-  EXPECT_TRUE(excluder_.Reset());
-  EXPECT_FALSE(excluder_.IsExcluded("abc"));
-  EXPECT_FALSE(excluder_.IsExcluded(kFakeHash));
-}
-
-}  // namespace chromeos_update_engine
diff --git a/cros/omaha_request_action.cc b/cros/omaha_request_action.cc
deleted file mode 100644
index 1e5c15f..0000000
--- a/cros/omaha_request_action.cc
+++ /dev/null
@@ -1,1783 +0,0 @@
-//
-// Copyright (C) 2012 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/omaha_request_action.h"
-
-#include <inttypes.h>
-
-#include <limits>
-#include <map>
-#include <sstream>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <base/bind.h>
-#include <base/files/file_util.h>
-#include <base/logging.h>
-#include <base/optional.h>
-#include <base/rand_util.h>
-#include <base/strings/string_number_conversions.h>
-#include <base/strings/string_split.h>
-#include <base/strings/string_util.h>
-#include <base/strings/stringprintf.h>
-#include <base/time/time.h>
-#include <brillo/key_value_store.h>
-#include <expat.h>
-#include <metrics/metrics_library.h>
-#include <policy/libpolicy.h>
-
-#include "update_engine/common/action_pipe.h"
-#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/system_state.h"
-#include "update_engine/common/utils.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"
-
-using base::Optional;
-using base::Time;
-using base::TimeDelta;
-using chromeos_update_manager::kRollforwardInfinity;
-using std::map;
-using std::string;
-using std::vector;
-
-namespace chromeos_update_engine {
-
-// List of custom attributes that we interpret in the Omaha response:
-constexpr char kAttrDeadline[] = "deadline";
-constexpr char kAttrDisableP2PForDownloading[] = "DisableP2PForDownloading";
-constexpr char kAttrDisableP2PForSharing[] = "DisableP2PForSharing";
-constexpr char kAttrDisablePayloadBackoff[] = "DisablePayloadBackoff";
-constexpr char kAttrVersion[] = "version";
-// Deprecated: "IsDelta"
-constexpr char kAttrIsDeltaPayload[] = "IsDeltaPayload";
-constexpr char kAttrMaxFailureCountPerUrl[] = "MaxFailureCountPerUrl";
-constexpr char kAttrMaxDaysToScatter[] = "MaxDaysToScatter";
-// Deprecated: "ManifestSignatureRsa"
-// Deprecated: "ManifestSize"
-constexpr char kAttrMetadataSignatureRsa[] = "MetadataSignatureRsa";
-constexpr char kAttrMetadataSize[] = "MetadataSize";
-constexpr char kAttrMoreInfo[] = "MoreInfo";
-constexpr char kAttrNoUpdate[] = "noupdate";
-// Deprecated: "NeedsAdmin"
-constexpr char kAttrPollInterval[] = "PollInterval";
-constexpr char kAttrPowerwash[] = "Powerwash";
-constexpr char kAttrPrompt[] = "Prompt";
-constexpr char kAttrPublicKeyRsa[] = "PublicKeyRsa";
-
-// List of attributes that we interpret in the Omaha response:
-constexpr char kAttrAppId[] = "appid";
-constexpr char kAttrCodeBase[] = "codebase";
-constexpr char kAttrCohort[] = "cohort";
-constexpr char kAttrCohortHint[] = "cohorthint";
-constexpr char kAttrCohortName[] = "cohortname";
-constexpr char kAttrElapsedDays[] = "elapsed_days";
-constexpr char kAttrElapsedSeconds[] = "elapsed_seconds";
-constexpr char kAttrEvent[] = "event";
-constexpr char kAttrFp[] = "fp";
-constexpr char kAttrHashSha256[] = "hash_sha256";
-// Deprecated: "hash"; Although we still need to pass it from the server for
-// backward compatibility.
-constexpr char kAttrName[] = "name";
-// Deprecated: "sha256"; Although we still need to pass it from the server for
-// backward compatibility.
-constexpr char kAttrSize[] = "size";
-constexpr char kAttrStatus[] = "status";
-
-// List of values that we interpret in the Omaha response:
-constexpr char kValPostInstall[] = "postinstall";
-constexpr char kValNoUpdate[] = "noupdate";
-
-// updatecheck attributes.
-// Deprecated: "eol"
-constexpr char kAttrEolDate[] = "_eol_date";
-constexpr char kAttrRollback[] = "_rollback";
-constexpr char kAttrFirmwareVersion[] = "_firmware_version";
-constexpr char kAttrKernelVersion[] = "_kernel_version";
-
-// Struct used for holding data obtained when parsing the XML.
-struct OmahaParserData {
-  OmahaParserData(XML_Parser _xml_parser, int _rollback_allowed_milestones)
-      : xml_parser(_xml_parser),
-        rollback_allowed_milestones(_rollback_allowed_milestones) {}
-
-  // Pointer to the expat XML_Parser object.
-  XML_Parser xml_parser;
-
-  // Some values that we need during parsing.
-  int rollback_allowed_milestones;
-
-  // This is the state of the parser as it's processing the XML.
-  bool failed = false;
-  bool entity_decl = false;
-  string current_path;
-
-  // These are the values extracted from the XML.
-  struct DayStart {
-    string elapsed_days;
-    string elapsed_seconds;
-  } daystart;
-
-  struct App {
-    string id;
-    Optional<string> cohort;
-    Optional<string> cohorthint;
-    Optional<string> cohortname;
-
-    struct Url {
-      string codebase;
-    };
-    vector<Url> urls;
-
-    struct Manifest {
-      string version;
-    } manifest;
-
-    struct UpdateCheck {
-      string status;
-      string poll_interval;
-      string eol_date;
-      string rollback;
-      string firmware_version;
-      string kernel_version;
-      string past_firmware_version;
-      string past_kernel_version;
-    } updatecheck;
-
-    struct PostInstallAction {
-      vector<string> is_delta_payloads;
-      vector<string> metadata_signature_rsas;
-      vector<string> metadata_sizes;
-      string max_days_to_scatter;
-      string no_update;
-      string more_info_url;
-      string prompt;
-      string deadline;
-      string disable_p2p_for_downloading;
-      string disable_p2p_for_sharing;
-      string public_key_rsa;
-      string max_failure_count_per_url;
-      string disable_payload_backoff;
-      string powerwash_required;
-    };
-    Optional<PostInstallAction> postinstall_action;
-
-    struct Package {
-      string name;
-      string size;
-      string hash;
-      string fp;
-    };
-    vector<Package> packages;
-  };
-  vector<App> apps;
-};
-
-namespace {
-
-// Callback function invoked by expat.
-void ParserHandlerStart(void* user_data,
-                        const XML_Char* element,
-                        const XML_Char** attr) {
-  OmahaParserData* data = reinterpret_cast<OmahaParserData*>(user_data);
-
-  if (data->failed)
-    return;
-
-  data->current_path += string("/") + element;
-
-  map<string, string> attrs;
-  if (attr != nullptr) {
-    for (int n = 0; attr[n] != nullptr && attr[n + 1] != nullptr; n += 2) {
-      string key = attr[n];
-      string value = attr[n + 1];
-      attrs[key] = value;
-    }
-  }
-
-  if (data->current_path == "/response/daystart") {
-    data->daystart = {
-        .elapsed_days = attrs[kAttrElapsedDays],
-        .elapsed_seconds = attrs[kAttrElapsedSeconds],
-    };
-  } else if (data->current_path == "/response/app") {
-    data->apps.push_back({.id = attrs[kAttrAppId]});
-    if (attrs.find(kAttrCohort) != attrs.end())
-      data->apps.back().cohort = attrs[kAttrCohort];
-    if (attrs.find(kAttrCohortHint) != attrs.end())
-      data->apps.back().cohorthint = attrs[kAttrCohortHint];
-    if (attrs.find(kAttrCohortName) != attrs.end())
-      data->apps.back().cohortname = attrs[kAttrCohortName];
-  } else if (data->current_path == "/response/app/updatecheck") {
-    data->apps.back().updatecheck = {
-        .status = attrs[kAttrStatus],
-        .poll_interval = attrs[kAttrPollInterval],
-        .eol_date = attrs[kAttrEolDate],
-        .rollback = attrs[kAttrRollback],
-        .firmware_version = attrs[kAttrFirmwareVersion],
-        .kernel_version = attrs[kAttrKernelVersion],
-        .past_firmware_version = attrs[base::StringPrintf(
-            "%s_%i", kAttrFirmwareVersion, data->rollback_allowed_milestones)],
-        .past_kernel_version = attrs[base::StringPrintf(
-            "%s_%i", kAttrKernelVersion, data->rollback_allowed_milestones)],
-    };
-  } else if (data->current_path == "/response/app/updatecheck/urls/url") {
-    data->apps.back().urls.push_back({.codebase = attrs[kAttrCodeBase]});
-  } else if (data->current_path ==
-             "/response/app/updatecheck/manifest/packages/package") {
-    data->apps.back().packages.push_back({
-        .name = attrs[kAttrName],
-        .size = attrs[kAttrSize],
-        .hash = attrs[kAttrHashSha256],
-        .fp = attrs[kAttrFp],
-    });
-  } else if (data->current_path == "/response/app/updatecheck/manifest") {
-    data->apps.back().manifest.version = attrs[kAttrVersion];
-  } else if (data->current_path ==
-             "/response/app/updatecheck/manifest/actions/action") {
-    // We only care about the postinstall action.
-    if (attrs[kAttrEvent] == kValPostInstall) {
-      OmahaParserData::App::PostInstallAction action = {
-          .is_delta_payloads = base::SplitString(attrs[kAttrIsDeltaPayload],
-                                                 ":",
-                                                 base::TRIM_WHITESPACE,
-                                                 base::SPLIT_WANT_ALL),
-          .metadata_signature_rsas =
-              base::SplitString(attrs[kAttrMetadataSignatureRsa],
-                                ":",
-                                base::TRIM_WHITESPACE,
-                                base::SPLIT_WANT_ALL),
-          .metadata_sizes = base::SplitString(attrs[kAttrMetadataSize],
-                                              ":",
-                                              base::TRIM_WHITESPACE,
-                                              base::SPLIT_WANT_ALL),
-          .max_days_to_scatter = attrs[kAttrMaxDaysToScatter],
-          .no_update = attrs[kAttrNoUpdate],
-          .more_info_url = attrs[kAttrMoreInfo],
-          .prompt = attrs[kAttrPrompt],
-          .deadline = attrs[kAttrDeadline],
-          .disable_p2p_for_downloading = attrs[kAttrDisableP2PForDownloading],
-          .disable_p2p_for_sharing = attrs[kAttrDisableP2PForSharing],
-          .public_key_rsa = attrs[kAttrPublicKeyRsa],
-          .max_failure_count_per_url = attrs[kAttrMaxFailureCountPerUrl],
-          .disable_payload_backoff = attrs[kAttrDisablePayloadBackoff],
-          .powerwash_required = attrs[kAttrPowerwash],
-      };
-      data->apps.back().postinstall_action = std::move(action);
-    }
-  }
-}
-
-// Callback function invoked by expat.
-void ParserHandlerEnd(void* user_data, const XML_Char* element) {
-  OmahaParserData* data = reinterpret_cast<OmahaParserData*>(user_data);
-  if (data->failed)
-    return;
-
-  const string path_suffix = string("/") + element;
-
-  if (!base::EndsWith(
-          data->current_path, path_suffix, base::CompareCase::SENSITIVE)) {
-    LOG(ERROR) << "Unexpected end element '" << element
-               << "' with current_path='" << data->current_path << "'";
-    data->failed = true;
-    return;
-  }
-  data->current_path.resize(data->current_path.size() - path_suffix.size());
-}
-
-// Callback function invoked by expat.
-//
-// This is called for entity declarations. Since Omaha is guaranteed
-// to never return any XML with entities our course of action is to
-// just stop parsing. This avoids potential resource exhaustion
-// problems AKA the "billion laughs". CVE-2013-0340.
-void ParserHandlerEntityDecl(void* user_data,
-                             const XML_Char* entity_name,
-                             int is_parameter_entity,
-                             const XML_Char* value,
-                             int value_length,
-                             const XML_Char* base,
-                             const XML_Char* system_id,
-                             const XML_Char* public_id,
-                             const XML_Char* notation_name) {
-  OmahaParserData* data = reinterpret_cast<OmahaParserData*>(user_data);
-
-  LOG(ERROR) << "XML entities are not supported. Aborting parsing.";
-  data->failed = true;
-  data->entity_decl = true;
-  XML_StopParser(data->xml_parser, false);
-}
-
-}  // namespace
-
-OmahaRequestAction::OmahaRequestAction(
-    OmahaEvent* event,
-    std::unique_ptr<HttpFetcher> http_fetcher,
-    bool ping_only,
-    const string& session_id)
-    : event_(event),
-      http_fetcher_(std::move(http_fetcher)),
-      policy_provider_(std::make_unique<policy::PolicyProvider>()),
-      ping_only_(ping_only),
-      ping_active_days_(0),
-      ping_roll_call_days_(0),
-      session_id_(session_id) {
-  policy_provider_->Reload();
-}
-
-OmahaRequestAction::~OmahaRequestAction() {}
-
-// Calculates the value to use for the ping days parameter.
-int OmahaRequestAction::CalculatePingDays(const string& key) {
-  int days = kPingNeverPinged;
-  int64_t last_ping = 0;
-  if (SystemState::Get()->prefs()->GetInt64(key, &last_ping) &&
-      last_ping >= 0) {
-    days = (Time::Now() - Time::FromInternalValue(last_ping)).InDays();
-    if (days < 0) {
-      // If |days| is negative, then the system clock must have jumped
-      // back in time since the ping was sent. Mark the value so that
-      // it doesn't get sent to the server but we still update the
-      // last ping daystart preference. This way the next ping time
-      // will be correct, hopefully.
-      days = kPingTimeJump;
-      LOG(WARNING)
-          << "System clock jumped back in time. Resetting ping daystarts.";
-    }
-  }
-  return days;
-}
-
-void OmahaRequestAction::InitPingDays() {
-  // We send pings only along with update checks, not with events.
-  if (IsEvent()) {
-    return;
-  }
-  // TODO(petkov): Figure a way to distinguish active use pings
-  // vs. roll call pings. Currently, the two pings are identical. A
-  // fix needs to change this code as well as UpdateLastPingDays and ShouldPing.
-  ping_active_days_ = CalculatePingDays(kPrefsLastActivePingDay);
-  ping_roll_call_days_ = CalculatePingDays(kPrefsLastRollCallPingDay);
-}
-
-bool OmahaRequestAction::ShouldPing() const {
-  if (ping_active_days_ == kPingNeverPinged &&
-      ping_roll_call_days_ == kPingNeverPinged) {
-    int powerwash_count = SystemState::Get()->hardware()->GetPowerwashCount();
-    if (powerwash_count > 0) {
-      LOG(INFO) << "Not sending ping with a=-1 r=-1 to omaha because "
-                << "powerwash_count is " << powerwash_count;
-      return false;
-    }
-    if (SystemState::Get()->hardware()->GetFirstActiveOmahaPingSent()) {
-      LOG(INFO) << "Not sending ping with a=-1 r=-1 to omaha because "
-                << "the first_active_omaha_ping_sent is true.";
-      return false;
-    }
-    return true;
-  }
-  return ping_active_days_ > 0 || ping_roll_call_days_ > 0;
-}
-
-// static
-int OmahaRequestAction::GetInstallDate() {
-  auto* prefs = SystemState::Get()->prefs();
-  // If we have the value stored on disk, just return it.
-  int64_t stored_value;
-  if (prefs->GetInt64(kPrefsInstallDateDays, &stored_value)) {
-    // Convert and validity-check.
-    int install_date_days = static_cast<int>(stored_value);
-    if (install_date_days >= 0)
-      return install_date_days;
-    LOG(ERROR) << "Dropping stored Omaha InstallData since its value num_days="
-               << install_date_days << " looks suspicious.";
-    prefs->Delete(kPrefsInstallDateDays);
-  }
-
-  // Otherwise, if OOBE is not complete then do nothing and wait for
-  // ParseResponse() to call ParseInstallDate() and then
-  // PersistInstallDate() to set the kPrefsInstallDateDays state
-  // variable. Once that is done, we'll then report back in future
-  // Omaha requests.  This works exactly because OOBE triggers an
-  // update check.
-  //
-  // However, if OOBE is complete and the kPrefsInstallDateDays state
-  // variable is not set, there are two possibilities
-  //
-  //   1. The update check in OOBE failed so we never got a response
-  //      from Omaha (no network etc.); or
-  //
-  //   2. OOBE was done on an older version that didn't write to the
-  //      kPrefsInstallDateDays state variable.
-  //
-  // In both cases, we approximate the install date by simply
-  // inspecting the timestamp of when OOBE happened.
-
-  Time time_of_oobe;
-  if (!SystemState::Get()->hardware()->IsOOBEEnabled() ||
-      !SystemState::Get()->hardware()->IsOOBEComplete(&time_of_oobe)) {
-    LOG(INFO) << "Not generating Omaha InstallData as we have "
-              << "no prefs file and OOBE is not complete or not enabled.";
-    return -1;
-  }
-
-  int num_days;
-  if (!utils::ConvertToOmahaInstallDate(time_of_oobe, &num_days)) {
-    LOG(ERROR) << "Not generating Omaha InstallData from time of OOBE "
-               << "as its value '" << utils::ToString(time_of_oobe)
-               << "' looks suspicious.";
-    return -1;
-  }
-
-  // Persist this to disk, for future use.
-  if (!OmahaRequestAction::PersistInstallDate(num_days,
-                                              kProvisionedFromOOBEMarker))
-    return -1;
-
-  LOG(INFO) << "Set the Omaha InstallDate from OOBE time-stamp to " << num_days
-            << " days.";
-
-  return num_days;
-}
-
-void OmahaRequestAction::StorePingReply(
-    const OmahaParserData& parser_data) const {
-  const auto* params = SystemState::Get()->request_params();
-  for (const auto& app : parser_data.apps) {
-    auto it = params->dlc_apps_params().find(app.id);
-    if (it == params->dlc_apps_params().end())
-      continue;
-
-    const OmahaRequestParams::AppParams& dlc_params = it->second;
-    const string& dlc_id = dlc_params.name;
-    // Skip if the ping for this DLC was not sent.
-    if (!dlc_params.send_ping)
-      continue;
-
-    auto* prefs = SystemState::Get()->prefs();
-    // Reset the active metadata value to |kPingInactiveValue|.
-    auto active_key =
-        prefs->CreateSubKey({kDlcPrefsSubDir, dlc_id, kPrefsPingActive});
-    if (!prefs->SetInt64(active_key, kPingInactiveValue))
-      LOG(ERROR) << "Failed to set the value of ping metadata '" << active_key
-                 << "'.";
-
-    auto last_rollcall_key =
-        prefs->CreateSubKey({kDlcPrefsSubDir, dlc_id, kPrefsPingLastRollcall});
-    if (!prefs->SetString(last_rollcall_key, parser_data.daystart.elapsed_days))
-      LOG(ERROR) << "Failed to set the value of ping metadata '"
-                 << last_rollcall_key << "'.";
-
-    if (dlc_params.ping_active) {
-      // Write the value of elapsed_days into |kPrefsPingLastActive| only if
-      // the previous ping was an active one.
-      auto last_active_key =
-          prefs->CreateSubKey({kDlcPrefsSubDir, dlc_id, kPrefsPingLastActive});
-      if (!prefs->SetString(last_active_key, parser_data.daystart.elapsed_days))
-        LOG(ERROR) << "Failed to set the value of ping metadata '"
-                   << last_active_key << "'.";
-    }
-  }
-}
-
-void OmahaRequestAction::PerformAction() {
-  http_fetcher_->set_delegate(this);
-  InitPingDays();
-  if (ping_only_ && !ShouldPing()) {
-    processor_->ActionComplete(this, ErrorCode::kSuccess);
-    return;
-  }
-
-  OmahaRequestBuilderXml omaha_request(event_.get(),
-                                       ping_only_,
-                                       ShouldPing(),  // include_ping
-                                       ping_active_days_,
-                                       ping_roll_call_days_,
-                                       GetInstallDate(),
-                                       session_id_);
-  string request_post = omaha_request.GetRequest();
-
-  // Set X-Goog-Update headers.
-  const auto* params = SystemState::Get()->request_params();
-  http_fetcher_->SetHeader(kXGoogleUpdateInteractivity,
-                           params->interactive() ? "fg" : "bg");
-  http_fetcher_->SetHeader(kXGoogleUpdateAppId, params->GetAppId());
-  http_fetcher_->SetHeader(
-      kXGoogleUpdateUpdater,
-      base::StringPrintf(
-          "%s-%s", constants::kOmahaUpdaterID, kOmahaUpdaterVersion));
-
-  http_fetcher_->SetPostData(
-      request_post.data(), request_post.size(), kHttpContentTypeTextXml);
-  LOG(INFO) << "Posting an Omaha request to " << params->update_url();
-  LOG(INFO) << "Request: " << request_post;
-  http_fetcher_->BeginTransfer(params->update_url());
-}
-
-void OmahaRequestAction::TerminateProcessing() {
-  http_fetcher_->TerminateTransfer();
-}
-
-// We just store the response in the buffer. Once we've received all bytes,
-// we'll look in the buffer and decide what to do.
-bool OmahaRequestAction::ReceivedBytes(HttpFetcher* fetcher,
-                                       const void* bytes,
-                                       size_t length) {
-  const uint8_t* byte_ptr = reinterpret_cast<const uint8_t*>(bytes);
-  response_buffer_.insert(response_buffer_.end(), byte_ptr, byte_ptr + length);
-  return true;
-}
-
-namespace {
-
-// Parses a 64 bit base-10 int from a string and returns it. Returns 0
-// on error. If the string contains "0", that's indistinguishable from
-// error.
-off_t ParseInt(const string& str) {
-  off_t ret = 0;
-  int rc = sscanf(str.c_str(), "%" PRIi64, &ret);  // NOLINT(runtime/printf)
-  if (rc < 1) {
-    // failure
-    return 0;
-  }
-  return ret;
-}
-
-// Parses |str| and returns |true| if, and only if, its value is "true".
-bool ParseBool(const string& str) {
-  return str == "true";
-}
-
-// Update the last ping day preferences based on the server daystart
-// response. Returns true on success, false otherwise.
-bool UpdateLastPingDays(OmahaParserData* parser_data) {
-  int64_t elapsed_seconds = 0;
-  TEST_AND_RETURN_FALSE(base::StringToInt64(
-      parser_data->daystart.elapsed_seconds, &elapsed_seconds));
-  TEST_AND_RETURN_FALSE(elapsed_seconds >= 0);
-
-  // Remember the local time that matches the server's last midnight
-  // time.
-  auto* prefs = SystemState::Get()->prefs();
-  Time daystart = Time::Now() - TimeDelta::FromSeconds(elapsed_seconds);
-  prefs->SetInt64(kPrefsLastActivePingDay, daystart.ToInternalValue());
-  prefs->SetInt64(kPrefsLastRollCallPingDay, daystart.ToInternalValue());
-  return true;
-}
-
-// Parses the package node in the given XML document and populates
-// |output_object| if valid. Returns true if we should continue the parsing.
-// False otherwise, in which case it sets any error code using |completer|.
-bool ParsePackage(OmahaParserData::App* app,
-                  OmahaResponse* output_object,
-                  bool can_exclude,
-                  ScopedActionCompleter* completer) {
-  if (app->updatecheck.status.empty() ||
-      app->updatecheck.status == kValNoUpdate) {
-    if (!app->packages.empty()) {
-      LOG(ERROR) << "No update in this <app> but <package> is not empty.";
-      completer->set_code(ErrorCode::kOmahaResponseInvalid);
-      return false;
-    }
-    return true;
-  }
-  if (app->packages.empty()) {
-    LOG(ERROR) << "Omaha Response has no packages.";
-    completer->set_code(ErrorCode::kOmahaResponseInvalid);
-    return false;
-  }
-  if (app->urls.empty()) {
-    LOG(ERROR) << "No Omaha Response URLs.";
-    completer->set_code(ErrorCode::kOmahaResponseInvalid);
-    return false;
-  }
-  for (size_t i = 0; i < app->packages.size(); i++) {
-    const auto& package = app->packages[i];
-    if (package.name.empty()) {
-      LOG(ERROR) << "Omaha Response has empty package name.";
-      completer->set_code(ErrorCode::kOmahaResponseInvalid);
-      return false;
-    }
-
-    OmahaResponse::Package out_package;
-    out_package.app_id = app->id;
-    out_package.can_exclude = can_exclude;
-    for (const auto& url : app->urls) {
-      if (url.codebase.empty()) {
-        LOG(ERROR) << "Omaha Response URL has empty codebase.";
-        completer->set_code(ErrorCode::kOmahaResponseInvalid);
-        return false;
-      }
-      out_package.payload_urls.push_back(url.codebase + package.name);
-    }
-
-    base::StringToUint64(package.size, &out_package.size);
-    if (out_package.size <= 0) {
-      LOG(ERROR) << "Omaha Response has invalid payload size: " << package.size;
-      completer->set_code(ErrorCode::kOmahaResponseInvalid);
-      return false;
-    }
-
-    if (i < app->postinstall_action->metadata_sizes.size())
-      base::StringToUint64(app->postinstall_action->metadata_sizes[i],
-                           &out_package.metadata_size);
-
-    if (i < app->postinstall_action->metadata_signature_rsas.size())
-      out_package.metadata_signature =
-          app->postinstall_action->metadata_signature_rsas[i];
-
-    out_package.hash = package.hash;
-    if (out_package.hash.empty()) {
-      LOG(ERROR) << "Omaha Response has empty hash_sha256 value.";
-      completer->set_code(ErrorCode::kOmahaResponseInvalid);
-      return false;
-    }
-
-    out_package.fp = package.fp;
-
-    if (i < app->postinstall_action->is_delta_payloads.size())
-      out_package.is_delta =
-          ParseBool(app->postinstall_action->is_delta_payloads[i]);
-
-    output_object->packages.push_back(std::move(out_package));
-  }
-
-  return true;
-}
-
-// 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();
-       /* Increment logic in loop */) {
-    // If package cannot be excluded, quickly continue.
-    if (!package_it->can_exclude) {
-      ++package_it;
-      continue;
-    }
-    // Remove the excluded payload URLs.
-    for (auto payload_url_it = package_it->payload_urls.begin();
-         payload_url_it != package_it->payload_urls.end();
-         /* Increment logic in loop */) {
-      auto exclusion_name = utils::GetExclusionName(*payload_url_it);
-      // If payload URL is not excluded, quickly continue.
-      if (!excluder->IsExcluded(exclusion_name)) {
-        ++payload_url_it;
-        continue;
-      }
-      LOG(INFO) << "Excluding payload URL=" << *payload_url_it
-                << " for payload hash=" << package_it->hash;
-      payload_url_it = package_it->payload_urls.erase(payload_url_it);
-    }
-    // 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;
-    }
-    ++package_it;
-  }
-}
-
-// Parses the 2 key version strings kernel_version and firmware_version. If the
-// field is not present, or cannot be parsed the values default to 0xffff.
-void ParseRollbackVersions(const OmahaParserData::App& platform_app,
-                           int allowed_milestones,
-                           OmahaResponse* output_object) {
-  // Defaults to false if attribute is not present.
-  output_object->is_rollback = ParseBool(platform_app.updatecheck.rollback);
-
-  utils::ParseRollbackKeyVersion(
-      platform_app.updatecheck.firmware_version,
-      &output_object->rollback_key_version.firmware_key,
-      &output_object->rollback_key_version.firmware);
-  utils::ParseRollbackKeyVersion(
-      platform_app.updatecheck.kernel_version,
-      &output_object->rollback_key_version.kernel_key,
-      &output_object->rollback_key_version.kernel);
-
-  string firmware_version = platform_app.updatecheck.past_firmware_version;
-  string kernel_version = platform_app.updatecheck.past_kernel_version;
-
-  LOG(INFO) << "For milestone N-" << allowed_milestones
-            << " firmware_key_version=" << firmware_version
-            << " kernel_key_version=" << kernel_version;
-
-  OmahaResponse::RollbackKeyVersion version;
-  utils::ParseRollbackKeyVersion(
-      firmware_version, &version.firmware_key, &version.firmware);
-  utils::ParseRollbackKeyVersion(
-      kernel_version, &version.kernel_key, &version.kernel);
-
-  output_object->past_rollback_key_version = std::move(version);
-}
-
-void PersistEolInfo(const OmahaParserData::App& platform_app) {
-  // If EOL date attribute is not sent, don't delete the old persisted EOL
-  // date information.
-  if (!platform_app.updatecheck.eol_date.empty() &&
-      !SystemState::Get()->prefs()->SetString(
-          kPrefsOmahaEolDate, platform_app.updatecheck.eol_date)) {
-    LOG(ERROR) << "Setting EOL date failed.";
-  }
-}
-
-}  // namespace
-
-bool OmahaRequestAction::ParseResponse(OmahaParserData* parser_data,
-                                       OmahaResponse* output_object,
-                                       ScopedActionCompleter* completer) {
-  if (parser_data->apps.empty()) {
-    completer->set_code(ErrorCode::kOmahaResponseInvalid);
-    return false;
-  }
-
-  // Locate the platform App since it's an important one that has specific
-  // information attached to it that may not be available from other Apps.
-  const auto* params = SystemState::Get()->request_params();
-  auto platform_app = std::find_if(parser_data->apps.begin(),
-                                   parser_data->apps.end(),
-                                   [&params](const OmahaParserData::App& app) {
-                                     return app.id == params->GetAppId();
-                                   });
-  if (platform_app == parser_data->apps.end()) {
-    LOG(WARNING) << "Platform App is missing.";
-  } else {
-    // chromium-os:37289: The PollInterval is not supported by Omaha server
-    // currently.  But still keeping this existing code in case we ever decide
-    // to slow down the request rate from the server-side. Note that the
-    // PollInterval is not persisted, so it has to be sent by the server on
-    // every response to guarantee that the scheduler uses this value
-    // (otherwise, if the device got rebooted after the last server-indicated
-    // value, it'll revert to the default value). Also kDefaultMaxUpdateChecks
-    // value for the scattering logic is based on the assumption that we perform
-    // an update check every hour so that the max value of 8 will roughly be
-    // equivalent to one work day. If we decide to use PollInterval permanently,
-    // we should update the max_update_checks_allowed to take PollInterval into
-    // account.  Note: The parsing for PollInterval happens even before parsing
-    // of the status because we may want to specify the PollInterval even when
-    // there's no update.
-    base::StringToInt(platform_app->updatecheck.poll_interval,
-                      &output_object->poll_interval);
-
-    PersistEolInfo(*platform_app);
-
-    // Parses the rollback versions of the current image. If the fields do not
-    // exist they default to 0xffff for the 4 key versions.
-    ParseRollbackVersions(
-        *platform_app, params->rollback_allowed_milestones(), output_object);
-  }
-
-  // Check for the "elapsed_days" attribute in the "daystart"
-  // element. This is the number of days since Jan 1 2007, 0:00
-  // PST. If we don't have a persisted value of the Omaha InstallDate,
-  // we'll use it to calculate it and then persist it.
-  if (ParseInstallDate(parser_data, output_object) && !HasInstallDate()) {
-    // Since output_object->install_date_days is never negative, the
-    // elapsed_days -> install-date calculation is reduced to simply
-    // rounding down to the nearest number divisible by 7.
-    int remainder = output_object->install_date_days % 7;
-    int install_date_days_rounded =
-        output_object->install_date_days - remainder;
-    if (PersistInstallDate(install_date_days_rounded,
-                           kProvisionedFromOmahaResponse)) {
-      LOG(INFO) << "Set the Omaha InstallDate from Omaha Response to "
-                << install_date_days_rounded << " days.";
-    }
-  }
-
-  // We persist the cohorts sent by omaha even if the status is "noupdate".
-  PersistCohorts(*parser_data);
-
-  if (!ParseStatus(parser_data, output_object, completer))
-    return false;
-
-  if (!ParseParams(parser_data, output_object, completer))
-    return false;
-
-  // Package has to be parsed after Params now because ParseParams need to make
-  // sure that postinstall action exists.
-  for (auto& app : parser_data->apps) {
-    // Only allow exclusions for a non-critical package during an update. For
-    // non-critical package installations, let the errors propagate instead
-    // of being handled inside update_engine as installations are a dlcservice
-    // specific feature.
-    bool can_exclude = !params->is_install() && params->IsDlcAppId(app.id);
-    if (!ParsePackage(&app, output_object, can_exclude, completer))
-      return false;
-  }
-
-  return true;
-}
-
-bool OmahaRequestAction::ParseStatus(OmahaParserData* parser_data,
-                                     OmahaResponse* output_object,
-                                     ScopedActionCompleter* completer) {
-  output_object->update_exists = false;
-  auto* params = SystemState::Get()->request_params();
-  for (const auto& app : parser_data->apps) {
-    const string& status = app.updatecheck.status;
-    if (status == kValNoUpdate) {
-      // If the app is a DLC, allow status "noupdate" to support DLC
-      // deprecations.
-      if (params->IsDlcAppId(app.id)) {
-        LOG(INFO) << "No update for App " << app.id
-                  << " but update continuing since a DLC.";
-        params->SetDlcNoUpdate(app.id);
-        continue;
-      }
-      // Don't update if any app has status="noupdate".
-      LOG(INFO) << "No update for App " << app.id;
-      output_object->update_exists = false;
-      break;
-    } else if (status == "ok") {
-      if (ParseBool(app.postinstall_action->no_update)) {
-        // noupdate="true" in postinstall attributes means it's an update to
-        // self, only update if there's at least one app really have update.
-        LOG(INFO) << "Update to self for App " << app.id;
-      } else {
-        output_object->update_exists = true;
-      }
-    } else if (status.empty() && params->is_install() &&
-               params->GetAppId() == app.id) {
-      // Skips the platform app for install operation.
-      LOG(INFO) << "No payload (and ignore) for App " << app.id;
-    } else {
-      LOG(ERROR) << "Unknown Omaha response status: " << status;
-      completer->set_code(ErrorCode::kOmahaResponseInvalid);
-      return false;
-    }
-  }
-  if (!output_object->update_exists) {
-    SetOutputObject(*output_object);
-    completer->set_code(ErrorCode::kSuccess);
-  }
-
-  return output_object->update_exists;
-}
-
-bool OmahaRequestAction::ParseParams(OmahaParserData* parser_data,
-                                     OmahaResponse* output_object,
-                                     ScopedActionCompleter* completer) {
-  const auto* params = SystemState::Get()->request_params();
-  const OmahaParserData::App* main_app = nullptr;
-  for (const auto& app : parser_data->apps) {
-    if (app.id == params->GetAppId() && app.postinstall_action) {
-      main_app = &app;
-    } else if (params->is_install()) {
-      if (app.manifest.version != params->app_version()) {
-        LOG(WARNING) << "An app has a version: " << app.manifest.version
-                     << " that is different than platform app version: "
-                     << params->app_version();
-      }
-    }
-    if (app.postinstall_action && main_app == nullptr) {
-      main_app = &app;
-    }
-  }
-
-  if (main_app == nullptr) {
-    LOG(ERROR) << "Omaha Response has no postinstall event action.";
-    completer->set_code(ErrorCode::kOmahaResponseInvalid);
-    return false;
-  }
-
-  const OmahaParserData::App& app = *main_app;
-  // Get the optional properties one by one.
-  output_object->version = app.manifest.version;
-  output_object->more_info_url = app.postinstall_action->more_info_url;
-  output_object->prompt = ParseBool(app.postinstall_action->prompt);
-  output_object->deadline = app.postinstall_action->deadline;
-  output_object->max_days_to_scatter =
-      ParseInt(app.postinstall_action->max_days_to_scatter);
-  output_object->disable_p2p_for_downloading =
-      ParseBool(app.postinstall_action->disable_p2p_for_downloading);
-  output_object->disable_p2p_for_sharing =
-      ParseBool(app.postinstall_action->disable_p2p_for_sharing);
-  output_object->public_key_rsa = app.postinstall_action->public_key_rsa;
-
-  if (!base::StringToUint(app.postinstall_action->max_failure_count_per_url,
-                          &output_object->max_failure_count_per_url))
-    output_object->max_failure_count_per_url = kDefaultMaxFailureCountPerUrl;
-
-  output_object->disable_payload_backoff =
-      ParseBool(app.postinstall_action->disable_payload_backoff);
-  output_object->powerwash_required =
-      ParseBool(app.postinstall_action->powerwash_required);
-
-  if (output_object->version.empty()) {
-    LOG(ERROR) << "Omaha Response does not have version in manifest!";
-    completer->set_code(ErrorCode::kOmahaResponseInvalid);
-    return false;
-  }
-
-  return true;
-}
-
-// If the transfer was successful, this uses expat to parse the response
-// and fill in the appropriate fields of the output object. Also, notifies
-// the processor that we're done.
-void OmahaRequestAction::TransferComplete(HttpFetcher* fetcher,
-                                          bool successful) {
-  ScopedActionCompleter completer(processor_, this);
-  string current_response(response_buffer_.begin(), response_buffer_.end());
-  LOG(INFO) << "Omaha request response: " << current_response;
-
-  PayloadStateInterface* const payload_state =
-      SystemState::Get()->payload_state();
-
-  // Set the max kernel key version based on whether rollback is allowed.
-  SetMaxKernelKeyVersionForRollback();
-
-  // Events are best effort transactions -- assume they always succeed.
-  if (IsEvent()) {
-    CHECK(!HasOutputPipe()) << "No output pipe allowed for event requests.";
-    completer.set_code(ErrorCode::kSuccess);
-    return;
-  }
-
-  ErrorCode aux_error_code = fetcher->GetAuxiliaryErrorCode();
-  if (aux_error_code != ErrorCode::kSuccess) {
-    metrics::DownloadErrorCode download_error_code =
-        metrics_utils::GetDownloadErrorCode(aux_error_code);
-    SystemState::Get()->metrics_reporter()->ReportUpdateCheckMetrics(
-        metrics::CheckResult::kUnset,
-        metrics::CheckReaction::kUnset,
-        download_error_code);
-  }
-
-  if (!successful) {
-    int code = GetHTTPResponseCode();
-    LOG(ERROR) << "Omaha request network transfer failed with HTTPResponseCode="
-               << code;
-    // Makes sure we send proper error values.
-    if (code < 0 || code >= 1000) {
-      code = 999;
-      LOG(WARNING) << "Converting to proper HTTPResponseCode=" << code;
-    }
-    completer.set_code(static_cast<ErrorCode>(
-        static_cast<int>(ErrorCode::kOmahaRequestHTTPResponseBase) + code));
-    return;
-  }
-
-  XML_Parser parser = XML_ParserCreate(nullptr);
-  OmahaParserData parser_data(
-      parser,
-      SystemState::Get()->request_params()->rollback_allowed_milestones());
-  XML_SetUserData(parser, &parser_data);
-  XML_SetElementHandler(parser, ParserHandlerStart, ParserHandlerEnd);
-  XML_SetEntityDeclHandler(parser, ParserHandlerEntityDecl);
-  XML_Status res =
-      XML_Parse(parser,
-                reinterpret_cast<const char*>(response_buffer_.data()),
-                response_buffer_.size(),
-                XML_TRUE);
-
-  if (res != XML_STATUS_OK || parser_data.failed) {
-    LOG(ERROR) << "Omaha response not valid XML: "
-               << XML_ErrorString(XML_GetErrorCode(parser)) << " at line "
-               << XML_GetCurrentLineNumber(parser) << " col "
-               << XML_GetCurrentColumnNumber(parser);
-    XML_ParserFree(parser);
-    ErrorCode error_code = ErrorCode::kOmahaRequestXMLParseError;
-    if (response_buffer_.empty()) {
-      error_code = ErrorCode::kOmahaRequestEmptyResponseError;
-    } else if (parser_data.entity_decl) {
-      error_code = ErrorCode::kOmahaRequestXMLHasEntityDecl;
-    }
-    completer.set_code(error_code);
-    return;
-  }
-  XML_ParserFree(parser);
-
-  // Update the last ping day preferences based on the server daystart response
-  // even if we didn't send a ping. Omaha always includes the daystart in the
-  // response, but log the error if it didn't.
-  LOG_IF(ERROR, !UpdateLastPingDays(&parser_data))
-      << "Failed to update the last ping day preferences!";
-
-  // Sets first_active_omaha_ping_sent to true (vpd in CrOS). We only do this if
-  // we have got a response from omaha and if its value has never been set to
-  // true before. Failure of this function should be ignored. There should be no
-  // need to check if a=-1 has been sent because older devices have already sent
-  // their a=-1 in the past and we have to set first_active_omaha_ping_sent for
-  // future checks.
-  if (!SystemState::Get()->hardware()->GetFirstActiveOmahaPingSent()) {
-    if (!SystemState::Get()->hardware()->SetFirstActiveOmahaPingSent()) {
-      SystemState::Get()->metrics_reporter()->ReportInternalErrorCode(
-          ErrorCode::kFirstActiveOmahaPingSentPersistenceError);
-    }
-  }
-
-  // Create/update the metadata files for each DLC app received.
-  StorePingReply(parser_data);
-
-  if (!HasOutputPipe()) {
-    // Just set success to whether or not the http transfer succeeded,
-    // which must be true at this point in the code.
-    completer.set_code(ErrorCode::kSuccess);
-    return;
-  }
-
-  OmahaResponse output_object;
-  if (!ParseResponse(&parser_data, &output_object, &completer))
-    return;
-  ProcessExclusions(&output_object,
-                    SystemState::Get()->request_params(),
-                    SystemState::Get()->update_attempter()->GetExcluder());
-  output_object.update_exists = true;
-  SetOutputObject(output_object);
-
-  LoadOrPersistUpdateFirstSeenAtPref();
-
-  ErrorCode error = ErrorCode::kSuccess;
-  if (ShouldIgnoreUpdate(output_object, &error)) {
-    // No need to change output_object.update_exists here, since the value
-    // has been output to the pipe.
-    completer.set_code(error);
-    return;
-  }
-
-  // If Omaha says to disable p2p, respect that
-  if (output_object.disable_p2p_for_downloading) {
-    LOG(INFO) << "Forcibly disabling use of p2p for downloading as "
-              << "requested by Omaha.";
-    payload_state->SetUsingP2PForDownloading(false);
-  }
-  if (output_object.disable_p2p_for_sharing) {
-    LOG(INFO) << "Forcibly disabling use of p2p for sharing as "
-              << "requested by Omaha.";
-    payload_state->SetUsingP2PForSharing(false);
-  }
-
-  // Update the payload state with the current response. The payload state
-  // will automatically reset all stale state if this response is different
-  // from what's stored already. We are updating the payload state as late
-  // as possible in this method so that if a new release gets pushed and then
-  // got pulled back due to some issues, we don't want to clear our internal
-  // state unnecessarily.
-  payload_state->SetResponse(output_object);
-
-  // It could be we've already exceeded the deadline for when p2p is
-  // allowed or that we've tried too many times with p2p. Check that.
-  if (payload_state->GetUsingP2PForDownloading()) {
-    payload_state->P2PNewAttempt();
-    if (!payload_state->P2PAttemptAllowed()) {
-      LOG(INFO) << "Forcibly disabling use of p2p for downloading because "
-                << "of previous failures when using p2p.";
-      payload_state->SetUsingP2PForDownloading(false);
-    }
-  }
-
-  // From here on, we'll complete stuff in CompleteProcessing() so
-  // disable |completer| since we'll create a new one in that
-  // function.
-  completer.set_should_complete(false);
-
-  // If we're allowed to use p2p for downloading we do not pay
-  // attention to wall-clock-based waiting if the URL is indeed
-  // available via p2p. Therefore, check if the file is available via
-  // p2p before deferring...
-  if (payload_state->GetUsingP2PForDownloading()) {
-    LookupPayloadViaP2P(output_object);
-  } else {
-    CompleteProcessing();
-  }
-}
-
-void OmahaRequestAction::CompleteProcessing() {
-  ScopedActionCompleter completer(processor_, this);
-  OmahaResponse& output_object = const_cast<OmahaResponse&>(GetOutputObject());
-  PayloadStateInterface* payload_state = SystemState::Get()->payload_state();
-
-  if (ShouldDeferDownload(&output_object)) {
-    output_object.update_exists = false;
-    LOG(INFO) << "Ignoring Omaha updates as updates are deferred by policy.";
-    completer.set_code(ErrorCode::kOmahaUpdateDeferredPerPolicy);
-    return;
-  }
-
-  if (payload_state->ShouldBackoffDownload()) {
-    output_object.update_exists = false;
-    LOG(INFO) << "Ignoring Omaha updates in order to backoff our retry "
-              << "attempts.";
-    completer.set_code(ErrorCode::kOmahaUpdateDeferredForBackoff);
-    return;
-  }
-  completer.set_code(ErrorCode::kSuccess);
-}
-
-void OmahaRequestAction::OnLookupPayloadViaP2PCompleted(const string& url) {
-  LOG(INFO) << "Lookup complete, p2p-client returned URL '" << url << "'";
-  if (!url.empty()) {
-    SystemState::Get()->payload_state()->SetP2PUrl(url);
-  } else {
-    LOG(INFO) << "Forcibly disabling use of p2p for downloading "
-              << "because no suitable peer could be found.";
-    SystemState::Get()->payload_state()->SetUsingP2PForDownloading(false);
-  }
-  CompleteProcessing();
-}
-
-void OmahaRequestAction::LookupPayloadViaP2P(const OmahaResponse& response) {
-  // If the device is in the middle of an update, the state variables
-  // kPrefsUpdateStateNextDataOffset, kPrefsUpdateStateNextDataLength
-  // tracks the offset and length of the operation currently in
-  // progress. The offset is based from the end of the manifest which
-  // is kPrefsManifestMetadataSize bytes long.
-  //
-  // To make forward progress and avoid deadlocks, we need to find a
-  // peer that has at least the entire operation we're currently
-  // working on. Otherwise we may end up in a situation where two
-  // devices bounce back and forth downloading from each other,
-  // neither making any forward progress until one of them decides to
-  // stop using p2p (via kMaxP2PAttempts and kMaxP2PAttemptTimeSeconds
-  // safe-guards). See http://crbug.com/297170 for an example)
-  size_t minimum_size = 0;
-  int64_t manifest_metadata_size = 0;
-  int64_t manifest_signature_size = 0;
-  int64_t next_data_offset = 0;
-  int64_t next_data_length = 0;
-  if (SystemState::Get()->prefs()->GetInt64(kPrefsManifestMetadataSize,
-                                            &manifest_metadata_size) &&
-      manifest_metadata_size != -1 &&
-      SystemState::Get()->prefs()->GetInt64(kPrefsManifestSignatureSize,
-                                            &manifest_signature_size) &&
-      manifest_signature_size != -1 &&
-      SystemState::Get()->prefs()->GetInt64(kPrefsUpdateStateNextDataOffset,
-                                            &next_data_offset) &&
-      next_data_offset != -1 &&
-      SystemState::Get()->prefs()->GetInt64(kPrefsUpdateStateNextDataLength,
-                                            &next_data_length)) {
-    minimum_size = manifest_metadata_size + manifest_signature_size +
-                   next_data_offset + next_data_length;
-  }
-
-  // TODO(senj): Fix P2P for multiple package.
-  brillo::Blob raw_hash;
-  if (!base::HexStringToBytes(response.packages[0].hash, &raw_hash))
-    return;
-  string file_id =
-      utils::CalculateP2PFileId(raw_hash, response.packages[0].size);
-  if (SystemState::Get()->p2p_manager()) {
-    LOG(INFO) << "Checking if payload is available via p2p, file_id=" << file_id
-              << " minimum_size=" << minimum_size;
-    SystemState::Get()->p2p_manager()->LookupUrlForFile(
-        file_id,
-        minimum_size,
-        TimeDelta::FromSeconds(kMaxP2PNetworkWaitTimeSeconds),
-        base::Bind(&OmahaRequestAction::OnLookupPayloadViaP2PCompleted,
-                   base::Unretained(this)));
-  }
-}
-
-bool OmahaRequestAction::ShouldDeferDownload(OmahaResponse* output_object) {
-  const auto* params = SystemState::Get()->request_params();
-  if (params->interactive()) {
-    LOG(INFO) << "Not deferring download because update is interactive.";
-    return false;
-  }
-
-  // If we're using p2p to download _and_ we have a p2p URL, we never
-  // defer the download. This is because the download will always
-  // happen from a peer on the LAN and we've been waiting in line for
-  // our turn.
-  const PayloadStateInterface* payload_state =
-      SystemState::Get()->payload_state();
-  if (payload_state->GetUsingP2PForDownloading() &&
-      !payload_state->GetP2PUrl().empty()) {
-    LOG(INFO) << "Download not deferred because download "
-              << "will happen from a local peer (via p2p).";
-    return false;
-  }
-
-  // We should defer the downloads only if we've first satisfied the
-  // wall-clock-based-waiting period and then the update-check-based waiting
-  // period, if required.
-  if (!params->wall_clock_based_wait_enabled()) {
-    LOG(INFO) << "Wall-clock-based waiting period is not enabled,"
-              << " so no deferring needed.";
-    return false;
-  }
-
-  switch (IsWallClockBasedWaitingSatisfied(output_object)) {
-    case kWallClockWaitNotSatisfied:
-      // We haven't even satisfied the first condition, passing the
-      // wall-clock-based waiting period, so we should defer the downloads
-      // until that happens.
-      LOG(INFO) << "wall-clock-based-wait not satisfied.";
-      return true;
-
-    case kWallClockWaitDoneButUpdateCheckWaitRequired:
-      LOG(INFO) << "wall-clock-based-wait satisfied and "
-                << "update-check-based-wait required.";
-      return !IsUpdateCheckCountBasedWaitingSatisfied();
-
-    case kWallClockWaitDoneAndUpdateCheckWaitNotRequired:
-      // Wall-clock-based waiting period is satisfied, and it's determined
-      // that we do not need the update-check-based wait. so no need to
-      // defer downloads.
-      LOG(INFO) << "wall-clock-based-wait satisfied and "
-                << "update-check-based-wait is not required.";
-      return false;
-
-    default:
-      // Returning false for this default case so we err on the
-      // side of downloading updates than deferring in case of any bugs.
-      NOTREACHED();
-      return false;
-  }
-}
-
-OmahaRequestAction::WallClockWaitResult
-OmahaRequestAction::IsWallClockBasedWaitingSatisfied(
-    OmahaResponse* output_object) {
-  Time update_first_seen_at = LoadOrPersistUpdateFirstSeenAtPref();
-  if (update_first_seen_at == base::Time()) {
-    LOG(INFO) << "Not scattering as UpdateFirstSeenAt value cannot be read or "
-                 "persisted.";
-    return kWallClockWaitDoneAndUpdateCheckWaitNotRequired;
-  }
-
-  TimeDelta elapsed_time =
-      SystemState::Get()->clock()->GetWallclockTime() - update_first_seen_at;
-  TimeDelta max_scatter_period =
-      TimeDelta::FromDays(output_object->max_days_to_scatter);
-  int64_t staging_wait_time_in_days = 0;
-  // Use staging and its default max value if staging is on.
-  if (SystemState::Get()->prefs()->GetInt64(kPrefsWallClockStagingWaitPeriod,
-                                            &staging_wait_time_in_days) &&
-      staging_wait_time_in_days > 0)
-    max_scatter_period = TimeDelta::FromDays(kMaxWaitTimeStagingInDays);
-
-  const auto* params = SystemState::Get()->request_params();
-  LOG(INFO) << "Waiting Period = "
-            << utils::FormatSecs(params->waiting_period().InSeconds())
-            << ", Time Elapsed = "
-            << utils::FormatSecs(elapsed_time.InSeconds())
-            << ", MaxDaysToScatter = " << max_scatter_period.InDays();
-
-  if (!output_object->deadline.empty()) {
-    // The deadline is set for all rules which serve a delta update from a
-    // previous FSI, which means this update will be applied mostly in OOBE
-    // cases. For these cases, we shouldn't scatter so as to finish the OOBE
-    // quickly.
-    LOG(INFO) << "Not scattering as deadline flag is set.";
-    return kWallClockWaitDoneAndUpdateCheckWaitNotRequired;
-  }
-
-  if (max_scatter_period.InDays() == 0) {
-    // This means the Omaha rule creator decides that this rule
-    // should not be scattered irrespective of the policy.
-    LOG(INFO) << "Not scattering as MaxDaysToScatter in rule is 0.";
-    return kWallClockWaitDoneAndUpdateCheckWaitNotRequired;
-  }
-
-  if (elapsed_time > max_scatter_period) {
-    // This means we've waited more than the upperbound wait in the rule
-    // from the time we first saw a valid update available to us.
-    // This will prevent update starvation.
-    LOG(INFO) << "Not scattering as we're past the MaxDaysToScatter limit.";
-    return kWallClockWaitDoneAndUpdateCheckWaitNotRequired;
-  }
-
-  // This means we are required to participate in scattering.
-  // See if our turn has arrived now.
-  TimeDelta remaining_wait_time = params->waiting_period() - elapsed_time;
-  if (remaining_wait_time.InSeconds() <= 0) {
-    // Yes, it's our turn now.
-    LOG(INFO) << "Successfully passed the wall-clock-based-wait.";
-
-    // But we can't download until the update-check-count-based wait is also
-    // satisfied, so mark it as required now if update checks are enabled.
-    return params->update_check_count_wait_enabled()
-               ? kWallClockWaitDoneButUpdateCheckWaitRequired
-               : kWallClockWaitDoneAndUpdateCheckWaitNotRequired;
-  }
-
-  // Not our turn yet, so we have to wait until our turn to
-  // help scatter the downloads across all clients of the enterprise.
-  LOG(INFO) << "Update deferred for another "
-            << utils::FormatSecs(remaining_wait_time.InSeconds())
-            << " per policy.";
-  return kWallClockWaitNotSatisfied;
-}
-
-bool OmahaRequestAction::IsUpdateCheckCountBasedWaitingSatisfied() {
-  int64_t update_check_count_value;
-  const auto* params = SystemState::Get()->request_params();
-
-  if (SystemState::Get()->prefs()->Exists(kPrefsUpdateCheckCount)) {
-    if (!SystemState::Get()->prefs()->GetInt64(kPrefsUpdateCheckCount,
-                                               &update_check_count_value)) {
-      // We are unable to read the update check count from file for some reason.
-      // So let's proceed anyway so as to not stall the update.
-      LOG(ERROR) << "Unable to read update check count. "
-                 << "Skipping update-check-count-based-wait.";
-      return true;
-    }
-  } else {
-    // This file does not exist. This means we haven't started our update
-    // check count down yet, so this is the right time to start the count down.
-    update_check_count_value =
-        base::RandInt(params->min_update_checks_needed(),
-                      params->max_update_checks_allowed());
-
-    LOG(INFO) << "Randomly picked update check count value = "
-              << update_check_count_value;
-
-    // Write out the initial value of update_check_count_value.
-    if (!SystemState::Get()->prefs()->SetInt64(kPrefsUpdateCheckCount,
-                                               update_check_count_value)) {
-      // We weren't able to write the update check count file for some reason.
-      // So let's proceed anyway so as to not stall the update.
-      LOG(ERROR) << "Unable to write update check count. "
-                 << "Skipping update-check-count-based-wait.";
-      return true;
-    }
-  }
-
-  if (update_check_count_value == 0) {
-    LOG(INFO) << "Successfully passed the update-check-based-wait.";
-    return true;
-  }
-
-  if (update_check_count_value < 0 ||
-      update_check_count_value > params->max_update_checks_allowed()) {
-    // We err on the side of skipping scattering logic instead of stalling
-    // a machine from receiving any updates in case of any unexpected state.
-    LOG(ERROR) << "Invalid value for update check count detected. "
-               << "Skipping update-check-count-based-wait.";
-    return true;
-  }
-
-  // Legal value, we need to wait for more update checks to happen
-  // until this becomes 0.
-  LOG(INFO) << "Deferring Omaha updates for another "
-            << update_check_count_value << " update checks per policy";
-  return false;
-}
-
-// static
-bool OmahaRequestAction::ParseInstallDate(OmahaParserData* parser_data,
-                                          OmahaResponse* output_object) {
-  int64_t elapsed_days = 0;
-  if (!base::StringToInt64(parser_data->daystart.elapsed_days, &elapsed_days))
-    return false;
-
-  if (elapsed_days < 0)
-    return false;
-
-  output_object->install_date_days = elapsed_days;
-  return true;
-}
-
-// static
-bool OmahaRequestAction::HasInstallDate() {
-  return SystemState::Get()->prefs()->Exists(kPrefsInstallDateDays);
-}
-
-// static
-bool OmahaRequestAction::PersistInstallDate(
-    int install_date_days,
-    InstallDateProvisioningSource source) {
-  TEST_AND_RETURN_FALSE(install_date_days >= 0);
-
-  auto* prefs = SystemState::Get()->prefs();
-  if (!prefs->SetInt64(kPrefsInstallDateDays, install_date_days))
-    return false;
-
-  SystemState::Get()->metrics_reporter()->ReportInstallDateProvisioningSource(
-      static_cast<int>(source),  // Sample.
-      kProvisionedMax);          // Maximum.
-  return true;
-}
-
-void OmahaRequestAction::PersistCohortData(const string& prefs_key,
-                                           const Optional<string>& new_value) {
-  if (!new_value)
-    return;
-  const string& value = new_value.value();
-  if (value.empty() && SystemState::Get()->prefs()->Exists(prefs_key)) {
-    if (!SystemState::Get()->prefs()->Delete(prefs_key))
-      LOG(ERROR) << "Failed to remove stored " << prefs_key << "value.";
-    else
-      LOG(INFO) << "Removed stored " << prefs_key << " value.";
-  } else if (!value.empty()) {
-    if (!SystemState::Get()->prefs()->SetString(prefs_key, value))
-      LOG(INFO) << "Failed to store new setting " << prefs_key << " as "
-                << value;
-    else
-      LOG(INFO) << "Stored cohort setting " << prefs_key << " as " << value;
-  }
-}
-
-void OmahaRequestAction::PersistCohorts(const OmahaParserData& parser_data) {
-  const auto* params = SystemState::Get()->request_params();
-  for (const auto& app : parser_data.apps) {
-    // For platform App ID.
-    if (app.id == params->GetAppId()) {
-      PersistCohortData(kPrefsOmahaCohort, app.cohort);
-      PersistCohortData(kPrefsOmahaCohortName, app.cohortname);
-      PersistCohortData(kPrefsOmahaCohortHint, app.cohorthint);
-    } else if (params->IsDlcAppId(app.id)) {
-      string dlc_id;
-      if (!params->GetDlcId(app.id, &dlc_id)) {
-        LOG(WARNING) << "Skip persisting cohorts for DLC App ID=" << app.id
-                     << " as it is not in the request params.";
-        continue;
-      }
-      auto* prefs = SystemState::Get()->prefs();
-      PersistCohortData(
-          prefs->CreateSubKey({kDlcPrefsSubDir, dlc_id, kPrefsOmahaCohort}),
-          app.cohort);
-      PersistCohortData(
-          prefs->CreateSubKey({kDlcPrefsSubDir, dlc_id, kPrefsOmahaCohortName}),
-          app.cohortname);
-      PersistCohortData(
-          prefs->CreateSubKey({kDlcPrefsSubDir, dlc_id, kPrefsOmahaCohortHint}),
-          app.cohorthint);
-    } else {
-      LOG(WARNING) << "Skip persisting cohorts for unknown App ID=" << app.id;
-    }
-  }
-}
-
-void OmahaRequestAction::ActionCompleted(ErrorCode code) {
-  // We only want to report this on "update check".
-  if (ping_only_ || event_ != nullptr)
-    return;
-
-  metrics::CheckResult result = metrics::CheckResult::kUnset;
-  metrics::CheckReaction reaction = metrics::CheckReaction::kUnset;
-  metrics::DownloadErrorCode download_error_code =
-      metrics::DownloadErrorCode::kUnset;
-
-  // Regular update attempt.
-  switch (code) {
-    case ErrorCode::kSuccess:
-      // OK, we parsed the response successfully but that does
-      // necessarily mean that an update is available.
-      if (HasOutputPipe()) {
-        const OmahaResponse& response = GetOutputObject();
-        if (response.update_exists) {
-          result = metrics::CheckResult::kUpdateAvailable;
-          reaction = metrics::CheckReaction::kUpdating;
-        } else {
-          result = metrics::CheckResult::kNoUpdateAvailable;
-        }
-      } else {
-        result = metrics::CheckResult::kNoUpdateAvailable;
-      }
-      break;
-
-    case ErrorCode::kOmahaUpdateIgnoredPerPolicy:
-    case ErrorCode::kOmahaUpdateIgnoredOverCellular:
-      result = metrics::CheckResult::kUpdateAvailable;
-      reaction = metrics::CheckReaction::kIgnored;
-      break;
-
-    case ErrorCode::kOmahaUpdateDeferredPerPolicy:
-      result = metrics::CheckResult::kUpdateAvailable;
-      reaction = metrics::CheckReaction::kDeferring;
-      break;
-
-    case ErrorCode::kOmahaUpdateDeferredForBackoff:
-      result = metrics::CheckResult::kUpdateAvailable;
-      reaction = metrics::CheckReaction::kBackingOff;
-      break;
-
-    default:
-      // We report two flavors of errors, "Download errors" and "Parsing
-      // error". Try to convert to the former and if that doesn't work
-      // we know it's the latter.
-      metrics::DownloadErrorCode tmp_error =
-          metrics_utils::GetDownloadErrorCode(code);
-      if (tmp_error != metrics::DownloadErrorCode::kInputMalformed) {
-        result = metrics::CheckResult::kDownloadError;
-        download_error_code = tmp_error;
-      } else {
-        result = metrics::CheckResult::kParsingError;
-      }
-      break;
-  }
-
-  SystemState::Get()->metrics_reporter()->ReportUpdateCheckMetrics(
-      result, reaction, download_error_code);
-}
-
-bool OmahaRequestAction::ShouldIgnoreUpdate(const OmahaResponse& response,
-                                            ErrorCode* error) const {
-  // Note: policy decision to not update to a version we rolled back from.
-  string rollback_version =
-      SystemState::Get()->payload_state()->GetRollbackVersion();
-  const auto* params = SystemState::Get()->request_params();
-  if (!rollback_version.empty()) {
-    LOG(INFO) << "Detected previous rollback from version " << rollback_version;
-    if (rollback_version == response.version) {
-      LOG(INFO) << "Received version that we rolled back from. Ignoring.";
-      *error = ErrorCode::kOmahaUpdateIgnoredPerPolicy;
-      return true;
-    }
-  }
-
-  if (SystemState::Get()->hardware()->IsOOBEEnabled() &&
-      !SystemState::Get()->hardware()->IsOOBEComplete(nullptr) &&
-      (response.deadline.empty() ||
-       SystemState::Get()->payload_state()->GetRollbackHappened()) &&
-      params->app_version() != "ForcedUpdate") {
-    LOG(INFO) << "Ignoring a non-critical Omaha update before OOBE completion.";
-    *error = ErrorCode::kNonCriticalUpdateInOOBE;
-    return true;
-  }
-
-  if (!IsUpdateAllowedOverCurrentConnection(error, response)) {
-    LOG(INFO) << "Update is not allowed over current connection.";
-    return true;
-  }
-
-  // Currently non-critical updates always update alongside the platform update
-  // (a critical update) so this case should never actually be hit if the
-  // request to Omaha for updates are correct. In other words, stop the update
-  // from happening as there are no packages in the response to process.
-  if (response.packages.empty()) {
-    LOG(ERROR) << "All packages were excluded.";
-  }
-
-  // Note: We could technically delete the UpdateFirstSeenAt state when we
-  // return true. If we do, it'll mean a device has to restart the
-  // UpdateFirstSeenAt and thus help scattering take effect when the AU is
-  // turned on again. On the other hand, it also increases the chance of update
-  // starvation if an admin turns AU on/off more frequently. We choose to err on
-  // the side of preventing starvation at the cost of not applying scattering in
-  // those cases.
-  return false;
-}
-
-bool OmahaRequestAction::IsUpdateAllowedOverCellularByPrefs(
-    const OmahaResponse& response) const {
-  auto* prefs = SystemState::Get()->prefs();
-  bool is_allowed;
-  if (prefs->Exists(kPrefsUpdateOverCellularPermission) &&
-      prefs->GetBoolean(kPrefsUpdateOverCellularPermission, &is_allowed) &&
-      is_allowed) {
-    LOG(INFO) << "Allowing updates over cellular as permission preference is "
-                 "set to true.";
-    return true;
-  }
-
-  if (!prefs->Exists(kPrefsUpdateOverCellularTargetVersion) ||
-      !prefs->Exists(kPrefsUpdateOverCellularTargetSize)) {
-    LOG(INFO) << "Disabling updates over cellular as permission preference is "
-                 "set to false or does not exist while target does not exist.";
-    return false;
-  }
-
-  std::string target_version;
-  int64_t target_size;
-
-  if (!prefs->GetString(kPrefsUpdateOverCellularTargetVersion,
-                        &target_version) ||
-      !prefs->GetInt64(kPrefsUpdateOverCellularTargetSize, &target_size)) {
-    LOG(INFO) << "Disabling updates over cellular as the target version or "
-                 "size is not accessible.";
-    return false;
-  }
-
-  uint64_t total_packages_size = 0;
-  for (const auto& package : response.packages) {
-    total_packages_size += package.size;
-  }
-  if (target_version == response.version &&
-      static_cast<uint64_t>(target_size) == total_packages_size) {
-    LOG(INFO) << "Allowing updates over cellular as the target matches the"
-                 "omaha response.";
-    return true;
-  } else {
-    LOG(INFO) << "Disabling updates over cellular as the target does not"
-                 "match the omaha response.";
-    return false;
-  }
-}
-
-bool OmahaRequestAction::IsUpdateAllowedOverCurrentConnection(
-    ErrorCode* error, const OmahaResponse& response) const {
-  ConnectionType type;
-  ConnectionTethering tethering;
-  ConnectionManagerInterface* connection_manager =
-      SystemState::Get()->connection_manager();
-  if (!connection_manager->GetConnectionProperties(&type, &tethering)) {
-    LOG(INFO) << "We could not determine our connection type. "
-              << "Defaulting to allow updates.";
-    return true;
-  }
-
-  bool is_allowed = connection_manager->IsUpdateAllowedOver(type, tethering);
-  bool is_device_policy_set =
-      connection_manager->IsAllowedConnectionTypesForUpdateSet();
-  // Treats tethered connection as if it is cellular connection.
-  bool is_over_cellular = type == ConnectionType::kCellular ||
-                          tethering == ConnectionTethering::kConfirmed;
-
-  if (!is_over_cellular) {
-    // There's no need to further check user preferences as we are not over
-    // cellular connection.
-    if (!is_allowed)
-      *error = ErrorCode::kOmahaUpdateIgnoredPerPolicy;
-  } else if (is_device_policy_set) {
-    // There's no need to further check user preferences as the device policy
-    // is set regarding updates over cellular.
-    if (!is_allowed)
-      *error = ErrorCode::kOmahaUpdateIgnoredPerPolicy;
-  } else {
-    // Deivce policy is not set, so user preferences overwrite whether to
-    // allow updates over cellular.
-    is_allowed = IsUpdateAllowedOverCellularByPrefs(response);
-    if (!is_allowed)
-      *error = ErrorCode::kOmahaUpdateIgnoredOverCellular;
-  }
-
-  LOG(INFO) << "We are connected via "
-            << connection_utils::StringForConnectionType(type)
-            << ", Updates allowed: " << (is_allowed ? "Yes" : "No");
-  return is_allowed;
-}
-
-bool OmahaRequestAction::IsRollbackEnabled() const {
-  if (policy_provider_->IsConsumerDevice()) {
-    LOG(INFO) << "Rollback is not enabled for consumer devices.";
-    return false;
-  }
-
-  if (!policy_provider_->device_policy_is_loaded()) {
-    LOG(INFO) << "No device policy is loaded. Assuming rollback enabled.";
-    return true;
-  }
-
-  int allowed_milestones;
-  if (!policy_provider_->GetDevicePolicy().GetRollbackAllowedMilestones(
-          &allowed_milestones)) {
-    LOG(INFO) << "RollbackAllowedMilestones policy can't be read. "
-                 "Defaulting to rollback enabled.";
-    return true;
-  }
-
-  LOG(INFO) << "Rollback allows " << allowed_milestones << " milestones.";
-  return allowed_milestones > 0;
-}
-
-void OmahaRequestAction::SetMaxKernelKeyVersionForRollback() const {
-  int max_kernel_rollforward;
-  int min_kernel_version =
-      SystemState::Get()->hardware()->GetMinKernelKeyVersion();
-  if (IsRollbackEnabled()) {
-    // If rollback is enabled, set the max kernel key version to the current
-    // kernel key version. This has the effect of freezing kernel key roll
-    // forwards.
-    //
-    // TODO(zentaro): This behavior is temporary, and ensures that no kernel
-    // key roll forward happens until the server side components of rollback
-    // are implemented. Future changes will allow the Omaha server to return
-    // the kernel key version from max_rollback_versions in the past. At that
-    // point the max kernel key version will be set to that value, creating a
-    // sliding window of versions that can be rolled back to.
-    LOG(INFO) << "Rollback is enabled. Setting kernel_max_rollforward to "
-              << min_kernel_version;
-    max_kernel_rollforward = min_kernel_version;
-  } else {
-    // For devices that are not rollback enabled (ie. consumer devices), the
-    // max kernel key version is set to 0xfffffffe, which is logically
-    // infinity. This maintains the previous behavior that that kernel key
-    // versions roll forward each time they are incremented.
-    LOG(INFO) << "Rollback is disabled. Setting kernel_max_rollforward to "
-              << kRollforwardInfinity;
-    max_kernel_rollforward = kRollforwardInfinity;
-  }
-
-  bool max_rollforward_set =
-      SystemState::Get()->hardware()->SetMaxKernelKeyRollforward(
-          max_kernel_rollforward);
-  if (!max_rollforward_set) {
-    LOG(ERROR) << "Failed to set kernel_max_rollforward";
-  }
-  // Report metrics
-  SystemState::Get()->metrics_reporter()->ReportKeyVersionMetrics(
-      min_kernel_version, max_kernel_rollforward, max_rollforward_set);
-}
-
-base::Time OmahaRequestAction::LoadOrPersistUpdateFirstSeenAtPref() const {
-  Time update_first_seen_at;
-  int64_t update_first_seen_at_int;
-  if (SystemState::Get()->prefs()->Exists(kPrefsUpdateFirstSeenAt)) {
-    if (SystemState::Get()->prefs()->GetInt64(kPrefsUpdateFirstSeenAt,
-                                              &update_first_seen_at_int)) {
-      // Note: This timestamp could be that of ANY update we saw in the past
-      // (not necessarily this particular update we're considering to apply)
-      // but never got to apply because of some reason (e.g. stop AU policy,
-      // updates being pulled out from Omaha, changes in target version prefix,
-      // new update being rolled out, etc.). But for the purposes of scattering
-      // it doesn't matter which update the timestamp corresponds to. i.e.
-      // the clock starts ticking the first time we see an update and we're
-      // ready to apply when the random wait period is satisfied relative to
-      // that first seen timestamp.
-      update_first_seen_at = Time::FromInternalValue(update_first_seen_at_int);
-      LOG(INFO) << "Using persisted value of UpdateFirstSeenAt: "
-                << utils::ToString(update_first_seen_at);
-    } else {
-      // This seems like an unexpected error where the persisted value exists
-      // but it's not readable for some reason.
-      LOG(INFO) << "UpdateFirstSeenAt value cannot be read";
-      return base::Time();
-    }
-  } else {
-    update_first_seen_at = SystemState::Get()->clock()->GetWallclockTime();
-    update_first_seen_at_int = update_first_seen_at.ToInternalValue();
-    if (SystemState::Get()->prefs()->SetInt64(kPrefsUpdateFirstSeenAt,
-                                              update_first_seen_at_int)) {
-      LOG(INFO) << "Persisted the new value for UpdateFirstSeenAt: "
-                << utils::ToString(update_first_seen_at);
-    } else {
-      // This seems like an unexpected error where the value cannot be
-      // persisted for some reason.
-      LOG(INFO) << "UpdateFirstSeenAt value "
-                << utils::ToString(update_first_seen_at)
-                << " cannot be persisted";
-      return base::Time();
-    }
-  }
-  return update_first_seen_at;
-}
-
-}  // namespace chromeos_update_engine
diff --git a/cros/omaha_request_action_unittest.cc b/cros/omaha_request_action_unittest.cc
deleted file mode 100644
index 01be1a8..0000000
--- a/cros/omaha_request_action_unittest.cc
+++ /dev/null
@@ -1,3189 +0,0 @@
-//
-// Copyright (C) 2012 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/omaha_request_action.h"
-
-#include <stdint.h>
-
-#include <limits>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <base/bind.h>
-#include <base/files/file_util.h>
-#include <base/files/scoped_temp_dir.h>
-#include <base/memory/ptr_util.h>
-#include <base/strings/string_number_conversions.h>
-#include <base/strings/string_util.h>
-#include <base/strings/stringprintf.h>
-#include <base/time/time.h>
-#include <brillo/message_loops/fake_message_loop.h>
-#include <brillo/message_loops/message_loop.h>
-#include <brillo/message_loops/message_loop_utils.h>
-#include <expat.h>
-#include <gtest/gtest.h>
-#include <policy/libpolicy.h>
-#include <policy/mock_libpolicy.h>
-
-#include "update_engine/common/action_pipe.h"
-#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/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;
-using base::TimeDelta;
-using chromeos_update_manager::kRollforwardInfinity;
-using std::pair;
-using std::string;
-using std::vector;
-using testing::_;
-using testing::AllOf;
-using testing::AnyNumber;
-using testing::DoAll;
-using testing::Ge;
-using testing::Le;
-using testing::NiceMock;
-using testing::Return;
-using testing::ReturnPointee;
-using testing::ReturnRef;
-using testing::SaveArg;
-using testing::SetArgPointee;
-using testing::StrictMock;
-
-namespace {
-
-static_assert(kRollforwardInfinity == 0xfffffffe,
-              "Don't change the value of kRollforward infinity unless its "
-              "size has been changed in firmware.");
-
-const char kCurrentVersion[] = "0.1.0.0";
-const char kTestAppId[] = "test-app-id";
-const char kTestAppId2[] = "test-app2-id";
-const char kTestAppIdSkipUpdatecheck[] = "test-app-id-skip-updatecheck";
-const char kDlcId1[] = "dlc-id-1";
-const char kDlcId2[] = "dlc-id-2";
-
-// This is a helper struct to allow unit tests build an update response with the
-// values they care about.
-struct FakeUpdateResponse {
-  string GetRollbackVersionAttributes() const {
-    string num_milestones;
-    num_milestones = base::NumberToString(rollback_allowed_milestones);
-    const string rollback_version =
-        " _firmware_version_" + num_milestones + "=\"" +
-        past_rollback_key_version.first + "\"" + " _kernel_version_" +
-        num_milestones + "=\"" + past_rollback_key_version.second + "\"";
-
-    return (rollback ? " _rollback=\"true\"" : "") + rollback_version +
-           (!rollback_firmware_version.empty()
-                ? " _firmware_version=\"" + rollback_firmware_version + "\""
-                : "") +
-           (!rollback_kernel_version.empty()
-                ? " _kernel_version=\"" + rollback_kernel_version + "\""
-                : "");
-  }
-
-  string GetNoUpdateResponse() const {
-    string entity_str;
-    if (include_entity)
-      entity_str = "<!DOCTYPE response [<!ENTITY CrOS \"ChromeOS\">]>";
-    return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + entity_str +
-           "<response protocol=\"3.0\">"
-           "<daystart elapsed_seconds=\"100\"/>"
-           "<app appid=\"" +
-           app_id + "\" " +
-           (include_cohorts
-                ? "cohort=\"" + cohort + "\" cohorthint=\"" + cohorthint +
-                      "\" cohortname=\"" + cohortname + "\" "
-                : "") +
-           " status=\"ok\">"
-           "<ping status=\"ok\"/>"
-           "<updatecheck status=\"noupdate\"/></app>" +
-           (multi_app_no_update
-                ? "<app appid=\"" + app_id2 +
-                      "\"><updatecheck status=\"noupdate\"/></app>"
-                : "") +
-           "</response>";
-  }
-
-  string GetUpdateResponse() const {
-    chromeos_update_engine::OmahaRequestParams request_params;
-    request_params.set_app_id(app_id);
-    return "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
-           "protocol=\"3.0\">"
-           "<daystart elapsed_seconds=\"100\"" +
-           (elapsed_days.empty() ? ""
-                                 : (" elapsed_days=\"" + elapsed_days + "\"")) +
-           "/>"
-           "<app appid=\"" +
-           app_id + "\" " +
-           (include_cohorts
-                ? "cohort=\"" + cohort + "\" cohorthint=\"" + cohorthint +
-                      "\" cohortname=\"" + cohortname + "\" "
-                : "") +
-           " status=\"ok\">"
-           "<ping status=\"ok\"/><updatecheck status=\"ok\"" +
-           GetRollbackVersionAttributes() + ">" + "<urls><url codebase=\"" +
-           codebase +
-           "\"/></urls>"
-           "<manifest version=\"" +
-           version +
-           "\">"
-           "<packages><package hash=\"not-used\" name=\"" +
-           filename + "\" size=\"" + base::NumberToString(size) + "\" fp=\"" +
-           fp + "\" hash_sha256=\"" + hash + "\"/>" +
-           (multi_package ? "<package name=\"package2\" size=\"222\" fp=\"" +
-                                fp2 + "\" hash_sha256=\"hash2\"/>"
-                          : "") +
-           "</packages>"
-           "<actions><action event=\"postinstall\" MetadataSize=\"11" +
-           (multi_package ? ":22" : "") + "\" MoreInfo=\"" + more_info_url +
-           "\" Prompt=\"" + prompt +
-           "\" "
-           "IsDeltaPayload=\"true" +
-           (multi_package ? ":false" : "") +
-           "\" "
-           "MaxDaysToScatter=\"" +
-           max_days_to_scatter +
-           "\" "
-           "sha256=\"not-used\" " +
-           (deadline.empty() ? "" : ("deadline=\"" + deadline + "\" ")) +
-           (disable_p2p_for_downloading ? "DisableP2PForDownloading=\"true\" "
-                                        : "") +
-           (disable_p2p_for_sharing ? "DisableP2PForSharing=\"true\" " : "") +
-           (powerwash ? "Powerwash=\"true\" " : "") +
-           "/></actions></manifest></updatecheck></app>" +
-           (multi_app
-                ? "<app appid=\"" + app_id2 + "\"" +
-                      (include_cohorts ? " cohort=\"cohort2\"" : "") +
-                      "><updatecheck status=\"ok\"><urls><url codebase=\"" +
-                      codebase2 + "\"/></urls><manifest version=\"" + version2 +
-                      "\"><packages>"
-                      "<package name=\"package3\" size=\"333\" fp=\"" +
-                      fp2 +
-                      "\" hash_sha256=\"hash3\"/></packages>"
-                      "<actions><action event=\"postinstall\" " +
-                      (multi_app_self_update
-                           ? "noupdate=\"true\" IsDeltaPayload=\"true\" "
-                           : "IsDeltaPayload=\"false\" ") +
-                      "MetadataSize=\"33\"/></actions>"
-                      "</manifest></updatecheck></app>"
-                : "") +
-           (multi_app_no_update
-                ? "<app><updatecheck status=\"noupdate\"/></app>"
-                : "") +
-           (multi_app_skip_updatecheck
-                ? "<app appid=\"" + app_id_skip_updatecheck + "\"></app>"
-                : "") +
-           (dlc_app_update
-                ? "<app appid=\"" + request_params.GetDlcAppId(kDlcId1) +
-                      "\" " +
-                      (include_dlc_cohorts
-                           ? "cohort=\"" + dlc_cohort + "\" cohorthint=\"" +
-                                 dlc_cohorthint + "\" cohortname=\"" +
-                                 dlc_cohortname + "\" "
-                           : "") +
-                      "status=\"ok\">"
-                      "<updatecheck status=\"ok\"><urls><url codebase=\"" +
-                      codebase + "\"/><url codebase=\"" + codebase2 +
-                      "\"/></urls><manifest version=\"" + version +
-                      "\"><packages><package name=\"package3\" size=\"333\" "
-                      "fp=\"" +
-                      fp2 +
-                      "\" hash_sha256=\"hash3\"/></packages>"
-                      "<actions><action event=\"install\" run=\".signed\"/>"
-                      "<action event=\"postinstall\" MetadataSize=\"33\"/>"
-                      "</actions></manifest></updatecheck></app>"
-                : "") +
-           (dlc_app_no_update
-                ? "<app appid=\"" + request_params.GetDlcAppId(kDlcId2) +
-                      +"\" " +
-                      (include_dlc_cohorts
-                           ? "cohort=\"" + dlc_cohort + "\" cohorthint=\"" +
-                                 dlc_cohorthint + "\" cohortname=\"" +
-                                 dlc_cohortname + "\" "
-                           : "") +
-                      "><updatecheck status=\"noupdate\"/></app>"
-                : "") +
-           "</response>";
-  }
-
-  // Return the payload URL, which is split in two fields in the XML response.
-  string GetPayloadUrl() { return codebase + filename; }
-
-  string app_id = kTestAppId;
-  string app_id2 = kTestAppId2;
-  string app_id_skip_updatecheck = kTestAppIdSkipUpdatecheck;
-  string version = "1.2.3.4";
-  string version2 = "2.3.4.5";
-  string more_info_url = "http://more/info";
-  string prompt = "true";
-  string codebase = "http://code/base/";
-  string codebase2 = "http://code/base/2/";
-  string filename = "file.signed";
-  string hash = "4841534831323334";
-  string fp = "3.98ba213e";
-  string fp2 = "3.755aff78e";
-  uint64_t size = 123;
-  string deadline = "";
-  string max_days_to_scatter = "7";
-  string elapsed_days = "42";
-
-  // P2P setting defaults to allowed.
-  bool disable_p2p_for_downloading = false;
-  bool disable_p2p_for_sharing = false;
-
-  bool powerwash = false;
-
-  // Omaha cohorts settings.
-  bool include_cohorts = false;
-  string cohort = "";
-  string cohorthint = "";
-  string cohortname = "";
-  // Whether to include Omaha cohorts for DLC apps.
-  bool include_dlc_cohorts = false;
-  string dlc_cohort = "";
-  string dlc_cohorthint = "";
-  string dlc_cohortname = "";
-
-  // Whether to include the CrOS <!ENTITY> in the XML response.
-  bool include_entity = false;
-
-  // Whether to include more than one app.
-  bool multi_app = false;
-  // Whether to include an app with noupdate="true".
-  bool multi_app_self_update = false;
-  // Whether to include an additional app with status="noupdate".
-  bool multi_app_no_update = false;
-  // Whether to include an additional app with no updatecheck tag.
-  bool multi_app_skip_updatecheck = false;
-  // Whether to include more than one package in an app.
-  bool multi_package = false;
-  // Whether to include a DLC app with updatecheck tag.
-  bool dlc_app_update = false;
-  // Whether to include a DLC app with no updatecheck tag.
-  bool dlc_app_no_update = false;
-
-  // Whether the payload is a rollback.
-  bool rollback = false;
-  // The verified boot firmware key version for the rollback image.
-  string rollback_firmware_version = "";
-  // The verified boot kernel key version for the rollback image.
-  string rollback_kernel_version = "";
-  // The number of milestones back that the verified boot key version has been
-  // supplied.
-  uint32_t rollback_allowed_milestones = 0;
-  // The verified boot key version for the
-  // |current - rollback_allowed_milestones| most recent release.
-  // The pair contains <firmware_key_version, kernel_key_version> each
-  // of which is in the form "key_version.version".
-  pair<string, string> past_rollback_key_version;
-};
-
-}  // namespace
-
-namespace chromeos_update_engine {
-
-class OmahaRequestActionTestProcessorDelegate : public ActionProcessorDelegate {
- public:
-  OmahaRequestActionTestProcessorDelegate()
-      : expected_code_(ErrorCode::kSuccess),
-        interactive_(false),
-        test_http_fetcher_headers_(false) {}
-  ~OmahaRequestActionTestProcessorDelegate() override = default;
-
-  void ProcessingDone(const ActionProcessor* processor,
-                      ErrorCode code) override {
-    brillo::MessageLoop::current()->BreakLoop();
-  }
-
-  void ActionCompleted(ActionProcessor* processor,
-                       AbstractAction* action,
-                       ErrorCode code) override {
-    // Make sure actions always succeed.
-    if (action->Type() == OmahaRequestAction::StaticType()) {
-      EXPECT_EQ(expected_code_, code);
-      // Check that the headers were set in the fetcher during the action. Note
-      // that we set this request as "interactive".
-      auto fetcher = static_cast<const MockHttpFetcher*>(
-          static_cast<OmahaRequestAction*>(action)->http_fetcher_.get());
-
-      if (test_http_fetcher_headers_) {
-        EXPECT_EQ(interactive_ ? "fg" : "bg",
-                  fetcher->GetHeader("X-Goog-Update-Interactivity"));
-        EXPECT_EQ(kTestAppId, fetcher->GetHeader("X-Goog-Update-AppId"));
-        EXPECT_NE("", fetcher->GetHeader("X-Goog-Update-Updater"));
-      }
-      post_data_ = fetcher->post_data();
-    } else if (action->Type() ==
-               ObjectCollectorAction<OmahaResponse>::StaticType()) {
-      EXPECT_EQ(ErrorCode::kSuccess, code);
-      auto collector_action =
-          static_cast<ObjectCollectorAction<OmahaResponse>*>(action);
-      omaha_response_.reset(new OmahaResponse(collector_action->object()));
-      EXPECT_TRUE(omaha_response_);
-    } else {
-      EXPECT_EQ(ErrorCode::kSuccess, code);
-    }
-  }
-  ErrorCode expected_code_;
-  brillo::Blob post_data_;
-  bool interactive_;
-  bool test_http_fetcher_headers_;
-  std::unique_ptr<OmahaResponse> omaha_response_;
-};
-
-struct TestUpdateCheckParams {
-  string http_response;
-  int fail_http_response_code;
-  bool ping_only;
-  bool is_consumer_device;
-  int rollback_allowed_milestones;
-  bool is_policy_loaded;
-  ErrorCode expected_code;
-  metrics::CheckResult expected_check_result;
-  metrics::CheckReaction expected_check_reaction;
-  metrics::DownloadErrorCode expected_download_error_code;
-  string session_id;
-};
-
-class OmahaRequestActionTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    FakeSystemState::CreateInstance();
-
-    request_params_.set_os_sp("service_pack");
-    request_params_.set_os_board("x86-generic");
-    request_params_.set_app_id(kTestAppId);
-    request_params_.set_app_version(kCurrentVersion);
-    request_params_.set_app_lang("en-US");
-    request_params_.set_current_channel("unittest");
-    request_params_.set_target_channel("unittest");
-    request_params_.set_hwid("OEM MODEL 09235 7471");
-    request_params_.set_delta_okay(true);
-    request_params_.set_interactive(false);
-    request_params_.set_update_url("http://url");
-    request_params_.set_target_version_prefix("");
-    request_params_.set_rollback_allowed(false);
-    request_params_.set_is_powerwash_allowed(false);
-    request_params_.set_is_install(false);
-    request_params_.set_dlc_apps_params({});
-
-    FakeSystemState::Get()->set_request_params(&request_params_);
-    fake_prefs_ = FakeSystemState::Get()->fake_prefs();
-
-    // Setting the default update check params. Lookup |TestUpdateCheck()|.
-    tuc_params_ = {
-        .http_response = "",
-        .fail_http_response_code = -1,
-        .ping_only = false,
-        .is_consumer_device = true,
-        .rollback_allowed_milestones = 0,
-        .is_policy_loaded = false,
-        .expected_code = ErrorCode::kSuccess,
-        .expected_check_result = metrics::CheckResult::kUpdateAvailable,
-        .expected_check_reaction = metrics::CheckReaction::kUpdating,
-        .expected_download_error_code = metrics::DownloadErrorCode::kUnset,
-    };
-
-    ON_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetExcluder())
-        .WillByDefault(Return(&mock_excluder_));
-  }
-
-  // This function uses the parameters in |tuc_params_| to do an update check.
-  // It will fill out |post_str_| with the result data and |response| with
-  // |OmahaResponse|. Returns true iff an output response was obtained from the
-  // |OmahaRequestAction|. If |fail_http_response_code| is non-negative, the
-  // transfer will fail with that code. |ping_only| is passed through to the
-  // |OmahaRequestAction| constructor.
-  //
-  // The |expected_check_result|, |expected_check_reaction| and
-  // |expected_error_code| parameters are for checking expectations about
-  // reporting UpdateEngine.Check.{Result,Reaction,DownloadError} UMA
-  // statistics. Use the appropriate ::kUnset value to specify that the given
-  // metric should not be reported.
-  bool TestUpdateCheck();
-
-  // Tests events using |event| and |https_response|. It will fill up
-  // |post_str_| with the result data.
-  void TestEvent(OmahaEvent* event, const string& http_response);
-
-  // Runs and checks a ping test. |ping_only| indicates whether it should send
-  // only a ping or also an updatecheck.
-  void PingTest(bool ping_only);
-
-  // InstallDate test helper function.
-  bool InstallDateParseHelper(const string& elapsed_days,
-                              OmahaResponse* response);
-
-  // P2P test helper function.
-  void P2PTest(bool initial_allow_p2p_for_downloading,
-               bool initial_allow_p2p_for_sharing,
-               bool omaha_disable_p2p_for_downloading,
-               bool omaha_disable_p2p_for_sharing,
-               bool payload_state_allow_p2p_attempt,
-               bool expect_p2p_client_lookup,
-               const string& p2p_client_result_url,
-               bool expected_allow_p2p_for_downloading,
-               bool expected_allow_p2p_for_sharing,
-               const string& expected_p2p_url);
-
-  StrictMock<MockExcluder> mock_excluder_;
-  FakeUpdateResponse fake_update_response_;
-  // Used by all tests.
-  OmahaRequestParams request_params_;
-
-  FakePrefs* fake_prefs_;
-
-  OmahaRequestActionTestProcessorDelegate delegate_;
-
-  bool test_http_fetcher_headers_{false};
-
-  TestUpdateCheckParams tuc_params_;
-
-  OmahaResponse response_;
-  string post_str_;
-};
-
-class OmahaRequestActionDlcPingTest : public OmahaRequestActionTest {
- protected:
-  void SetUp() override {
-    OmahaRequestActionTest::SetUp();
-    dlc_id_ = "dlc0";
-    active_key_ = PrefsInterface::CreateSubKey(
-        {kDlcPrefsSubDir, dlc_id_, kPrefsPingActive});
-    last_active_key_ = PrefsInterface::CreateSubKey(
-        {kDlcPrefsSubDir, dlc_id_, kPrefsPingLastActive});
-    last_rollcall_key_ = PrefsInterface::CreateSubKey(
-        {kDlcPrefsSubDir, dlc_id_, kPrefsPingLastRollcall});
-
-    tuc_params_.http_response =
-        "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
-        "protocol=\"3.0\"><daystart elapsed_days=\"4763\" "
-        "elapsed_seconds=\"36540\"/><app appid=\"test-app-id\" status=\"ok\">\""
-        "<updatecheck status=\"noupdate\"/></app><app "
-        "appid=\"test-app-id_dlc0\" "
-        "status=\"ok\"><ping status=\"ok\"/><updatecheck status=\"noupdate\"/>"
-        "</app></response>";
-    tuc_params_.expected_check_result =
-        metrics::CheckResult::kNoUpdateAvailable;
-    tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-  }
-
-  std::string dlc_id_;
-  std::string active_key_;
-  std::string last_active_key_;
-  std::string last_rollcall_key_;
-};
-
-bool OmahaRequestActionTest::TestUpdateCheck() {
-  brillo::FakeMessageLoop loop(nullptr);
-  loop.SetAsCurrent();
-  auto fetcher =
-      std::make_unique<MockHttpFetcher>(tuc_params_.http_response.data(),
-                                        tuc_params_.http_response.size(),
-                                        nullptr);
-  if (tuc_params_.fail_http_response_code >= 0) {
-    fetcher->FailTransfer(tuc_params_.fail_http_response_code);
-  }
-  // This ensures the tests didn't forget to update |FakeSystemState| if they
-  // are not using the default |request_params_|.
-  EXPECT_EQ(&request_params_, FakeSystemState::Get()->request_params());
-
-  auto omaha_request_action =
-      std::make_unique<OmahaRequestAction>(nullptr,
-                                           std::move(fetcher),
-                                           tuc_params_.ping_only,
-                                           tuc_params_.session_id);
-
-  auto mock_policy_provider =
-      std::make_unique<NiceMock<policy::MockPolicyProvider>>();
-  EXPECT_CALL(*mock_policy_provider, IsConsumerDevice())
-      .WillRepeatedly(Return(tuc_params_.is_consumer_device));
-
-  EXPECT_CALL(*mock_policy_provider, device_policy_is_loaded())
-      .WillRepeatedly(Return(tuc_params_.is_policy_loaded));
-
-  const policy::MockDevicePolicy device_policy;
-  const bool get_allowed_milestone_succeeds =
-      tuc_params_.rollback_allowed_milestones >= 0;
-  EXPECT_CALL(device_policy, GetRollbackAllowedMilestones(_))
-      .WillRepeatedly(
-          DoAll(SetArgPointee<0>(tuc_params_.rollback_allowed_milestones),
-                Return(get_allowed_milestone_succeeds)));
-
-  EXPECT_CALL(*mock_policy_provider, GetDevicePolicy())
-      .WillRepeatedly(ReturnRef(device_policy));
-  omaha_request_action->policy_provider_ = std::move(mock_policy_provider);
-
-  delegate_.expected_code_ = tuc_params_.expected_code;
-  delegate_.interactive_ = request_params_.interactive();
-  delegate_.test_http_fetcher_headers_ = test_http_fetcher_headers_;
-  ActionProcessor processor;
-  processor.set_delegate(&delegate_);
-
-  auto collector_action =
-      std::make_unique<ObjectCollectorAction<OmahaResponse>>();
-  BondActions(omaha_request_action.get(), collector_action.get());
-  processor.EnqueueAction(std::move(omaha_request_action));
-  processor.EnqueueAction(std::move(collector_action));
-
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
-              ReportUpdateCheckMetrics(_, _, _))
-      .Times(AnyNumber());
-
-  EXPECT_CALL(
-      *FakeSystemState::Get()->mock_metrics_reporter(),
-      ReportUpdateCheckMetrics(tuc_params_.expected_check_result,
-                               tuc_params_.expected_check_reaction,
-                               tuc_params_.expected_download_error_code))
-      .Times(tuc_params_.ping_only ? 0 : 1);
-
-  loop.PostTask(base::Bind(
-      [](ActionProcessor* processor) { processor->StartProcessing(); },
-      base::Unretained(&processor)));
-  loop.Run();
-  EXPECT_FALSE(loop.PendingTasks());
-  if (delegate_.omaha_response_)
-    response_ = *delegate_.omaha_response_;
-  post_str_ = string(delegate_.post_data_.begin(), delegate_.post_data_.end());
-  return delegate_.omaha_response_ != nullptr;
-}
-
-// Tests Event requests -- they should always succeed. |out_post_data| may be
-// null; if non-null, the post-data received by the mock HttpFetcher is
-// returned.
-void OmahaRequestActionTest::TestEvent(OmahaEvent* event,
-                                       const string& http_response) {
-  brillo::FakeMessageLoop loop(nullptr);
-  loop.SetAsCurrent();
-
-  auto action = std::make_unique<OmahaRequestAction>(
-      event,
-      std::make_unique<MockHttpFetcher>(
-          http_response.data(), http_response.size(), nullptr),
-      false,
-      "");
-  ActionProcessor processor;
-  processor.set_delegate(&delegate_);
-  processor.EnqueueAction(std::move(action));
-
-  loop.PostTask(base::Bind(
-      [](ActionProcessor* processor) { processor->StartProcessing(); },
-      base::Unretained(&processor)));
-  loop.Run();
-  EXPECT_FALSE(loop.PendingTasks());
-
-  post_str_ = string(delegate_.post_data_.begin(), delegate_.post_data_.end());
-}
-
-TEST_F(OmahaRequestActionTest, RejectEntities) {
-  fake_update_response_.include_entity = true;
-  tuc_params_.http_response = fake_update_response_.GetNoUpdateResponse();
-  tuc_params_.expected_code = ErrorCode::kOmahaRequestXMLHasEntityDecl;
-  tuc_params_.expected_check_result = metrics::CheckResult::kParsingError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_FALSE(TestUpdateCheck());
-  EXPECT_FALSE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, NoUpdateTest) {
-  tuc_params_.http_response = fake_update_response_.GetNoUpdateResponse();
-  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_TRUE(TestUpdateCheck());
-  EXPECT_FALSE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, MultiAppNoUpdateTest) {
-  fake_update_response_.multi_app_no_update = true;
-  tuc_params_.http_response = fake_update_response_.GetNoUpdateResponse();
-  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_TRUE(TestUpdateCheck());
-  EXPECT_FALSE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, MultiAppNoPartialUpdateTest) {
-  fake_update_response_.multi_app_no_update = true;
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_TRUE(TestUpdateCheck());
-  EXPECT_FALSE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, NoSelfUpdateTest) {
-  tuc_params_.http_response =
-      "<response><app><updatecheck status=\"ok\"><manifest><actions><action "
-      "event=\"postinstall\" noupdate=\"true\"/></actions>"
-      "</manifest></updatecheck></app></response>";
-  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_TRUE(TestUpdateCheck());
-  EXPECT_FALSE(response_.update_exists);
-}
-
-// Test that all the values in the response are parsed in a normal update
-// response_.
-TEST_F(OmahaRequestActionTest, ValidUpdateTest) {
-  fake_update_response_.deadline = "20101020";
-  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_.GetPayloadUrl(),
-            response_.packages[0].payload_urls[0]);
-  EXPECT_EQ(fake_update_response_.more_info_url, response_.more_info_url);
-  EXPECT_EQ(fake_update_response_.hash, response_.packages[0].hash);
-  EXPECT_EQ(fake_update_response_.size, response_.packages[0].size);
-  EXPECT_EQ(fake_update_response_.fp, response_.packages[0].fp);
-  EXPECT_EQ(true, response_.packages[0].is_delta);
-  EXPECT_EQ(fake_update_response_.prompt == "true", response_.prompt);
-  EXPECT_EQ(fake_update_response_.deadline, response_.deadline);
-  EXPECT_FALSE(response_.powerwash_required);
-  // Omaha cohort attributes are not set in the response, so they should not be
-  // persisted.
-  EXPECT_FALSE(fake_prefs_->Exists(kPrefsOmahaCohort));
-  EXPECT_FALSE(fake_prefs_->Exists(kPrefsOmahaCohortHint));
-  EXPECT_FALSE(fake_prefs_->Exists(kPrefsOmahaCohortName));
-}
-
-TEST_F(OmahaRequestActionTest, MultiPackageUpdateTest) {
-  fake_update_response_.multi_package = true;
-  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_.GetPayloadUrl(),
-            response_.packages[0].payload_urls[0]);
-  EXPECT_EQ(fake_update_response_.codebase + "package2",
-            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(fake_update_response_.fp, response_.packages[0].fp);
-  EXPECT_EQ(true, response_.packages[0].is_delta);
-  EXPECT_EQ(11u, response_.packages[0].metadata_size);
-  ASSERT_EQ(2u, response_.packages.size());
-  EXPECT_EQ(string("hash2"), response_.packages[1].hash);
-  EXPECT_EQ(222u, response_.packages[1].size);
-  EXPECT_EQ(fake_update_response_.fp2, response_.packages[1].fp);
-  EXPECT_EQ(22u, response_.packages[1].metadata_size);
-  EXPECT_EQ(false, response_.packages[1].is_delta);
-}
-
-TEST_F(OmahaRequestActionTest, MultiAppUpdateTest) {
-  fake_update_response_.multi_app = true;
-  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_.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(fake_update_response_.fp, response_.packages[0].fp);
-  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(fake_update_response_.fp2, response_.packages[1].fp);
-  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;
-  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_.GetPayloadUrl(),
-            response_.packages[0].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(fake_update_response_.fp, response_.packages[0].fp);
-  EXPECT_EQ(11u, response_.packages[0].metadata_size);
-  ASSERT_EQ(2u, response_.packages.size());
-  EXPECT_EQ(string("hash3"), response_.packages[1].hash);
-  EXPECT_EQ(333u, response_.packages[1].size);
-  EXPECT_EQ(fake_update_response_.fp2, response_.packages[1].fp);
-  EXPECT_EQ(33u, response_.packages[1].metadata_size);
-  EXPECT_EQ(true, response_.packages[1].is_delta);
-}
-
-TEST_F(OmahaRequestActionTest, MultiAppMultiPackageUpdateTest) {
-  fake_update_response_.multi_app = true;
-  fake_update_response_.multi_package = true;
-  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_.GetPayloadUrl(),
-            response_.packages[0].payload_urls[0]);
-  EXPECT_EQ(fake_update_response_.codebase + "package2",
-            response_.packages[1].payload_urls[0]);
-  EXPECT_EQ(fake_update_response_.codebase2 + "package3",
-            response_.packages[2].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(fake_update_response_.fp, response_.packages[0].fp);
-  EXPECT_EQ(11u, response_.packages[0].metadata_size);
-  EXPECT_EQ(true, response_.packages[0].is_delta);
-  ASSERT_EQ(3u, response_.packages.size());
-  EXPECT_EQ(string("hash2"), response_.packages[1].hash);
-  EXPECT_EQ(222u, response_.packages[1].size);
-  EXPECT_EQ(fake_update_response_.fp2, response_.packages[1].fp);
-  EXPECT_EQ(22u, response_.packages[1].metadata_size);
-  EXPECT_EQ(false, response_.packages[1].is_delta);
-  EXPECT_EQ(string("hash3"), response_.packages[2].hash);
-  EXPECT_EQ(333u, response_.packages[2].size);
-  EXPECT_EQ(fake_update_response_.fp2, response_.packages[2].fp);
-  EXPECT_EQ(33u, response_.packages[2].metadata_size);
-  EXPECT_EQ(false, response_.packages[2].is_delta);
-}
-
-TEST_F(OmahaRequestActionTest, PowerwashTest) {
-  fake_update_response_.powerwash = true;
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_TRUE(response_.update_exists);
-  EXPECT_TRUE(response_.powerwash_required);
-}
-
-TEST_F(OmahaRequestActionTest, ExtraHeadersSentInteractiveTest) {
-  request_params_.set_interactive(true);
-  test_http_fetcher_headers_ = true;
-  tuc_params_.http_response = "invalid xml>";
-  tuc_params_.expected_code = ErrorCode::kOmahaRequestXMLParseError;
-  tuc_params_.expected_check_result = metrics::CheckResult::kParsingError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_FALSE(TestUpdateCheck());
-
-  EXPECT_FALSE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, ExtraHeadersSentNoInteractiveTest) {
-  request_params_.set_interactive(false);
-  test_http_fetcher_headers_ = true;
-  tuc_params_.http_response = "invalid xml>";
-  tuc_params_.expected_code = ErrorCode::kOmahaRequestXMLParseError;
-  tuc_params_.expected_check_result = metrics::CheckResult::kParsingError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_FALSE(TestUpdateCheck());
-
-  EXPECT_FALSE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, ValidUpdateBlockedByConnection) {
-  // Set up a connection manager that doesn't allow a valid update over
-  // the current ethernet connection.
-  MockConnectionManager mock_cm;
-  FakeSystemState::Get()->set_connection_manager(&mock_cm);
-
-  EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
-      .WillRepeatedly(DoAll(SetArgPointee<0>(ConnectionType::kEthernet),
-                            SetArgPointee<1>(ConnectionTethering::kUnknown),
-                            Return(true)));
-  EXPECT_CALL(mock_cm, IsUpdateAllowedOver(ConnectionType::kEthernet, _))
-      .WillRepeatedly(Return(false));
-
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.expected_code = ErrorCode::kOmahaUpdateIgnoredPerPolicy;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kIgnored;
-
-  ASSERT_FALSE(TestUpdateCheck());
-
-  EXPECT_FALSE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, ValidUpdateOverCellularAllowedByDevicePolicy) {
-  // This test tests that update over cellular is allowed as device policy
-  // says yes.
-  MockConnectionManager mock_cm;
-  FakeSystemState::Get()->set_connection_manager(&mock_cm);
-
-  EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
-      .WillRepeatedly(DoAll(SetArgPointee<0>(ConnectionType::kCellular),
-                            SetArgPointee<1>(ConnectionTethering::kUnknown),
-                            Return(true)));
-  EXPECT_CALL(mock_cm, IsAllowedConnectionTypesForUpdateSet())
-      .WillRepeatedly(Return(true));
-  EXPECT_CALL(mock_cm, IsUpdateAllowedOver(ConnectionType::kCellular, _))
-      .WillRepeatedly(Return(true));
-
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_TRUE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, ValidUpdateOverCellularBlockedByDevicePolicy) {
-  // This test tests that update over cellular is blocked as device policy
-  // says no.
-  MockConnectionManager mock_cm;
-  FakeSystemState::Get()->set_connection_manager(&mock_cm);
-
-  EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
-      .WillRepeatedly(DoAll(SetArgPointee<0>(ConnectionType::kCellular),
-                            SetArgPointee<1>(ConnectionTethering::kUnknown),
-                            Return(true)));
-  EXPECT_CALL(mock_cm, IsAllowedConnectionTypesForUpdateSet())
-      .WillRepeatedly(Return(true));
-  EXPECT_CALL(mock_cm, IsUpdateAllowedOver(ConnectionType::kCellular, _))
-      .WillRepeatedly(Return(false));
-
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.expected_code = ErrorCode::kOmahaUpdateIgnoredPerPolicy;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kIgnored;
-
-  ASSERT_FALSE(TestUpdateCheck());
-
-  EXPECT_FALSE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest,
-       ValidUpdateOverCellularAllowedByUserPermissionTrue) {
-  // This test tests that, when device policy is not set, update over cellular
-  // is allowed as permission for update over cellular is set to true.
-  MockConnectionManager mock_cm;
-  fake_prefs_->SetBoolean(kPrefsUpdateOverCellularPermission, true);
-  FakeSystemState::Get()->set_connection_manager(&mock_cm);
-
-  EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
-      .WillRepeatedly(DoAll(SetArgPointee<0>(ConnectionType::kCellular),
-                            SetArgPointee<1>(ConnectionTethering::kUnknown),
-                            Return(true)));
-  EXPECT_CALL(mock_cm, IsAllowedConnectionTypesForUpdateSet())
-      .WillRepeatedly(Return(false));
-  EXPECT_CALL(mock_cm, IsUpdateAllowedOver(ConnectionType::kCellular, _))
-      .WillRepeatedly(Return(true));
-
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_TRUE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest,
-       ValidUpdateOverCellularBlockedByUpdateTargetNotMatch) {
-  // This test tests that, when device policy is not set and permission for
-  // update over cellular is set to false or does not exist, update over
-  // cellular is blocked as update target does not match the omaha response.
-  MockConnectionManager mock_cm;
-  // A version different from the version in omaha response.
-  string diff_version = "99.99.99";
-  // A size different from the size in omaha response.
-  int64_t diff_size = 999;
-
-  fake_prefs_->SetString(kPrefsUpdateOverCellularTargetVersion, diff_version);
-  fake_prefs_->SetInt64(kPrefsUpdateOverCellularTargetSize, diff_size);
-  // This test tests cellular (3G) being the only connection type being allowed.
-  FakeSystemState::Get()->set_connection_manager(&mock_cm);
-
-  EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
-      .WillRepeatedly(DoAll(SetArgPointee<0>(ConnectionType::kCellular),
-                            SetArgPointee<1>(ConnectionTethering::kUnknown),
-                            Return(true)));
-  EXPECT_CALL(mock_cm, IsAllowedConnectionTypesForUpdateSet())
-      .WillRepeatedly(Return(false));
-  EXPECT_CALL(mock_cm, IsUpdateAllowedOver(ConnectionType::kCellular, _))
-      .WillRepeatedly(Return(true));
-
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.expected_code = ErrorCode::kOmahaUpdateIgnoredOverCellular;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kIgnored;
-
-  ASSERT_FALSE(TestUpdateCheck());
-
-  EXPECT_FALSE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest,
-       ValidUpdateOverCellularAllowedByUpdateTargetMatch) {
-  // This test tests that, when device policy is not set and permission for
-  // update over cellular is set to false or does not exist, update over
-  // cellular is allowed as update target matches the omaha response.
-  MockConnectionManager mock_cm;
-  // A version same as the version in omaha response.
-  string new_version = fake_update_response_.version;
-  // A size same as the size in omaha response.
-  int64_t new_size = fake_update_response_.size;
-
-  fake_prefs_->SetString(kPrefsUpdateOverCellularTargetVersion, new_version);
-  fake_prefs_->SetInt64(kPrefsUpdateOverCellularTargetSize, new_size);
-  FakeSystemState::Get()->set_connection_manager(&mock_cm);
-
-  EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
-      .WillRepeatedly(DoAll(SetArgPointee<0>(ConnectionType::kCellular),
-                            SetArgPointee<1>(ConnectionTethering::kUnknown),
-                            Return(true)));
-  EXPECT_CALL(mock_cm, IsAllowedConnectionTypesForUpdateSet())
-      .WillRepeatedly(Return(false));
-  EXPECT_CALL(mock_cm, IsUpdateAllowedOver(ConnectionType::kCellular, _))
-      .WillRepeatedly(Return(true));
-
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_TRUE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, ValidUpdateBlockedByRollback) {
-  string rollback_version = "1234.0.0";
-  MockPayloadState mock_payload_state;
-  FakeSystemState::Get()->set_payload_state(&mock_payload_state);
-  fake_update_response_.version = rollback_version;
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.expected_code = ErrorCode::kOmahaUpdateIgnoredPerPolicy;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kIgnored;
-
-  EXPECT_CALL(mock_payload_state, GetRollbackVersion())
-      .WillRepeatedly(Return(rollback_version));
-
-  ASSERT_FALSE(TestUpdateCheck());
-
-  EXPECT_FALSE(response_.update_exists);
-}
-
-// Verify that update checks called during OOBE will not try to download an
-// update if the response doesn't include the deadline field.
-TEST_F(OmahaRequestActionTest, SkipNonCriticalUpdatesBeforeOOBE) {
-  FakeSystemState::Get()->fake_hardware()->UnsetIsOOBEComplete();
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.expected_code = ErrorCode::kNonCriticalUpdateInOOBE;
-  tuc_params_.expected_check_result = metrics::CheckResult::kParsingError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  // TODO(senj): set better default value for metrics::checkresult in
-  // OmahaRequestAction::ActionCompleted.
-  ASSERT_FALSE(TestUpdateCheck());
-
-  EXPECT_FALSE(response_.update_exists);
-}
-
-// Verify that the IsOOBEComplete() value is ignored when the OOBE flow is not
-// enabled.
-TEST_F(OmahaRequestActionTest, SkipNonCriticalUpdatesBeforeOOBEDisabled) {
-  FakeSystemState::Get()->fake_hardware()->UnsetIsOOBEComplete();
-  FakeSystemState::Get()->fake_hardware()->SetIsOOBEEnabled(false);
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_TRUE(response_.update_exists);
-}
-
-// Verify that update checks called during OOBE will still try to download an
-// update if the response includes the deadline field.
-TEST_F(OmahaRequestActionTest, SkipNonCriticalUpdatesBeforeOOBEDeadlineSet) {
-  FakeSystemState::Get()->fake_hardware()->UnsetIsOOBEComplete();
-  fake_update_response_.deadline = "20101020";
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_TRUE(response_.update_exists);
-}
-
-// Verify that update checks called during OOBE will not try to download an
-// update if a rollback happened, even when the response includes the deadline
-// field.
-TEST_F(OmahaRequestActionTest, SkipNonCriticalUpdatesBeforeOOBERollback) {
-  FakeSystemState::Get()->fake_hardware()->UnsetIsOOBEComplete();
-  fake_update_response_.deadline = "20101020";
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.expected_code = ErrorCode::kNonCriticalUpdateInOOBE;
-  tuc_params_.expected_check_result = metrics::CheckResult::kParsingError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  EXPECT_CALL(*(FakeSystemState::Get()->mock_payload_state()),
-              GetRollbackHappened())
-      .WillOnce(Return(true));
-
-  ASSERT_FALSE(TestUpdateCheck());
-
-  EXPECT_FALSE(response_.update_exists);
-}
-
-// Verify that non-critical updates are skipped by reporting the
-// kNonCriticalUpdateInOOBE error code when attempted over cellular network -
-// i.e. when the update would need user permission. Note that reporting
-// kOmahaUpdateIgnoredOverCellular error in this case  might cause undesired UX
-// in OOBE (warning the user about an update that will be skipped).
-TEST_F(OmahaRequestActionTest, SkipNonCriticalUpdatesInOOBEOverCellular) {
-  FakeSystemState::Get()->fake_hardware()->UnsetIsOOBEComplete();
-
-  MockConnectionManager mock_cm;
-  FakeSystemState::Get()->set_connection_manager(&mock_cm);
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.expected_code = ErrorCode::kNonCriticalUpdateInOOBE;
-  tuc_params_.expected_check_result = metrics::CheckResult::kParsingError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
-      .WillRepeatedly(DoAll(SetArgPointee<0>(ConnectionType::kCellular),
-                            SetArgPointee<1>(ConnectionTethering::kUnknown),
-                            Return(true)));
-  EXPECT_CALL(mock_cm, IsAllowedConnectionTypesForUpdateSet())
-      .WillRepeatedly(Return(false));
-
-  ASSERT_FALSE(TestUpdateCheck());
-
-  EXPECT_FALSE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, WallClockBasedWaitAloneCausesScattering) {
-  request_params_.set_wall_clock_based_wait_enabled(true);
-  request_params_.set_update_check_count_wait_enabled(false);
-  request_params_.set_waiting_period(TimeDelta::FromDays(2));
-  FakeSystemState::Get()->fake_clock()->SetWallclockTime(Time::Now());
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.expected_code = ErrorCode::kOmahaUpdateDeferredPerPolicy;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kDeferring;
-
-  ASSERT_FALSE(TestUpdateCheck());
-
-  EXPECT_FALSE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest,
-       WallClockBasedWaitAloneCausesScatteringInteractive) {
-  request_params_.set_wall_clock_based_wait_enabled(true);
-  request_params_.set_update_check_count_wait_enabled(false);
-  request_params_.set_waiting_period(TimeDelta::FromDays(2));
-  request_params_.set_interactive(true);
-  FakeSystemState::Get()->fake_clock()->SetWallclockTime(Time::Now());
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  // Verify if we are interactive check we don't defer.
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_TRUE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, NoWallClockBasedWaitCausesNoScattering) {
-  request_params_.set_wall_clock_based_wait_enabled(false);
-  request_params_.set_waiting_period(TimeDelta::FromDays(2));
-  request_params_.set_update_check_count_wait_enabled(true);
-  request_params_.set_min_update_checks_needed(1);
-  request_params_.set_max_update_checks_allowed(8);
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_TRUE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, ZeroMaxDaysToScatterCausesNoScattering) {
-  request_params_.set_wall_clock_based_wait_enabled(true);
-  request_params_.set_waiting_period(TimeDelta::FromDays(2));
-  request_params_.set_update_check_count_wait_enabled(true);
-  request_params_.set_min_update_checks_needed(1);
-  request_params_.set_max_update_checks_allowed(8);
-  fake_update_response_.max_days_to_scatter = "0";
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_TRUE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, ZeroUpdateCheckCountCausesNoScattering) {
-  request_params_.set_wall_clock_based_wait_enabled(true);
-  request_params_.set_waiting_period(TimeDelta());
-  request_params_.set_update_check_count_wait_enabled(true);
-  request_params_.set_min_update_checks_needed(0);
-  request_params_.set_max_update_checks_allowed(0);
-  FakeSystemState::Get()->fake_clock()->SetWallclockTime(Time::Now());
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  int64_t count;
-  ASSERT_TRUE(fake_prefs_->GetInt64(kPrefsUpdateCheckCount, &count));
-  ASSERT_EQ(count, 0);
-  EXPECT_TRUE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, NonZeroUpdateCheckCountCausesScattering) {
-  request_params_.set_wall_clock_based_wait_enabled(true);
-  request_params_.set_waiting_period(TimeDelta());
-  request_params_.set_update_check_count_wait_enabled(true);
-  request_params_.set_min_update_checks_needed(1);
-  request_params_.set_max_update_checks_allowed(8);
-  FakeSystemState::Get()->fake_clock()->SetWallclockTime(Time::Now());
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.expected_code = ErrorCode::kOmahaUpdateDeferredPerPolicy;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kDeferring;
-
-  ASSERT_FALSE(TestUpdateCheck());
-
-  int64_t count;
-  ASSERT_TRUE(fake_prefs_->GetInt64(kPrefsUpdateCheckCount, &count));
-  ASSERT_GT(count, 0);
-  EXPECT_FALSE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest,
-       NonZeroUpdateCheckCountCausesScatteringInteractive) {
-  request_params_.set_wall_clock_based_wait_enabled(true);
-  request_params_.set_waiting_period(TimeDelta());
-  request_params_.set_update_check_count_wait_enabled(true);
-  request_params_.set_min_update_checks_needed(1);
-  request_params_.set_max_update_checks_allowed(8);
-  request_params_.set_interactive(true);
-  FakeSystemState::Get()->fake_clock()->SetWallclockTime(Time::Now());
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  // Verify if we are interactive check we don't defer.
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_TRUE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, ExistingUpdateCheckCountCausesScattering) {
-  request_params_.set_wall_clock_based_wait_enabled(true);
-  request_params_.set_waiting_period(TimeDelta());
-  request_params_.set_update_check_count_wait_enabled(true);
-  request_params_.set_min_update_checks_needed(1);
-  request_params_.set_max_update_checks_allowed(8);
-  FakeSystemState::Get()->fake_clock()->SetWallclockTime(Time::Now());
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.expected_code = ErrorCode::kOmahaUpdateDeferredPerPolicy;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kDeferring;
-
-  ASSERT_TRUE(fake_prefs_->SetInt64(kPrefsUpdateCheckCount, 5));
-  ASSERT_FALSE(TestUpdateCheck());
-
-  int64_t count;
-  ASSERT_TRUE(fake_prefs_->GetInt64(kPrefsUpdateCheckCount, &count));
-  // |count| remains the same, as the decrementing happens in update_attempter
-  // which this test doesn't exercise.
-  ASSERT_EQ(count, 5);
-  EXPECT_FALSE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest,
-       ExistingUpdateCheckCountCausesScatteringInteractive) {
-  request_params_.set_wall_clock_based_wait_enabled(true);
-  request_params_.set_waiting_period(TimeDelta());
-  request_params_.set_update_check_count_wait_enabled(true);
-  request_params_.set_min_update_checks_needed(1);
-  request_params_.set_max_update_checks_allowed(8);
-  request_params_.set_interactive(true);
-  FakeSystemState::Get()->fake_clock()->SetWallclockTime(Time::Now());
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  ASSERT_TRUE(fake_prefs_->SetInt64(kPrefsUpdateCheckCount, 5));
-
-  // Verify if we are interactive check we don't defer.
-  ASSERT_TRUE(TestUpdateCheck());
-  EXPECT_TRUE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, StagingTurnedOnCausesScattering) {
-  // If staging is on, the value for max days to scatter should be ignored, and
-  // staging's scatter value should be used.
-  request_params_.set_wall_clock_based_wait_enabled(true);
-  request_params_.set_waiting_period(TimeDelta::FromDays(6));
-  request_params_.set_update_check_count_wait_enabled(false);
-  FakeSystemState::Get()->fake_clock()->SetWallclockTime(Time::Now());
-
-  ASSERT_TRUE(fake_prefs_->SetInt64(kPrefsWallClockStagingWaitPeriod, 6));
-  // This should not prevent scattering due to staging.
-  fake_update_response_.max_days_to_scatter = "0";
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.expected_code = ErrorCode::kOmahaUpdateDeferredPerPolicy;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kDeferring;
-
-  ASSERT_FALSE(TestUpdateCheck());
-
-  EXPECT_FALSE(response_.update_exists);
-
-  // Interactive updates should not be affected.
-  request_params_.set_interactive(true);
-  tuc_params_.expected_code = ErrorCode::kSuccess;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUpdating;
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_TRUE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, CohortsArePersisted) {
-  fake_update_response_.include_cohorts = true;
-  fake_update_response_.cohort = "s/154454/8479665";
-  fake_update_response_.cohorthint = "please-put-me-on-beta";
-  fake_update_response_.cohortname = "stable";
-  request_params_.set_dlc_apps_params(
-      {{request_params_.GetDlcAppId(kDlcId1), {.name = kDlcId1}}});
-  fake_update_response_.dlc_app_update = true;
-  fake_update_response_.include_dlc_cohorts = true;
-  fake_update_response_.dlc_cohort = "s/154454/8479665/dlc";
-  fake_update_response_.dlc_cohorthint = "please-put-me-on-beta-dlc";
-  fake_update_response_.dlc_cohortname = "stable-dlc";
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  EXPECT_CALL(mock_excluder_, IsExcluded(_)).WillRepeatedly(Return(false));
-  ASSERT_TRUE(TestUpdateCheck());
-
-  string value;
-  EXPECT_TRUE(fake_prefs_->GetString(kPrefsOmahaCohort, &value));
-  EXPECT_EQ(fake_update_response_.cohort, value);
-
-  EXPECT_TRUE(fake_prefs_->GetString(kPrefsOmahaCohortHint, &value));
-  EXPECT_EQ(fake_update_response_.cohorthint, value);
-
-  EXPECT_TRUE(fake_prefs_->GetString(kPrefsOmahaCohortName, &value));
-  EXPECT_EQ(fake_update_response_.cohortname, value);
-
-  EXPECT_TRUE(fake_prefs_->GetString(
-      fake_prefs_->CreateSubKey({kDlcPrefsSubDir, kDlcId1, kPrefsOmahaCohort}),
-      &value));
-  EXPECT_EQ(fake_update_response_.dlc_cohort, value);
-
-  EXPECT_TRUE(fake_prefs_->GetString(
-      fake_prefs_->CreateSubKey(
-          {kDlcPrefsSubDir, kDlcId1, kPrefsOmahaCohortHint}),
-      &value));
-  EXPECT_EQ(fake_update_response_.dlc_cohorthint, value);
-
-  EXPECT_TRUE(fake_prefs_->GetString(
-      fake_prefs_->CreateSubKey(
-          {kDlcPrefsSubDir, kDlcId1, kPrefsOmahaCohortName}),
-      &value));
-  EXPECT_EQ(fake_update_response_.dlc_cohortname, value);
-}
-
-TEST_F(OmahaRequestActionTest, CohortsAreUpdated) {
-  EXPECT_TRUE(fake_prefs_->SetString(kPrefsOmahaCohort, "old_value"));
-  EXPECT_TRUE(fake_prefs_->SetString(kPrefsOmahaCohortHint, "old_hint"));
-  EXPECT_TRUE(fake_prefs_->SetString(kPrefsOmahaCohortName, "old_name"));
-  const string dlc_cohort_key =
-      fake_prefs_->CreateSubKey({kDlcPrefsSubDir, kDlcId1, kPrefsOmahaCohort});
-  const string dlc_cohort_hint_key = fake_prefs_->CreateSubKey(
-      {kDlcPrefsSubDir, kDlcId1, kPrefsOmahaCohortHint});
-  const string dlc_cohort_name_key = fake_prefs_->CreateSubKey(
-      {kDlcPrefsSubDir, kDlcId1, kPrefsOmahaCohortName});
-  request_params_.set_dlc_apps_params(
-      {{request_params_.GetDlcAppId(kDlcId1), {.name = kDlcId1}}});
-  EXPECT_TRUE(fake_prefs_->SetString(dlc_cohort_key, "old_value_dlc"));
-  EXPECT_TRUE(fake_prefs_->SetString(dlc_cohort_hint_key, "old_hint_dlc"));
-  EXPECT_TRUE(fake_prefs_->SetString(dlc_cohort_name_key, "old_name_dlc"));
-  fake_update_response_.include_cohorts = true;
-  fake_update_response_.cohort = "s/154454/8479665";
-  fake_update_response_.cohorthint = "please-put-me-on-beta";
-  fake_update_response_.cohortname = "";
-  fake_update_response_.dlc_app_update = true;
-  fake_update_response_.include_dlc_cohorts = true;
-  fake_update_response_.dlc_cohort = "s/154454/8479665/dlc";
-  fake_update_response_.dlc_cohorthint = "please-put-me-on-beta-dlc";
-  fake_update_response_.dlc_cohortname = "";
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  EXPECT_CALL(mock_excluder_, IsExcluded(_)).WillRepeatedly(Return(false));
-  ASSERT_TRUE(TestUpdateCheck());
-
-  string value;
-  EXPECT_TRUE(fake_prefs_->GetString(kPrefsOmahaCohort, &value));
-  EXPECT_EQ(fake_update_response_.cohort, value);
-
-  EXPECT_TRUE(fake_prefs_->GetString(kPrefsOmahaCohortHint, &value));
-  EXPECT_EQ(fake_update_response_.cohorthint, value);
-
-  EXPECT_FALSE(fake_prefs_->GetString(kPrefsOmahaCohortName, &value));
-
-  EXPECT_TRUE(fake_prefs_->GetString(dlc_cohort_key, &value));
-  EXPECT_EQ(fake_update_response_.dlc_cohort, value);
-
-  EXPECT_TRUE(fake_prefs_->GetString(dlc_cohort_hint_key, &value));
-  EXPECT_EQ(fake_update_response_.dlc_cohorthint, value);
-
-  EXPECT_FALSE(fake_prefs_->GetString(dlc_cohort_name_key, &value));
-}
-
-TEST_F(OmahaRequestActionTest, CohortsAreNotModifiedWhenMissing) {
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  EXPECT_TRUE(fake_prefs_->SetString(kPrefsOmahaCohort, "old_value"));
-  const string dlc_cohort_key =
-      fake_prefs_->CreateSubKey({kDlcPrefsSubDir, kDlcId1, kPrefsOmahaCohort});
-  EXPECT_TRUE(fake_prefs_->SetString(dlc_cohort_key, "old_value_dlc"));
-  ASSERT_TRUE(TestUpdateCheck());
-
-  string value;
-  EXPECT_TRUE(fake_prefs_->GetString(kPrefsOmahaCohort, &value));
-  EXPECT_EQ("old_value", value);
-
-  EXPECT_FALSE(fake_prefs_->GetString(kPrefsOmahaCohortHint, &value));
-  EXPECT_FALSE(fake_prefs_->GetString(kPrefsOmahaCohortName, &value));
-
-  EXPECT_TRUE(fake_prefs_->GetString(dlc_cohort_key, &value));
-  EXPECT_EQ("old_value_dlc", value);
-
-  EXPECT_FALSE(fake_prefs_->GetString(
-      fake_prefs_->CreateSubKey(
-          {kDlcPrefsSubDir, kDlcId1, kPrefsOmahaCohortHint}),
-      &value));
-  EXPECT_FALSE(fake_prefs_->GetString(
-      fake_prefs_->CreateSubKey(
-          {kDlcPrefsSubDir, kDlcId1, kPrefsOmahaCohortName}),
-      &value));
-}
-
-TEST_F(OmahaRequestActionTest, CohortsArePersistedWhenNoUpdate) {
-  fake_update_response_.include_cohorts = true;
-  fake_update_response_.cohort = "s/154454/8479665";
-  fake_update_response_.cohorthint = "please-put-me-on-beta";
-  fake_update_response_.cohortname = "stable";
-  tuc_params_.http_response = fake_update_response_.GetNoUpdateResponse();
-  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  string value;
-  EXPECT_TRUE(fake_prefs_->GetString(kPrefsOmahaCohort, &value));
-  EXPECT_EQ(fake_update_response_.cohort, value);
-
-  EXPECT_TRUE(fake_prefs_->GetString(kPrefsOmahaCohortHint, &value));
-  EXPECT_EQ(fake_update_response_.cohorthint, value);
-
-  EXPECT_TRUE(fake_prefs_->GetString(kPrefsOmahaCohortName, &value));
-  EXPECT_EQ(fake_update_response_.cohortname, value);
-}
-
-TEST_F(OmahaRequestActionTest, MultiAppCohortTest) {
-  fake_update_response_.multi_app = true;
-  fake_update_response_.include_cohorts = true;
-  fake_update_response_.cohort = "s/154454/8479665";
-  fake_update_response_.cohorthint = "please-put-me-on-beta";
-  fake_update_response_.cohortname = "stable";
-  request_params_.set_dlc_apps_params(
-      {{request_params_.GetDlcAppId(kDlcId1), {.name = kDlcId1}},
-       {request_params_.GetDlcAppId(kDlcId2), {.name = kDlcId2}}});
-  fake_update_response_.dlc_app_update = true;
-  fake_update_response_.dlc_app_no_update = true;
-  fake_update_response_.include_dlc_cohorts = true;
-  fake_update_response_.dlc_cohort = "s/154454/8479665/dlc";
-  fake_update_response_.dlc_cohorthint = "please-put-me-on-beta-dlc";
-  fake_update_response_.dlc_cohortname = "stable-dlc";
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  EXPECT_CALL(mock_excluder_, IsExcluded(_)).WillRepeatedly(Return(false));
-  ASSERT_TRUE(TestUpdateCheck());
-
-  string value;
-  EXPECT_TRUE(fake_prefs_->GetString(kPrefsOmahaCohort, &value));
-  EXPECT_EQ(fake_update_response_.cohort, value);
-
-  EXPECT_TRUE(fake_prefs_->GetString(kPrefsOmahaCohortHint, &value));
-  EXPECT_EQ(fake_update_response_.cohorthint, value);
-
-  EXPECT_TRUE(fake_prefs_->GetString(kPrefsOmahaCohortName, &value));
-  EXPECT_EQ(fake_update_response_.cohortname, value);
-
-  EXPECT_TRUE(fake_prefs_->GetString(
-      fake_prefs_->CreateSubKey({kDlcPrefsSubDir, kDlcId1, kPrefsOmahaCohort}),
-      &value));
-  EXPECT_EQ(fake_update_response_.dlc_cohort, value);
-  EXPECT_TRUE(fake_prefs_->GetString(
-      fake_prefs_->CreateSubKey({kDlcPrefsSubDir, kDlcId2, kPrefsOmahaCohort}),
-      &value));
-  EXPECT_EQ(fake_update_response_.dlc_cohort, value);
-
-  EXPECT_TRUE(fake_prefs_->GetString(
-      fake_prefs_->CreateSubKey(
-          {kDlcPrefsSubDir, kDlcId1, kPrefsOmahaCohortHint}),
-      &value));
-  EXPECT_EQ(fake_update_response_.dlc_cohorthint, value);
-  EXPECT_TRUE(fake_prefs_->GetString(
-      fake_prefs_->CreateSubKey(
-          {kDlcPrefsSubDir, kDlcId2, kPrefsOmahaCohortHint}),
-      &value));
-  EXPECT_EQ(fake_update_response_.dlc_cohorthint, value);
-
-  EXPECT_TRUE(fake_prefs_->GetString(
-      fake_prefs_->CreateSubKey(
-          {kDlcPrefsSubDir, kDlcId1, kPrefsOmahaCohortName}),
-      &value));
-  EXPECT_EQ(fake_update_response_.dlc_cohortname, value);
-  EXPECT_TRUE(fake_prefs_->GetString(
-      fake_prefs_->CreateSubKey(
-          {kDlcPrefsSubDir, kDlcId2, kPrefsOmahaCohortName}),
-      &value));
-  EXPECT_EQ(fake_update_response_.dlc_cohortname, value);
-}
-
-TEST_F(OmahaRequestActionTest, NoOutputPipeTest) {
-  const string http_response(fake_update_response_.GetNoUpdateResponse());
-  brillo::FakeMessageLoop loop(nullptr);
-  loop.SetAsCurrent();
-
-  auto action = std::make_unique<OmahaRequestAction>(
-      nullptr,
-      std::make_unique<MockHttpFetcher>(
-          http_response.data(), http_response.size(), nullptr),
-      false,
-      "");
-  ActionProcessor processor;
-  processor.set_delegate(&delegate_);
-  processor.EnqueueAction(std::move(action));
-
-  loop.PostTask(base::Bind(
-      [](ActionProcessor* processor) { processor->StartProcessing(); },
-      base::Unretained(&processor)));
-  loop.Run();
-  EXPECT_FALSE(loop.PendingTasks());
-  EXPECT_FALSE(processor.IsRunning());
-}
-
-TEST_F(OmahaRequestActionTest, InvalidXmlTest) {
-  tuc_params_.http_response = "invalid xml>";
-  tuc_params_.expected_code = ErrorCode::kOmahaRequestXMLParseError;
-  tuc_params_.expected_check_result = metrics::CheckResult::kParsingError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_FALSE(TestUpdateCheck());
-  EXPECT_FALSE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, EmptyResponseTest) {
-  tuc_params_.expected_code = ErrorCode::kOmahaRequestEmptyResponseError;
-  tuc_params_.expected_check_result = metrics::CheckResult::kParsingError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_FALSE(TestUpdateCheck());
-  EXPECT_FALSE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, MissingStatusTest) {
-  tuc_params_.http_response =
-      "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\">"
-      "<daystart elapsed_seconds=\"100\"/>"
-      "<app appid=\"foo\" status=\"ok\">"
-      "<ping status=\"ok\"/>"
-      "<updatecheck/></app></response>";
-  tuc_params_.expected_code = ErrorCode::kOmahaResponseInvalid;
-  tuc_params_.expected_check_result = metrics::CheckResult::kParsingError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_FALSE(TestUpdateCheck());
-  EXPECT_FALSE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, InvalidStatusTest) {
-  tuc_params_.http_response =
-      "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\">"
-      "<daystart elapsed_seconds=\"100\"/>"
-      "<app appid=\"foo\" status=\"ok\">"
-      "<ping status=\"ok\"/>"
-      "<updatecheck status=\"InvalidStatusTest\"/></app></response>";
-  tuc_params_.expected_code = ErrorCode::kOmahaResponseInvalid;
-  tuc_params_.expected_check_result = metrics::CheckResult::kParsingError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_FALSE(TestUpdateCheck());
-  EXPECT_FALSE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, MissingNodesetTest) {
-  tuc_params_.http_response =
-      "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\">"
-      "<daystart elapsed_seconds=\"100\"/>"
-      "<app appid=\"foo\" status=\"ok\">"
-      "<ping status=\"ok\"/>"
-      "</app></response>";
-  tuc_params_.expected_code = ErrorCode::kOmahaResponseInvalid;
-  tuc_params_.expected_check_result = metrics::CheckResult::kParsingError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_FALSE(TestUpdateCheck());
-  EXPECT_FALSE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, MissingFieldTest) {
-  tuc_params_.http_response =
-      "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\">"
-      "<daystart elapsed_seconds=\"100\"/>"
-      // the appid needs to match that in the request params
-      "<app appid=\"" +
-      fake_update_response_.app_id +
-      "\" status=\"ok\">"
-      "<updatecheck status=\"ok\">"
-      "<urls><url codebase=\"http://missing/field/test/\"/></urls>"
-      "<manifest version=\"10.2.3.4\">"
-      "<packages><package hash=\"not-used\" name=\"f\" "
-      "size=\"587\" fp=\"3.789\" hash_sha256=\"lkq34j5345\"/></packages>"
-      "<actions><action event=\"postinstall\" "
-      "Prompt=\"false\" "
-      "IsDeltaPayload=\"false\" "
-      "sha256=\"not-used\" "
-      "/></actions></manifest></updatecheck></app></response>";
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_TRUE(response_.update_exists);
-  EXPECT_EQ("10.2.3.4", response_.version);
-  EXPECT_EQ("http://missing/field/test/f",
-            response_.packages[0].payload_urls[0]);
-  EXPECT_EQ("", response_.more_info_url);
-  EXPECT_EQ("lkq34j5345", response_.packages[0].hash);
-  EXPECT_EQ(string("3.789"), response_.packages[0].fp);
-  EXPECT_EQ(587u, response_.packages[0].size);
-  EXPECT_FALSE(response_.prompt);
-  EXPECT_TRUE(response_.deadline.empty());
-}
-
-namespace {
-class TerminateEarlyTestProcessorDelegate : public ActionProcessorDelegate {
- public:
-  void ProcessingStopped(const ActionProcessor* processor) {
-    brillo::MessageLoop::current()->BreakLoop();
-  }
-};
-
-void TerminateTransferTestStarter(ActionProcessor* processor) {
-  processor->StartProcessing();
-  CHECK(processor->IsRunning());
-  processor->StopProcessing();
-}
-}  // namespace
-
-TEST_F(OmahaRequestActionTest, TerminateTransferTest) {
-  brillo::FakeMessageLoop loop(nullptr);
-  loop.SetAsCurrent();
-
-  string http_response("doesn't matter");
-  auto action = std::make_unique<OmahaRequestAction>(
-      nullptr,
-      std::make_unique<MockHttpFetcher>(
-          http_response.data(), http_response.size(), nullptr),
-      false,
-      "");
-  TerminateEarlyTestProcessorDelegate delegate;
-  ActionProcessor processor;
-  processor.set_delegate(&delegate);
-  processor.EnqueueAction(std::move(action));
-
-  loop.PostTask(base::Bind(&TerminateTransferTestStarter, &processor));
-  loop.Run();
-  EXPECT_FALSE(loop.PendingTasks());
-}
-
-TEST_F(OmahaRequestActionTest, XmlEncodeIsUsedForParams) {
-  // Make sure XML Encode is being called on the params.
-  request_params_.set_os_sp("testtheservice_pack>");
-  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\\");
-  fake_prefs_->SetString(
-      kPrefsOmahaCohortName,
-      base::JoinString(vector<string>(100, "My spoon is too big."), " "));
-  tuc_params_.http_response = "invalid xml>";
-  tuc_params_.expected_code = ErrorCode::kOmahaRequestXMLParseError;
-  tuc_params_.expected_check_result = metrics::CheckResult::kParsingError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_FALSE(TestUpdateCheck());
-
-  EXPECT_NE(string::npos, post_str_.find("testtheservice_pack&gt;"));
-  EXPECT_EQ(string::npos, post_str_.find("testtheservice_pack>"));
-  EXPECT_NE(string::npos, post_str_.find("x86 generic&lt;id"));
-  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\""));
-  EXPECT_EQ(string::npos, post_str_.find("cohorthint=\"evil&string\\\""));
-  EXPECT_NE(string::npos, post_str_.find("cohorthint=\"evil&amp;string\\\""));
-  // Values from Prefs that are too big are removed from the XML instead of
-  // encoded.
-  EXPECT_EQ(string::npos, post_str_.find("cohortname="));
-}
-
-TEST_F(OmahaRequestActionTest, XmlDecodeTest) {
-  fake_update_response_.deadline = "&lt;20110101";
-  fake_update_response_.more_info_url = "testthe&lt;url";
-  fake_update_response_.codebase = "testthe&amp;codebase/";
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_EQ("testthe<url", response_.more_info_url);
-  EXPECT_EQ("testthe&codebase/file.signed",
-            response_.packages[0].payload_urls[0]);
-  EXPECT_EQ("<20110101", response_.deadline);
-}
-
-TEST_F(OmahaRequestActionTest, ParseIntTest) {
-  // overflows int32_t:
-  fake_update_response_.size = 123123123123123ull;
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  ASSERT_TRUE(TestUpdateCheck());
-  EXPECT_EQ(fake_update_response_.size, response_.packages[0].size);
-}
-
-TEST_F(OmahaRequestActionTest, FormatUpdateCheckOutputTest) {
-  NiceMock<MockPrefs> prefs;
-  FakeSystemState::Get()->set_prefs(&prefs);
-  tuc_params_.http_response = "invalid xml>";
-  tuc_params_.expected_code = ErrorCode::kOmahaRequestXMLParseError;
-  tuc_params_.expected_check_result = metrics::CheckResult::kParsingError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  EXPECT_CALL(prefs, GetString(kPrefsPreviousVersion, _))
-      .WillOnce(DoAll(SetArgPointee<1>(string("")), Return(true)));
-  // An existing but empty previous version means that we didn't reboot to a new
-  // update, therefore, no need to update the previous version.
-  EXPECT_CALL(prefs, SetString(kPrefsPreviousVersion, _)).Times(0);
-  ASSERT_FALSE(TestUpdateCheck());
-
-  EXPECT_NE(
-      post_str_.find("        <ping active=\"1\" a=\"-1\" r=\"-1\"></ping>\n"
-                     "        <updatecheck></updatecheck>\n"),
-      string::npos);
-  EXPECT_NE(post_str_.find("hardware_class=\"OEM MODEL 09235 7471\""),
-            string::npos);
-  // No <event> tag should be sent if we didn't reboot to an update.
-  EXPECT_EQ(post_str_.find("<event"), string::npos);
-}
-
-TEST_F(OmahaRequestActionTest, FormatSuccessEventOutputTest) {
-  TestEvent(new OmahaEvent(OmahaEvent::kTypeUpdateDownloadStarted),
-            "invalid xml>");
-
-  string expected_event = base::StringPrintf(
-      "        <event eventtype=\"%d\" eventresult=\"%d\"></event>\n",
-      OmahaEvent::kTypeUpdateDownloadStarted,
-      OmahaEvent::kResultSuccess);
-  EXPECT_NE(post_str_.find(expected_event), string::npos);
-  EXPECT_EQ(post_str_.find("ping"), string::npos);
-  EXPECT_EQ(post_str_.find("updatecheck"), string::npos);
-}
-
-TEST_F(OmahaRequestActionTest, FormatErrorEventOutputTest) {
-  TestEvent(new OmahaEvent(OmahaEvent::kTypeDownloadComplete,
-                           OmahaEvent::kResultError,
-                           ErrorCode::kError),
-            "invalid xml>");
-
-  string expected_event = base::StringPrintf(
-      "        <event eventtype=\"%d\" eventresult=\"%d\" "
-      "errorcode=\"%d\"></event>\n",
-      OmahaEvent::kTypeDownloadComplete,
-      OmahaEvent::kResultError,
-      static_cast<int>(ErrorCode::kError));
-  EXPECT_NE(post_str_.find(expected_event), string::npos);
-  EXPECT_EQ(post_str_.find("updatecheck"), string::npos);
-}
-
-TEST_F(OmahaRequestActionTest, IsEventTest) {
-  string http_response("doesn't matter");
-  OmahaRequestAction update_check_action(
-      nullptr,
-      std::make_unique<MockHttpFetcher>(
-          http_response.data(), http_response.size(), nullptr),
-      false,
-      "");
-  EXPECT_FALSE(update_check_action.IsEvent());
-
-  OmahaRequestAction event_action(
-      new OmahaEvent(OmahaEvent::kTypeUpdateComplete),
-      std::make_unique<MockHttpFetcher>(
-          http_response.data(), http_response.size(), nullptr),
-      false,
-      "");
-  EXPECT_TRUE(event_action.IsEvent());
-}
-
-TEST_F(OmahaRequestActionTest, FormatDeltaOkayOutputTest) {
-  tuc_params_.http_response = "invalid xml>";
-  tuc_params_.expected_code = ErrorCode::kOmahaRequestXMLParseError;
-  tuc_params_.expected_check_result = metrics::CheckResult::kParsingError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  for (int i = 0; i < 2; i++) {
-    bool delta_okay = i == 1;
-    const char* delta_okay_str = delta_okay ? "true" : "false";
-    request_params_.set_delta_okay(delta_okay);
-
-    ASSERT_FALSE(TestUpdateCheck());
-    EXPECT_NE(post_str_.find(
-                  base::StringPrintf(" delta_okay=\"%s\"", delta_okay_str)),
-              string::npos)
-        << "i = " << i;
-  }
-}
-
-TEST_F(OmahaRequestActionTest, FormatInteractiveOutputTest) {
-  tuc_params_.http_response = "invalid xml>";
-  tuc_params_.expected_code = ErrorCode::kOmahaRequestXMLParseError;
-  tuc_params_.expected_check_result = metrics::CheckResult::kParsingError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  for (int i = 0; i < 2; i++) {
-    bool interactive = i == 1;
-    const char* interactive_str = interactive ? "ondemandupdate" : "scheduler";
-    request_params_.set_interactive(interactive);
-
-    ASSERT_FALSE(TestUpdateCheck());
-    EXPECT_NE(post_str_.find(
-                  base::StringPrintf("installsource=\"%s\"", interactive_str)),
-              string::npos)
-        << "i = " << i;
-  }
-}
-
-TEST_F(OmahaRequestActionTest, FormatTargetVersionPrefixOutputTest) {
-  tuc_params_.http_response = "invalid xml>";
-  tuc_params_.expected_code = ErrorCode::kOmahaRequestXMLParseError;
-  tuc_params_.expected_check_result = metrics::CheckResult::kParsingError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  for (int i = 0; i < 2; i++) {
-    bool target_version_set = i == 1;
-    const char* target_version_prefix = target_version_set ? "10032." : "";
-    request_params_.set_target_version_prefix(target_version_prefix);
-
-    ASSERT_FALSE(TestUpdateCheck());
-    if (target_version_set) {
-      EXPECT_NE(post_str_.find("<updatecheck targetversionprefix=\"10032.\">"),
-                string::npos)
-          << "i = " << i;
-    } else {
-      EXPECT_EQ(post_str_.find("targetversionprefix"), string::npos)
-          << "i = " << i;
-    }
-  }
-}
-
-TEST_F(OmahaRequestActionTest, FormatRollbackAllowedOutputTest) {
-  tuc_params_.http_response = "invalid xml>";
-  tuc_params_.expected_code = ErrorCode::kOmahaRequestXMLParseError;
-  tuc_params_.expected_check_result = metrics::CheckResult::kParsingError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  for (int i = 0; i < 4; i++) {
-    bool rollback_allowed = i / 2 == 0;
-    bool target_version_set = i % 2 == 0;
-    request_params_.set_target_version_prefix(target_version_set ? "10032."
-                                                                 : "");
-    request_params_.set_rollback_allowed(rollback_allowed);
-
-    ASSERT_FALSE(TestUpdateCheck());
-    if (rollback_allowed && target_version_set) {
-      EXPECT_NE(post_str_.find("rollback_allowed=\"true\""), string::npos)
-          << "i = " << i;
-    } else {
-      EXPECT_EQ(post_str_.find("rollback_allowed"), string::npos)
-          << "i = " << i;
-    }
-  }
-}
-
-TEST_F(OmahaRequestActionTest, OmahaEventTest) {
-  OmahaEvent default_event;
-  EXPECT_EQ(OmahaEvent::kTypeUnknown, default_event.type);
-  EXPECT_EQ(OmahaEvent::kResultError, default_event.result);
-  EXPECT_EQ(ErrorCode::kError, default_event.error_code);
-
-  OmahaEvent success_event(OmahaEvent::kTypeUpdateDownloadStarted);
-  EXPECT_EQ(OmahaEvent::kTypeUpdateDownloadStarted, success_event.type);
-  EXPECT_EQ(OmahaEvent::kResultSuccess, success_event.result);
-  EXPECT_EQ(ErrorCode::kSuccess, success_event.error_code);
-
-  OmahaEvent error_event(OmahaEvent::kTypeUpdateDownloadFinished,
-                         OmahaEvent::kResultError,
-                         ErrorCode::kError);
-  EXPECT_EQ(OmahaEvent::kTypeUpdateDownloadFinished, error_event.type);
-  EXPECT_EQ(OmahaEvent::kResultError, error_event.result);
-  EXPECT_EQ(ErrorCode::kError, error_event.error_code);
-}
-
-TEST_F(OmahaRequestActionTest, DeviceQuickFixBuildTokenIsSetTest) {
-  // If DeviceQuickFixBuildToken value is set it takes precedence over pref
-  // value.
-  constexpr char autoupdate_token[] = "autoupdate_token>";
-  constexpr char xml_encoded_autoupdate_token[] = "autoupdate_token&gt;";
-  constexpr char omaha_cohort_hint[] = "cohort_hint";
-
-  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_autoupdate_token(autoupdate_token);
-  fake_prefs_->SetString(kPrefsOmahaCohortHint, omaha_cohort_hint);
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_NE(string::npos,
-            post_str_.find("cohorthint=\"" +
-                           string(xml_encoded_autoupdate_token) + "\""));
-  EXPECT_EQ(string::npos, post_str_.find(autoupdate_token));
-  EXPECT_EQ(string::npos, post_str_.find(omaha_cohort_hint));
-}
-
-TEST_F(OmahaRequestActionTest, DeviceQuickFixBuildTokenIsNotSetTest) {
-  // If DeviceQuickFixBuildToken is not set, pref value will be provided in
-  // cohorthint attribute.
-  constexpr char omaha_cohort_hint[] = "evil_string>";
-  constexpr char xml_encoded_cohort_hint[] = "evil_string&gt;";
-
-  tuc_params_.http_response = fake_update_response_.GetNoUpdateResponse();
-  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-  fake_prefs_->SetString(kPrefsOmahaCohortHint, omaha_cohort_hint);
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_NE(
-      string::npos,
-      post_str_.find("cohorthint=\"" + string(xml_encoded_cohort_hint) + "\""));
-  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;
-  FakeSystemState::Get()->set_prefs(&prefs);
-  EXPECT_CALL(prefs, GetInt64(kPrefsMetricsCheckLastReportingTime, _))
-      .Times(AnyNumber());
-  EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
-  // Add a few hours to the day difference to test no rounding, etc.
-  int64_t five_days_ago =
-      (Time::Now() - TimeDelta::FromHours(5 * 24 + 13)).ToInternalValue();
-  int64_t six_days_ago =
-      (Time::Now() - TimeDelta::FromHours(6 * 24 + 11)).ToInternalValue();
-  EXPECT_CALL(prefs, GetInt64(kPrefsInstallDateDays, _))
-      .WillOnce(DoAll(SetArgPointee<1>(0), Return(true)));
-  EXPECT_CALL(prefs, GetInt64(kPrefsLastActivePingDay, _))
-      .WillOnce(DoAll(SetArgPointee<1>(six_days_ago), Return(true)));
-  EXPECT_CALL(prefs, GetInt64(kPrefsLastRollCallPingDay, _))
-      .WillOnce(DoAll(SetArgPointee<1>(five_days_ago), Return(true)));
-
-  tuc_params_.http_response = fake_update_response_.GetNoUpdateResponse();
-  tuc_params_.ping_only = ping_only;
-  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_NE(post_str_.find("<ping active=\"1\" a=\"6\" r=\"5\"></ping>"),
-            string::npos);
-  if (ping_only) {
-    EXPECT_EQ(post_str_.find("updatecheck"), string::npos);
-    EXPECT_EQ(post_str_.find("previousversion"), string::npos);
-  } else {
-    EXPECT_NE(post_str_.find("updatecheck"), string::npos);
-    EXPECT_NE(post_str_.find("previousversion"), string::npos);
-  }
-}
-
-TEST_F(OmahaRequestActionTest, PingTestSendOnlyAPing) {
-  PingTest(true /* ping_only */);
-}
-
-TEST_F(OmahaRequestActionTest, PingTestSendAlsoAnUpdateCheck) {
-  PingTest(false /* ping_only */);
-}
-
-TEST_F(OmahaRequestActionTest, ActivePingTest) {
-  NiceMock<MockPrefs> prefs;
-  FakeSystemState::Get()->set_prefs(&prefs);
-  EXPECT_CALL(prefs, GetInt64(kPrefsMetricsCheckLastReportingTime, _))
-      .Times(AnyNumber());
-  EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
-  int64_t three_days_ago =
-      (Time::Now() - TimeDelta::FromHours(3 * 24 + 12)).ToInternalValue();
-  int64_t now = Time::Now().ToInternalValue();
-  EXPECT_CALL(prefs, GetInt64(kPrefsInstallDateDays, _))
-      .WillOnce(DoAll(SetArgPointee<1>(0), Return(true)));
-  EXPECT_CALL(prefs, GetInt64(kPrefsLastActivePingDay, _))
-      .WillOnce(DoAll(SetArgPointee<1>(three_days_ago), Return(true)));
-  EXPECT_CALL(prefs, GetInt64(kPrefsLastRollCallPingDay, _))
-      .WillOnce(DoAll(SetArgPointee<1>(now), Return(true)));
-
-  tuc_params_.http_response = fake_update_response_.GetNoUpdateResponse();
-  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_NE(post_str_.find("<ping active=\"1\" a=\"3\"></ping>"), string::npos);
-}
-
-TEST_F(OmahaRequestActionTest, RollCallPingTest) {
-  NiceMock<MockPrefs> prefs;
-  FakeSystemState::Get()->set_prefs(&prefs);
-  EXPECT_CALL(prefs, GetInt64(kPrefsMetricsCheckLastReportingTime, _))
-      .Times(AnyNumber());
-  EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
-  int64_t four_days_ago =
-      (Time::Now() - TimeDelta::FromHours(4 * 24)).ToInternalValue();
-  int64_t now = Time::Now().ToInternalValue();
-  EXPECT_CALL(prefs, GetInt64(kPrefsInstallDateDays, _))
-      .WillOnce(DoAll(SetArgPointee<1>(0), Return(true)));
-  EXPECT_CALL(prefs, GetInt64(kPrefsLastActivePingDay, _))
-      .WillOnce(DoAll(SetArgPointee<1>(now), Return(true)));
-  EXPECT_CALL(prefs, GetInt64(kPrefsLastRollCallPingDay, _))
-      .WillOnce(DoAll(SetArgPointee<1>(four_days_ago), Return(true)));
-
-  tuc_params_.http_response = fake_update_response_.GetNoUpdateResponse();
-  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_NE(post_str_.find("<ping active=\"1\" r=\"4\"></ping>\n"),
-            string::npos);
-}
-
-TEST_F(OmahaRequestActionTest, NoPingTest) {
-  NiceMock<MockPrefs> prefs;
-  FakeSystemState::Get()->set_prefs(&prefs);
-  EXPECT_CALL(prefs, GetInt64(kPrefsMetricsCheckLastReportingTime, _))
-      .Times(AnyNumber());
-  EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
-  int64_t one_hour_ago =
-      (Time::Now() - TimeDelta::FromHours(1)).ToInternalValue();
-  EXPECT_CALL(prefs, GetInt64(kPrefsInstallDateDays, _))
-      .WillOnce(DoAll(SetArgPointee<1>(0), Return(true)));
-  EXPECT_CALL(prefs, GetInt64(kPrefsLastActivePingDay, _))
-      .WillOnce(DoAll(SetArgPointee<1>(one_hour_ago), Return(true)));
-  EXPECT_CALL(prefs, GetInt64(kPrefsLastRollCallPingDay, _))
-      .WillOnce(DoAll(SetArgPointee<1>(one_hour_ago), Return(true)));
-  // LastActivePingDay and PrefsLastRollCallPingDay are set even if we didn't
-  // send a ping.
-  EXPECT_CALL(prefs, SetInt64(kPrefsLastActivePingDay, _))
-      .WillOnce(Return(true));
-  EXPECT_CALL(prefs, SetInt64(kPrefsLastRollCallPingDay, _))
-      .WillOnce(Return(true));
-
-  tuc_params_.http_response = fake_update_response_.GetNoUpdateResponse();
-  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_EQ(post_str_.find("ping"), string::npos);
-}
-
-TEST_F(OmahaRequestActionTest, IgnoreEmptyPingTest) {
-  // This test ensures that we ignore empty ping only requests.
-  NiceMock<MockPrefs> prefs;
-  FakeSystemState::Get()->set_prefs(&prefs);
-  int64_t now = Time::Now().ToInternalValue();
-  EXPECT_CALL(prefs, GetInt64(kPrefsLastActivePingDay, _))
-      .WillOnce(DoAll(SetArgPointee<1>(now), Return(true)));
-  EXPECT_CALL(prefs, GetInt64(kPrefsLastRollCallPingDay, _))
-      .WillOnce(DoAll(SetArgPointee<1>(now), Return(true)));
-  EXPECT_CALL(prefs, SetInt64(kPrefsLastActivePingDay, _)).Times(0);
-  EXPECT_CALL(prefs, SetInt64(kPrefsLastRollCallPingDay, _)).Times(0);
-
-  tuc_params_.http_response = fake_update_response_.GetNoUpdateResponse();
-  tuc_params_.ping_only = true;
-  tuc_params_.expected_check_result = metrics::CheckResult::kUnset;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  EXPECT_TRUE(TestUpdateCheck());
-  EXPECT_TRUE(post_str_.empty());
-}
-
-TEST_F(OmahaRequestActionTest, BackInTimePingTest) {
-  NiceMock<MockPrefs> prefs;
-  FakeSystemState::Get()->set_prefs(&prefs);
-  EXPECT_CALL(prefs, GetInt64(kPrefsMetricsCheckLastReportingTime, _))
-      .Times(AnyNumber());
-  EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
-  int64_t future =
-      (Time::Now() + TimeDelta::FromHours(3 * 24 + 4)).ToInternalValue();
-  EXPECT_CALL(prefs, GetInt64(kPrefsInstallDateDays, _))
-      .WillOnce(DoAll(SetArgPointee<1>(0), Return(true)));
-  EXPECT_CALL(prefs, GetInt64(kPrefsLastActivePingDay, _))
-      .WillOnce(DoAll(SetArgPointee<1>(future), Return(true)));
-  EXPECT_CALL(prefs, GetInt64(kPrefsLastRollCallPingDay, _))
-      .WillOnce(DoAll(SetArgPointee<1>(future), Return(true)));
-  EXPECT_CALL(prefs, SetInt64(kPrefsLastActivePingDay, _))
-      .WillOnce(Return(true));
-  EXPECT_CALL(prefs, SetInt64(kPrefsLastRollCallPingDay, _))
-      .WillOnce(Return(true));
-
-  tuc_params_.http_response =
-      "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
-      "protocol=\"3.0\"><daystart elapsed_seconds=\"100\"/>"
-      "<app appid=\"foo\" status=\"ok\"><ping status=\"ok\"/>"
-      "<updatecheck status=\"noupdate\"/></app></response>";
-  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_TRUE(TestUpdateCheck());
-  EXPECT_EQ(post_str_.find("ping"), string::npos);
-}
-
-TEST_F(OmahaRequestActionTest, LastPingDayUpdateTest) {
-  // This test checks that the action updates the last ping day to now
-  // minus 200 seconds with a slack of 5 seconds. Therefore, the test
-  // may fail if it runs for longer than 5 seconds. It shouldn't run
-  // that long though.
-  int64_t midnight =
-      (Time::Now() - TimeDelta::FromSeconds(200)).ToInternalValue();
-  int64_t midnight_slack =
-      (Time::Now() - TimeDelta::FromSeconds(195)).ToInternalValue();
-  NiceMock<MockPrefs> prefs;
-  FakeSystemState::Get()->set_prefs(&prefs);
-  EXPECT_CALL(prefs, GetInt64(_, _)).Times(AnyNumber());
-  EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
-  EXPECT_CALL(prefs,
-              SetInt64(kPrefsLastActivePingDay,
-                       AllOf(Ge(midnight), Le(midnight_slack))))
-      .WillOnce(Return(true));
-  EXPECT_CALL(prefs,
-              SetInt64(kPrefsLastRollCallPingDay,
-                       AllOf(Ge(midnight), Le(midnight_slack))))
-      .WillOnce(Return(true));
-
-  tuc_params_.http_response =
-      "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
-      "protocol=\"3.0\"><daystart elapsed_seconds=\"200\"/>"
-      "<app appid=\"foo\" status=\"ok\"><ping status=\"ok\"/>"
-      "<updatecheck status=\"noupdate\"/></app></response>";
-  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_TRUE(TestUpdateCheck());
-}
-
-TEST_F(OmahaRequestActionTest, NoElapsedSecondsTest) {
-  NiceMock<MockPrefs> prefs;
-  FakeSystemState::Get()->set_prefs(&prefs);
-  EXPECT_CALL(prefs, GetInt64(_, _)).Times(AnyNumber());
-  EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
-  EXPECT_CALL(prefs, SetInt64(kPrefsLastActivePingDay, _)).Times(0);
-  EXPECT_CALL(prefs, SetInt64(kPrefsLastRollCallPingDay, _)).Times(0);
-
-  tuc_params_.http_response =
-      "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
-      "protocol=\"3.0\"><daystart blah=\"200\"/>"
-      "<app appid=\"foo\" status=\"ok\"><ping status=\"ok\"/>"
-      "<updatecheck status=\"noupdate\"/></app></response>";
-  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_TRUE(TestUpdateCheck());
-}
-
-TEST_F(OmahaRequestActionTest, BadElapsedSecondsTest) {
-  NiceMock<MockPrefs> prefs;
-  FakeSystemState::Get()->set_prefs(&prefs);
-  EXPECT_CALL(prefs, GetInt64(_, _)).Times(AnyNumber());
-  EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
-  EXPECT_CALL(prefs, SetInt64(kPrefsLastActivePingDay, _)).Times(0);
-  EXPECT_CALL(prefs, SetInt64(kPrefsLastRollCallPingDay, _)).Times(0);
-
-  tuc_params_.http_response =
-      "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
-      "protocol=\"3.0\"><daystart elapsed_seconds=\"x\"/>"
-      "<app appid=\"foo\" status=\"ok\"><ping status=\"ok\"/>"
-      "<updatecheck status=\"noupdate\"/></app></response>";
-  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_TRUE(TestUpdateCheck());
-}
-
-TEST_F(OmahaRequestActionTest, NoUniqueIDTest) {
-  tuc_params_.http_response = "invalid xml>";
-  tuc_params_.expected_code = ErrorCode::kOmahaRequestXMLParseError;
-  tuc_params_.expected_check_result = metrics::CheckResult::kParsingError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_FALSE(TestUpdateCheck());
-
-  EXPECT_EQ(post_str_.find("machineid="), string::npos);
-  EXPECT_EQ(post_str_.find("userid="), string::npos);
-}
-
-TEST_F(OmahaRequestActionTest, NetworkFailureTest) {
-  const int http_error_code =
-      static_cast<int>(ErrorCode::kOmahaRequestHTTPResponseBase) + 501;
-  tuc_params_.fail_http_response_code = 501;
-  tuc_params_.expected_code = static_cast<ErrorCode>(http_error_code);
-  tuc_params_.expected_check_result = metrics::CheckResult::kDownloadError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-  tuc_params_.expected_download_error_code =
-      static_cast<metrics::DownloadErrorCode>(501);
-
-  ASSERT_FALSE(TestUpdateCheck());
-
-  EXPECT_FALSE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, NetworkFailureBadHTTPCodeTest) {
-  const int http_error_code =
-      static_cast<int>(ErrorCode::kOmahaRequestHTTPResponseBase) + 999;
-
-  tuc_params_.fail_http_response_code = 1500;
-  tuc_params_.expected_code = static_cast<ErrorCode>(http_error_code);
-  tuc_params_.expected_check_result = metrics::CheckResult::kDownloadError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-  tuc_params_.expected_download_error_code =
-      metrics::DownloadErrorCode::kHttpStatusOther;
-
-  ASSERT_FALSE(TestUpdateCheck());
-  EXPECT_FALSE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, TestUpdateFirstSeenAtGetsPersistedFirstTime) {
-  request_params_.set_wall_clock_based_wait_enabled(true);
-  request_params_.set_waiting_period(TimeDelta().FromDays(1));
-  request_params_.set_update_check_count_wait_enabled(false);
-
-  Time arbitrary_date;
-  ASSERT_TRUE(Time::FromString("6/4/1989", &arbitrary_date));
-  FakeSystemState::Get()->fake_clock()->SetWallclockTime(arbitrary_date);
-
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.expected_code = ErrorCode::kOmahaUpdateDeferredPerPolicy;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kDeferring;
-
-  ASSERT_FALSE(TestUpdateCheck());
-
-  int64_t timestamp = 0;
-  ASSERT_TRUE(fake_prefs_->GetInt64(kPrefsUpdateFirstSeenAt, &timestamp));
-  EXPECT_EQ(arbitrary_date.ToInternalValue(), timestamp);
-  EXPECT_FALSE(response_.update_exists);
-
-  // Verify if we are interactive check we don't defer.
-  request_params_.set_interactive(true);
-  tuc_params_.expected_code = ErrorCode::kSuccess;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUpdating;
-
-  ASSERT_TRUE(TestUpdateCheck());
-  EXPECT_TRUE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, TestUpdateFirstSeenAtGetsUsedIfAlreadyPresent) {
-  request_params_.set_wall_clock_based_wait_enabled(true);
-  request_params_.set_waiting_period(TimeDelta().FromDays(1));
-  request_params_.set_update_check_count_wait_enabled(false);
-
-  Time t1, t2;
-  ASSERT_TRUE(Time::FromString("1/1/2012", &t1));
-  ASSERT_TRUE(Time::FromString("1/3/2012", &t2));
-  ASSERT_TRUE(
-      fake_prefs_->SetInt64(kPrefsUpdateFirstSeenAt, t1.ToInternalValue()));
-  FakeSystemState::Get()->fake_clock()->SetWallclockTime(t2);
-
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  ASSERT_TRUE(TestUpdateCheck());
-  EXPECT_TRUE(response_.update_exists);
-
-  // Make sure the timestamp t1 is unchanged showing that it was reused.
-  int64_t timestamp = 0;
-  ASSERT_TRUE(fake_prefs_->GetInt64(kPrefsUpdateFirstSeenAt, &timestamp));
-  ASSERT_TRUE(timestamp == t1.ToInternalValue());
-}
-
-TEST_F(OmahaRequestActionTest, TestChangingToMoreStableChannel) {
-  // Create a uniquely named test directory.
-  base::ScopedTempDir tempdir;
-  ASSERT_TRUE(tempdir.CreateUniqueTempDir());
-
-  request_params_.set_root(tempdir.GetPath().value());
-  request_params_.set_app_id("{22222222-2222-2222-2222-222222222222}");
-  request_params_.set_app_version("1.2.3.4");
-  request_params_.set_product_components("o.bundle=1");
-  request_params_.set_current_channel("canary-channel");
-  EXPECT_TRUE(
-      request_params_.SetTargetChannel("stable-channel", true, nullptr));
-  request_params_.UpdateDownloadChannel();
-  EXPECT_TRUE(request_params_.ShouldPowerwash());
-
-  tuc_params_.http_response = "invalid xml>";
-  tuc_params_.expected_code = ErrorCode::kOmahaRequestXMLParseError;
-  tuc_params_.expected_check_result = metrics::CheckResult::kParsingError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_FALSE(TestUpdateCheck());
-
-  EXPECT_NE(string::npos,
-            post_str_.find(
-                "appid=\"{22222222-2222-2222-2222-222222222222}\" "
-                "version=\"0.0.0.0\" from_version=\"1.2.3.4\" "
-                "track=\"stable-channel\" from_track=\"canary-channel\" "));
-  EXPECT_EQ(string::npos, post_str_.find("o.bundle"));
-}
-
-TEST_F(OmahaRequestActionTest, TestChangingToLessStableChannel) {
-  // Create a uniquely named test directory.
-  base::ScopedTempDir tempdir;
-  ASSERT_TRUE(tempdir.CreateUniqueTempDir());
-
-  request_params_.set_root(tempdir.GetPath().value());
-  request_params_.set_app_id("{11111111-1111-1111-1111-111111111111}");
-  request_params_.set_app_version("5.6.7.8");
-  request_params_.set_product_components("o.bundle=1");
-  request_params_.set_current_channel("stable-channel");
-  EXPECT_TRUE(
-      request_params_.SetTargetChannel("canary-channel", false, nullptr));
-  request_params_.UpdateDownloadChannel();
-  EXPECT_FALSE(request_params_.ShouldPowerwash());
-
-  tuc_params_.http_response = "invalid xml>";
-  tuc_params_.expected_code = ErrorCode::kOmahaRequestXMLParseError;
-  tuc_params_.expected_check_result = metrics::CheckResult::kParsingError;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_FALSE(TestUpdateCheck());
-
-  EXPECT_NE(
-      string::npos,
-      post_str_.find("appid=\"{11111111-1111-1111-1111-111111111111}\" "
-                     "version=\"5.6.7.8\" "
-                     "track=\"canary-channel\" from_track=\"stable-channel\""));
-  EXPECT_EQ(string::npos, post_str_.find("from_version"));
-  EXPECT_NE(string::npos, post_str_.find("o.bundle.version=\"1\""));
-}
-
-// Checks that the initial ping with a=-1 r=-1 is not send when the device
-// was powerwashed.
-TEST_F(OmahaRequestActionTest, PingWhenPowerwashed) {
-  fake_prefs_->SetString(kPrefsPreviousVersion, "");
-
-  // Flag that the device was powerwashed in the past.
-  FakeSystemState::Get()->fake_hardware()->SetPowerwashCount(1);
-  tuc_params_.http_response = fake_update_response_.GetNoUpdateResponse();
-  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  // We shouldn't send a ping in this case since powerwash > 0.
-  EXPECT_EQ(string::npos, post_str_.find("<ping"));
-}
-
-// Checks that the initial ping with a=-1 r=-1 is not send when the device
-// first_active_omaha_ping_sent is set.
-TEST_F(OmahaRequestActionTest, PingWhenFirstActiveOmahaPingIsSent) {
-  fake_prefs_->SetString(kPrefsPreviousVersion, "");
-
-  // Flag that the device was not powerwashed in the past.
-  FakeSystemState::Get()->fake_hardware()->SetPowerwashCount(0);
-
-  // Flag that the device has sent first active ping in the past.
-  FakeSystemState::Get()->fake_hardware()->SetFirstActiveOmahaPingSent();
-
-  tuc_params_.http_response = fake_update_response_.GetNoUpdateResponse();
-  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  // We shouldn't send a ping in this case since
-  // first_active_omaha_ping_sent=true
-  EXPECT_EQ(string::npos, post_str_.find("<ping"));
-}
-
-// Checks that the event 54 is sent on a reboot to a new update.
-TEST_F(OmahaRequestActionTest, RebootAfterUpdateEvent) {
-  // Flag that the device was updated in a previous boot.
-  fake_prefs_->SetString(kPrefsPreviousVersion, "1.2.3.4");
-
-  tuc_params_.http_response = fake_update_response_.GetNoUpdateResponse();
-  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  // An event 54 is included and has the right version.
-  EXPECT_NE(
-      string::npos,
-      post_str_.find(base::StringPrintf("<event eventtype=\"%d\"",
-                                        OmahaEvent::kTypeRebootedAfterUpdate)));
-  EXPECT_NE(string::npos,
-            post_str_.find("previousversion=\"1.2.3.4\"></event>"));
-
-  // The previous version flag should have been removed.
-  EXPECT_TRUE(fake_prefs_->Exists(kPrefsPreviousVersion));
-  string prev_version;
-  EXPECT_TRUE(fake_prefs_->GetString(kPrefsPreviousVersion, &prev_version));
-  EXPECT_TRUE(prev_version.empty());
-}
-
-void OmahaRequestActionTest::P2PTest(bool initial_allow_p2p_for_downloading,
-                                     bool initial_allow_p2p_for_sharing,
-                                     bool omaha_disable_p2p_for_downloading,
-                                     bool omaha_disable_p2p_for_sharing,
-                                     bool payload_state_allow_p2p_attempt,
-                                     bool expect_p2p_client_lookup,
-                                     const string& p2p_client_result_url,
-                                     bool expected_allow_p2p_for_downloading,
-                                     bool expected_allow_p2p_for_sharing,
-                                     const string& expected_p2p_url) {
-  bool actual_allow_p2p_for_downloading = initial_allow_p2p_for_downloading;
-  bool actual_allow_p2p_for_sharing = initial_allow_p2p_for_sharing;
-  string actual_p2p_url;
-
-  MockPayloadState mock_payload_state;
-  FakeSystemState::Get()->set_payload_state(&mock_payload_state);
-  EXPECT_CALL(mock_payload_state, P2PAttemptAllowed())
-      .WillRepeatedly(Return(payload_state_allow_p2p_attempt));
-  EXPECT_CALL(mock_payload_state, GetUsingP2PForDownloading())
-      .WillRepeatedly(ReturnPointee(&actual_allow_p2p_for_downloading));
-  EXPECT_CALL(mock_payload_state, GetUsingP2PForSharing())
-      .WillRepeatedly(ReturnPointee(&actual_allow_p2p_for_sharing));
-  EXPECT_CALL(mock_payload_state, SetUsingP2PForDownloading(_))
-      .WillRepeatedly(SaveArg<0>(&actual_allow_p2p_for_downloading));
-  EXPECT_CALL(mock_payload_state, SetUsingP2PForSharing(_))
-      .WillRepeatedly(SaveArg<0>(&actual_allow_p2p_for_sharing));
-  EXPECT_CALL(mock_payload_state, SetP2PUrl(_))
-      .WillRepeatedly(SaveArg<0>(&actual_p2p_url));
-
-  MockP2PManager mock_p2p_manager;
-  FakeSystemState::Get()->set_p2p_manager(&mock_p2p_manager);
-  mock_p2p_manager.fake().SetLookupUrlForFileResult(p2p_client_result_url);
-
-  TimeDelta timeout = TimeDelta::FromSeconds(kMaxP2PNetworkWaitTimeSeconds);
-  EXPECT_CALL(mock_p2p_manager, LookupUrlForFile(_, _, timeout, _))
-      .Times(expect_p2p_client_lookup ? 1 : 0);
-
-  fake_update_response_.disable_p2p_for_downloading =
-      omaha_disable_p2p_for_downloading;
-  fake_update_response_.disable_p2p_for_sharing = omaha_disable_p2p_for_sharing;
-
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.expected_check_result = metrics::CheckResult::kUpdateAvailable;
-
-  ASSERT_TRUE(TestUpdateCheck());
-  EXPECT_TRUE(response_.update_exists);
-
-  EXPECT_EQ(omaha_disable_p2p_for_downloading,
-            response_.disable_p2p_for_downloading);
-  EXPECT_EQ(omaha_disable_p2p_for_sharing, response_.disable_p2p_for_sharing);
-
-  EXPECT_EQ(expected_allow_p2p_for_downloading,
-            actual_allow_p2p_for_downloading);
-  EXPECT_EQ(expected_allow_p2p_for_sharing, actual_allow_p2p_for_sharing);
-  EXPECT_EQ(expected_p2p_url, actual_p2p_url);
-}
-
-TEST_F(OmahaRequestActionTest, P2PWithPeer) {
-  P2PTest(true,                   // initial_allow_p2p_for_downloading
-          true,                   // initial_allow_p2p_for_sharing
-          false,                  // omaha_disable_p2p_for_downloading
-          false,                  // omaha_disable_p2p_for_sharing
-          true,                   // payload_state_allow_p2p_attempt
-          true,                   // expect_p2p_client_lookup
-          "http://1.3.5.7/p2p",   // p2p_client_result_url
-          true,                   // expected_allow_p2p_for_downloading
-          true,                   // expected_allow_p2p_for_sharing
-          "http://1.3.5.7/p2p");  // expected_p2p_url
-}
-
-TEST_F(OmahaRequestActionTest, P2PWithoutPeer) {
-  P2PTest(true,   // initial_allow_p2p_for_downloading
-          true,   // initial_allow_p2p_for_sharing
-          false,  // omaha_disable_p2p_for_downloading
-          false,  // omaha_disable_p2p_for_sharing
-          true,   // payload_state_allow_p2p_attempt
-          true,   // expect_p2p_client_lookup
-          "",     // p2p_client_result_url
-          false,  // expected_allow_p2p_for_downloading
-          true,   // expected_allow_p2p_for_sharing
-          "");    // expected_p2p_url
-}
-
-TEST_F(OmahaRequestActionTest, P2PDownloadNotAllowed) {
-  P2PTest(false,    // initial_allow_p2p_for_downloading
-          true,     // initial_allow_p2p_for_sharing
-          false,    // omaha_disable_p2p_for_downloading
-          false,    // omaha_disable_p2p_for_sharing
-          true,     // payload_state_allow_p2p_attempt
-          false,    // expect_p2p_client_lookup
-          "unset",  // p2p_client_result_url
-          false,    // expected_allow_p2p_for_downloading
-          true,     // expected_allow_p2p_for_sharing
-          "");      // expected_p2p_url
-}
-
-TEST_F(OmahaRequestActionTest, P2PWithPeerDownloadDisabledByOmaha) {
-  P2PTest(true,     // initial_allow_p2p_for_downloading
-          true,     // initial_allow_p2p_for_sharing
-          true,     // omaha_disable_p2p_for_downloading
-          false,    // omaha_disable_p2p_for_sharing
-          true,     // payload_state_allow_p2p_attempt
-          false,    // expect_p2p_client_lookup
-          "unset",  // p2p_client_result_url
-          false,    // expected_allow_p2p_for_downloading
-          true,     // expected_allow_p2p_for_sharing
-          "");      // expected_p2p_url
-}
-
-TEST_F(OmahaRequestActionTest, P2PWithPeerSharingDisabledByOmaha) {
-  P2PTest(true,                   // initial_allow_p2p_for_downloading
-          true,                   // initial_allow_p2p_for_sharing
-          false,                  // omaha_disable_p2p_for_downloading
-          true,                   // omaha_disable_p2p_for_sharing
-          true,                   // payload_state_allow_p2p_attempt
-          true,                   // expect_p2p_client_lookup
-          "http://1.3.5.7/p2p",   // p2p_client_result_url
-          true,                   // expected_allow_p2p_for_downloading
-          false,                  // expected_allow_p2p_for_sharing
-          "http://1.3.5.7/p2p");  // expected_p2p_url
-}
-
-TEST_F(OmahaRequestActionTest, P2PWithPeerBothDisabledByOmaha) {
-  P2PTest(true,     // initial_allow_p2p_for_downloading
-          true,     // initial_allow_p2p_for_sharing
-          true,     // omaha_disable_p2p_for_downloading
-          true,     // omaha_disable_p2p_for_sharing
-          true,     // payload_state_allow_p2p_attempt
-          false,    // expect_p2p_client_lookup
-          "unset",  // p2p_client_result_url
-          false,    // expected_allow_p2p_for_downloading
-          false,    // expected_allow_p2p_for_sharing
-          "");      // expected_p2p_url
-}
-
-bool OmahaRequestActionTest::InstallDateParseHelper(const string& elapsed_days,
-                                                    OmahaResponse* response) {
-  fake_update_response_.elapsed_days = elapsed_days;
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  return TestUpdateCheck();
-}
-
-TEST_F(OmahaRequestActionTest, ParseInstallDateFromResponse) {
-  // Simulate a successful update check that happens during OOBE.  The
-  // deadline in the response is needed to force the update attempt to
-  // occur; responses without a deadline seen during OOBE will normally
-  // return ErrorCode::kNonCriticalUpdateInOOBE.
-  FakeSystemState::Get()->fake_hardware()->UnsetIsOOBEComplete();
-  fake_update_response_.deadline = "20101020";
-
-  // Check that we parse elapsed_days in the Omaha Response correctly.
-  // and that the kPrefsInstallDateDays value is written to.
-  EXPECT_FALSE(fake_prefs_->Exists(kPrefsInstallDateDays));
-  EXPECT_TRUE(InstallDateParseHelper("42", &response_));
-  EXPECT_TRUE(response_.update_exists);
-  EXPECT_EQ(42, response_.install_date_days);
-  EXPECT_TRUE(fake_prefs_->Exists(kPrefsInstallDateDays));
-  int64_t prefs_days;
-  EXPECT_TRUE(fake_prefs_->GetInt64(kPrefsInstallDateDays, &prefs_days));
-  EXPECT_EQ(prefs_days, 42);
-
-  // If there already is a value set, we shouldn't do anything.
-  EXPECT_TRUE(InstallDateParseHelper("7", &response_));
-  EXPECT_TRUE(response_.update_exists);
-  EXPECT_EQ(7, response_.install_date_days);
-  EXPECT_TRUE(fake_prefs_->GetInt64(kPrefsInstallDateDays, &prefs_days));
-  EXPECT_EQ(prefs_days, 42);
-
-  // Note that elapsed_days is not necessarily divisible by 7 so check
-  // that we round down correctly when populating kPrefsInstallDateDays.
-  EXPECT_TRUE(fake_prefs_->Delete(kPrefsInstallDateDays));
-  EXPECT_TRUE(InstallDateParseHelper("23", &response_));
-  EXPECT_TRUE(response_.update_exists);
-  EXPECT_EQ(23, response_.install_date_days);
-  EXPECT_TRUE(fake_prefs_->GetInt64(kPrefsInstallDateDays, &prefs_days));
-  EXPECT_EQ(prefs_days, 21);
-
-  // Check that we correctly handle elapsed_days not being included in
-  // the Omaha Response_.
-  EXPECT_TRUE(InstallDateParseHelper("", &response_));
-  EXPECT_TRUE(response_.update_exists);
-  EXPECT_EQ(-1, response_.install_date_days);
-}
-
-// If there is no prefs and OOBE is not complete, we should not
-// report anything to Omaha.
-TEST_F(OmahaRequestActionTest, GetInstallDateWhenNoPrefsNorOOBE) {
-  FakeSystemState::Get()->fake_hardware()->UnsetIsOOBEComplete();
-  EXPECT_EQ(OmahaRequestAction::GetInstallDate(), -1);
-  EXPECT_FALSE(fake_prefs_->Exists(kPrefsInstallDateDays));
-}
-
-// If OOBE is complete and happened on a valid date (e.g. after Jan
-// 1 2007 0:00 PST), that date should be used and written to
-// prefs. However, first try with an invalid date and check we do
-// nothing.
-TEST_F(OmahaRequestActionTest, GetInstallDateWhenOOBECompletedWithInvalidDate) {
-  Time oobe_date = Time::FromTimeT(42);  // Dec 31, 1969 16:00:42 PST.
-  FakeSystemState::Get()->fake_hardware()->SetIsOOBEComplete(oobe_date);
-  EXPECT_EQ(OmahaRequestAction::GetInstallDate(), -1);
-  EXPECT_FALSE(fake_prefs_->Exists(kPrefsInstallDateDays));
-}
-
-// Then check with a valid date. The date Jan 20, 2007 0:00 PST
-// should yield an InstallDate of 14.
-TEST_F(OmahaRequestActionTest, GetInstallDateWhenOOBECompletedWithValidDate) {
-  Time oobe_date = Time::FromTimeT(1169280000);  // Jan 20, 2007 0:00 PST.
-  FakeSystemState::Get()->fake_hardware()->SetIsOOBEComplete(oobe_date);
-  EXPECT_EQ(OmahaRequestAction::GetInstallDate(), 14);
-  EXPECT_TRUE(fake_prefs_->Exists(kPrefsInstallDateDays));
-
-  int64_t prefs_days;
-  EXPECT_TRUE(fake_prefs_->GetInt64(kPrefsInstallDateDays, &prefs_days));
-  EXPECT_EQ(prefs_days, 14);
-}
-
-// Now that we have a valid date in prefs, check that we keep using
-// that even if OOBE date reports something else. The date Jan 30,
-// 2007 0:00 PST should yield an InstallDate of 28... but since
-// there's a prefs file, we should still get 14.
-TEST_F(OmahaRequestActionTest, GetInstallDateWhenOOBECompletedDateChanges) {
-  // Set a valid date in the prefs first.
-  EXPECT_TRUE(fake_prefs_->SetInt64(kPrefsInstallDateDays, 14));
-
-  Time oobe_date = Time::FromTimeT(1170144000);  // Jan 30, 2007 0:00 PST.
-  FakeSystemState::Get()->fake_hardware()->SetIsOOBEComplete(oobe_date);
-  EXPECT_EQ(OmahaRequestAction::GetInstallDate(), 14);
-
-  int64_t prefs_days;
-  EXPECT_TRUE(fake_prefs_->GetInt64(kPrefsInstallDateDays, &prefs_days));
-  EXPECT_EQ(prefs_days, 14);
-
-  // If we delete the prefs file, we should get 28 days.
-  EXPECT_TRUE(fake_prefs_->Delete(kPrefsInstallDateDays));
-  EXPECT_EQ(OmahaRequestAction::GetInstallDate(), 28);
-  EXPECT_TRUE(fake_prefs_->GetInt64(kPrefsInstallDateDays, &prefs_days));
-  EXPECT_EQ(prefs_days, 28);
-}
-
-// Verifies that a device with no device policy, and is not a consumer
-// device sets the max kernel key version to the current version.
-// ie. the same behavior as if rollback is enabled.
-TEST_F(OmahaRequestActionTest, NoPolicyEnterpriseDevicesSetMaxRollback) {
-  FakeHardware* fake_hw = FakeSystemState::Get()->fake_hardware();
-
-  // Setup and verify some initial default values for the kernel TPM
-  // values that control verified boot and rollback.
-  const int min_kernel_version = 4;
-  fake_hw->SetMinKernelKeyVersion(min_kernel_version);
-  fake_hw->SetMaxKernelKeyRollforward(kRollforwardInfinity);
-  EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
-  EXPECT_EQ(kRollforwardInfinity, fake_hw->GetMaxKernelKeyRollforward());
-
-  EXPECT_CALL(
-      *FakeSystemState::Get()->mock_metrics_reporter(),
-      ReportKeyVersionMetrics(min_kernel_version, min_kernel_version, true))
-      .Times(1);
-
-  fake_update_response_.deadline = "20101020";
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.is_consumer_device = false;
-  tuc_params_.rollback_allowed_milestones = 3;
-
-  EXPECT_TRUE(TestUpdateCheck());
-  EXPECT_TRUE(response_.update_exists);
-
-  // Verify kernel_max_rollforward was set to the current minimum
-  // kernel key version. This has the effect of freezing roll
-  // forwards indefinitely. This will hold the rollback window
-  // open until a future change will be able to move this forward
-  // relative the configured window.
-  EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
-  EXPECT_EQ(min_kernel_version, fake_hw->GetMaxKernelKeyRollforward());
-}
-
-// Verifies that a conmsumer device with no device policy sets the
-// max kernel key version to the current version. ie. the same
-// behavior as if rollback is enabled.
-TEST_F(OmahaRequestActionTest, NoPolicyConsumerDevicesSetMaxRollback) {
-  FakeHardware* fake_hw = FakeSystemState::Get()->fake_hardware();
-
-  // Setup and verify some initial default values for the kernel TPM
-  // values that control verified boot and rollback.
-  const int min_kernel_version = 3;
-  fake_hw->SetMinKernelKeyVersion(min_kernel_version);
-  fake_hw->SetMaxKernelKeyRollforward(kRollforwardInfinity);
-  EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
-  EXPECT_EQ(kRollforwardInfinity, fake_hw->GetMaxKernelKeyRollforward());
-
-  EXPECT_CALL(
-      *FakeSystemState::Get()->mock_metrics_reporter(),
-      ReportKeyVersionMetrics(min_kernel_version, kRollforwardInfinity, true))
-      .Times(1);
-
-  fake_update_response_.deadline = "20101020";
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.is_consumer_device = true;
-  tuc_params_.rollback_allowed_milestones = 3;
-
-  EXPECT_TRUE(TestUpdateCheck());
-  EXPECT_TRUE(response_.update_exists);
-
-  // Verify that with rollback disabled that kernel_max_rollforward
-  // was set to logical infinity. This is the expected behavior for
-  // consumer devices and matches the existing behavior prior to the
-  // rollback features.
-  EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
-  EXPECT_EQ(kRollforwardInfinity, fake_hw->GetMaxKernelKeyRollforward());
-}
-
-// Verifies that a device with rollback enabled sets kernel_max_rollforward
-// in the TPM to prevent roll forward.
-TEST_F(OmahaRequestActionTest, RollbackEnabledDevicesSetMaxRollback) {
-  FakeHardware* fake_hw = FakeSystemState::Get()->fake_hardware();
-
-  // Setup and verify some initial default values for the kernel TPM
-  // values that control verified boot and rollback.
-  const int allowed_milestones = 4;
-  const int min_kernel_version = 3;
-  fake_hw->SetMinKernelKeyVersion(min_kernel_version);
-  fake_hw->SetMaxKernelKeyRollforward(kRollforwardInfinity);
-  EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
-  EXPECT_EQ(kRollforwardInfinity, fake_hw->GetMaxKernelKeyRollforward());
-
-  EXPECT_CALL(
-      *FakeSystemState::Get()->mock_metrics_reporter(),
-      ReportKeyVersionMetrics(min_kernel_version, min_kernel_version, true))
-      .Times(1);
-
-  fake_update_response_.deadline = "20101020";
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.is_consumer_device = false;
-  tuc_params_.rollback_allowed_milestones = allowed_milestones;
-  tuc_params_.is_policy_loaded = true;
-
-  EXPECT_TRUE(TestUpdateCheck());
-  EXPECT_TRUE(response_.update_exists);
-
-  // Verify that with rollback enabled that kernel_max_rollforward
-  // was set to the current minimum kernel key version. This has
-  // the effect of freezing roll forwards indefinitely. This will
-  // hold the rollback window open until a future change will
-  // be able to move this forward relative the configured window.
-  EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
-  EXPECT_EQ(min_kernel_version, fake_hw->GetMaxKernelKeyRollforward());
-}
-
-// Verifies that a device with rollback disabled sets kernel_max_rollforward
-// in the TPM to logical infinity, to allow roll forward.
-TEST_F(OmahaRequestActionTest, RollbackDisabledDevicesSetMaxRollback) {
-  FakeHardware* fake_hw = FakeSystemState::Get()->fake_hardware();
-
-  // Setup and verify some initial default values for the kernel TPM
-  // values that control verified boot and rollback.
-  const int allowed_milestones = 0;
-  const int min_kernel_version = 3;
-  fake_hw->SetMinKernelKeyVersion(min_kernel_version);
-  fake_hw->SetMaxKernelKeyRollforward(kRollforwardInfinity);
-  EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
-  EXPECT_EQ(kRollforwardInfinity, fake_hw->GetMaxKernelKeyRollforward());
-
-  EXPECT_CALL(
-      *FakeSystemState::Get()->mock_metrics_reporter(),
-      ReportKeyVersionMetrics(min_kernel_version, kRollforwardInfinity, true))
-      .Times(1);
-
-  fake_update_response_.deadline = "20101020";
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.is_consumer_device = false;
-  tuc_params_.rollback_allowed_milestones = allowed_milestones;
-  tuc_params_.is_policy_loaded = true;
-
-  EXPECT_TRUE(TestUpdateCheck());
-  EXPECT_TRUE(response_.update_exists);
-
-  // Verify that with rollback disabled that kernel_max_rollforward
-  // was set to logical infinity.
-  EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
-  EXPECT_EQ(kRollforwardInfinity, fake_hw->GetMaxKernelKeyRollforward());
-}
-
-TEST_F(OmahaRequestActionTest, RollbackResponseParsedNoEntries) {
-  fake_update_response_.rollback = true;
-  fake_update_response_.deadline = "20101020";
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.is_consumer_device = false;
-  tuc_params_.rollback_allowed_milestones = 4;
-  tuc_params_.is_policy_loaded = true;
-
-  EXPECT_TRUE(TestUpdateCheck());
-  EXPECT_TRUE(response_.update_exists);
-  EXPECT_TRUE(response_.is_rollback);
-}
-
-TEST_F(OmahaRequestActionTest, RollbackResponseValidVersionsParsed) {
-  fake_update_response_.rollback_firmware_version = "1.2";
-  fake_update_response_.rollback_kernel_version = "3.4";
-  fake_update_response_.rollback = true;
-  fake_update_response_.deadline = "20101020";
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.is_consumer_device = false;
-  tuc_params_.rollback_allowed_milestones = 4;
-  tuc_params_.is_policy_loaded = true;
-
-  EXPECT_TRUE(TestUpdateCheck());
-  EXPECT_TRUE(response_.update_exists);
-  EXPECT_TRUE(response_.is_rollback);
-  EXPECT_EQ(1, response_.rollback_key_version.firmware_key);
-  EXPECT_EQ(2, response_.rollback_key_version.firmware);
-  EXPECT_EQ(3, response_.rollback_key_version.kernel_key);
-  EXPECT_EQ(4, response_.rollback_key_version.kernel);
-}
-
-TEST_F(OmahaRequestActionTest,
-       TestUpdateFirstSeenAtPrefPersistedIfUpdateExists) {
-  FakeClock fake_clock;
-  Time now = Time::Now();
-  fake_clock.SetWallclockTime(now);
-  FakeSystemState::Get()->set_clock(&fake_clock);
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_TRUE(response_.update_exists);
-  EXPECT_TRUE(fake_prefs_->Exists(kPrefsUpdateFirstSeenAt));
-
-  int64_t stored_first_seen_at_time;
-  EXPECT_TRUE(fake_prefs_->GetInt64(kPrefsUpdateFirstSeenAt,
-                                    &stored_first_seen_at_time));
-  EXPECT_EQ(now.ToInternalValue(), stored_first_seen_at_time);
-}
-
-TEST_F(OmahaRequestActionTest,
-       TestUpdateFirstSeenAtPrefNotPersistedIfUpdateFails) {
-  FakeClock fake_clock;
-  Time now = Time::Now();
-  fake_clock.SetWallclockTime(now);
-  FakeSystemState::Get()->set_clock(&fake_clock);
-
-  tuc_params_.http_response = fake_update_response_.GetNoUpdateResponse();
-  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_FALSE(response_.update_exists);
-  EXPECT_FALSE(fake_prefs_->Exists(kPrefsUpdateFirstSeenAt));
-}
-
-TEST_F(OmahaRequestActionTest, InstallTest) {
-  request_params_.set_is_install(true);
-  request_params_.set_dlc_apps_params(
-      {{request_params_.GetDlcAppId("dlc_no_0"), {.name = "dlc_no_0"}},
-       {request_params_.GetDlcAppId("dlc_no_1"), {.name = "dlc_no_1"}}});
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  for (const auto& it : request_params_.dlc_apps_params()) {
-    EXPECT_NE(string::npos, post_str_.find("appid=\"" + it.first + "\""));
-  }
-  EXPECT_NE(string::npos,
-            post_str_.find("appid=\"" + fake_update_response_.app_id + "\""));
-
-  // Count number of updatecheck tag in response_.
-  int updatecheck_count = 0;
-  size_t pos = 0;
-  while ((pos = post_str_.find("<updatecheck", pos)) != string::npos) {
-    updatecheck_count++;
-    pos++;
-  }
-  EXPECT_EQ(request_params_.dlc_apps_params().size(), updatecheck_count);
-  EXPECT_TRUE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, InstallMissingPlatformVersionTest) {
-  fake_update_response_.multi_app_skip_updatecheck = true;
-  fake_update_response_.multi_app_no_update = false;
-  request_params_.set_is_install(true);
-  request_params_.set_dlc_apps_params(
-      {{request_params_.GetDlcAppId("dlc_no_0"), {.name = "dlc_no_0"}},
-       {request_params_.GetDlcAppId("dlc_no_1"), {.name = "dlc_no_1"}}});
-  request_params_.set_app_id(fake_update_response_.app_id_skip_updatecheck);
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_TRUE(response_.update_exists);
-  EXPECT_EQ(fake_update_response_.version, response_.version);
-}
-
-TEST_F(OmahaRequestActionTest, UpdateWithDlcTest) {
-  request_params_.set_dlc_apps_params(
-      {{request_params_.GetDlcAppId(kDlcId1), {.name = kDlcId1}}});
-  fake_update_response_.dlc_app_update = true;
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  EXPECT_CALL(mock_excluder_, IsExcluded(_)).WillRepeatedly(Return(false));
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_EQ(response_.packages.size(), 2u);
-  // Two candidate URLs.
-  EXPECT_EQ(response_.packages[1].payload_urls.size(), 2u);
-  EXPECT_TRUE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, UpdateWithPartiallyExcludedDlcTest) {
-  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.
-  EXPECT_CALL(mock_excluder_, IsExcluded(_))
-      .WillOnce(Return(true))
-      .WillOnce(Return(false));
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_EQ(response_.packages.size(), 2u);
-  // 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) {
-  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.
-  EXPECT_CALL(mock_excluder_, IsExcluded(_))
-      .WillOnce(Return(true))
-      .WillOnce(Return(true));
-  ASSERT_TRUE(TestUpdateCheck());
-
-  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) {
-  request_params_.set_dlc_apps_params(
-      {{request_params_.GetDlcAppId(kDlcId2), {.name = kDlcId2}}});
-  fake_update_response_.dlc_app_no_update = true;
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  EXPECT_CALL(mock_excluder_, IsExcluded(_)).WillRepeatedly(Return(false));
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_TRUE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, UpdateWithDlcAndDeprecatedDlcTest) {
-  request_params_.set_dlc_apps_params(
-      {{request_params_.GetDlcAppId(kDlcId1), {.name = kDlcId1}},
-       {request_params_.GetDlcAppId(kDlcId2), {.name = kDlcId2}}});
-  fake_update_response_.dlc_app_update = true;
-  fake_update_response_.dlc_app_no_update = true;
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  EXPECT_CALL(mock_excluder_, IsExcluded(_)).WillRepeatedly(Return(false));
-  ASSERT_TRUE(TestUpdateCheck());
-
-  EXPECT_TRUE(response_.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, PastRollbackVersionsNoEntries) {
-  fake_update_response_.rollback = true;
-  fake_update_response_.rollback_allowed_milestones = 4;
-  request_params_.set_rollback_allowed_milestones(4);
-  fake_update_response_.deadline = "20101020";
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.is_consumer_device = false;
-  tuc_params_.rollback_allowed_milestones = 4;
-  tuc_params_.is_policy_loaded = true;
-
-  EXPECT_TRUE(TestUpdateCheck());
-  EXPECT_TRUE(response_.update_exists);
-  EXPECT_TRUE(response_.is_rollback);
-  EXPECT_EQ(std::numeric_limits<uint16_t>::max(),
-            response_.past_rollback_key_version.firmware_key);
-  EXPECT_EQ(std::numeric_limits<uint16_t>::max(),
-            response_.past_rollback_key_version.firmware);
-  EXPECT_EQ(std::numeric_limits<uint16_t>::max(),
-            response_.past_rollback_key_version.kernel_key);
-  EXPECT_EQ(std::numeric_limits<uint16_t>::max(),
-            response_.past_rollback_key_version.kernel);
-}
-
-TEST_F(OmahaRequestActionTest, PastRollbackVersionsValidEntries) {
-  request_params_.set_rollback_allowed_milestones(4);
-  fake_update_response_.rollback = true;
-  fake_update_response_.rollback_allowed_milestones = 4;
-  fake_update_response_.rollback_firmware_version = "4.3";
-  fake_update_response_.rollback_kernel_version = "2.1";
-  fake_update_response_.past_rollback_key_version =
-      std::make_pair("16.15", "14.13");
-  fake_update_response_.deadline = "20101020";
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.is_consumer_device = false;
-  tuc_params_.rollback_allowed_milestones = 4;
-  tuc_params_.is_policy_loaded = true;
-
-  EXPECT_TRUE(TestUpdateCheck());
-  EXPECT_TRUE(response_.update_exists);
-  EXPECT_TRUE(response_.is_rollback);
-  EXPECT_EQ(16, response_.past_rollback_key_version.firmware_key);
-  EXPECT_EQ(15, response_.past_rollback_key_version.firmware);
-  EXPECT_EQ(14, response_.past_rollback_key_version.kernel_key);
-  EXPECT_EQ(13, response_.past_rollback_key_version.kernel);
-}
-
-TEST_F(OmahaRequestActionTest, MismatchNumberOfVersions) {
-  fake_update_response_.rollback = true;
-  fake_update_response_.rollback_allowed_milestones = 2;
-  fake_update_response_.deadline = "20101020";
-  request_params_.set_rollback_allowed_milestones(4);
-
-  // Since |request_params_.rollback_allowed_milestones| is 4 but the response
-  // is constructed with |fake_update_response_.rollback_allowed_milestones| set
-  // to 2, OmahaRequestAction will look for the key values of N-4 version but
-  // only the N-2 version will exist.
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  tuc_params_.is_consumer_device = false;
-  tuc_params_.rollback_allowed_milestones = 2;
-  tuc_params_.is_policy_loaded = true;
-
-  EXPECT_TRUE(TestUpdateCheck());
-  EXPECT_TRUE(response_.update_exists);
-  EXPECT_TRUE(response_.is_rollback);
-  EXPECT_EQ(std::numeric_limits<uint16_t>::max(),
-            response_.past_rollback_key_version.firmware_key);
-  EXPECT_EQ(std::numeric_limits<uint16_t>::max(),
-            response_.past_rollback_key_version.firmware);
-  EXPECT_EQ(std::numeric_limits<uint16_t>::max(),
-            response_.past_rollback_key_version.kernel_key);
-  EXPECT_EQ(std::numeric_limits<uint16_t>::max(),
-            response_.past_rollback_key_version.kernel);
-}
-
-TEST_F(OmahaRequestActionTest, IncludeRequisitionTest) {
-  request_params_.set_device_requisition("remora");
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  ASSERT_TRUE(TestUpdateCheck());
-  EXPECT_NE(string::npos, post_str_.find("requisition=\"remora\""));
-}
-
-TEST_F(OmahaRequestActionTest, NoIncludeRequisitionTest) {
-  request_params_.set_device_requisition("");
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-  ASSERT_TRUE(TestUpdateCheck());
-  EXPECT_EQ(string::npos, post_str_.find("requisition"));
-}
-
-TEST_F(OmahaRequestActionTest, PersistEolDateTest) {
-  tuc_params_.http_response =
-      "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
-      "protocol=\"3.0\"><app appid=\"test-app-id\" status=\"ok\">"
-      "<ping status=\"ok\"/><updatecheck status=\"noupdate\" "
-      "_eol_date=\"200\" _foo=\"bar\"/></app></response>";
-  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  string eol_date;
-  EXPECT_TRUE(FakeSystemState::Get()->prefs()->GetString(kPrefsOmahaEolDate,
-                                                         &eol_date));
-  EXPECT_EQ("200", eol_date);
-}
-
-TEST_F(OmahaRequestActionTest, PersistEolMissingDateTest) {
-  tuc_params_.http_response =
-      "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
-      "protocol=\"3.0\"><app appid=\"test-app-id\" status=\"ok\">"
-      "<ping status=\"ok\"/><updatecheck status=\"noupdate\" "
-      "_foo=\"bar\"/></app></response>";
-  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  const string kDate = "123";
-  FakeSystemState::Get()->prefs()->SetString(kPrefsOmahaEolDate, kDate);
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  string eol_date;
-  EXPECT_TRUE(FakeSystemState::Get()->prefs()->GetString(kPrefsOmahaEolDate,
-                                                         &eol_date));
-  EXPECT_EQ(kDate, eol_date);
-}
-
-TEST_F(OmahaRequestActionTest, PersistEolBadDateTest) {
-  tuc_params_.http_response =
-      "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
-      "protocol=\"3.0\"><app appid=\"test-app-id\" status=\"ok\">"
-      "<ping status=\"ok\"/><updatecheck status=\"noupdate\" "
-      "_eol_date=\"bad\" foo=\"bar\"/></app></response>";
-  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
-  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  string eol_date;
-  EXPECT_TRUE(FakeSystemState::Get()->prefs()->GetString(kPrefsOmahaEolDate,
-                                                         &eol_date));
-  EXPECT_EQ(kEolDateInvalid, StringToEolDate(eol_date));
-}
-
-TEST_F(OmahaRequestActionDlcPingTest, StorePingReplyNoPing) {
-  OmahaRequestParams::AppParams app_param = {.name = dlc_id_};
-  request_params_.set_dlc_apps_params(
-      {{request_params_.GetDlcAppId(dlc_id_), app_param}});
-
-  ASSERT_TRUE(TestUpdateCheck());
-
-  int64_t temp_int;
-  // If there was no ping, the metadata files shouldn't exist yet.
-  EXPECT_FALSE(fake_prefs_->GetInt64(active_key_, &temp_int));
-  EXPECT_FALSE(fake_prefs_->GetInt64(last_active_key_, &temp_int));
-  EXPECT_FALSE(fake_prefs_->GetInt64(last_rollcall_key_, &temp_int));
-}
-
-TEST_F(OmahaRequestActionDlcPingTest, StorePingReplyActiveTest) {
-  // Create Active value
-  fake_prefs_->SetInt64(active_key_, 0);
-
-  OmahaRequestParams::AppParams app_param = {
-      .active_counting_type = OmahaRequestParams::kDateBased,
-      .name = dlc_id_,
-      .ping_active = 1,
-      .send_ping = true};
-  request_params_.set_dlc_apps_params(
-      {{request_params_.GetDlcAppId(dlc_id_), app_param}});
-
-  int64_t temp_int;
-  string temp_str;
-  ASSERT_TRUE(TestUpdateCheck());
-  EXPECT_TRUE(fake_prefs_->GetInt64(active_key_, &temp_int));
-  EXPECT_EQ(temp_int, kPingInactiveValue);
-  EXPECT_TRUE(fake_prefs_->GetString(last_active_key_, &temp_str));
-  EXPECT_EQ(temp_str, "4763");
-  EXPECT_TRUE(fake_prefs_->GetString(last_rollcall_key_, &temp_str));
-  EXPECT_EQ(temp_str, "4763");
-}
-
-TEST_F(OmahaRequestActionDlcPingTest, StorePingReplyInactiveTest) {
-  // Create Active value
-  fake_prefs_->SetInt64(active_key_, 0);
-
-  OmahaRequestParams::AppParams app_param = {
-      .active_counting_type = OmahaRequestParams::kDateBased,
-      .name = dlc_id_,
-      .ping_active = 0,
-      .send_ping = true};
-  request_params_.set_dlc_apps_params(
-      {{request_params_.GetDlcAppId(dlc_id_), app_param}});
-
-  // Set the previous active value to an older value than 4763.
-  fake_prefs_->SetString(last_active_key_, "555");
-
-  int64_t temp_int;
-  ASSERT_TRUE(TestUpdateCheck());
-  EXPECT_TRUE(fake_prefs_->GetInt64(active_key_, &temp_int));
-  EXPECT_EQ(temp_int, kPingInactiveValue);
-  string temp_str;
-  EXPECT_TRUE(fake_prefs_->GetString(last_active_key_, &temp_str));
-  EXPECT_EQ(temp_str, "555");
-  EXPECT_TRUE(fake_prefs_->GetString(last_rollcall_key_, &temp_str));
-  EXPECT_EQ(temp_str, "4763");
-}
-
-TEST_F(OmahaRequestActionTest, OmahaResponseUpdateCanExcludeCheck) {
-  request_params_.set_dlc_apps_params(
-      {{request_params_.GetDlcAppId(kDlcId1), {.name = kDlcId1}}});
-  fake_update_response_.dlc_app_update = true;
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  EXPECT_CALL(mock_excluder_, IsExcluded(_)).WillRepeatedly(Return(false));
-  ASSERT_TRUE(TestUpdateCheck());
-  ASSERT_TRUE(delegate_.omaha_response_);
-  const auto& packages = delegate_.omaha_response_->packages;
-  ASSERT_EQ(packages.size(), 2);
-
-  EXPECT_FALSE(packages[0].can_exclude);
-  EXPECT_TRUE(packages[1].can_exclude);
-}
-
-TEST_F(OmahaRequestActionTest, OmahaResponseInstallCannotExcludeCheck) {
-  request_params_.set_is_install(true);
-  request_params_.set_dlc_apps_params(
-      {{request_params_.GetDlcAppId(kDlcId1), {.name = kDlcId1}}});
-  fake_update_response_.dlc_app_update = true;
-  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
-
-  EXPECT_CALL(mock_excluder_, IsExcluded(_)).WillRepeatedly(Return(false));
-  ASSERT_TRUE(TestUpdateCheck());
-  ASSERT_TRUE(delegate_.omaha_response_);
-  const auto& packages = delegate_.omaha_response_->packages;
-  ASSERT_EQ(packages.size(), 2);
-
-  EXPECT_FALSE(packages[0].can_exclude);
-  EXPECT_FALSE(packages[1].can_exclude);
-}
-
-}  // namespace chromeos_update_engine
diff --git a/cros/omaha_request_builder_xml.cc b/cros/omaha_request_builder_xml.cc
deleted file mode 100644
index 6cd9ab8..0000000
--- a/cros/omaha_request_builder_xml.cc
+++ /dev/null
@@ -1,462 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include "update_engine/cros/omaha_request_builder_xml.h"
-
-#include <inttypes.h>
-
-#include <string>
-
-#include <base/guid.h>
-#include <base/logging.h>
-#include <base/strings/string_number_conversions.h>
-#include <base/strings/string_util.h>
-#include <base/strings/stringprintf.h>
-#include <base/time/time.h>
-
-#include "update_engine/common/constants.h"
-#include "update_engine/common/system_state.h"
-#include "update_engine/common/utils.h"
-#include "update_engine/cros/omaha_request_params.h"
-
-using std::string;
-
-namespace chromeos_update_engine {
-
-const char kNoVersion[] = "0.0.0.0";
-const int kPingNeverPinged = -1;
-const int kPingUnknownValue = -2;
-const int kPingActiveValue = 1;
-const int kPingInactiveValue = 0;
-
-bool XmlEncode(const string& input, string* output) {
-  if (std::find_if(input.begin(), input.end(), [](const char c) {
-        return c & 0x80;
-      }) != input.end()) {
-    LOG(WARNING) << "Invalid ASCII-7 string passed to the XML encoder:";
-    utils::HexDumpString(input);
-    return false;
-  }
-  output->clear();
-  // We need at least input.size() space in the output, but the code below will
-  // handle it if we need more.
-  output->reserve(input.size());
-  for (char c : input) {
-    switch (c) {
-      case '\"':
-        output->append("&quot;");
-        break;
-      case '\'':
-        output->append("&apos;");
-        break;
-      case '&':
-        output->append("&amp;");
-        break;
-      case '<':
-        output->append("&lt;");
-        break;
-      case '>':
-        output->append("&gt;");
-        break;
-      default:
-        output->push_back(c);
-    }
-  }
-  return true;
-}
-
-string XmlEncodeWithDefault(const string& input, const string& default_value) {
-  string output;
-  if (XmlEncode(input, &output))
-    return output;
-  return default_value;
-}
-
-string OmahaRequestBuilderXml::GetPing() const {
-  // Returns an XML ping element attribute assignment with attribute
-  // |name| and value |ping_days| if |ping_days| has a value that needs
-  // to be sent, or an empty string otherwise.
-  auto GetPingAttribute = [](const char* name, int ping_days) -> string {
-    if (ping_days > 0 || ping_days == kPingNeverPinged)
-      return base::StringPrintf(" %s=\"%d\"", name, ping_days);
-    return "";
-  };
-
-  string ping_active = GetPingAttribute("a", ping_active_days_);
-  string ping_roll_call = GetPingAttribute("r", ping_roll_call_days_);
-  if (!ping_active.empty() || !ping_roll_call.empty()) {
-    return base::StringPrintf("        <ping active=\"1\"%s%s></ping>\n",
-                              ping_active.c_str(),
-                              ping_roll_call.c_str());
-  }
-  return "";
-}
-
-string OmahaRequestBuilderXml::GetPingDateBased(
-    const OmahaRequestParams::AppParams& app_params) const {
-  if (!app_params.send_ping)
-    return "";
-  string ping_active = "";
-  string ping_ad = "";
-  if (app_params.ping_active == kPingActiveValue) {
-    ping_active =
-        base::StringPrintf(" active=\"%" PRId64 "\"", app_params.ping_active);
-    ping_ad = base::StringPrintf(" ad=\"%" PRId64 "\"",
-                                 app_params.ping_date_last_active);
-  }
-
-  string ping_rd = base::StringPrintf(" rd=\"%" PRId64 "\"",
-                                      app_params.ping_date_last_rollcall);
-
-  return base::StringPrintf("        <ping%s%s%s></ping>\n",
-                            ping_active.c_str(),
-                            ping_ad.c_str(),
-                            ping_rd.c_str());
-}
-
-string OmahaRequestBuilderXml::GetAppBody(const OmahaAppData& app_data) const {
-  string app_body;
-  if (event_ == nullptr) {
-    if (app_data.app_params.send_ping) {
-      switch (app_data.app_params.active_counting_type) {
-        case OmahaRequestParams::kDayBased:
-          app_body = GetPing();
-          break;
-        case OmahaRequestParams::kDateBased:
-          app_body = GetPingDateBased(app_data.app_params);
-          break;
-        default:
-          NOTREACHED();
-      }
-    }
-    if (!ping_only_) {
-      if (!app_data.skip_update) {
-        const auto* params = SystemState::Get()->request_params();
-        app_body += "        <updatecheck";
-        if (!params->target_version_prefix().empty()) {
-          app_body += base::StringPrintf(
-              " targetversionprefix=\"%s\"",
-              XmlEncodeWithDefault(params->target_version_prefix()).c_str());
-          // Rollback requires target_version_prefix set.
-          if (params->rollback_allowed()) {
-            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";
-      }
-
-      // If this is the first update check after a reboot following a previous
-      // update, generate an event containing the previous version number. If
-      // the previous version preference file doesn't exist the event is still
-      // generated with a previous version of 0.0.0.0 -- this is relevant for
-      // older clients or new installs. The previous version event is not sent
-      // for ping-only requests because they come before the client has
-      // rebooted. The previous version event is also not sent if it was already
-      // sent for this new version with a previous updatecheck.
-      auto* prefs = SystemState::Get()->prefs();
-      string prev_version;
-      if (!prefs->GetString(kPrefsPreviousVersion, &prev_version)) {
-        prev_version = kNoVersion;
-      }
-      // We only store a non-empty previous version value after a successful
-      // update in the previous boot. After reporting it back to the server,
-      // we clear the previous version value so it doesn't get reported again.
-      if (!prev_version.empty()) {
-        app_body += base::StringPrintf(
-            "        <event eventtype=\"%d\" eventresult=\"%d\" "
-            "previousversion=\"%s\"></event>\n",
-            OmahaEvent::kTypeRebootedAfterUpdate,
-            OmahaEvent::kResultSuccess,
-            XmlEncodeWithDefault(prev_version, kNoVersion).c_str());
-        LOG_IF(WARNING, !prefs->SetString(kPrefsPreviousVersion, ""))
-            << "Unable to reset the previous version.";
-      }
-    }
-  } 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) {
-      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,
-        error_code.c_str());
-  }
-
-  return app_body;
-}
-
-string OmahaRequestBuilderXml::GetCohortArg(
-    const string& arg_name,
-    const string& prefs_key,
-    const string& override_value) const {
-  string cohort_value;
-  if (!override_value.empty()) {
-    // |override_value| take precedence over pref value.
-    cohort_value = override_value;
-  } else {
-    // There's nothing wrong with not having a given cohort setting, so we check
-    // existence first to avoid the warning log message.
-    const auto* prefs = SystemState::Get()->prefs();
-    if (!prefs->Exists(prefs_key))
-      return "";
-    if (!prefs->GetString(prefs_key, &cohort_value) || cohort_value.empty())
-      return "";
-  }
-  // This is a validity check to avoid sending a huge XML file back to Ohama due
-  // to a compromised stateful partition making the update check fail in low
-  // network environments envent after a reboot.
-  if (cohort_value.size() > 1024) {
-    LOG(WARNING) << "The omaha cohort setting " << arg_name
-                 << " has a too big value, which must be an error or an "
-                    "attacker trying to inhibit updates.";
-    return "";
-  }
-
-  string escaped_xml_value;
-  if (!XmlEncode(cohort_value, &escaped_xml_value)) {
-    LOG(WARNING) << "The omaha cohort setting " << arg_name
-                 << " is ASCII-7 invalid, ignoring it.";
-    return "";
-  }
-
-  return base::StringPrintf(
-      "%s=\"%s\" ", arg_name.c_str(), escaped_xml_value.c_str());
-}
-
-bool IsValidComponentID(const string& id) {
-  for (char c : id) {
-    if (!isalnum(c) && c != '-' && c != '_' && c != '.')
-      return false;
-  }
-  return true;
-}
-
-string OmahaRequestBuilderXml::GetApp(const OmahaAppData& app_data) const {
-  string app_body = GetAppBody(app_data);
-  string app_versions;
-  const auto* params = SystemState::Get()->request_params();
-
-  // If we are downgrading to a more stable channel and we are allowed to do
-  // powerwash, then pass 0.0.0.0 as the version. This is needed to get the
-  // highest-versioned payload on the destination channel.
-  if (params->ShouldPowerwash()) {
-    LOG(INFO) << "Passing OS version as 0.0.0.0 as we are set to powerwash "
-              << "on downgrading to the version in the more stable channel";
-    app_versions = "version=\"" + string(kNoVersion) + "\" from_version=\"" +
-                   XmlEncodeWithDefault(app_data.version, kNoVersion) + "\" ";
-  } else {
-    app_versions = "version=\"" +
-                   XmlEncodeWithDefault(app_data.version, kNoVersion) + "\" ";
-  }
-
-  string download_channel = params->download_channel();
-  string app_channels =
-      "track=\"" + XmlEncodeWithDefault(download_channel) + "\" ";
-  if (params->current_channel() != download_channel) {
-    app_channels += "from_track=\"" +
-                    XmlEncodeWithDefault(params->current_channel()) + "\" ";
-  }
-
-  string delta_okay_str =
-      params->delta_okay() && !params->is_install() ? "true" : "false";
-
-  // If install_date_days is not set (e.g. its value is -1 ), don't
-  // include the attribute.
-  string install_date_in_days_str = "";
-  if (install_date_in_days_ >= 0) {
-    install_date_in_days_str =
-        base::StringPrintf("installdate=\"%d\" ", install_date_in_days_);
-  }
-
-  string app_cohort_args;
-  string cohort_key = kPrefsOmahaCohort;
-  string cohortname_key = kPrefsOmahaCohortName;
-  string cohorthint_key = kPrefsOmahaCohortHint;
-
-  // Override the cohort keys for DLC App IDs.
-  const auto& dlc_apps_params = params->dlc_apps_params();
-  auto itr = dlc_apps_params.find(app_data.id);
-  if (itr != dlc_apps_params.end()) {
-    auto dlc_id = itr->second.name;
-    const auto* prefs = SystemState::Get()->prefs();
-    cohort_key =
-        prefs->CreateSubKey({kDlcPrefsSubDir, dlc_id, kPrefsOmahaCohort});
-    cohortname_key =
-        prefs->CreateSubKey({kDlcPrefsSubDir, dlc_id, kPrefsOmahaCohortName});
-    cohorthint_key =
-        prefs->CreateSubKey({kDlcPrefsSubDir, dlc_id, kPrefsOmahaCohortHint});
-  }
-
-  app_cohort_args += GetCohortArg("cohort", cohort_key);
-  app_cohort_args += GetCohortArg("cohortname", cohortname_key);
-  // Policy provided value overrides pref.
-  app_cohort_args +=
-      GetCohortArg("cohorthint",
-                   cohorthint_key,
-                   params->autoupdate_token() /* override_value */);
-
-  string fingerprint_arg;
-  if (!params->os_build_fingerprint().empty()) {
-    fingerprint_arg = "fingerprint=\"" +
-                      XmlEncodeWithDefault(params->os_build_fingerprint()) +
-                      "\" ";
-  }
-
-  string buildtype_arg;
-  if (!params->os_build_type().empty()) {
-    buildtype_arg = "os_build_type=\"" +
-                    XmlEncodeWithDefault(params->os_build_type()) + "\" ";
-  }
-
-  string product_components_args;
-  if (!params->ShouldPowerwash() && !app_data.product_components.empty()) {
-    brillo::KeyValueStore store;
-    if (store.LoadFromString(app_data.product_components)) {
-      for (const string& key : store.GetKeys()) {
-        if (!IsValidComponentID(key)) {
-          LOG(ERROR) << "Invalid component id: " << key;
-          continue;
-        }
-        string version;
-        if (!store.GetString(key, &version)) {
-          LOG(ERROR) << "Failed to get version for " << key
-                     << " in product_components.";
-          continue;
-        }
-        product_components_args +=
-            base::StringPrintf("_%s.version=\"%s\" ",
-                               key.c_str(),
-                               XmlEncodeWithDefault(version).c_str());
-      }
-    } else {
-      LOG(ERROR) << "Failed to parse product_components:\n"
-                 << app_data.product_components;
-    }
-  }
-
-  string requisition_arg;
-  if (!params->device_requisition().empty()) {
-    requisition_arg = "requisition=\"" +
-                      XmlEncodeWithDefault(params->device_requisition()) +
-                      "\" ";
-  }
-
-  // clang-format off
-  string app_xml = "    <app "
-      "appid=\"" + XmlEncodeWithDefault(app_data.id) + "\" " +
-      app_cohort_args +
-      app_versions +
-      app_channels +
-      product_components_args +
-      fingerprint_arg +
-      buildtype_arg +
-      "board=\"" + XmlEncodeWithDefault(params->os_board()) + "\" " +
-      "hardware_class=\"" + XmlEncodeWithDefault(params->hwid()) + "\" " +
-      "delta_okay=\"" + delta_okay_str + "\" " +
-      install_date_in_days_str +
-
-      // DLC excluded for installs and updates.
-      (app_data.is_dlc ? "" :
-      "lang=\"" + XmlEncodeWithDefault(params->app_lang(), "en-US") + "\" " +
-      requisition_arg) +
-
-      ">\n" +
-         app_body +
-      "    </app>\n";
-  // clang-format on
-  return app_xml;
-}
-
-string OmahaRequestBuilderXml::GetOs() const {
-  const auto* params = SystemState::Get()->request_params();
-  string os_xml =
-      "    <os "
-      "version=\"" +
-      XmlEncodeWithDefault(params->os_version()) + "\" " + "platform=\"" +
-      XmlEncodeWithDefault(params->os_platform()) + "\" " + "sp=\"" +
-      XmlEncodeWithDefault(params->os_sp()) +
-      "\">"
-      "</os>\n";
-  return os_xml;
-}
-
-string OmahaRequestBuilderXml::GetRequest() const {
-  const auto* params = SystemState::Get()->request_params();
-  string os_xml = GetOs();
-  string app_xml = GetApps();
-
-  string request_xml = base::StringPrintf(
-      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-      "<request requestid=\"%s\" sessionid=\"%s\""
-      " protocol=\"3.0\" updater=\"%s\" updaterversion=\"%s\""
-      " installsource=\"%s\" ismachine=\"1\">\n%s%s</request>\n",
-      base::GenerateGUID().c_str() /* requestid */,
-      session_id_.c_str(),
-      constants::kOmahaUpdaterID,
-      kOmahaUpdaterVersion,
-      params->interactive() ? "ondemandupdate" : "scheduler",
-      os_xml.c_str(),
-      app_xml.c_str());
-
-  return request_xml;
-}
-
-string OmahaRequestBuilderXml::GetApps() const {
-  const auto* params = SystemState::Get()->request_params();
-  string app_xml = "";
-  OmahaAppData product_app = {
-      .id = params->GetAppId(),
-      .version = params->app_version(),
-      .product_components = params->product_components(),
-      // Skips updatecheck for platform app in case of an install operation.
-      .skip_update = params->is_install(),
-      .is_dlc = false,
-
-      .app_params = {.active_counting_type = OmahaRequestParams::kDayBased,
-                     .send_ping = include_ping_}};
-  app_xml += GetApp(product_app);
-  for (const auto& it : params->dlc_apps_params()) {
-    OmahaAppData dlc_app_data = {
-        .id = it.first,
-        .version = params->is_install() ? kNoVersion : params->app_version(),
-        .skip_update = false,
-        .is_dlc = true,
-        .app_params = it.second};
-    app_xml += GetApp(dlc_app_data);
-  }
-  return app_xml;
-}
-
-}  // namespace chromeos_update_engine
diff --git a/cros/omaha_request_builder_xml.h b/cros/omaha_request_builder_xml.h
deleted file mode 100644
index 7c246f7..0000000
--- a/cros/omaha_request_builder_xml.h
+++ /dev/null
@@ -1,192 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef UPDATE_ENGINE_CROS_OMAHA_REQUEST_BUILDER_XML_H_
-#define UPDATE_ENGINE_CROS_OMAHA_REQUEST_BUILDER_XML_H_
-
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <gtest/gtest_prod.h>  // for FRIEND_TEST
-
-#include <brillo/secure_blob.h>
-#include <curl/curl.h>
-
-#include "update_engine/common/action.h"
-#include "update_engine/common/http_fetcher.h"
-#include "update_engine/cros/omaha_request_params.h"
-#include "update_engine/cros/omaha_response.h"
-
-namespace chromeos_update_engine {
-
-extern const char kNoVersion[];
-extern const int kPingNeverPinged;
-extern const int kPingUnknownValue;
-extern const int kPingActiveValue;
-extern const int kPingInactiveValue;
-
-// This struct encapsulates the Omaha event information. For a
-// complete list of defined event types and results, see
-// http://code.google.com/p/omaha/wiki/ServerProtocol#event
-struct OmahaEvent {
-  // The Type values correspond to EVENT_TYPE values of Omaha.
-  enum Type {
-    kTypeUnknown = 0,
-    kTypeDownloadComplete = 1,
-    kTypeInstallComplete = 2,
-    kTypeUpdateComplete = 3,
-    kTypeUpdateDownloadStarted = 13,
-    kTypeUpdateDownloadFinished = 14,
-    // Chromium OS reserved type sent after the first reboot following an update
-    // completed.
-    kTypeRebootedAfterUpdate = 54,
-  };
-
-  // The Result values correspond to EVENT_RESULT values of Omaha.
-  enum Result {
-    kResultError = 0,
-    kResultSuccess = 1,
-    kResultUpdateDeferred = 9,  // When we ignore/defer updates due to policy.
-  };
-
-  OmahaEvent()
-      : type(kTypeUnknown),
-        result(kResultError),
-        error_code(ErrorCode::kError) {}
-  explicit OmahaEvent(Type in_type)
-      : type(in_type),
-        result(kResultSuccess),
-        error_code(ErrorCode::kSuccess) {}
-  OmahaEvent(Type in_type, Result in_result, ErrorCode in_error_code)
-      : type(in_type), result(in_result), error_code(in_error_code) {}
-
-  Type type;
-  Result result;
-  ErrorCode error_code;
-};
-
-struct OmahaAppData {
-  std::string id;
-  std::string version;
-  std::string product_components;
-  bool skip_update;
-  bool is_dlc;
-  OmahaRequestParams::AppParams app_params;
-};
-
-// Encodes XML entities in a given string. Input must be ASCII-7 valid. If
-// the input is invalid, the default value is used instead.
-std::string XmlEncodeWithDefault(const std::string& input,
-                                 const std::string& default_value = "");
-
-// Escapes text so it can be included as character data and attribute
-// values. The |input| string must be valid ASCII-7, no UTF-8 supported.
-// Returns whether the |input| was valid and escaped properly in |output|.
-bool XmlEncode(const std::string& input, std::string* output);
-
-// Returns a boolean based on examining each character on whether it's a valid
-// component (meaning all characters are an alphanum excluding '-', '_', '.').
-bool IsValidComponentID(const std::string& id);
-
-class OmahaRequestBuilder {
- public:
-  OmahaRequestBuilder() = default;
-  virtual ~OmahaRequestBuilder() = default;
-
-  virtual std::string GetRequest() const = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(OmahaRequestBuilder);
-};
-
-class OmahaRequestBuilderXml : OmahaRequestBuilder {
- public:
-  OmahaRequestBuilderXml(const OmahaEvent* event,
-                         bool ping_only,
-                         bool include_ping,
-                         int ping_active_days,
-                         int ping_roll_call_days,
-                         int install_date_in_days,
-                         const std::string& session_id)
-      : event_(event),
-        ping_only_(ping_only),
-        include_ping_(include_ping),
-        ping_active_days_(ping_active_days),
-        ping_roll_call_days_(ping_roll_call_days),
-        install_date_in_days_(install_date_in_days),
-        session_id_(session_id) {}
-
-  ~OmahaRequestBuilderXml() override = default;
-
-  // Returns an XML that corresponds to the entire Omaha request.
-  std::string GetRequest() const override;
-
- private:
-  FRIEND_TEST(OmahaRequestBuilderXmlTest, PlatformGetAppTest);
-  FRIEND_TEST(OmahaRequestBuilderXmlTest, DlcGetAppTest);
-
-  // Returns an XML that corresponds to the entire <os> node of the Omaha
-  // request based on the member variables.
-  std::string GetOs() const;
-
-  // Returns an XML that corresponds to all <app> nodes of the Omaha
-  // request based on the given parameters.
-  std::string GetApps() const;
-
-  // Returns an XML that corresponds to the single <app> node of the Omaha
-  // request based on the given parameters.
-  std::string GetApp(const OmahaAppData& app_data) const;
-
-  // Returns an XML that goes into the body of the <app> element of the Omaha
-  // request based on the given parameters.
-  std::string GetAppBody(const OmahaAppData& app_data) const;
-
-  // Returns the cohort* argument to include in the <app> tag for the passed
-  // |arg_name| and |prefs_key|, if any. The return value is suitable to
-  // concatenate to the list of arguments and includes a space at the end.
-  std::string GetCohortArg(const std::string& arg_name,
-                           const std::string& prefs_key,
-                           const std::string& override_value = "") const;
-
-  // Returns an XML ping element if any of the elapsed days need to be
-  // sent, or an empty string otherwise.
-  std::string GetPing() const;
-
-  // Returns an XML ping element if any of the elapsed days need to be
-  // sent, or an empty string otherwise.
-  std::string GetPingDateBased(
-      const OmahaRequestParams::AppParams& app_params) const;
-
-  const OmahaEvent* event_;
-  bool ping_only_;
-  bool include_ping_;
-  int ping_active_days_;
-  int ping_roll_call_days_;
-  int install_date_in_days_;
-  std::string session_id_;
-
-  DISALLOW_COPY_AND_ASSIGN(OmahaRequestBuilderXml);
-};
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_CROS_OMAHA_REQUEST_BUILDER_XML_H_
diff --git a/cros/omaha_request_builder_xml_unittest.cc b/cros/omaha_request_builder_xml_unittest.cc
deleted file mode 100644
index 76a7241..0000000
--- a/cros/omaha_request_builder_xml_unittest.cc
+++ /dev/null
@@ -1,421 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include "update_engine/cros/omaha_request_builder_xml.h"
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <base/guid.h>
-#include <base/strings/stringprintf.h>
-#include <gtest/gtest.h>
-
-#include "update_engine/cros/fake_system_state.h"
-
-using std::pair;
-using std::string;
-using std::vector;
-using testing::_;
-using testing::DoAll;
-using testing::Return;
-using testing::SetArgPointee;
-
-namespace chromeos_update_engine {
-
-namespace {
-// Helper to find key and extract value from the given string |xml|, instead
-// of using a full parser. The attribute key will be followed by "=\"" as xml
-// attribute values must be within double quotes (not single quotes).
-static string FindAttributeKeyValueInXml(const string& xml,
-                                         const string& key,
-                                         const size_t val_size) {
-  string key_with_quotes = key + "=\"";
-  const size_t val_start_pos = xml.find(key);
-  if (val_start_pos == string::npos)
-    return "";
-  return xml.substr(val_start_pos + key_with_quotes.size(), val_size);
-}
-// Helper to find the count of substring in a string.
-static size_t CountSubstringInString(const string& str, const string& substr) {
-  size_t count = 0, pos = 0;
-  while ((pos = str.find(substr, pos ? pos + 1 : 0)) != string::npos)
-    ++count;
-  return count;
-}
-}  // namespace
-
-class OmahaRequestBuilderXmlTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    FakeSystemState::CreateInstance();
-    FakeSystemState::Get()->set_request_params(&params_);
-  }
-  void TearDown() override {}
-
-  static constexpr size_t kGuidSize = 36;
-
-  OmahaRequestParams params_;
-};
-
-TEST_F(OmahaRequestBuilderXmlTest, XmlEncodeTest) {
-  string output;
-  vector<pair<string, string>> xml_encode_pairs = {
-      {"ab", "ab"},
-      {"a<b", "a&lt;b"},
-      {"<&>\"\'\\", "&lt;&amp;&gt;&quot;&apos;\\"},
-      {"&lt;&amp;&gt;", "&amp;lt;&amp;amp;&amp;gt;"}};
-  for (const auto& xml_encode_pair : xml_encode_pairs) {
-    const auto& before_encoding = xml_encode_pair.first;
-    const auto& after_encoding = xml_encode_pair.second;
-    EXPECT_TRUE(XmlEncode(before_encoding, &output));
-    EXPECT_EQ(after_encoding, output);
-  }
-  // Check that unterminated UTF-8 strings are handled properly.
-  EXPECT_FALSE(XmlEncode("\xc2", &output));
-  // Fail with invalid ASCII-7 chars.
-  EXPECT_FALSE(XmlEncode("This is an 'n' with a tilde: \xc3\xb1", &output));
-}
-
-TEST_F(OmahaRequestBuilderXmlTest, XmlEncodeWithDefaultTest) {
-  EXPECT_EQ("", XmlEncodeWithDefault(""));
-  EXPECT_EQ("&lt;&amp;&gt;", XmlEncodeWithDefault("<&>", "something else"));
-  EXPECT_EQ("<not escaped>", XmlEncodeWithDefault("\xc2", "<not escaped>"));
-}
-
-TEST_F(OmahaRequestBuilderXmlTest, PlatformGetAppTest) {
-  params_.set_device_requisition("device requisition");
-  OmahaRequestBuilderXml omaha_request{nullptr,
-                                       false,
-                                       false,
-                                       0,
-                                       0,
-                                       0,
-                                       ""};
-  OmahaAppData dlc_app_data = {.id = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
-                               .version = "",
-                               .skip_update = false,
-                               .is_dlc = false};
-
-  // Verify that the attributes that shouldn't be missing for Platform AppID are
-  // 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("requisition="));
-}
-
-TEST_F(OmahaRequestBuilderXmlTest, DlcGetAppTest) {
-  params_.set_device_requisition("device requisition");
-  OmahaRequestBuilderXml omaha_request{nullptr,
-                                       false,
-                                       false,
-                                       0,
-                                       0,
-                                       0,
-                                       ""};
-  OmahaAppData dlc_app_data = {
-      .id = "_dlc_id", .version = "", .skip_update = false, .is_dlc = true};
-
-  // Verify that the attributes that should be missing for DLC AppIDs are in
-  // 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("requisition="));
-}
-
-TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlRequestIdTest) {
-  OmahaRequestBuilderXml omaha_request{nullptr,
-                                       false,
-                                       false,
-                                       0,
-                                       0,
-                                       0,
-                                       ""};
-  const string request_xml = omaha_request.GetRequest();
-  const string key = "requestid";
-  const string request_id =
-      FindAttributeKeyValueInXml(request_xml, 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));
-}
-
-TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlSessionIdTest) {
-  const string gen_session_id = base::GenerateGUID();
-  OmahaRequestBuilderXml omaha_request{nullptr,
-                                       false,
-                                       false,
-                                       0,
-                                       0,
-                                       0,
-                                       gen_session_id};
-  const string request_xml = omaha_request.GetRequest();
-  const string key = "sessionid";
-  const string session_id =
-      FindAttributeKeyValueInXml(request_xml, 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));
-  }
-  EXPECT_EQ(gen_session_id, session_id);
-}
-
-TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlPlatformUpdateTest) {
-  OmahaRequestBuilderXml omaha_request{nullptr,
-                                       false,
-                                       false,
-                                       0,
-                                       0,
-                                       0,
-                                       ""};
-  const string request_xml = omaha_request.GetRequest();
-  EXPECT_EQ(1, CountSubstringInString(request_xml, "<updatecheck"))
-      << request_xml;
-}
-
-TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlPlatformUpdateWithDlcsTest) {
-  params_.set_dlc_apps_params(
-      {{params_.GetDlcAppId("dlc_no_0"), {.name = "dlc_no_0"}},
-       {params_.GetDlcAppId("dlc_no_1"), {.name = "dlc_no_1"}}});
-  OmahaRequestBuilderXml omaha_request{nullptr,
-                                       false,
-                                       false,
-                                       0,
-                                       0,
-                                       0,
-                                       ""};
-  const string request_xml = omaha_request.GetRequest();
-  EXPECT_EQ(3, CountSubstringInString(request_xml, "<updatecheck"))
-      << request_xml;
-}
-
-TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlDlcInstallationTest) {
-  const std::map<std::string, OmahaRequestParams::AppParams> dlcs = {
-      {params_.GetDlcAppId("dlc_no_0"), {.name = "dlc_no_0"}},
-      {params_.GetDlcAppId("dlc_no_1"), {.name = "dlc_no_1"}}};
-  params_.set_dlc_apps_params(dlcs);
-  params_.set_is_install(true);
-  OmahaRequestBuilderXml omaha_request{nullptr,
-                                       false,
-                                       false,
-                                       0,
-                                       0,
-                                       0,
-                                       ""};
-  const string request_xml = omaha_request.GetRequest();
-  EXPECT_EQ(2, CountSubstringInString(request_xml, "<updatecheck"))
-      << request_xml;
-
-  auto FindAppId = [request_xml](size_t pos) -> size_t {
-    return request_xml.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;
-    const string dlc_app_id_version = FindAttributeKeyValueInXml(
-        request_xml.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());
-    EXPECT_EQ(false_str, dlc_app_id_delta_okay);
-  }
-}
-
-TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlDlcNoPing) {
-  params_.set_dlc_apps_params(
-      {{params_.GetDlcAppId("dlc_no_0"), {.name = "dlc_no_0"}}});
-  OmahaRequestBuilderXml omaha_request{nullptr,
-                                       false,
-                                       false,
-                                       0,
-                                       0,
-                                       0,
-                                       ""};
-  const string request_xml = omaha_request.GetRequest();
-  EXPECT_EQ(0, CountSubstringInString(request_xml, "<ping")) << request_xml;
-}
-
-TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlDlcPingRollCallNoActive) {
-  params_.set_dlc_apps_params(
-      {{params_.GetDlcAppId("dlc_no_0"),
-        {.active_counting_type = OmahaRequestParams::kDateBased,
-         .name = "dlc_no_0",
-         .ping_date_last_active = 25,
-         .ping_date_last_rollcall = 36,
-         .send_ping = true}}});
-  OmahaRequestBuilderXml omaha_request{nullptr,
-                                       false,
-                                       false,
-                                       0,
-                                       0,
-                                       0,
-                                       ""};
-  const string request_xml = omaha_request.GetRequest();
-  EXPECT_EQ(1, CountSubstringInString(request_xml, "<ping rd=\"36\""))
-      << request_xml;
-}
-
-TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlDlcPingRollCallAndActive) {
-  params_.set_dlc_apps_params(
-      {{params_.GetDlcAppId("dlc_no_0"),
-        {.active_counting_type = OmahaRequestParams::kDateBased,
-         .name = "dlc_no_0",
-         .ping_active = 1,
-         .ping_date_last_active = 25,
-         .ping_date_last_rollcall = 36,
-         .send_ping = true}}});
-  OmahaRequestBuilderXml omaha_request{nullptr,
-                                       false,
-                                       false,
-                                       0,
-                                       0,
-                                       0,
-                                       ""};
-  const string request_xml = omaha_request.GetRequest();
-  EXPECT_EQ(1,
-            CountSubstringInString(request_xml,
-                                   "<ping active=\"1\" ad=\"25\" rd=\"36\""))
-      << request_xml;
-}
-
-TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlUpdateCompleteEvent) {
-  OmahaEvent event(OmahaEvent::kTypeUpdateComplete);
-  OmahaRequestBuilderXml omaha_request{&event,
-                                       false,
-                                       false,
-                                       0,
-                                       0,
-                                       0,
-                                       ""};
-  const string request_xml = omaha_request.GetRequest();
-  LOG(INFO) << request_xml;
-  EXPECT_EQ(
-      1,
-      CountSubstringInString(
-          request_xml, "<event eventtype=\"3\" eventresult=\"1\"></event>"))
-      << request_xml;
-}
-
-TEST_F(OmahaRequestBuilderXmlTest,
-       GetRequestXmlUpdateCompleteEventSomeDlcsExcluded) {
-  params_.set_dlc_apps_params({
-      {params_.GetDlcAppId("dlc_1"), {.updated = true}},
-      {params_.GetDlcAppId("dlc_2"), {.updated = false}},
-  });
-  OmahaEvent event(OmahaEvent::kTypeUpdateComplete);
-  OmahaRequestBuilderXml omaha_request{&event,
-                                       false,
-                                       false,
-                                       0,
-                                       0,
-                                       0,
-                                       ""};
-  const string request_xml = omaha_request.GetRequest();
-  EXPECT_EQ(
-      2,
-      CountSubstringInString(
-          request_xml, "<event eventtype=\"3\" eventresult=\"1\"></event>"))
-      << request_xml;
-  EXPECT_EQ(
-      1,
-      CountSubstringInString(
-          request_xml,
-          "<event eventtype=\"3\" eventresult=\"0\" errorcode=\"62\"></event>"))
-      << request_xml;
-}
-
-TEST_F(OmahaRequestBuilderXmlTest,
-       GetRequestXmlUpdateCompleteEventAllDlcsExcluded) {
-  params_.set_dlc_apps_params({
-      {params_.GetDlcAppId("dlc_1"), {.updated = false}},
-      {params_.GetDlcAppId("dlc_2"), {.updated = false}},
-  });
-  OmahaEvent event(OmahaEvent::kTypeUpdateComplete);
-  OmahaRequestBuilderXml omaha_request{&event,
-                                       false,
-                                       false,
-                                       0,
-                                       0,
-                                       0,
-                                       ""};
-  const string request_xml = omaha_request.GetRequest();
-  EXPECT_EQ(
-      1,
-      CountSubstringInString(
-          request_xml, "<event eventtype=\"3\" eventresult=\"1\"></event>"))
-      << request_xml;
-  EXPECT_EQ(
-      2,
-      CountSubstringInString(
-          request_xml,
-          "<event eventtype=\"3\" eventresult=\"0\" errorcode=\"62\"></event>"))
-      << request_xml;
-}
-
-TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlDlcCohortMissingCheck) {
-  constexpr char kDlcId[] = "test-dlc-id";
-  params_.set_dlc_apps_params(
-      {{params_.GetDlcAppId(kDlcId), {.name = kDlcId}}});
-  OmahaEvent event(OmahaEvent::kTypeUpdateDownloadStarted);
-  OmahaRequestBuilderXml omaha_request{&event, false, false, 0, 0, 0, ""};
-  const string request_xml = omaha_request.GetRequest();
-
-  // Check that no cohorts are in the request.
-  EXPECT_EQ(0, CountSubstringInString(request_xml, "cohort=")) << request_xml;
-  EXPECT_EQ(0, CountSubstringInString(request_xml, "cohortname="))
-      << request_xml;
-  EXPECT_EQ(0, CountSubstringInString(request_xml, "cohorthint="))
-      << request_xml;
-}
-
-TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlDlcCohortCheck) {
-  const string kDlcId = "test-dlc-id";
-  params_.set_dlc_apps_params(
-      {{params_.GetDlcAppId(kDlcId), {.name = kDlcId}}});
-  auto* fake_prefs = FakeSystemState::Get()->fake_prefs();
-  OmahaEvent event(OmahaEvent::kTypeUpdateDownloadStarted);
-  OmahaRequestBuilderXml omaha_request{&event, false, false, 0, 0, 0, ""};
-  // DLC App ID Expectations.
-  const string dlc_cohort_key = PrefsInterface::CreateSubKey(
-      {kDlcPrefsSubDir, kDlcId, kPrefsOmahaCohort});
-  const string kDlcCohortVal = "test-cohort";
-  EXPECT_TRUE(fake_prefs->SetString(dlc_cohort_key, kDlcCohortVal));
-  const string dlc_cohort_name_key = PrefsInterface::CreateSubKey(
-      {kDlcPrefsSubDir, kDlcId, kPrefsOmahaCohortName});
-  const string kDlcCohortNameVal = "test-cohortname";
-  EXPECT_TRUE(fake_prefs->SetString(dlc_cohort_name_key, kDlcCohortNameVal));
-  const string dlc_cohort_hint_key = PrefsInterface::CreateSubKey(
-      {kDlcPrefsSubDir, kDlcId, kPrefsOmahaCohortHint});
-  const string kDlcCohortHintVal = "test-cohortval";
-  EXPECT_TRUE(fake_prefs->SetString(dlc_cohort_hint_key, kDlcCohortHintVal));
-  const string request_xml = omaha_request.GetRequest();
-
-  EXPECT_EQ(1,
-            CountSubstringInString(
-                request_xml,
-                base::StringPrintf(
-                    "cohort=\"%s\" cohortname=\"%s\" cohorthint=\"%s\"",
-                    kDlcCohortVal.c_str(),
-                    kDlcCohortNameVal.c_str(),
-                    kDlcCohortHintVal.c_str())))
-      << request_xml;
-}
-
-}  // namespace chromeos_update_engine
diff --git a/cros/omaha_response_handler_action.cc b/cros/omaha_response_handler_action.cc
deleted file mode 100644
index 04cae3e..0000000
--- a/cros/omaha_response_handler_action.cc
+++ /dev/null
@@ -1,339 +0,0 @@
-//
-// Copyright (C) 2011 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/omaha_response_handler_action.h"
-
-#include <limits>
-#include <string>
-
-#include <base/logging.h>
-#include <base/strings/string_number_conversions.h>
-#include <base/version.h>
-#include <policy/device_policy.h>
-
-#include "update_engine/common/constants.h"
-#include "update_engine/common/hardware_interface.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/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/update_manager/policy.h"
-#include "update_engine/update_manager/update_manager.h"
-
-using chromeos_update_manager::kRollforwardInfinity;
-using chromeos_update_manager::Policy;
-using chromeos_update_manager::UpdateManager;
-using std::numeric_limits;
-using std::string;
-
-namespace chromeos_update_engine {
-
-OmahaResponseHandlerAction::OmahaResponseHandlerAction()
-    : deadline_file_(constants::kOmahaResponseDeadlineFile) {}
-
-void OmahaResponseHandlerAction::PerformAction() {
-  CHECK(HasInputObject());
-  ScopedActionCompleter completer(processor_, this);
-  const OmahaResponse& response = GetInputObject();
-  if (!response.update_exists) {
-    LOG(INFO) << "There are no updates. Aborting.";
-    completer.set_code(ErrorCode::kNoUpdate);
-    return;
-  }
-
-  // All decisions as to which URL should be used have already been done. So,
-  // make the current URL as the download URL.
-  string current_url = SystemState::Get()->payload_state()->GetCurrentUrl();
-  if (current_url.empty()) {
-    // This shouldn't happen as we should always supply the HTTPS backup URL.
-    // Handling this anyway, just in case.
-    LOG(ERROR) << "There are no suitable URLs in the response to use.";
-    completer.set_code(ErrorCode::kOmahaResponseInvalid);
-    return;
-  }
-
-  // This is the url to the first package, not all packages.
-  // (For updates): All |Action|s prior to this must pass in non-excluded URLs
-  // within the |OmahaResponse|, reference exlusion logic in
-  // |OmahaRequestAction| and keep the enforcement of exclusions for updates.
-  install_plan_.download_url = current_url;
-  install_plan_.version = response.version;
-
-  OmahaRequestParams* const params = SystemState::Get()->request_params();
-  PayloadStateInterface* const payload_state =
-      SystemState::Get()->payload_state();
-
-  // If we're using p2p to download and there is a local peer, use it.
-  if (payload_state->GetUsingP2PForDownloading() &&
-      !payload_state->GetP2PUrl().empty()) {
-    LOG(INFO) << "Replacing URL " << install_plan_.download_url
-              << " with local URL " << payload_state->GetP2PUrl()
-              << " since p2p is enabled.";
-    install_plan_.download_url = payload_state->GetP2PUrl();
-    payload_state->SetUsingP2PForDownloading(true);
-  }
-
-  // Fill up the other properties based on the response.
-  string update_check_response_hash;
-  for (const auto& package : response.packages) {
-    brillo::Blob raw_hash;
-    if (!base::HexStringToBytes(package.hash, &raw_hash)) {
-      LOG(ERROR) << "Failed to convert payload hash from hex string to bytes: "
-                 << package.hash;
-      completer.set_code(ErrorCode::kOmahaResponseInvalid);
-      return;
-    }
-    install_plan_.payloads.push_back(
-        {.payload_urls = package.payload_urls,
-         .size = package.size,
-         .metadata_size = package.metadata_size,
-         .metadata_signature = package.metadata_signature,
-         .hash = raw_hash,
-         .type = package.is_delta ? InstallPayloadType::kDelta
-                                  : InstallPayloadType::kFull,
-         .fp = package.fp,
-         .app_id = package.app_id});
-    update_check_response_hash += package.hash + ":";
-  }
-  install_plan_.public_key_rsa = response.public_key_rsa;
-  install_plan_.hash_checks_mandatory = AreHashChecksMandatory(response);
-  install_plan_.is_resume = DeltaPerformer::CanResumeUpdate(
-      SystemState::Get()->prefs(), update_check_response_hash);
-  if (install_plan_.is_resume) {
-    payload_state->UpdateResumed();
-  } else {
-    payload_state->UpdateRestarted();
-    LOG_IF(WARNING,
-           !DeltaPerformer::ResetUpdateProgress(SystemState::Get()->prefs(),
-                                                false))
-        << "Unable to reset the update progress.";
-    LOG_IF(WARNING,
-           !SystemState::Get()->prefs()->SetString(
-               kPrefsUpdateCheckResponseHash, update_check_response_hash))
-        << "Unable to save the update check response hash.";
-  }
-
-  if (params->is_install()) {
-    install_plan_.target_slot =
-        SystemState::Get()->boot_control()->GetCurrentSlot();
-    install_plan_.source_slot = BootControlInterface::kInvalidSlot;
-  } else {
-    install_plan_.source_slot =
-        SystemState::Get()->boot_control()->GetCurrentSlot();
-    install_plan_.target_slot = install_plan_.source_slot == 0 ? 1 : 0;
-  }
-
-  // The Omaha response doesn't include the channel name for this image, so we
-  // use the download_channel we used during the request to tag the target slot.
-  // This will be used in the next boot to know the channel the image was
-  // downloaded from.
-  string current_channel_key =
-      kPrefsChannelOnSlotPrefix + std::to_string(install_plan_.target_slot);
-  SystemState::Get()->prefs()->SetString(current_channel_key,
-                                         params->download_channel());
-
-  // Checking whether device is able to boot up the returned rollback image.
-  if (response.is_rollback) {
-    if (!params->rollback_allowed()) {
-      LOG(ERROR) << "Received rollback image but rollback is not allowed.";
-      completer.set_code(ErrorCode::kOmahaResponseInvalid);
-      return;
-    }
-
-    // Calculate the values on the version values on current device.
-    auto min_kernel_key_version = static_cast<uint32_t>(
-        SystemState::Get()->hardware()->GetMinKernelKeyVersion());
-    auto min_firmware_key_version = static_cast<uint32_t>(
-        SystemState::Get()->hardware()->GetMinFirmwareKeyVersion());
-
-    uint32_t kernel_key_version =
-        static_cast<uint32_t>(response.rollback_key_version.kernel_key) << 16 |
-        static_cast<uint32_t>(response.rollback_key_version.kernel);
-    uint32_t firmware_key_version =
-        static_cast<uint32_t>(response.rollback_key_version.firmware_key)
-            << 16 |
-        static_cast<uint32_t>(response.rollback_key_version.firmware);
-
-    LOG(INFO) << "Rollback image versions:"
-              << " device_kernel_key_version=" << min_kernel_key_version
-              << " image_kernel_key_version=" << kernel_key_version
-              << " device_firmware_key_version=" << min_firmware_key_version
-              << " image_firmware_key_version=" << firmware_key_version;
-
-    // Don't attempt a rollback if the versions are incompatible or the
-    // target image does not specify the version information.
-    if (kernel_key_version == numeric_limits<uint32_t>::max() ||
-        firmware_key_version == numeric_limits<uint32_t>::max() ||
-        kernel_key_version < min_kernel_key_version ||
-        firmware_key_version < min_firmware_key_version) {
-      LOG(ERROR) << "Device won't be able to boot up the rollback image.";
-      completer.set_code(ErrorCode::kRollbackNotPossible);
-      return;
-    }
-    install_plan_.is_rollback = true;
-    install_plan_.rollback_data_save_requested =
-        params->rollback_data_save_requested();
-  }
-
-  // Powerwash if either the response requires it or the parameters indicated
-  // 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() && !response.is_rollback) {
-    base::Version new_version(response.version);
-    base::Version current_version(params->app_version());
-
-    if (!new_version.IsValid()) {
-      LOG(WARNING) << "Not powerwashing,"
-                   << " the update's version number is unreadable."
-                   << " Update's version number: " << response.version;
-    } else if (!current_version.IsValid()) {
-      LOG(WARNING) << "Not powerwashing,"
-                   << " the current version number is unreadable."
-                   << " 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 =
-          SystemState::Get()->device_policy() &&
-          SystemState::Get()->device_policy()->IsEnterpriseEnrolled();
-    }
-  }
-
-  TEST_AND_RETURN(HasOutputPipe());
-  if (HasOutputPipe())
-    SetOutputObject(install_plan_);
-  install_plan_.Dump();
-
-  // Send the deadline data (if any) to Chrome through a file. This is a pretty
-  // hacky solution but should be OK for now.
-  //
-  // TODO(petkov): Re-architect this to avoid communication through a
-  // file. Ideally, we would include this information in D-Bus's GetStatus
-  // method and UpdateStatus signal. A potential issue is that update_engine may
-  // be unresponsive during an update download.
-  if (!deadline_file_.empty()) {
-    if (payload_state->GetRollbackHappened()) {
-      // Don't do forced update if rollback has happened since the last update
-      // check where policy was present.
-      LOG(INFO) << "Not forcing update because a rollback happened.";
-      utils::WriteFile(deadline_file_.c_str(), nullptr, 0);
-    } else {
-      utils::WriteFile(deadline_file_.c_str(),
-                       response.deadline.data(),
-                       response.deadline.size());
-    }
-    chmod(deadline_file_.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-  }
-
-  // Check the generated install-plan with the Policy to confirm that
-  // it can be applied at this time (or at all).
-  UpdateManager* const update_manager = SystemState::Get()->update_manager();
-  CHECK(update_manager);
-  auto ec = ErrorCode::kSuccess;
-  update_manager->PolicyRequest(
-      &Policy::UpdateCanBeApplied, &ec, &install_plan_);
-  completer.set_code(ec);
-
-  const auto allowed_milestones = params->rollback_allowed_milestones();
-  if (allowed_milestones > 0) {
-    auto max_firmware_rollforward = numeric_limits<uint32_t>::max();
-    auto max_kernel_rollforward = numeric_limits<uint32_t>::max();
-
-    // Determine the version to update the max rollforward verified boot
-    // value.
-    OmahaResponse::RollbackKeyVersion version =
-        response.past_rollback_key_version;
-
-    // Determine the max rollforward values to be set in the TPM.
-    max_firmware_rollforward = static_cast<uint32_t>(version.firmware_key)
-                                   << 16 |
-                               static_cast<uint32_t>(version.firmware);
-    max_kernel_rollforward = static_cast<uint32_t>(version.kernel_key) << 16 |
-                             static_cast<uint32_t>(version.kernel);
-
-    // In the case that the value is 0xffffffff, log a warning because the
-    // device should not be installing a rollback image without having version
-    // information.
-    if (max_firmware_rollforward == numeric_limits<uint32_t>::max() ||
-        max_kernel_rollforward == numeric_limits<uint32_t>::max()) {
-      LOG(WARNING)
-          << "Max rollforward values were not sent in rollback response: "
-          << " max_kernel_rollforward=" << max_kernel_rollforward
-          << " max_firmware_rollforward=" << max_firmware_rollforward
-          << " rollback_allowed_milestones="
-          << params->rollback_allowed_milestones();
-    } else {
-      LOG(INFO) << "Setting the max rollforward values: "
-                << " max_kernel_rollforward=" << max_kernel_rollforward
-                << " max_firmware_rollforward=" << max_firmware_rollforward
-                << " rollback_allowed_milestones="
-                << params->rollback_allowed_milestones();
-      SystemState::Get()->hardware()->SetMaxKernelKeyRollforward(
-          max_kernel_rollforward);
-      // TODO(crbug/783998): Set max firmware rollforward when implemented.
-    }
-  } else {
-    LOG(INFO) << "Rollback is not allowed. Setting max rollforward values"
-              << " to infinity";
-    // When rollback is not allowed, explicitly set the max roll forward to
-    // infinity.
-    SystemState::Get()->hardware()->SetMaxKernelKeyRollforward(
-        kRollforwardInfinity);
-    // TODO(crbug/783998): Set max firmware rollforward when implemented.
-  }
-}
-
-bool OmahaResponseHandlerAction::AreHashChecksMandatory(
-    const OmahaResponse& response) {
-  // We sometimes need to waive the hash checks in order to download from
-  // sources that don't provide hashes, such as dev server.
-  // At this point UpdateAttempter::IsAnyUpdateSourceAllowed() has already been
-  // checked, so an unofficial update URL won't get this far unless it's OK to
-  // use without a hash. Additionally, we want to always waive hash checks on
-  // unofficial builds (i.e. dev/test images).
-  // The end result is this:
-  //  * Base image:
-  //    - Official URLs require a hash.
-  //    - Unofficial URLs only get this far if the IsAnyUpdateSourceAllowed()
-  //      devmode/debugd checks pass, in which case the hash is waived.
-  //  * Dev/test image:
-  //    - Any URL is allowed through with no hash checking.
-  if (!SystemState::Get()->request_params()->IsUpdateUrlOfficial() ||
-      !SystemState::Get()->hardware()->IsOfficialBuild()) {
-    // Still do a hash check if a public key is included.
-    if (!response.public_key_rsa.empty()) {
-      // The autoupdate_CatchBadSignatures test checks for this string
-      // in log-files. Keep in sync.
-      LOG(INFO) << "Mandating payload hash checks since Omaha Response "
-                << "for unofficial build includes public RSA key.";
-      return true;
-    } else {
-      LOG(INFO) << "Waiving payload hash checks for unofficial update URL.";
-      return false;
-    }
-  }
-
-  LOG(INFO) << "Mandating hash checks for official URL on official build.";
-  return true;
-}
-
-}  // namespace chromeos_update_engine
diff --git a/cros/omaha_response_handler_action_unittest.cc b/cros/omaha_response_handler_action_unittest.cc
deleted file mode 100644
index c9b46b1..0000000
--- a/cros/omaha_response_handler_action_unittest.cc
+++ /dev/null
@@ -1,1022 +0,0 @@
-//
-// Copyright (C) 2011 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/omaha_response_handler_action.h"
-
-#include <memory>
-#include <string>
-#include <utility>
-
-#include <base/files/file_util.h>
-#include <base/files/scoped_temp_dir.h>
-#include <brillo/message_loops/fake_message_loop.h>
-#include <gtest/gtest.h>
-
-#include "update_engine/common/constants.h"
-#include "update_engine/common/platform_constants.h"
-#include "update_engine/common/test_utils.h"
-#include "update_engine/common/utils.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"
-
-using chromeos_update_engine::test_utils::System;
-using chromeos_update_engine::test_utils::WriteFileString;
-using chromeos_update_manager::EvalStatus;
-using chromeos_update_manager::FakeUpdateManager;
-using chromeos_update_manager::kRollforwardInfinity;
-using chromeos_update_manager::MockPolicy;
-using std::string;
-using testing::_;
-using testing::DoAll;
-using testing::Return;
-using testing::SetArgPointee;
-
-namespace chromeos_update_engine {
-
-class OmahaResponseHandlerActionProcessorDelegate
-    : public ActionProcessorDelegate {
- public:
-  OmahaResponseHandlerActionProcessorDelegate()
-      : code_(ErrorCode::kError), code_set_(false) {}
-  void ActionCompleted(ActionProcessor* processor,
-                       AbstractAction* action,
-                       ErrorCode code) {
-    if (action->Type() == OmahaResponseHandlerAction::StaticType()) {
-      auto response_handler_action =
-          static_cast<OmahaResponseHandlerAction*>(action);
-      code_ = code;
-      code_set_ = true;
-      response_handler_action_install_plan_.reset(
-          new InstallPlan(response_handler_action->install_plan_));
-    } else if (action->Type() ==
-               ObjectCollectorAction<InstallPlan>::StaticType()) {
-      auto collector_action =
-          static_cast<ObjectCollectorAction<InstallPlan>*>(action);
-      collector_action_install_plan_.reset(
-          new InstallPlan(collector_action->object()));
-    }
-  }
-  ErrorCode code_;
-  bool code_set_;
-  std::unique_ptr<InstallPlan> collector_action_install_plan_;
-  std::unique_ptr<InstallPlan> response_handler_action_install_plan_;
-};
-
-class OmahaResponseHandlerActionTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    FakeSystemState::CreateInstance();
-    // Enable MockPrefs;
-    FakeSystemState::Get()->set_prefs(nullptr);
-    FakeBootControl* fake_boot_control =
-        FakeSystemState::Get()->fake_boot_control();
-    fake_boot_control->SetPartitionDevice(kPartitionNameKernel, 0, "/dev/sdz2");
-    fake_boot_control->SetPartitionDevice(kPartitionNameRoot, 0, "/dev/sdz3");
-    fake_boot_control->SetPartitionDevice(kPartitionNameKernel, 1, "/dev/sdz4");
-    fake_boot_control->SetPartitionDevice(kPartitionNameRoot, 1, "/dev/sdz5");
-  }
-
-  // Return true iff the OmahaResponseHandlerAction succeeded.
-  // If out is non-null, it's set w/ the response from the action.
-  bool DoTest(const OmahaResponse& in,
-              const string& deadline_file,
-              InstallPlan* out);
-
-  // Delegate passed to the ActionProcessor.
-  OmahaResponseHandlerActionProcessorDelegate delegate_;
-
-  // Captures the action's result code, for tests that need to directly verify
-  // it in non-success cases.
-  ErrorCode action_result_code_;
-
-  // "Hash+"
-  const brillo::Blob expected_hash_ = {0x48, 0x61, 0x73, 0x68, 0x2b};
-};
-
-namespace {
-const char* const kLongName =
-    "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
-    "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
-    "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
-    "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
-    "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
-    "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
-    "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
-    "-the_update_a.b.c.d_DELTA_.tgz";
-const char* const kBadVersion = "don't update me";
-const char* const kPayloadHashHex = "486173682b";
-const char* const kPayloadFp1 = "1.755aff78ec73dfc7f590893ac";
-const char* const kPayloadFp2 = "1.98ba213e0ccec0d0e8cdc74a5";
-const char* const kPayloadAppId = "test_app_id";
-}  // namespace
-
-bool OmahaResponseHandlerActionTest::DoTest(const OmahaResponse& in,
-                                            const string& test_deadline_file,
-                                            InstallPlan* out) {
-  brillo::FakeMessageLoop loop(nullptr);
-  loop.SetAsCurrent();
-  ActionProcessor processor;
-  processor.set_delegate(&delegate_);
-
-  auto feeder_action = std::make_unique<ObjectFeederAction<OmahaResponse>>();
-  feeder_action->set_obj(in);
-  if (in.update_exists && in.version != kBadVersion) {
-    string expected_hash;
-    for (const auto& package : in.packages)
-      expected_hash += package.hash + ":";
-    EXPECT_CALL(*(FakeSystemState::Get()->mock_prefs()),
-                SetString(kPrefsUpdateCheckResponseHash, expected_hash))
-        .WillOnce(Return(true));
-
-    int slot =
-        FakeSystemState::Get()->request_params()->is_install()
-            ? FakeSystemState::Get()->fake_boot_control()->GetCurrentSlot()
-            : 1 - FakeSystemState::Get()->fake_boot_control()->GetCurrentSlot();
-    string key = kPrefsChannelOnSlotPrefix + std::to_string(slot);
-    EXPECT_CALL(*(FakeSystemState::Get()->mock_prefs()),
-                SetString(key, testing::_))
-        .WillOnce(Return(true));
-  }
-
-  string current_url = in.packages.size() ? in.packages[0].payload_urls[0] : "";
-  EXPECT_CALL(*(FakeSystemState::Get()->mock_payload_state()), GetCurrentUrl())
-      .WillRepeatedly(Return(current_url));
-
-  auto response_handler_action = std::make_unique<OmahaResponseHandlerAction>();
-  if (!test_deadline_file.empty())
-    response_handler_action->deadline_file_ = test_deadline_file;
-
-  auto collector_action =
-      std::make_unique<ObjectCollectorAction<InstallPlan>>();
-
-  BondActions(feeder_action.get(), response_handler_action.get());
-  BondActions(response_handler_action.get(), collector_action.get());
-  processor.EnqueueAction(std::move(feeder_action));
-  processor.EnqueueAction(std::move(response_handler_action));
-  processor.EnqueueAction(std::move(collector_action));
-  processor.StartProcessing();
-  EXPECT_TRUE(!processor.IsRunning())
-      << "Update test to handle non-async actions";
-
-  if (out && delegate_.collector_action_install_plan_)
-    *out = *delegate_.collector_action_install_plan_;
-
-  EXPECT_TRUE(delegate_.code_set_);
-  action_result_code_ = delegate_.code_;
-  return delegate_.code_ == ErrorCode::kSuccess;
-}
-
-TEST_F(OmahaResponseHandlerActionTest, SimpleTest) {
-  ScopedTempFile test_deadline_file(
-      "omaha_response_handler_action_unittest-XXXXXX");
-  {
-    OmahaResponse in;
-    in.update_exists = true;
-    in.version = "a.b.c.d";
-    in.packages.push_back(
-        {.payload_urls = {"http://foo/the_update_a.b.c.d.tgz"},
-         .size = 12,
-         .hash = kPayloadHashHex,
-         .app_id = kPayloadAppId,
-         .fp = kPayloadFp1});
-    in.more_info_url = "http://more/info";
-    in.prompt = false;
-    in.deadline = "20101020";
-    InstallPlan install_plan;
-    EXPECT_TRUE(DoTest(in, test_deadline_file.path(), &install_plan));
-    EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
-    EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
-    EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
-    EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
-    EXPECT_EQ(1U, install_plan.target_slot);
-    string deadline;
-    EXPECT_TRUE(utils::ReadFile(test_deadline_file.path(), &deadline));
-    EXPECT_EQ("20101020", deadline);
-    struct stat deadline_stat;
-    EXPECT_EQ(0, stat(test_deadline_file.path().c_str(), &deadline_stat));
-    EXPECT_EQ(
-        static_cast<mode_t>(S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH),
-        deadline_stat.st_mode);
-    EXPECT_EQ(in.version, install_plan.version);
-  }
-  {
-    OmahaResponse in;
-    in.update_exists = true;
-    in.version = "a.b.c.d";
-    in.packages.push_back(
-        {.payload_urls = {"http://foo/the_update_a.b.c.d.tgz"},
-         .size = 12,
-         .hash = kPayloadHashHex,
-         .app_id = kPayloadAppId,
-         .fp = kPayloadFp1});
-    in.more_info_url = "http://more/info";
-    in.prompt = true;
-    InstallPlan install_plan;
-    // Set the other slot as current.
-    FakeSystemState::Get()->fake_boot_control()->SetCurrentSlot(1);
-    EXPECT_TRUE(DoTest(in, test_deadline_file.path(), &install_plan));
-    EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
-    EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
-    EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
-    EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
-    EXPECT_EQ(0U, install_plan.target_slot);
-    string deadline;
-    EXPECT_TRUE(utils::ReadFile(test_deadline_file.path(), &deadline) &&
-                deadline.empty());
-    EXPECT_EQ(in.version, install_plan.version);
-  }
-  {
-    OmahaResponse in;
-    in.update_exists = true;
-    in.version = "a.b.c.d";
-    in.packages.push_back({.payload_urls = {kLongName},
-                           .size = 12,
-                           .hash = kPayloadHashHex,
-                           .app_id = kPayloadAppId,
-                           .fp = kPayloadFp1});
-    in.more_info_url = "http://more/info";
-    in.prompt = true;
-    in.deadline = "some-deadline";
-    InstallPlan install_plan;
-    FakeSystemState::Get()->fake_boot_control()->SetCurrentSlot(0);
-    // Because rollback happened, the deadline shouldn't be written into the
-    // file.
-    EXPECT_CALL(*(FakeSystemState::Get()->mock_payload_state()),
-                GetRollbackHappened())
-        .WillOnce(Return(true));
-    EXPECT_TRUE(DoTest(in, test_deadline_file.path(), &install_plan));
-    EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
-    EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
-    EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
-    EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
-    EXPECT_EQ(1U, install_plan.target_slot);
-    string deadline;
-    EXPECT_TRUE(utils::ReadFile(test_deadline_file.path(), &deadline));
-    EXPECT_TRUE(deadline.empty());
-    EXPECT_EQ(in.version, install_plan.version);
-  }
-  {
-    OmahaResponse in;
-    in.update_exists = true;
-    in.version = "a.b.c.d";
-    in.packages.push_back({.payload_urls = {kLongName},
-                           .size = 12,
-                           .hash = kPayloadHashHex,
-                           .app_id = kPayloadAppId,
-                           .fp = kPayloadFp1});
-    in.more_info_url = "http://more/info";
-    in.prompt = true;
-    in.deadline = "some-deadline";
-    InstallPlan install_plan;
-    FakeSystemState::Get()->fake_boot_control()->SetCurrentSlot(0);
-    EXPECT_CALL(*(FakeSystemState::Get()->mock_payload_state()),
-                GetRollbackHappened())
-        .WillOnce(Return(false));
-    EXPECT_TRUE(DoTest(in, test_deadline_file.path(), &install_plan));
-    EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
-    EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
-    EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
-    EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
-    EXPECT_EQ(1U, install_plan.target_slot);
-    string deadline;
-    EXPECT_TRUE(utils::ReadFile(test_deadline_file.path(), &deadline));
-    EXPECT_EQ("some-deadline", deadline);
-    EXPECT_EQ(in.version, install_plan.version);
-  }
-}
-
-TEST_F(OmahaResponseHandlerActionTest, NoUpdatesTest) {
-  OmahaResponse in;
-  in.update_exists = false;
-  InstallPlan install_plan;
-  EXPECT_FALSE(DoTest(in, "", &install_plan));
-  EXPECT_TRUE(install_plan.partitions.empty());
-}
-
-TEST_F(OmahaResponseHandlerActionTest, InstallTest) {
-  OmahaResponse in;
-  in.update_exists = true;
-  in.version = "a.b.c.d";
-  in.packages.push_back(
-      {.payload_urls = {kLongName}, .size = 1, .hash = kPayloadHashHex});
-  in.packages.push_back(
-      {.payload_urls = {kLongName}, .size = 2, .hash = kPayloadHashHex});
-  in.more_info_url = "http://more/info";
-
-  OmahaRequestParams params;
-  params.set_is_install(true);
-
-  FakeSystemState::Get()->set_request_params(&params);
-  InstallPlan install_plan;
-  EXPECT_TRUE(DoTest(in, "", &install_plan));
-  EXPECT_EQ(install_plan.source_slot, UINT_MAX);
-}
-
-TEST_F(OmahaResponseHandlerActionTest, MultiPackageTest) {
-  OmahaResponse in;
-  in.update_exists = true;
-  in.version = "a.b.c.d";
-  in.packages.push_back({.payload_urls = {"http://package/1"},
-                         .size = 1,
-                         .hash = kPayloadHashHex,
-                         .app_id = kPayloadAppId,
-                         .fp = kPayloadFp1});
-  in.packages.push_back({.payload_urls = {"http://package/2"},
-                         .size = 2,
-                         .hash = kPayloadHashHex,
-                         .app_id = kPayloadAppId,
-                         .fp = kPayloadFp2});
-  in.more_info_url = "http://more/info";
-  InstallPlan install_plan;
-  EXPECT_TRUE(DoTest(in, "", &install_plan));
-  EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
-  EXPECT_EQ(2u, install_plan.payloads.size());
-  EXPECT_EQ(in.packages[0].size, install_plan.payloads[0].size);
-  EXPECT_EQ(in.packages[1].size, install_plan.payloads[1].size);
-  EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
-  EXPECT_EQ(expected_hash_, install_plan.payloads[1].hash);
-  EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
-  EXPECT_EQ(in.packages[1].app_id, install_plan.payloads[1].app_id);
-  EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
-  EXPECT_EQ(in.packages[1].fp, install_plan.payloads[1].fp);
-  EXPECT_EQ(in.version, install_plan.version);
-}
-
-TEST_F(OmahaResponseHandlerActionTest, HashChecksForHttpTest) {
-  OmahaResponse in;
-  in.update_exists = true;
-  in.version = "a.b.c.d";
-  in.packages.push_back(
-      {.payload_urls = {"http://test.should/need/hash.checks.signed"},
-       .size = 12,
-       .hash = kPayloadHashHex,
-       .app_id = kPayloadAppId,
-       .fp = kPayloadFp1});
-  in.more_info_url = "http://more/info";
-  // Hash checks are always skipped for non-official update URLs.
-  EXPECT_CALL(*(FakeSystemState::Get()->mock_request_params()),
-              IsUpdateUrlOfficial())
-      .WillRepeatedly(Return(true));
-  InstallPlan install_plan;
-  EXPECT_TRUE(DoTest(in, "", &install_plan));
-  EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
-  EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
-  EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
-  EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
-  EXPECT_TRUE(install_plan.hash_checks_mandatory);
-  EXPECT_EQ(in.version, install_plan.version);
-}
-
-TEST_F(OmahaResponseHandlerActionTest, HashChecksForUnofficialUpdateUrl) {
-  OmahaResponse in;
-  in.update_exists = true;
-  in.version = "a.b.c.d";
-  in.packages.push_back(
-      {.payload_urls = {"http://url.normally/needs/hash.checks.signed"},
-       .size = 12,
-       .hash = kPayloadHashHex,
-       .app_id = kPayloadAppId,
-       .fp = kPayloadFp1});
-  in.more_info_url = "http://more/info";
-  EXPECT_CALL(*(FakeSystemState::Get()->mock_request_params()),
-              IsUpdateUrlOfficial())
-      .WillRepeatedly(Return(false));
-  InstallPlan install_plan;
-  EXPECT_TRUE(DoTest(in, "", &install_plan));
-  EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
-  EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
-  EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
-  EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
-  EXPECT_FALSE(install_plan.hash_checks_mandatory);
-  EXPECT_EQ(in.version, install_plan.version);
-}
-
-TEST_F(OmahaResponseHandlerActionTest,
-       HashChecksForOfficialUrlUnofficialBuildTest) {
-  // Official URLs for unofficial builds (dev/test images) don't require hash.
-  OmahaResponse in;
-  in.update_exists = true;
-  in.version = "a.b.c.d";
-  in.packages.push_back(
-      {.payload_urls = {"http://url.normally/needs/hash.checks.signed"},
-       .size = 12,
-       .hash = kPayloadHashHex,
-       .app_id = kPayloadAppId,
-       .fp = kPayloadFp1});
-  in.more_info_url = "http://more/info";
-  EXPECT_CALL(*(FakeSystemState::Get()->mock_request_params()),
-              IsUpdateUrlOfficial())
-      .WillRepeatedly(Return(true));
-  FakeSystemState::Get()->fake_hardware()->SetIsOfficialBuild(false);
-  InstallPlan install_plan;
-  EXPECT_TRUE(DoTest(in, "", &install_plan));
-  EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
-  EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
-  EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
-  EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
-  EXPECT_FALSE(install_plan.hash_checks_mandatory);
-  EXPECT_EQ(in.version, install_plan.version);
-}
-
-TEST_F(OmahaResponseHandlerActionTest, HashChecksForHttpsTest) {
-  OmahaResponse in;
-  in.update_exists = true;
-  in.version = "a.b.c.d";
-  in.packages.push_back(
-      {.payload_urls = {"https://test.should/need/hash.checks.signed"},
-       .size = 12,
-       .hash = kPayloadHashHex,
-       .app_id = kPayloadAppId,
-       .fp = kPayloadFp1});
-  in.more_info_url = "http://more/info";
-  EXPECT_CALL(*(FakeSystemState::Get()->mock_request_params()),
-              IsUpdateUrlOfficial())
-      .WillRepeatedly(Return(true));
-  InstallPlan install_plan;
-  EXPECT_TRUE(DoTest(in, "", &install_plan));
-  EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
-  EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
-  EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
-  EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
-  EXPECT_TRUE(install_plan.hash_checks_mandatory);
-  EXPECT_EQ(in.version, install_plan.version);
-}
-
-TEST_F(OmahaResponseHandlerActionTest, HashChecksForBothHttpAndHttpsTest) {
-  OmahaResponse in;
-  in.update_exists = true;
-  in.version = "a.b.c.d";
-  in.packages.push_back(
-      {.payload_urls = {"http://test.should.still/need/hash.checks",
-                        "https://test.should.still/need/hash.checks"},
-       .size = 12,
-       .hash = kPayloadHashHex,
-       .app_id = kPayloadAppId,
-       .fp = kPayloadFp1});
-  in.more_info_url = "http://more/info";
-  EXPECT_CALL(*(FakeSystemState::Get()->mock_request_params()),
-              IsUpdateUrlOfficial())
-      .WillRepeatedly(Return(true));
-  InstallPlan install_plan;
-  EXPECT_TRUE(DoTest(in, "", &install_plan));
-  EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
-  EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
-  EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
-  EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
-  EXPECT_TRUE(install_plan.hash_checks_mandatory);
-  EXPECT_EQ(in.version, install_plan.version);
-}
-
-TEST_F(OmahaResponseHandlerActionTest,
-       ChangeToMoreStableVersionAndChannelTest) {
-  OmahaResponse in;
-  in.update_exists = true;
-  in.version = "1.0.0.0";
-  in.packages.push_back({.payload_urls = {"https://MoreStableChannelTest"},
-                         .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;
-  FakeSystemState::Get()->fake_hardware()->SetIsOfficialBuild(false);
-  params.set_root(tempdir.GetPath().value());
-  params.set_current_channel("canary-channel");
-  EXPECT_TRUE(params.SetTargetChannel("stable-channel", true, nullptr));
-  params.UpdateDownloadChannel();
-  params.set_app_version("2.0.0.0");
-
-  FakeSystemState::Get()->set_request_params(&params);
-  InstallPlan install_plan;
-  EXPECT_TRUE(DoTest(in, "", &install_plan));
-  EXPECT_TRUE(install_plan.powerwash_required);
-}
-
-TEST_F(OmahaResponseHandlerActionTest,
-       ChangeToMoreStableVersionAndChannelPowerwashNotAllowedTest) {
-  OmahaResponse in;
-  in.update_exists = true;
-  in.version = "1.0.0.0";
-  in.packages.push_back({.payload_urls = {"https://MoreStableChannelTest"},
-                         .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;
-  FakeSystemState::Get()->fake_hardware()->SetIsOfficialBuild(false);
-  params.set_root(tempdir.GetPath().value());
-  params.set_current_channel("canary-channel");
-  EXPECT_TRUE(params.SetTargetChannel("stable-channel", false, nullptr));
-  params.UpdateDownloadChannel();
-  params.set_app_version("2.0.0.0");
-
-  FakeSystemState::Get()->set_request_params(&params);
-  InstallPlan install_plan;
-  EXPECT_TRUE(DoTest(in, "", &install_plan));
-  EXPECT_FALSE(install_plan.powerwash_required);
-}
-
-TEST_F(OmahaResponseHandlerActionTest,
-       ChangeToMoreStableChannelButNewerVersionTest) {
-  OmahaResponse in;
-  in.update_exists = true;
-  in.version = "12345.96.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;
-  FakeSystemState::Get()->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.48.0.0");
-
-  FakeSystemState::Get()->set_request_params(&params);
-  InstallPlan install_plan;
-  EXPECT_TRUE(DoTest(in, "", &install_plan));
-  EXPECT_FALSE(install_plan.powerwash_required);
-}
-
-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;
-  FakeSystemState::Get()->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");
-
-  FakeSystemState::Get()->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;
-  FakeSystemState::Get()->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));
-  FakeSystemState::Get()->set_device_policy(&mock_device_policy);
-
-  FakeSystemState::Get()->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;
-  FakeSystemState::Get()->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));
-  FakeSystemState::Get()->set_device_policy(&mock_device_policy);
-
-  FakeSystemState::Get()->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;
-  FakeSystemState::Get()->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");
-
-  FakeSystemState::Get()->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;
-  in.version = "2.0.0.0";
-  in.packages.push_back({.payload_urls = {"https://LessStableChannelTest"},
-                         .size = 15,
-                         .hash = kPayloadHashHex});
-  in.more_info_url = "http://more/info";
-
-  // Create a uniquely named test directory.
-  base::ScopedTempDir tempdir;
-  ASSERT_TRUE(tempdir.CreateUniqueTempDir());
-
-  OmahaRequestParams params;
-  FakeSystemState::Get()->fake_hardware()->SetIsOfficialBuild(false);
-  params.set_root(tempdir.GetPath().value());
-  params.set_current_channel("stable-channel");
-  EXPECT_TRUE(params.SetTargetChannel("canary-channel", false, nullptr));
-  params.UpdateDownloadChannel();
-  params.set_app_version("1.0.0.0");
-
-  FakeSystemState::Get()->set_request_params(&params);
-  InstallPlan install_plan;
-  EXPECT_TRUE(DoTest(in, "", &install_plan));
-  EXPECT_FALSE(install_plan.powerwash_required);
-}
-
-TEST_F(OmahaResponseHandlerActionTest, P2PUrlIsUsedAndHashChecksMandatory) {
-  OmahaResponse in;
-  in.update_exists = true;
-  in.version = "a.b.c.d";
-  in.packages.push_back(
-      {.payload_urls = {"https://would.not/cause/hash/checks"},
-       .size = 12,
-       .hash = kPayloadHashHex,
-       .app_id = kPayloadAppId,
-       .fp = kPayloadFp1});
-  in.more_info_url = "http://more/info";
-
-  OmahaRequestParams params;
-  // We're using a real OmahaRequestParams object here so we can't mock
-  // IsUpdateUrlOfficial(), but setting the update URL to the AutoUpdate test
-  // server will cause IsUpdateUrlOfficial() to return true.
-  params.set_update_url(constants::kOmahaDefaultAUTestURL);
-  FakeSystemState::Get()->set_request_params(&params);
-
-  EXPECT_CALL(*FakeSystemState::Get()->mock_payload_state(),
-              SetUsingP2PForDownloading(true));
-
-  string p2p_url = "http://9.8.7.6/p2p";
-  EXPECT_CALL(*FakeSystemState::Get()->mock_payload_state(), GetP2PUrl())
-      .WillRepeatedly(Return(p2p_url));
-  EXPECT_CALL(*FakeSystemState::Get()->mock_payload_state(),
-              GetUsingP2PForDownloading())
-      .WillRepeatedly(Return(true));
-
-  InstallPlan install_plan;
-  EXPECT_TRUE(DoTest(in, "", &install_plan));
-  EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
-  EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
-  EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
-  EXPECT_EQ(p2p_url, install_plan.download_url);
-  EXPECT_TRUE(install_plan.hash_checks_mandatory);
-}
-
-TEST_F(OmahaResponseHandlerActionTest, RollbackTest) {
-  OmahaResponse in;
-  in.update_exists = true;
-  in.packages.push_back({.payload_urls = {"https://RollbackTest"},
-                         .size = 1,
-                         .hash = kPayloadHashHex});
-  in.is_rollback = true;
-
-  // The rollback payload is 2 versions behind stable.
-  in.rollback_key_version.kernel = 24;
-  in.rollback_key_version.kernel = 23;
-  in.rollback_key_version.firmware_key = 22;
-  in.rollback_key_version.firmware = 21;
-
-  OmahaResponse::RollbackKeyVersion m4;
-  m4.firmware_key = 16;
-  m4.firmware = 15;
-  m4.kernel_key = 14;
-  m4.kernel = 13;
-
-  in.past_rollback_key_version = m4;
-
-  FakeSystemState::Get()->fake_hardware()->SetMinKernelKeyVersion(0x00010002);
-  FakeSystemState::Get()->fake_hardware()->SetMinFirmwareKeyVersion(0x00030004);
-
-  FakeSystemState::Get()->fake_hardware()->SetMaxKernelKeyRollforward(
-      0xaaaaaaaa);
-  // TODO(crbug/783998): Add support for firmware when implemented.
-
-  OmahaRequestParams params;
-  params.set_rollback_allowed(true);
-  params.set_rollback_allowed_milestones(4);
-
-  FakeSystemState::Get()->set_request_params(&params);
-  InstallPlan install_plan;
-  EXPECT_TRUE(DoTest(in, "", &install_plan));
-  EXPECT_TRUE(install_plan.is_rollback);
-
-  // The max rollforward should be set the values of the image
-  // rollback_allowed_milestones (4 for this test) in the past.
-  const uint32_t expected_max_kernel_rollforward =
-      static_cast<uint32_t>(m4.kernel_key) << 16 |
-      static_cast<uint32_t>(m4.kernel);
-  EXPECT_EQ(
-      expected_max_kernel_rollforward,
-      FakeSystemState::Get()->fake_hardware()->GetMaxKernelKeyRollforward());
-  // TODO(crbug/783998): Add support for firmware when implemented.
-}
-
-TEST_F(OmahaResponseHandlerActionTest, RollbackKernelVersionErrorTest) {
-  OmahaResponse in;
-  in.update_exists = true;
-  in.packages.push_back({.payload_urls = {"https://RollbackTest"},
-                         .size = 1,
-                         .hash = kPayloadHashHex});
-  in.is_rollback = true;
-  in.rollback_key_version.kernel_key = 1;
-  in.rollback_key_version.kernel = 1;  // This is lower than the minimum.
-  in.rollback_key_version.firmware_key = 3;
-  in.rollback_key_version.firmware = 4;
-
-  OmahaResponse::RollbackKeyVersion m4;
-  m4.firmware_key = 16;
-  m4.firmware = 15;
-  m4.kernel_key = 14;
-  m4.kernel = 13;
-  in.past_rollback_key_version = m4;
-
-  FakeSystemState::Get()->fake_hardware()->SetMinKernelKeyVersion(0x00010002);
-  FakeSystemState::Get()->fake_hardware()->SetMinFirmwareKeyVersion(0x00030004);
-  const uint32_t current_kernel_max_rollforward = 0xaaaaaaaa;
-  FakeSystemState::Get()->fake_hardware()->SetMaxKernelKeyRollforward(
-      current_kernel_max_rollforward);
-
-  OmahaRequestParams params;
-  params.set_rollback_allowed(true);
-  params.set_rollback_allowed_milestones(4);
-
-  FakeSystemState::Get()->set_request_params(&params);
-  InstallPlan install_plan;
-  EXPECT_FALSE(DoTest(in, "", &install_plan));
-
-  // Max rollforward is not changed in error cases.
-  EXPECT_EQ(
-      current_kernel_max_rollforward,
-      FakeSystemState::Get()->fake_hardware()->GetMaxKernelKeyRollforward());
-  // TODO(crbug/783998): Add support for firmware when implemented.
-}
-
-TEST_F(OmahaResponseHandlerActionTest, RollbackFirmwareVersionErrorTest) {
-  // TODO(crbug/783998): Add handling for max_firmware_rollforward when
-  // implemented.
-  OmahaResponse in;
-  in.update_exists = true;
-  in.packages.push_back({.payload_urls = {"https://RollbackTest"},
-                         .size = 1,
-                         .hash = kPayloadHashHex});
-  in.is_rollback = true;
-  in.rollback_key_version.kernel_key = 1;
-  in.rollback_key_version.kernel = 2;
-  in.rollback_key_version.firmware_key = 3;
-  in.rollback_key_version.firmware = 3;  // This is lower than the minimum.
-
-  FakeSystemState::Get()->fake_hardware()->SetMinKernelKeyVersion(0x00010002);
-  FakeSystemState::Get()->fake_hardware()->SetMinFirmwareKeyVersion(0x00030004);
-
-  OmahaRequestParams params;
-  params.set_rollback_allowed(true);
-  params.set_rollback_allowed_milestones(4);
-
-  FakeSystemState::Get()->set_request_params(&params);
-  InstallPlan install_plan;
-  EXPECT_FALSE(DoTest(in, "", &install_plan));
-}
-
-TEST_F(OmahaResponseHandlerActionTest, RollbackNotRollbackTest) {
-  OmahaResponse in;
-  in.update_exists = true;
-  in.packages.push_back({.payload_urls = {"https://RollbackTest"},
-                         .size = 1,
-                         .hash = kPayloadHashHex});
-  in.is_rollback = false;
-
-  const uint32_t current_kernel_max_rollforward = 0xaaaaaaaa;
-  FakeSystemState::Get()->fake_hardware()->SetMaxKernelKeyRollforward(
-      current_kernel_max_rollforward);
-
-  OmahaRequestParams params;
-  params.set_rollback_allowed(true);
-  params.set_rollback_allowed_milestones(4);
-
-  FakeSystemState::Get()->set_request_params(&params);
-  InstallPlan install_plan;
-  EXPECT_TRUE(DoTest(in, "", &install_plan));
-  EXPECT_FALSE(install_plan.is_rollback);
-
-  // Max rollforward is not changed for non-rollback cases.
-  EXPECT_EQ(
-      current_kernel_max_rollforward,
-      FakeSystemState::Get()->fake_hardware()->GetMaxKernelKeyRollforward());
-  // TODO(crbug/783998): Add support for firmware when implemented.
-}
-
-TEST_F(OmahaResponseHandlerActionTest, RollbackNotAllowedTest) {
-  OmahaResponse in;
-  in.update_exists = true;
-  in.packages.push_back({.payload_urls = {"https://RollbackTest"},
-                         .size = 1,
-                         .hash = kPayloadHashHex});
-  in.is_rollback = true;
-
-  OmahaRequestParams params;
-  params.set_rollback_allowed(false);
-  params.set_rollback_allowed_milestones(4);
-
-  const uint32_t current_kernel_max_rollforward = 0xaaaaaaaa;
-  FakeSystemState::Get()->fake_hardware()->SetMaxKernelKeyRollforward(
-      current_kernel_max_rollforward);
-
-  FakeSystemState::Get()->set_request_params(&params);
-  InstallPlan install_plan;
-  EXPECT_FALSE(DoTest(in, "", &install_plan));
-
-  // This case generates an error so, do not update max rollforward.
-  EXPECT_EQ(
-      current_kernel_max_rollforward,
-      FakeSystemState::Get()->fake_hardware()->GetMaxKernelKeyRollforward());
-  // TODO(crbug/783998): Add support for firmware when implemented.
-}
-
-TEST_F(OmahaResponseHandlerActionTest, NormalUpdateWithZeroMilestonesAllowed) {
-  OmahaResponse in;
-  in.update_exists = true;
-  in.packages.push_back({.payload_urls = {"https://RollbackTest"},
-                         .size = 1,
-                         .hash = kPayloadHashHex});
-  in.is_rollback = false;
-
-  OmahaRequestParams params;
-  params.set_rollback_allowed(true);
-  params.set_rollback_allowed_milestones(0);
-
-  const uint32_t current_kernel_max_rollforward = 0xaaaaaaaa;
-  FakeSystemState::Get()->fake_hardware()->SetMaxKernelKeyRollforward(
-      current_kernel_max_rollforward);
-
-  FakeSystemState::Get()->set_request_params(&params);
-  InstallPlan install_plan;
-  EXPECT_TRUE(DoTest(in, "", &install_plan));
-
-  // When allowed_milestones is 0, this is set to infinity.
-  EXPECT_EQ(
-      kRollforwardInfinity,
-      FakeSystemState::Get()->fake_hardware()->GetMaxKernelKeyRollforward());
-  // TODO(crbug/783998): Add support for firmware when implemented.
-}
-
-TEST_F(OmahaResponseHandlerActionTest, SystemVersionTest) {
-  OmahaResponse in;
-  in.update_exists = true;
-  in.version = "a.b.c.d";
-  in.packages.push_back({.payload_urls = {"http://package/1"},
-                         .size = 1,
-                         .hash = kPayloadHashHex,
-                         .app_id = kPayloadAppId,
-                         .fp = kPayloadFp1});
-  in.packages.push_back({.payload_urls = {"http://package/2"},
-                         .size = 2,
-                         .hash = kPayloadHashHex,
-                         .app_id = kPayloadAppId,
-                         .fp = kPayloadFp2});
-  in.more_info_url = "http://more/info";
-  InstallPlan install_plan;
-  EXPECT_TRUE(DoTest(in, "", &install_plan));
-  EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
-  EXPECT_EQ(2u, install_plan.payloads.size());
-  EXPECT_EQ(in.packages[0].size, install_plan.payloads[0].size);
-  EXPECT_EQ(in.packages[1].size, install_plan.payloads[1].size);
-  EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
-  EXPECT_EQ(expected_hash_, install_plan.payloads[1].hash);
-  EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
-  EXPECT_EQ(in.packages[1].app_id, install_plan.payloads[1].app_id);
-  EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
-  EXPECT_EQ(in.packages[1].fp, install_plan.payloads[1].fp);
-  EXPECT_EQ(in.version, install_plan.version);
-}
-
-TEST_F(OmahaResponseHandlerActionTest, TestDeferredByPolicy) {
-  OmahaResponse in;
-  in.update_exists = true;
-  in.version = "a.b.c.d";
-  in.packages.push_back({.payload_urls = {"http://foo/the_update_a.b.c.d.tgz"},
-                         .size = 12,
-                         .hash = kPayloadHashHex,
-                         .app_id = kPayloadAppId,
-                         .fp = kPayloadFp1});
-  // Setup the UpdateManager to disallow the update.
-  MockPolicy* mock_policy = new MockPolicy();
-  FakeUpdateManager* fake_update_manager =
-      FakeSystemState::Get()->fake_update_manager();
-  fake_update_manager->set_policy(mock_policy);
-  EXPECT_CALL(*mock_policy, UpdateCanBeApplied(_, _, _, _, _))
-      .WillOnce(
-          DoAll(SetArgPointee<3>(ErrorCode::kOmahaUpdateDeferredPerPolicy),
-                Return(EvalStatus::kSucceeded)));
-  // Perform the Action. It should "fail" with kOmahaUpdateDeferredPerPolicy.
-  InstallPlan install_plan;
-  EXPECT_FALSE(DoTest(in, "", &install_plan));
-  EXPECT_EQ(ErrorCode::kOmahaUpdateDeferredPerPolicy, action_result_code_);
-  // Verify that DoTest() didn't set the output install plan.
-  EXPECT_EQ("", install_plan.version);
-  // Now verify the InstallPlan that was generated.
-  install_plan = *delegate_.response_handler_action_install_plan_;
-  EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
-  EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
-  EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
-  EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
-  EXPECT_EQ(1U, install_plan.target_slot);
-  EXPECT_EQ(in.version, install_plan.version);
-}
-
-}  // namespace chromeos_update_engine
diff --git a/cros/omaha_utils.cc b/cros/omaha_utils.cc
deleted file mode 100644
index fc05cb9..0000000
--- a/cros/omaha_utils.cc
+++ /dev/null
@@ -1,37 +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/cros/omaha_utils.h"
-
-#include <base/logging.h>
-#include <base/strings/string_number_conversions.h>
-
-namespace chromeos_update_engine {
-
-const EolDate kEolDateInvalid = -9999;
-
-std::string EolDateToString(EolDate eol_date) {
-  return base::NumberToString(eol_date);
-}
-
-EolDate StringToEolDate(const std::string& eol_date) {
-  EolDate date = kEolDateInvalid;
-  if (!base::StringToInt64(eol_date, &date))
-    return kEolDateInvalid;
-  return date;
-}
-
-}  // namespace chromeos_update_engine
diff --git a/cros/omaha_utils.h b/cros/omaha_utils.h
deleted file mode 100644
index 6741635..0000000
--- a/cros/omaha_utils.h
+++ /dev/null
@@ -1,39 +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_CROS_OMAHA_UTILS_H_
-#define UPDATE_ENGINE_CROS_OMAHA_UTILS_H_
-
-#include <string>
-
-namespace chromeos_update_engine {
-
-using EolDate = int64_t;
-
-// |EolDate| indicating an invalid end-of-life date.
-extern const EolDate kEolDateInvalid;
-
-// Returns the string representation of the |eol_date|.
-std::string EolDateToString(EolDate eol_date);
-
-// Converts the end-of-life date string to an EolDate numeric value. In case
-// of an invalid string, the default |kEolDateInvalid| value will be used
-// instead.
-EolDate StringToEolDate(const std::string& eol_date);
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_CROS_OMAHA_UTILS_H_
diff --git a/cros/real_system_state.h b/cros/real_system_state.h
deleted file mode 100644
index 81a5e0e..0000000
--- a/cros/real_system_state.h
+++ /dev/null
@@ -1,181 +0,0 @@
-//
-// 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.
-//
-
-#ifndef UPDATE_ENGINE_CROS_REAL_SYSTEM_STATE_H_
-#define UPDATE_ENGINE_CROS_REAL_SYSTEM_STATE_H_
-
-#include "update_engine/common/system_state.h"
-
-#include <memory>
-#include <set>
-
-#include <policy/device_policy.h>
-#include <kiosk-app/dbus-proxies.h>
-
-#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/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 {
-
-// A real implementation of the SystemStateInterface which is
-// used by the actual product code.
-class RealSystemState : public SystemState {
- public:
-  // Constructs all system objects that do not require separate initialization;
-  // see Initialize() below for the remaining ones.
-  RealSystemState() = default;
-  ~RealSystemState() = default;
-
-  static void SetInstance(RealSystemState* system_state) {
-    CHECK(g_pointer_ == nullptr) << "SystemState has been previously set.";
-    g_pointer_ = system_state;
-    LOG_IF(FATAL, !system_state->Initialize())
-        << "Failed to initialize system state.";
-  }
-
-  // SystemState overrides.
-  void set_device_policy(const policy::DevicePolicy* device_policy) override {
-    device_policy_ = device_policy;
-  }
-
-  const policy::DevicePolicy* device_policy() override {
-    return device_policy_;
-  }
-
-  BootControlInterface* boot_control() override { return boot_control_.get(); }
-
-  ClockInterface* clock() override { return &clock_; }
-
-  ConnectionManagerInterface* connection_manager() override {
-    return connection_manager_.get();
-  }
-
-  HardwareInterface* hardware() override { return hardware_.get(); }
-
-  MetricsReporterInterface* metrics_reporter() override {
-    return &metrics_reporter_;
-  }
-
-  PrefsInterface* prefs() override { return prefs_.get(); }
-
-  PrefsInterface* powerwash_safe_prefs() override {
-    return powerwash_safe_prefs_.get();
-  }
-
-  PayloadStateInterface* payload_state() override { return &payload_state_; }
-
-  UpdateAttempter* update_attempter() override {
-    return update_attempter_.get();
-  }
-
-  OmahaRequestParams* request_params() override { return &request_params_; }
-
-  P2PManager* p2p_manager() override { return p2p_manager_.get(); }
-
-  chromeos_update_manager::UpdateManager* update_manager() override {
-    return update_manager_.get();
-  }
-
-  PowerManagerInterface* power_manager() override {
-    return power_manager_.get();
-  }
-
-  bool system_rebooted() override { return system_rebooted_; }
-
-  DlcServiceInterface* dlcservice() override { return dlcservice_.get(); }
-
- private:
-  // Initializes and sets systems objects that require an initialization
-  // separately from construction. Returns |true| on success.
-  bool Initialize();
-
-  // Real DBus proxies using the DBus connection.
-  std::unique_ptr<org::chromium::KioskAppServiceInterfaceProxy>
-      kiosk_app_proxy_;
-
-  // Interface for the power manager.
-  std::unique_ptr<PowerManagerInterface> power_manager_;
-
-  // Interface for dlcservice.
-  std::unique_ptr<DlcServiceInterface> dlcservice_;
-
-  // Interface for the bootloader control.
-  std::unique_ptr<BootControlInterface> boot_control_;
-
-  // Interface for the clock.
-  Clock clock_;
-
-  // The latest device policy object from the policy provider.
-  const policy::DevicePolicy* device_policy_{nullptr};
-
-  // The connection manager object that makes download decisions depending on
-  // the current type of connection.
-  std::unique_ptr<ConnectionManagerInterface> connection_manager_;
-
-  // Interface for the hardware functions.
-  std::unique_ptr<HardwareInterface> hardware_;
-
-  // The Metrics reporter for reporting UMA stats.
-  MetricsReporterOmaha metrics_reporter_;
-
-  // Interface for persisted store.
-  std::unique_ptr<PrefsInterface> prefs_;
-
-  // Interface for persisted store that persists across powerwashes.
-  std::unique_ptr<PrefsInterface> powerwash_safe_prefs_;
-
-  // All state pertaining to payload state such as response, URL, backoff
-  // states.
-  PayloadState payload_state_;
-
-  // OpenSSLWrapper and CertificateChecker used for checking SSL certificates.
-  OpenSSLWrapper openssl_wrapper_;
-  std::unique_ptr<CertificateChecker> certificate_checker_;
-
-  // Pointer to the update attempter object.
-  std::unique_ptr<UpdateAttempter> update_attempter_;
-
-  // Common parameters for all Omaha requests.
-  OmahaRequestParams request_params_;
-
-  std::unique_ptr<P2PManager> p2p_manager_;
-
-  std::unique_ptr<chromeos_update_manager::UpdateManager> update_manager_;
-
-  policy::PolicyProvider policy_provider_;
-
-  // If true, this is the first instance of the update engine since the system
-  // rebooted. Important for tracking whether you are running instance of the
-  // update engine on first boot or due to a crash/restart.
-  bool system_rebooted_{false};
-};
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_CROS_REAL_SYSTEM_STATE_H_
diff --git a/cros/requisition_util.cc b/cros/requisition_util.cc
deleted file mode 100644
index 6296d0b..0000000
--- a/cros/requisition_util.cc
+++ /dev/null
@@ -1,69 +0,0 @@
-//
-// 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
deleted file mode 100644
index 6ec4783..0000000
--- a/cros/requisition_util.h
+++ /dev/null
@@ -1,32 +0,0 @@
-//
-// 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
deleted file mode 100644
index 269585e..0000000
--- a/cros/requisition_util_unittest.cc
+++ /dev/null
@@ -1,94 +0,0 @@
-//
-// 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/cros/update_attempter_unittest.cc b/cros/update_attempter_unittest.cc
deleted file mode 100644
index a7f5236..0000000
--- a/cros/update_attempter_unittest.cc
+++ /dev/null
@@ -1,2479 +0,0 @@
-//
-// Copyright (C) 2012 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/update_attempter.h"
-
-#include <stdint.h>
-
-#include <limits>
-#include <map>
-#include <memory>
-#include <string>
-#include <unordered_set>
-
-#include <base/files/file_util.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>
-#include <gtest/gtest.h>
-#include <policy/libpolicy.h>
-#include <policy/mock_device_policy.h>
-#include <policy/mock_libpolicy.h>
-
-#include "update_engine/common/constants.h"
-#include "update_engine/common/dlcservice_interface.h"
-#include "update_engine/common/mock_action.h"
-#include "update_engine/common/mock_action_processor.h"
-#include "update_engine/common/mock_http_fetcher.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/cros/download_action_chromeos.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/payload_consumer/filesystem_verifier_action.h"
-#include "update_engine/payload_consumer/install_plan.h"
-#include "update_engine/payload_consumer/payload_constants.h"
-#include "update_engine/payload_consumer/postinstall_runner_action.h"
-#include "update_engine/update_boot_flags_action.h"
-#include "update_engine/update_manager/mock_update_manager.h"
-
-using base::Time;
-using base::TimeDelta;
-using chromeos_update_manager::EvalStatus;
-using chromeos_update_manager::MockUpdateManager;
-using chromeos_update_manager::StagingSchedule;
-using chromeos_update_manager::UpdateCheckParams;
-using policy::DevicePolicy;
-using std::map;
-using std::string;
-using std::unique_ptr;
-using std::unordered_set;
-using std::vector;
-using testing::_;
-using testing::Contains;
-using testing::DoAll;
-using testing::ElementsAre;
-using testing::Field;
-using testing::InSequence;
-using testing::Invoke;
-using testing::Ne;
-using testing::NiceMock;
-using testing::Pointee;
-using testing::Property;
-using testing::Return;
-using testing::ReturnPointee;
-using testing::ReturnRef;
-using testing::SaveArg;
-using testing::SetArgPointee;
-using update_engine::UpdateAttemptFlags;
-using update_engine::UpdateEngineStatus;
-using update_engine::UpdateStatus;
-
-namespace chromeos_update_engine {
-
-namespace {
-
-const UpdateStatus kNonIdleUpdateStatuses[] = {
-    UpdateStatus::CHECKING_FOR_UPDATE,
-    UpdateStatus::UPDATE_AVAILABLE,
-    UpdateStatus::DOWNLOADING,
-    UpdateStatus::VERIFYING,
-    UpdateStatus::FINALIZING,
-    UpdateStatus::UPDATED_NEED_REBOOT,
-    UpdateStatus::REPORTING_ERROR_EVENT,
-    UpdateStatus::ATTEMPTING_ROLLBACK,
-    UpdateStatus::DISABLED,
-    UpdateStatus::NEED_PERMISSION_TO_UPDATE,
-};
-
-struct CheckForUpdateTestParams {
-  // Setups + Inputs:
-  UpdateStatus status = UpdateStatus::IDLE;
-  string app_version = "fake_app_version";
-  string omaha_url = "fake_omaha_url";
-  UpdateAttemptFlags flags = UpdateAttemptFlags::kNone;
-  bool is_official_build = true;
-  bool are_dev_features_enabled = false;
-
-  // Expects:
-  string expected_forced_app_version = "";
-  string expected_forced_omaha_url = "";
-  bool should_schedule_updates_be_called = true;
-  bool expected_result = true;
-};
-
-struct OnUpdateScheduledTestParams {
-  // Setups + Inputs:
-  UpdateCheckParams params = {};
-  EvalStatus status = EvalStatus::kFailed;
-  // Expects:
-  UpdateStatus exit_status = UpdateStatus::IDLE;
-  bool should_schedule_updates_be_called = false;
-  bool should_update_be_called = false;
-};
-
-struct ProcessingDoneTestParams {
-  // Setups + Inputs:
-  bool is_install = false;
-  UpdateStatus status = UpdateStatus::CHECKING_FOR_UPDATE;
-  ActionProcessor* processor = nullptr;
-  ErrorCode code = ErrorCode::kSuccess;
-  map<string, OmahaRequestParams::AppParams> dlc_apps_params;
-
-  // Expects:
-  const bool kExpectedIsInstall = false;
-  bool should_schedule_updates_be_called = true;
-  UpdateStatus expected_exit_status = UpdateStatus::IDLE;
-  bool should_install_completed_be_called = false;
-  bool should_update_completed_be_called = false;
-  vector<string> args_to_install_completed;
-  vector<string> args_to_update_completed;
-};
-
-class MockDlcService : public DlcServiceInterface {
- public:
-  MOCK_METHOD1(GetDlcsToUpdate, bool(vector<string>*));
-  MOCK_METHOD1(InstallCompleted, bool(const vector<string>&));
-  MOCK_METHOD1(UpdateCompleted, bool(const vector<string>&));
-};
-
-}  // namespace
-
-const char kRollbackVersion[] = "10575.39.2";
-
-// Test a subclass rather than the main class directly so that we can mock out
-// methods within the class. There're explicit unit tests for the mocked out
-// methods.
-class UpdateAttempterUnderTest : public UpdateAttempter {
- public:
-  UpdateAttempterUnderTest() : UpdateAttempter(nullptr) {}
-
-  void Update(const UpdateCheckParams& params) override {
-    update_called_ = true;
-    if (do_update_) {
-      UpdateAttempter::Update(params);
-      return;
-    }
-    LOG(INFO) << "[TEST] Update() disabled.";
-    status_ = UpdateStatus::CHECKING_FOR_UPDATE;
-  }
-
-  void DisableUpdate() { do_update_ = false; }
-
-  bool WasUpdateCalled() const { return update_called_; }
-
-  // Wrap the update scheduling method, allowing us to opt out of scheduled
-  // updates for testing purposes.
-  bool ScheduleUpdates() override {
-    schedule_updates_called_ = true;
-    if (do_schedule_updates_)
-      return UpdateAttempter::ScheduleUpdates();
-    LOG(INFO) << "[TEST] Update scheduling disabled.";
-    waiting_for_scheduled_check_ = true;
-    return true;
-  }
-
-  void DisableScheduleUpdates() { do_schedule_updates_ = false; }
-
-  // Indicates whether |ScheduleUpdates()| was called.
-  bool WasScheduleUpdatesCalled() const { return schedule_updates_called_; }
-
-  // Need to expose following private members of |UpdateAttempter| for tests.
-  const string& forced_app_version() const { return forced_app_version_; }
-  const string& forced_omaha_url() const { return forced_omaha_url_; }
-
-  // Need to expose |waiting_for_scheduled_check_| for testing.
-  void SetWaitingForScheduledCheck(bool waiting) {
-    waiting_for_scheduled_check_ = waiting;
-  }
-
- private:
-  // Used for overrides of |Update()|.
-  bool update_called_ = false;
-  bool do_update_ = true;
-
-  // Used for overrides of |ScheduleUpdates()|.
-  bool schedule_updates_called_ = false;
-  bool do_schedule_updates_ = true;
-};
-
-class UpdateAttempterTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    // Override system state members.
-    FakeSystemState::CreateInstance();
-    FakeSystemState::Get()->set_connection_manager(&mock_connection_manager);
-    FakeSystemState::Get()->set_update_attempter(&attempter_);
-    FakeSystemState::Get()->set_dlcservice(&mock_dlcservice_);
-    FakeSystemState::Get()->set_update_manager(&mock_update_manager_);
-    loop_.SetAsCurrent();
-
-    prefs_ = FakeSystemState::Get()->fake_prefs();
-    certificate_checker_.reset(
-        new CertificateChecker(prefs_, &openssl_wrapper_));
-    certificate_checker_->Init();
-
-    attempter_.set_forced_update_pending_callback(
-        new base::Callback<void(bool, bool)>(base::Bind([](bool, bool) {})));
-    // Finish initializing the attempter.
-    attempter_.Init();
-
-    EXPECT_EQ(0, attempter_.http_response_code_);
-    EXPECT_EQ(UpdateStatus::IDLE, attempter_.status_);
-    EXPECT_EQ(0.0, attempter_.download_progress_);
-    EXPECT_EQ(0, attempter_.last_checked_time_);
-    EXPECT_EQ("0.0.0.0", attempter_.new_version_);
-    EXPECT_EQ(0ULL, attempter_.new_payload_size_);
-    processor_ = new NiceMock<MockActionProcessor>();
-    attempter_.processor_.reset(processor_);  // Transfers ownership.
-
-    // Setup store/load semantics of P2P properties via the mock |PayloadState|.
-    actual_using_p2p_for_downloading_ = false;
-    EXPECT_CALL(*FakeSystemState::Get()->mock_payload_state(),
-                SetUsingP2PForDownloading(_))
-        .WillRepeatedly(SaveArg<0>(&actual_using_p2p_for_downloading_));
-    EXPECT_CALL(*FakeSystemState::Get()->mock_payload_state(),
-                GetUsingP2PForDownloading())
-        .WillRepeatedly(ReturnPointee(&actual_using_p2p_for_downloading_));
-    actual_using_p2p_for_sharing_ = false;
-    EXPECT_CALL(*FakeSystemState::Get()->mock_payload_state(),
-                SetUsingP2PForSharing(_))
-        .WillRepeatedly(SaveArg<0>(&actual_using_p2p_for_sharing_));
-    EXPECT_CALL(*FakeSystemState::Get()->mock_payload_state(),
-                GetUsingP2PForDownloading())
-        .WillRepeatedly(ReturnPointee(&actual_using_p2p_for_sharing_));
-  }
-
- public:
-  void ScheduleQuitMainLoop();
-
-  // Callbacks to run the different tests from the main loop.
-  void UpdateTestStart();
-  void UpdateTestVerify();
-  void RollbackTestStart(bool enterprise_rollback, bool valid_slot);
-  void RollbackTestVerify();
-  void PingOmahaTestStart();
-  void ReadScatterFactorFromPolicyTestStart();
-  void DecrementUpdateCheckCountTestStart();
-  void NoScatteringDoneDuringManualUpdateTestStart();
-  void P2PNotEnabledStart();
-  void P2PEnabledStart();
-  void P2PEnabledInteractiveStart();
-  void P2PEnabledStartingFailsStart();
-  void P2PEnabledHousekeepingFailsStart();
-  void SessionIdTestChange();
-  void SessionIdTestEnforceEmptyStrPingOmaha();
-  void SessionIdTestConsistencyInUpdateFlow();
-  void SessionIdTestInDownloadAction();
-  void ResetRollbackHappenedStart(bool is_consumer,
-                                  bool is_policy_available,
-                                  bool expected_reset);
-  // Staging related callbacks.
-  void SetUpStagingTest(const StagingSchedule& schedule);
-  void CheckStagingOff();
-  void StagingSetsPrefsAndTurnsOffScatteringStart();
-  void StagingOffIfInteractiveStart();
-  void StagingOffIfOobeStart();
-
-  bool actual_using_p2p_for_downloading() {
-    return actual_using_p2p_for_downloading_;
-  }
-  bool actual_using_p2p_for_sharing() { return actual_using_p2p_for_sharing_; }
-
-  // |CheckForUpdate()| related member functions.
-  void TestCheckForUpdate();
-
-  // |OnUpdateScheduled()| related member functions.
-  void TestOnUpdateScheduled();
-
-  // |ProcessingDone()| related member functions.
-  void TestProcessingDone();
-
-  base::SingleThreadTaskExecutor base_loop_{base::MessagePumpType::IO};
-  brillo::BaseMessageLoop loop_{base_loop_.task_runner()};
-
-  UpdateAttempterUnderTest attempter_;
-  OpenSSLWrapper openssl_wrapper_;
-  std::unique_ptr<CertificateChecker> certificate_checker_;
-  MockDlcService mock_dlcservice_;
-  MockUpdateManager mock_update_manager_;
-
-  NiceMock<MockActionProcessor>* processor_;
-  NiceMock<MockConnectionManager> mock_connection_manager;
-
-  FakePrefs* prefs_;
-
-  // |CheckForUpdate()| test params.
-  CheckForUpdateTestParams cfu_params_;
-
-  // |OnUpdateScheduled()| test params.
-  OnUpdateScheduledTestParams ous_params_;
-
-  // |ProcessingDone()| test params.
-  ProcessingDoneTestParams pd_params_;
-
-  bool actual_using_p2p_for_downloading_;
-  bool actual_using_p2p_for_sharing_;
-};
-
-void UpdateAttempterTest::TestCheckForUpdate() {
-  // Setup
-  attempter_.status_ = cfu_params_.status;
-  FakeSystemState::Get()->fake_hardware()->SetIsOfficialBuild(
-      cfu_params_.is_official_build);
-  FakeSystemState::Get()->fake_hardware()->SetAreDevFeaturesEnabled(
-      cfu_params_.are_dev_features_enabled);
-
-  // Invocation
-  EXPECT_EQ(
-      cfu_params_.expected_result,
-      attempter_.CheckForUpdate(
-          cfu_params_.app_version, cfu_params_.omaha_url, cfu_params_.flags));
-
-  // Verify
-  EXPECT_EQ(cfu_params_.expected_forced_app_version,
-            attempter_.forced_app_version());
-  EXPECT_EQ(cfu_params_.expected_forced_omaha_url,
-            attempter_.forced_omaha_url());
-  EXPECT_EQ(cfu_params_.should_schedule_updates_be_called,
-            attempter_.WasScheduleUpdatesCalled());
-}
-
-void UpdateAttempterTest::TestProcessingDone() {
-  // Setup
-  attempter_.DisableScheduleUpdates();
-  attempter_.is_install_ = pd_params_.is_install;
-  attempter_.status_ = pd_params_.status;
-  attempter_.omaha_request_params_->set_dlc_apps_params(
-      pd_params_.dlc_apps_params);
-
-  // Expects
-  if (pd_params_.should_install_completed_be_called)
-    EXPECT_CALL(mock_dlcservice_,
-                InstallCompleted(pd_params_.args_to_install_completed))
-        .WillOnce(Return(true));
-  else
-    EXPECT_CALL(mock_dlcservice_, InstallCompleted(_)).Times(0);
-  if (pd_params_.should_update_completed_be_called)
-    EXPECT_CALL(mock_dlcservice_,
-                UpdateCompleted(pd_params_.args_to_update_completed))
-        .WillOnce(Return(true));
-  else
-    EXPECT_CALL(mock_dlcservice_, UpdateCompleted(_)).Times(0);
-
-  // Invocation
-  attempter_.ProcessingDone(pd_params_.processor, pd_params_.code);
-
-  // Verify
-  EXPECT_EQ(pd_params_.kExpectedIsInstall, attempter_.is_install_);
-  EXPECT_EQ(pd_params_.should_schedule_updates_be_called,
-            attempter_.WasScheduleUpdatesCalled());
-  EXPECT_EQ(pd_params_.expected_exit_status, attempter_.status_);
-}
-
-void UpdateAttempterTest::ScheduleQuitMainLoop() {
-  loop_.PostTask(
-      FROM_HERE,
-      base::Bind([](brillo::BaseMessageLoop* loop) { loop->BreakLoop(); },
-                 base::Unretained(&loop_)));
-}
-
-void UpdateAttempterTest::SessionIdTestChange() {
-  EXPECT_NE(UpdateStatus::UPDATED_NEED_REBOOT, attempter_.status());
-  const auto old_session_id = attempter_.session_id_;
-  attempter_.Update({});
-  EXPECT_NE(old_session_id, attempter_.session_id_);
-  ScheduleQuitMainLoop();
-}
-
-TEST_F(UpdateAttempterTest, SessionIdTestChange) {
-  loop_.PostTask(FROM_HERE,
-                 base::Bind(&UpdateAttempterTest::SessionIdTestChange,
-                            base::Unretained(this)));
-  loop_.Run();
-}
-
-void UpdateAttempterTest::SessionIdTestEnforceEmptyStrPingOmaha() {
-  // The |session_id_| should not be changed and should remain as an empty
-  // string when |status_| is |UPDATED_NEED_REBOOT| (only for consistency)
-  // and |PingOmaha()| is called.
-  attempter_.DisableScheduleUpdates();
-  attempter_.status_ = UpdateStatus::UPDATED_NEED_REBOOT;
-  const auto old_session_id = attempter_.session_id_;
-  auto CheckIfEmptySessionId = [](AbstractAction* aa) {
-    if (aa->Type() == OmahaRequestAction::StaticType()) {
-      EXPECT_TRUE(static_cast<OmahaRequestAction*>(aa)->session_id_.empty());
-    }
-  };
-  EXPECT_CALL(*processor_, EnqueueAction(Pointee(_)))
-      .WillRepeatedly(Invoke(CheckIfEmptySessionId));
-  EXPECT_CALL(*processor_, StartProcessing());
-  attempter_.PingOmaha();
-  EXPECT_EQ(old_session_id, attempter_.session_id_);
-  EXPECT_EQ(UpdateStatus::UPDATED_NEED_REBOOT, attempter_.status_);
-  ScheduleQuitMainLoop();
-}
-
-TEST_F(UpdateAttempterTest, SessionIdTestEnforceEmptyStrPingOmaha) {
-  loop_.PostTask(
-      FROM_HERE,
-      base::Bind(&UpdateAttempterTest::SessionIdTestEnforceEmptyStrPingOmaha,
-                 base::Unretained(this)));
-  loop_.Run();
-}
-
-void UpdateAttempterTest::SessionIdTestConsistencyInUpdateFlow() {
-  // All session IDs passed into |OmahaRequestActions| should be enforced to
-  // have the same value in |BuildUpdateActions()|.
-  unordered_set<string> session_ids;
-  // Gather all the session IDs being passed to |OmahaRequestActions|.
-  auto CheckSessionId = [&session_ids](AbstractAction* aa) {
-    if (aa->Type() == OmahaRequestAction::StaticType())
-      session_ids.insert(static_cast<OmahaRequestAction*>(aa)->session_id_);
-  };
-  EXPECT_CALL(*processor_, EnqueueAction(Pointee(_)))
-      .WillRepeatedly(Invoke(CheckSessionId));
-  attempter_.BuildUpdateActions(false);
-  // Validate that all the session IDs are the same.
-  EXPECT_EQ(1, session_ids.size());
-  ScheduleQuitMainLoop();
-}
-
-TEST_F(UpdateAttempterTest, SessionIdTestConsistencyInUpdateFlow) {
-  loop_.PostTask(
-      FROM_HERE,
-      base::Bind(&UpdateAttempterTest::SessionIdTestConsistencyInUpdateFlow,
-                 base::Unretained(this)));
-  loop_.Run();
-}
-
-void UpdateAttempterTest::SessionIdTestInDownloadAction() {
-  // The session ID passed into |DownloadAction|'s |LibcurlHttpFetcher| should
-  // be enforced to be included in the HTTP header as X-Goog-Update-SessionId.
-  string header_value;
-  auto CheckSessionIdInDownloadAction = [&header_value](AbstractAction* aa) {
-    if (aa->Type() == DownloadActionChromeos::StaticType()) {
-      DownloadActionChromeos* da = static_cast<DownloadActionChromeos*>(aa);
-      EXPECT_TRUE(da->http_fetcher()->GetHeader(kXGoogleUpdateSessionId,
-                                                &header_value));
-    }
-  };
-  EXPECT_CALL(*processor_, EnqueueAction(Pointee(_)))
-      .WillRepeatedly(Invoke(CheckSessionIdInDownloadAction));
-  attempter_.BuildUpdateActions(false);
-  // Validate that X-Goog-Update_SessionId is set correctly in HTTP Header.
-  EXPECT_EQ(attempter_.session_id_, header_value);
-  ScheduleQuitMainLoop();
-}
-
-TEST_F(UpdateAttempterTest, SessionIdTestInDownloadAction) {
-  loop_.PostTask(FROM_HERE,
-                 base::Bind(&UpdateAttempterTest::SessionIdTestInDownloadAction,
-                            base::Unretained(this)));
-  loop_.Run();
-}
-
-TEST_F(UpdateAttempterTest, ActionCompletedDownloadTest) {
-  unique_ptr<MockHttpFetcher> fetcher(new MockHttpFetcher("", 0, nullptr));
-  fetcher->FailTransfer(503);  // Sets the HTTP response code.
-  DownloadActionChromeos action(
-      prefs_, nullptr, nullptr, fetcher.release(), false /* interactive */);
-  attempter_.ActionCompleted(nullptr, &action, ErrorCode::kSuccess);
-  EXPECT_FALSE(prefs_->Exists(kPrefsDeltaUpdateFailures));
-  EXPECT_EQ(UpdateStatus::FINALIZING, attempter_.status());
-  EXPECT_EQ(0.0, attempter_.download_progress_);
-  ASSERT_EQ(nullptr, attempter_.error_event_.get());
-}
-
-TEST_F(UpdateAttempterTest, ActionCompletedErrorTest) {
-  MockAction action;
-  EXPECT_CALL(action, Type()).WillRepeatedly(Return("MockAction"));
-  attempter_.status_ = UpdateStatus::DOWNLOADING;
-  attempter_.ActionCompleted(nullptr, &action, ErrorCode::kError);
-  ASSERT_NE(nullptr, attempter_.error_event_.get());
-}
-
-TEST_F(UpdateAttempterTest, DownloadProgressAccumulationTest) {
-  // Simple test case, where all the values match (nothing was skipped)
-  uint64_t bytes_progressed_1 = 1024 * 1024;  // 1MB
-  uint64_t bytes_progressed_2 = 1024 * 1024;  // 1MB
-  uint64_t bytes_received_1 = bytes_progressed_1;
-  uint64_t bytes_received_2 = bytes_received_1 + bytes_progressed_2;
-  uint64_t bytes_total = 20 * 1024 * 1024;  // 20MB
-
-  double progress_1 =
-      static_cast<double>(bytes_received_1) / static_cast<double>(bytes_total);
-  double progress_2 =
-      static_cast<double>(bytes_received_2) / static_cast<double>(bytes_total);
-
-  EXPECT_EQ(0.0, attempter_.download_progress_);
-  // This is set via inspecting the InstallPlan payloads when the
-  // |OmahaResponseAction| is completed.
-  attempter_.new_payload_size_ = bytes_total;
-  NiceMock<MockServiceObserver> observer;
-  EXPECT_CALL(observer,
-              SendStatusUpdate(AllOf(
-                  Field(&UpdateEngineStatus::progress, progress_1),
-                  Field(&UpdateEngineStatus::status, UpdateStatus::DOWNLOADING),
-                  Field(&UpdateEngineStatus::new_size_bytes, bytes_total))));
-  EXPECT_CALL(observer,
-              SendStatusUpdate(AllOf(
-                  Field(&UpdateEngineStatus::progress, progress_2),
-                  Field(&UpdateEngineStatus::status, UpdateStatus::DOWNLOADING),
-                  Field(&UpdateEngineStatus::new_size_bytes, bytes_total))));
-  attempter_.AddObserver(&observer);
-  attempter_.BytesReceived(bytes_progressed_1, bytes_received_1, bytes_total);
-  EXPECT_EQ(progress_1, attempter_.download_progress_);
-  // This iteration validates that a later set of updates to the variables are
-  // properly handled (so that |getStatus()| will return the same progress info
-  // as the callback is receiving.
-  attempter_.BytesReceived(bytes_progressed_2, bytes_received_2, bytes_total);
-  EXPECT_EQ(progress_2, attempter_.download_progress_);
-}
-
-TEST_F(UpdateAttempterTest, ChangeToDownloadingOnReceivedBytesTest) {
-  // The transition into |UpdateStatus::DOWNLOADING| happens when the
-  // first bytes are received.
-  uint64_t bytes_progressed = 1024 * 1024;    // 1MB
-  uint64_t bytes_received = 2 * 1024 * 1024;  // 2MB
-  uint64_t bytes_total = 20 * 1024 * 1024;    // 300MB
-  attempter_.status_ = UpdateStatus::CHECKING_FOR_UPDATE;
-  // This is set via inspecting the InstallPlan payloads when the
-  // |OmahaResponseAction| is completed.
-  attempter_.new_payload_size_ = bytes_total;
-  EXPECT_EQ(0.0, attempter_.download_progress_);
-  NiceMock<MockServiceObserver> observer;
-  EXPECT_CALL(observer,
-              SendStatusUpdate(AllOf(
-                  Field(&UpdateEngineStatus::status, UpdateStatus::DOWNLOADING),
-                  Field(&UpdateEngineStatus::new_size_bytes, bytes_total))));
-  attempter_.AddObserver(&observer);
-  attempter_.BytesReceived(bytes_progressed, bytes_received, bytes_total);
-  EXPECT_EQ(UpdateStatus::DOWNLOADING, attempter_.status_);
-}
-
-TEST_F(UpdateAttempterTest, BroadcastCompleteDownloadTest) {
-  // There is a special case to ensure that at 100% downloaded,
-  // |download_progress_| is updated and broadcastest.
-  uint64_t bytes_progressed = 0;              // ignored
-  uint64_t bytes_received = 5 * 1024 * 1024;  // ignored
-  uint64_t bytes_total = 5 * 1024 * 1024;     // 300MB
-  attempter_.status_ = UpdateStatus::DOWNLOADING;
-  attempter_.new_payload_size_ = bytes_total;
-  EXPECT_EQ(0.0, attempter_.download_progress_);
-  NiceMock<MockServiceObserver> observer;
-  EXPECT_CALL(observer,
-              SendStatusUpdate(AllOf(
-                  Field(&UpdateEngineStatus::progress, 1.0),
-                  Field(&UpdateEngineStatus::status, UpdateStatus::DOWNLOADING),
-                  Field(&UpdateEngineStatus::new_size_bytes, bytes_total))));
-  attempter_.AddObserver(&observer);
-  attempter_.BytesReceived(bytes_progressed, bytes_received, bytes_total);
-  EXPECT_EQ(1.0, attempter_.download_progress_);
-}
-
-TEST_F(UpdateAttempterTest, ActionCompletedOmahaRequestTest) {
-  unique_ptr<MockHttpFetcher> fetcher(new MockHttpFetcher("", 0, nullptr));
-  fetcher->FailTransfer(500);  // Sets the HTTP response code.
-  OmahaRequestAction action(nullptr, std::move(fetcher), false, "");
-  ObjectCollectorAction<OmahaResponse> collector_action;
-  BondActions(&action, &collector_action);
-  OmahaResponse response;
-  response.poll_interval = 234;
-  action.SetOutputObject(response);
-  attempter_.ActionCompleted(nullptr, &action, ErrorCode::kSuccess);
-  EXPECT_FALSE(prefs_->Exists(kPrefsDeltaUpdateFailures));
-  EXPECT_EQ(500, attempter_.http_response_code());
-  EXPECT_EQ(UpdateStatus::IDLE, attempter_.status());
-  EXPECT_EQ(234U, attempter_.server_dictated_poll_interval_);
-  ASSERT_TRUE(attempter_.error_event_.get() == nullptr);
-}
-
-TEST_F(UpdateAttempterTest, ConstructWithUpdatedMarkerTest) {
-  string boot_id;
-  EXPECT_TRUE(utils::GetBootId(&boot_id));
-  FakeSystemState::Get()->fake_prefs()->SetString(kPrefsUpdateCompletedOnBootId,
-                                                  boot_id);
-  attempter_.Init();
-  EXPECT_EQ(UpdateStatus::UPDATED_NEED_REBOOT, attempter_.status());
-}
-
-TEST_F(UpdateAttempterTest, GetErrorCodeForActionTest) {
-  EXPECT_EQ(ErrorCode::kSuccess,
-            GetErrorCodeForAction(nullptr, ErrorCode::kSuccess));
-
-  OmahaRequestAction omaha_request_action(nullptr, nullptr, false, "");
-  EXPECT_EQ(ErrorCode::kOmahaRequestError,
-            GetErrorCodeForAction(&omaha_request_action, ErrorCode::kError));
-  OmahaResponseHandlerAction omaha_response_handler_action;
-  EXPECT_EQ(
-      ErrorCode::kOmahaResponseHandlerError,
-      GetErrorCodeForAction(&omaha_response_handler_action, ErrorCode::kError));
-  DynamicPartitionControlStub dynamic_control_stub;
-  FilesystemVerifierAction filesystem_verifier_action(&dynamic_control_stub);
-  EXPECT_EQ(
-      ErrorCode::kFilesystemVerifierError,
-      GetErrorCodeForAction(&filesystem_verifier_action, ErrorCode::kError));
-  PostinstallRunnerAction postinstall_runner_action(
-      FakeSystemState::Get()->fake_boot_control(),
-      FakeSystemState::Get()->fake_hardware());
-  EXPECT_EQ(
-      ErrorCode::kPostinstallRunnerError,
-      GetErrorCodeForAction(&postinstall_runner_action, ErrorCode::kError));
-  MockAction action_mock;
-  EXPECT_CALL(action_mock, Type()).WillOnce(Return("MockAction"));
-  EXPECT_EQ(ErrorCode::kError,
-            GetErrorCodeForAction(&action_mock, ErrorCode::kError));
-}
-
-TEST_F(UpdateAttempterTest, DisableDeltaUpdateIfNeededTest) {
-  attempter_.omaha_request_params_->set_delta_okay(true);
-  attempter_.DisableDeltaUpdateIfNeeded();
-  EXPECT_TRUE(attempter_.omaha_request_params_->delta_okay());
-  prefs_->SetInt64(kPrefsDeltaUpdateFailures,
-                   UpdateAttempter::kMaxDeltaUpdateFailures - 1);
-  attempter_.DisableDeltaUpdateIfNeeded();
-  EXPECT_TRUE(attempter_.omaha_request_params_->delta_okay());
-  prefs_->SetInt64(kPrefsDeltaUpdateFailures,
-                   UpdateAttempter::kMaxDeltaUpdateFailures);
-  attempter_.DisableDeltaUpdateIfNeeded();
-  EXPECT_FALSE(attempter_.omaha_request_params_->delta_okay());
-  attempter_.DisableDeltaUpdateIfNeeded();
-  EXPECT_FALSE(attempter_.omaha_request_params_->delta_okay());
-}
-
-TEST_F(UpdateAttempterTest, MarkDeltaUpdateFailureTest) {
-  attempter_.MarkDeltaUpdateFailure();
-
-  EXPECT_TRUE(prefs_->SetInt64(kPrefsDeltaUpdateFailures, -1));
-  attempter_.MarkDeltaUpdateFailure();
-  int64_t value = 0;
-  EXPECT_TRUE(prefs_->GetInt64(kPrefsDeltaUpdateFailures, &value));
-  EXPECT_EQ(value, 1);
-
-  attempter_.MarkDeltaUpdateFailure();
-  EXPECT_TRUE(prefs_->GetInt64(kPrefsDeltaUpdateFailures, &value));
-  EXPECT_EQ(value, 2);
-
-  EXPECT_TRUE(prefs_->SetInt64(kPrefsDeltaUpdateFailures,
-                               UpdateAttempter::kMaxDeltaUpdateFailures));
-  attempter_.MarkDeltaUpdateFailure();
-  EXPECT_TRUE(prefs_->GetInt64(kPrefsDeltaUpdateFailures, &value));
-  EXPECT_EQ(value, UpdateAttempter::kMaxDeltaUpdateFailures + 1);
-}
-
-TEST_F(UpdateAttempterTest, ScheduleErrorEventActionNoEventTest) {
-  EXPECT_CALL(*processor_, EnqueueAction(_)).Times(0);
-  EXPECT_CALL(*processor_, StartProcessing()).Times(0);
-  EXPECT_CALL(*FakeSystemState::Get()->mock_payload_state(), UpdateFailed(_))
-      .Times(0);
-  OmahaResponse response;
-  string url1 = "http://url1";
-  response.packages.push_back({.payload_urls = {url1, "https://url"}});
-  EXPECT_CALL(*(FakeSystemState::Get()->mock_payload_state()), GetCurrentUrl())
-      .WillRepeatedly(Return(url1));
-  FakeSystemState::Get()->mock_payload_state()->SetResponse(response);
-  attempter_.ScheduleErrorEventAction();
-  EXPECT_EQ(url1,
-            FakeSystemState::Get()->mock_payload_state()->GetCurrentUrl());
-}
-
-TEST_F(UpdateAttempterTest, ScheduleErrorEventActionTest) {
-  EXPECT_CALL(*processor_,
-              EnqueueAction(Pointee(Property(
-                  &AbstractAction::Type, OmahaRequestAction::StaticType()))));
-  EXPECT_CALL(*processor_, StartProcessing());
-  ErrorCode err = ErrorCode::kError;
-  EXPECT_CALL(*FakeSystemState::Get()->mock_payload_state(), UpdateFailed(err));
-  attempter_.error_event_.reset(new OmahaEvent(
-      OmahaEvent::kTypeUpdateComplete, OmahaEvent::kResultError, err));
-  attempter_.ScheduleErrorEventAction();
-  EXPECT_EQ(UpdateStatus::REPORTING_ERROR_EVENT, attempter_.status());
-}
-
-namespace {
-// Actions that will be built as part of an update check.
-vector<string> GetUpdateActionTypes() {
-  return {OmahaRequestAction::StaticType(),
-          OmahaResponseHandlerAction::StaticType(),
-          UpdateBootFlagsAction::StaticType(),
-          OmahaRequestAction::StaticType(),
-          DownloadActionChromeos::StaticType(),
-          OmahaRequestAction::StaticType(),
-          FilesystemVerifierAction::StaticType(),
-          PostinstallRunnerAction::StaticType(),
-          OmahaRequestAction::StaticType()};
-}
-
-// Actions that will be built as part of a user-initiated rollback.
-vector<string> GetRollbackActionTypes() {
-  return {InstallPlanAction::StaticType(),
-          PostinstallRunnerAction::StaticType()};
-}
-
-const StagingSchedule kValidStagingSchedule = {
-    {4, 10}, {10, 40}, {19, 70}, {26, 100}};
-
-}  // namespace
-
-void UpdateAttempterTest::UpdateTestStart() {
-  attempter_.set_http_response_code(200);
-
-  // Expect that the device policy is loaded by the |UpdateAttempter| at some
-  // point by calling |RefreshDevicePolicy()|.
-  auto device_policy = std::make_unique<policy::MockDevicePolicy>();
-  EXPECT_CALL(*device_policy, LoadPolicy())
-      .Times(testing::AtLeast(1))
-      .WillRepeatedly(Return(true));
-  attempter_.policy_provider_.reset(
-      new policy::PolicyProvider(std::move(device_policy)));
-
-  {
-    InSequence s;
-    for (const auto& update_action_type : GetUpdateActionTypes()) {
-      EXPECT_CALL(*processor_,
-                  EnqueueAction(Pointee(
-                      Property(&AbstractAction::Type, update_action_type))));
-    }
-    EXPECT_CALL(*processor_, StartProcessing());
-  }
-
-  attempter_.Update({});
-  loop_.PostTask(FROM_HERE,
-                 base::Bind(&UpdateAttempterTest::UpdateTestVerify,
-                            base::Unretained(this)));
-}
-
-void UpdateAttempterTest::UpdateTestVerify() {
-  EXPECT_EQ(0, attempter_.http_response_code());
-  EXPECT_EQ(&attempter_, processor_->delegate());
-  EXPECT_EQ(UpdateStatus::CHECKING_FOR_UPDATE, attempter_.status());
-  loop_.BreakLoop();
-}
-
-void UpdateAttempterTest::RollbackTestStart(bool enterprise_rollback,
-                                            bool valid_slot) {
-  // Create a device policy so that we can change settings.
-  auto device_policy = std::make_unique<policy::MockDevicePolicy>();
-  EXPECT_CALL(*device_policy, LoadPolicy()).WillRepeatedly(Return(true));
-  FakeSystemState::Get()->set_device_policy(device_policy.get());
-  if (enterprise_rollback) {
-    // We return an empty owner as this is an enterprise.
-    EXPECT_CALL(*device_policy, GetOwner(_))
-        .WillRepeatedly(DoAll(SetArgPointee<0>(string("")), Return(true)));
-  } else {
-    // We return a fake owner as this is an owned consumer device.
-    EXPECT_CALL(*device_policy, GetOwner(_))
-        .WillRepeatedly(DoAll(SetArgPointee<0>(string("fake.mail@fake.com")),
-                              Return(true)));
-  }
-
-  attempter_.policy_provider_.reset(
-      new policy::PolicyProvider(std::move(device_policy)));
-
-  if (valid_slot) {
-    BootControlInterface::Slot rollback_slot = 1;
-    LOG(INFO) << "Test Mark Bootable: "
-              << BootControlInterface::SlotName(rollback_slot);
-    FakeSystemState::Get()->fake_boot_control()->SetSlotBootable(rollback_slot,
-                                                                 true);
-  }
-
-  bool is_rollback_allowed = false;
-
-  // We only allow rollback on devices that are not enterprise enrolled and
-  // which have a valid slot to rollback to.
-  if (!enterprise_rollback && valid_slot) {
-    is_rollback_allowed = true;
-  }
-
-  if (is_rollback_allowed) {
-    InSequence s;
-    for (const auto& rollback_action_type : GetRollbackActionTypes()) {
-      EXPECT_CALL(*processor_,
-                  EnqueueAction(Pointee(
-                      Property(&AbstractAction::Type, rollback_action_type))));
-    }
-    EXPECT_CALL(*processor_, StartProcessing());
-
-    EXPECT_TRUE(attempter_.Rollback(true));
-    loop_.PostTask(FROM_HERE,
-                   base::Bind(&UpdateAttempterTest::RollbackTestVerify,
-                              base::Unretained(this)));
-  } else {
-    EXPECT_FALSE(attempter_.Rollback(true));
-    loop_.BreakLoop();
-  }
-}
-
-void UpdateAttempterTest::RollbackTestVerify() {
-  // Verifies the actions that were enqueued.
-  EXPECT_EQ(&attempter_, processor_->delegate());
-  EXPECT_EQ(UpdateStatus::ATTEMPTING_ROLLBACK, attempter_.status());
-  EXPECT_EQ(0U, attempter_.install_plan_->partitions.size());
-  EXPECT_EQ(attempter_.install_plan_->powerwash_required, true);
-  loop_.BreakLoop();
-}
-
-TEST_F(UpdateAttempterTest, UpdateTest) {
-  UpdateTestStart();
-  loop_.Run();
-}
-
-TEST_F(UpdateAttempterTest, RollbackTest) {
-  loop_.PostTask(FROM_HERE,
-                 base::Bind(&UpdateAttempterTest::RollbackTestStart,
-                            base::Unretained(this),
-                            false,
-                            true));
-  loop_.Run();
-}
-
-TEST_F(UpdateAttempterTest, InvalidSlotRollbackTest) {
-  loop_.PostTask(FROM_HERE,
-                 base::Bind(&UpdateAttempterTest::RollbackTestStart,
-                            base::Unretained(this),
-                            false,
-                            false));
-  loop_.Run();
-}
-
-TEST_F(UpdateAttempterTest, EnterpriseRollbackTest) {
-  loop_.PostTask(FROM_HERE,
-                 base::Bind(&UpdateAttempterTest::RollbackTestStart,
-                            base::Unretained(this),
-                            true,
-                            true));
-  loop_.Run();
-}
-
-void UpdateAttempterTest::PingOmahaTestStart() {
-  EXPECT_CALL(*processor_,
-              EnqueueAction(Pointee(Property(
-                  &AbstractAction::Type, OmahaRequestAction::StaticType()))));
-  EXPECT_CALL(*processor_, StartProcessing());
-  attempter_.PingOmaha();
-  ScheduleQuitMainLoop();
-}
-
-TEST_F(UpdateAttempterTest, PingOmahaTest) {
-  EXPECT_FALSE(attempter_.waiting_for_scheduled_check_);
-  EXPECT_FALSE(attempter_.WasScheduleUpdatesCalled());
-  // Disable scheduling of subsequnet checks; we're using the |DefaultPolicy| in
-  // testing, which is more permissive than we want to handle here.
-  attempter_.DisableScheduleUpdates();
-  loop_.PostTask(FROM_HERE,
-                 base::Bind(&UpdateAttempterTest::PingOmahaTestStart,
-                            base::Unretained(this)));
-  brillo::MessageLoopRunMaxIterations(&loop_, 100);
-  EXPECT_EQ(UpdateStatus::UPDATED_NEED_REBOOT, attempter_.status());
-  EXPECT_TRUE(attempter_.WasScheduleUpdatesCalled());
-}
-
-TEST_F(UpdateAttempterTest, CreatePendingErrorEventTest) {
-  MockAction action;
-  const ErrorCode kCode = ErrorCode::kDownloadTransferError;
-  attempter_.CreatePendingErrorEvent(&action, kCode);
-  ASSERT_NE(nullptr, attempter_.error_event_.get());
-  EXPECT_EQ(OmahaEvent::kTypeUpdateComplete, attempter_.error_event_->type);
-  EXPECT_EQ(OmahaEvent::kResultError, attempter_.error_event_->result);
-  EXPECT_EQ(
-      static_cast<ErrorCode>(static_cast<int>(kCode) |
-                             static_cast<int>(ErrorCode::kTestOmahaUrlFlag)),
-      attempter_.error_event_->error_code);
-}
-
-TEST_F(UpdateAttempterTest, CreatePendingErrorEventResumedTest) {
-  attempter_.install_plan_.reset(new InstallPlan);
-  attempter_.install_plan_->is_resume = true;
-  MockAction action;
-  const ErrorCode kCode = ErrorCode::kInstallDeviceOpenError;
-  attempter_.CreatePendingErrorEvent(&action, kCode);
-  ASSERT_NE(nullptr, attempter_.error_event_.get());
-  EXPECT_EQ(OmahaEvent::kTypeUpdateComplete, attempter_.error_event_->type);
-  EXPECT_EQ(OmahaEvent::kResultError, attempter_.error_event_->result);
-  EXPECT_EQ(
-      static_cast<ErrorCode>(static_cast<int>(kCode) |
-                             static_cast<int>(ErrorCode::kResumedFlag) |
-                             static_cast<int>(ErrorCode::kTestOmahaUrlFlag)),
-      attempter_.error_event_->error_code);
-}
-
-TEST_F(UpdateAttempterTest, P2PNotStartedAtStartupWhenNotEnabled) {
-  MockP2PManager mock_p2p_manager;
-  FakeSystemState::Get()->set_p2p_manager(&mock_p2p_manager);
-  mock_p2p_manager.fake().SetP2PEnabled(false);
-  EXPECT_CALL(mock_p2p_manager, EnsureP2PRunning()).Times(0);
-  attempter_.UpdateEngineStarted();
-}
-
-TEST_F(UpdateAttempterTest, P2PNotStartedAtStartupWhenEnabledButNotSharing) {
-  MockP2PManager mock_p2p_manager;
-  FakeSystemState::Get()->set_p2p_manager(&mock_p2p_manager);
-  mock_p2p_manager.fake().SetP2PEnabled(true);
-  EXPECT_CALL(mock_p2p_manager, EnsureP2PRunning()).Times(0);
-  attempter_.UpdateEngineStarted();
-}
-
-TEST_F(UpdateAttempterTest, P2PStartedAtStartupWhenEnabledAndSharing) {
-  MockP2PManager mock_p2p_manager;
-  FakeSystemState::Get()->set_p2p_manager(&mock_p2p_manager);
-  mock_p2p_manager.fake().SetP2PEnabled(true);
-  mock_p2p_manager.fake().SetCountSharedFilesResult(1);
-  EXPECT_CALL(mock_p2p_manager, EnsureP2PRunning());
-  attempter_.UpdateEngineStarted();
-}
-
-TEST_F(UpdateAttempterTest, P2PNotEnabled) {
-  loop_.PostTask(FROM_HERE,
-                 base::Bind(&UpdateAttempterTest::P2PNotEnabledStart,
-                            base::Unretained(this)));
-  loop_.Run();
-}
-
-void UpdateAttempterTest::P2PNotEnabledStart() {
-  // If P2P is not enabled, check that we do not attempt housekeeping
-  // and do not convey that P2P is to be used.
-  MockP2PManager mock_p2p_manager;
-  FakeSystemState::Get()->set_p2p_manager(&mock_p2p_manager);
-  mock_p2p_manager.fake().SetP2PEnabled(false);
-  EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(0);
-  attempter_.Update({});
-  EXPECT_FALSE(actual_using_p2p_for_downloading_);
-  EXPECT_FALSE(actual_using_p2p_for_sharing());
-  ScheduleQuitMainLoop();
-}
-
-TEST_F(UpdateAttempterTest, P2PEnabledStartingFails) {
-  loop_.PostTask(FROM_HERE,
-                 base::Bind(&UpdateAttempterTest::P2PEnabledStartingFailsStart,
-                            base::Unretained(this)));
-  loop_.Run();
-}
-
-void UpdateAttempterTest::P2PEnabledStartingFailsStart() {
-  // If P2P is enabled, but starting it fails ensure we don't do
-  // any housekeeping and do not convey that P2P should be used.
-  MockP2PManager mock_p2p_manager;
-  FakeSystemState::Get()->set_p2p_manager(&mock_p2p_manager);
-  mock_p2p_manager.fake().SetP2PEnabled(true);
-  mock_p2p_manager.fake().SetEnsureP2PRunningResult(false);
-  mock_p2p_manager.fake().SetPerformHousekeepingResult(false);
-  EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(0);
-  attempter_.Update({});
-  EXPECT_FALSE(actual_using_p2p_for_downloading());
-  EXPECT_FALSE(actual_using_p2p_for_sharing());
-  ScheduleQuitMainLoop();
-}
-
-TEST_F(UpdateAttempterTest, P2PEnabledHousekeepingFails) {
-  loop_.PostTask(
-      FROM_HERE,
-      base::Bind(&UpdateAttempterTest::P2PEnabledHousekeepingFailsStart,
-                 base::Unretained(this)));
-  loop_.Run();
-}
-
-void UpdateAttempterTest::P2PEnabledHousekeepingFailsStart() {
-  // If P2P is enabled, starting it works but housekeeping fails, ensure
-  // we do not convey P2P is to be used.
-  MockP2PManager mock_p2p_manager;
-  FakeSystemState::Get()->set_p2p_manager(&mock_p2p_manager);
-  mock_p2p_manager.fake().SetP2PEnabled(true);
-  mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
-  mock_p2p_manager.fake().SetPerformHousekeepingResult(false);
-  EXPECT_CALL(mock_p2p_manager, PerformHousekeeping());
-  attempter_.Update({});
-  EXPECT_FALSE(actual_using_p2p_for_downloading());
-  EXPECT_FALSE(actual_using_p2p_for_sharing());
-  ScheduleQuitMainLoop();
-}
-
-TEST_F(UpdateAttempterTest, P2PEnabled) {
-  loop_.PostTask(FROM_HERE,
-                 base::Bind(&UpdateAttempterTest::P2PEnabledStart,
-                            base::Unretained(this)));
-  loop_.Run();
-}
-
-void UpdateAttempterTest::P2PEnabledStart() {
-  MockP2PManager mock_p2p_manager;
-  FakeSystemState::Get()->set_p2p_manager(&mock_p2p_manager);
-  // If P2P is enabled and starting it works, check that we performed
-  // housekeeping and that we convey P2P should be used.
-  mock_p2p_manager.fake().SetP2PEnabled(true);
-  mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
-  mock_p2p_manager.fake().SetPerformHousekeepingResult(true);
-  EXPECT_CALL(mock_p2p_manager, PerformHousekeeping());
-  attempter_.Update({});
-  EXPECT_TRUE(actual_using_p2p_for_downloading());
-  EXPECT_TRUE(actual_using_p2p_for_sharing());
-  ScheduleQuitMainLoop();
-}
-
-TEST_F(UpdateAttempterTest, P2PEnabledInteractive) {
-  loop_.PostTask(FROM_HERE,
-                 base::Bind(&UpdateAttempterTest::P2PEnabledInteractiveStart,
-                            base::Unretained(this)));
-  loop_.Run();
-}
-
-void UpdateAttempterTest::P2PEnabledInteractiveStart() {
-  MockP2PManager mock_p2p_manager;
-  FakeSystemState::Get()->set_p2p_manager(&mock_p2p_manager);
-  // For an interactive check, if P2P is enabled and starting it
-  // works, check that we performed housekeeping and that we convey
-  // P2P should be used for sharing but NOT for downloading.
-  mock_p2p_manager.fake().SetP2PEnabled(true);
-  mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
-  mock_p2p_manager.fake().SetPerformHousekeepingResult(true);
-  EXPECT_CALL(mock_p2p_manager, PerformHousekeeping());
-  attempter_.Update({.interactive = true});
-  EXPECT_FALSE(actual_using_p2p_for_downloading());
-  EXPECT_TRUE(actual_using_p2p_for_sharing());
-  ScheduleQuitMainLoop();
-}
-
-TEST_F(UpdateAttempterTest, ReadScatterFactorFromPolicy) {
-  loop_.PostTask(
-      FROM_HERE,
-      base::Bind(&UpdateAttempterTest::ReadScatterFactorFromPolicyTestStart,
-                 base::Unretained(this)));
-  loop_.Run();
-}
-
-// Tests that the scatter_factor_in_seconds value is properly fetched
-// from the device policy.
-void UpdateAttempterTest::ReadScatterFactorFromPolicyTestStart() {
-  int64_t scatter_factor_in_seconds = 36000;
-
-  auto device_policy = std::make_unique<policy::MockDevicePolicy>();
-  EXPECT_CALL(*device_policy, LoadPolicy()).WillRepeatedly(Return(true));
-  FakeSystemState::Get()->set_device_policy(device_policy.get());
-
-  EXPECT_CALL(*device_policy, GetScatterFactorInSeconds(_))
-      .WillRepeatedly(
-          DoAll(SetArgPointee<0>(scatter_factor_in_seconds), Return(true)));
-
-  attempter_.policy_provider_.reset(
-      new policy::PolicyProvider(std::move(device_policy)));
-
-  attempter_.Update({});
-  EXPECT_EQ(scatter_factor_in_seconds, attempter_.scatter_factor_.InSeconds());
-
-  ScheduleQuitMainLoop();
-}
-
-TEST_F(UpdateAttempterTest, DecrementUpdateCheckCountTest) {
-  loop_.PostTask(
-      FROM_HERE,
-      base::Bind(&UpdateAttempterTest::DecrementUpdateCheckCountTestStart,
-                 base::Unretained(this)));
-  loop_.Run();
-}
-
-void UpdateAttempterTest::DecrementUpdateCheckCountTestStart() {
-  // Tests that the scatter_factor_in_seconds value is properly fetched
-  // from the device policy and is decremented if value > 0.
-  int64_t initial_value = 5;
-  auto* fake_prefs = FakeSystemState::Get()->fake_prefs();
-  FakeSystemState::Get()->fake_hardware()->SetIsOOBEComplete(Time::UnixEpoch());
-
-  EXPECT_TRUE(fake_prefs->SetInt64(kPrefsUpdateCheckCount, initial_value));
-
-  int64_t scatter_factor_in_seconds = 10;
-
-  auto device_policy = std::make_unique<policy::MockDevicePolicy>();
-  EXPECT_CALL(*device_policy, LoadPolicy()).WillRepeatedly(Return(true));
-  FakeSystemState::Get()->set_device_policy(device_policy.get());
-
-  EXPECT_CALL(*device_policy, GetScatterFactorInSeconds(_))
-      .WillRepeatedly(
-          DoAll(SetArgPointee<0>(scatter_factor_in_seconds), Return(true)));
-
-  attempter_.policy_provider_.reset(
-      new policy::PolicyProvider(std::move(device_policy)));
-
-  attempter_.Update({});
-  EXPECT_EQ(scatter_factor_in_seconds, attempter_.scatter_factor_.InSeconds());
-
-  // Make sure the file still exists.
-  EXPECT_TRUE(fake_prefs->Exists(kPrefsUpdateCheckCount));
-
-  int64_t new_value;
-  EXPECT_TRUE(fake_prefs->GetInt64(kPrefsUpdateCheckCount, &new_value));
-  EXPECT_EQ(initial_value - 1, new_value);
-
-  EXPECT_TRUE(
-      attempter_.omaha_request_params_->update_check_count_wait_enabled());
-
-  // 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({});
-  EXPECT_TRUE(fake_prefs->Exists(kPrefsUpdateCheckCount));
-  EXPECT_TRUE(fake_prefs->GetInt64(kPrefsUpdateCheckCount, &new_value));
-  EXPECT_EQ(initial_value, new_value);
-
-  ScheduleQuitMainLoop();
-}
-
-TEST_F(UpdateAttempterTest, NoScatteringDoneDuringManualUpdateTestStart) {
-  loop_.PostTask(
-      FROM_HERE,
-      base::Bind(
-          &UpdateAttempterTest::NoScatteringDoneDuringManualUpdateTestStart,
-          base::Unretained(this)));
-  loop_.Run();
-}
-
-void UpdateAttempterTest::NoScatteringDoneDuringManualUpdateTestStart() {
-  // Tests that no scattering logic is enabled if the update check
-  // is manually done (as opposed to a scheduled update check)
-  int64_t initial_value = 8;
-  auto* fake_prefs = FakeSystemState::Get()->fake_prefs();
-  FakeSystemState::Get()->fake_hardware()->SetIsOOBEComplete(Time::UnixEpoch());
-
-  EXPECT_TRUE(
-      fake_prefs->SetInt64(kPrefsWallClockScatteringWaitPeriod, initial_value));
-  EXPECT_TRUE(fake_prefs->SetInt64(kPrefsUpdateCheckCount, initial_value));
-
-  // make sure scatter_factor is non-zero as scattering is disabled
-  // otherwise.
-  int64_t scatter_factor_in_seconds = 50;
-
-  auto device_policy = std::make_unique<policy::MockDevicePolicy>();
-  EXPECT_CALL(*device_policy, LoadPolicy()).WillRepeatedly(Return(true));
-  FakeSystemState::Get()->set_device_policy(device_policy.get());
-
-  EXPECT_CALL(*device_policy, GetScatterFactorInSeconds(_))
-      .WillRepeatedly(
-          DoAll(SetArgPointee<0>(scatter_factor_in_seconds), Return(true)));
-
-  attempter_.policy_provider_.reset(
-      new policy::PolicyProvider(std::move(device_policy)));
-
-  // Trigger an interactive check so we can test that scattering is disabled.
-  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
-  // checks and all artifacts are removed.
-  EXPECT_FALSE(
-      attempter_.omaha_request_params_->wall_clock_based_wait_enabled());
-  EXPECT_FALSE(fake_prefs->Exists(kPrefsWallClockScatteringWaitPeriod));
-  EXPECT_EQ(0, attempter_.omaha_request_params_->waiting_period().InSeconds());
-  EXPECT_FALSE(
-      attempter_.omaha_request_params_->update_check_count_wait_enabled());
-  EXPECT_FALSE(fake_prefs->Exists(kPrefsUpdateCheckCount));
-
-  ScheduleQuitMainLoop();
-}
-
-void UpdateAttempterTest::SetUpStagingTest(const StagingSchedule& schedule) {
-  int64_t initial_value = 8;
-  EXPECT_TRUE(
-      prefs_->SetInt64(kPrefsWallClockScatteringWaitPeriod, initial_value));
-  EXPECT_TRUE(prefs_->SetInt64(kPrefsUpdateCheckCount, initial_value));
-  attempter_.scatter_factor_ = TimeDelta::FromSeconds(20);
-
-  auto device_policy = std::make_unique<policy::MockDevicePolicy>();
-  EXPECT_CALL(*device_policy, LoadPolicy()).WillRepeatedly(Return(true));
-  FakeSystemState::Get()->set_device_policy(device_policy.get());
-  EXPECT_CALL(*device_policy, GetDeviceUpdateStagingSchedule(_))
-      .WillRepeatedly(DoAll(SetArgPointee<0>(schedule), Return(true)));
-
-  attempter_.policy_provider_.reset(
-      new policy::PolicyProvider(std::move(device_policy)));
-}
-
-TEST_F(UpdateAttempterTest, StagingSetsPrefsAndTurnsOffScattering) {
-  loop_.PostTask(
-      FROM_HERE,
-      base::Bind(
-          &UpdateAttempterTest::StagingSetsPrefsAndTurnsOffScatteringStart,
-          base::Unretained(this)));
-  loop_.Run();
-}
-
-void UpdateAttempterTest::StagingSetsPrefsAndTurnsOffScatteringStart() {
-  // Tests that staging sets its prefs properly and turns off scattering.
-  FakeSystemState::Get()->fake_hardware()->SetIsOOBEComplete(Time::UnixEpoch());
-  SetUpStagingTest(kValidStagingSchedule);
-
-  attempter_.Update({});
-  auto* fake_prefs = FakeSystemState::Get()->fake_prefs();
-  // Check that prefs have the correct values.
-  int64_t update_count;
-  EXPECT_TRUE(fake_prefs->GetInt64(kPrefsUpdateCheckCount, &update_count));
-  int64_t waiting_time_days;
-  EXPECT_TRUE(fake_prefs->GetInt64(kPrefsWallClockStagingWaitPeriod,
-                                   &waiting_time_days));
-  EXPECT_GT(waiting_time_days, 0);
-  // Update count should have been decremented.
-  EXPECT_EQ(7, update_count);
-  // Check that Omaha parameters were updated correctly.
-  EXPECT_TRUE(
-      attempter_.omaha_request_params_->update_check_count_wait_enabled());
-  EXPECT_TRUE(
-      attempter_.omaha_request_params_->wall_clock_based_wait_enabled());
-  EXPECT_EQ(waiting_time_days,
-            attempter_.omaha_request_params_->waiting_period().InDays());
-  // Check class variables.
-  EXPECT_EQ(waiting_time_days, attempter_.staging_wait_time_.InDays());
-  EXPECT_EQ(kValidStagingSchedule, attempter_.staging_schedule_);
-  // Check that scattering is turned off
-  EXPECT_EQ(0, attempter_.scatter_factor_.InSeconds());
-  EXPECT_FALSE(fake_prefs->Exists(kPrefsWallClockScatteringWaitPeriod));
-
-  ScheduleQuitMainLoop();
-}
-
-void UpdateAttempterTest::CheckStagingOff() {
-  // Check that all prefs were removed.
-  EXPECT_FALSE(prefs_->Exists(kPrefsUpdateCheckCount));
-  EXPECT_FALSE(prefs_->Exists(kPrefsWallClockScatteringWaitPeriod));
-  EXPECT_FALSE(prefs_->Exists(kPrefsWallClockStagingWaitPeriod));
-  // Check that the Omaha parameters have the correct value.
-  EXPECT_EQ(0, attempter_.omaha_request_params_->waiting_period().InDays());
-  EXPECT_EQ(attempter_.omaha_request_params_->waiting_period(),
-            attempter_.staging_wait_time_);
-  EXPECT_FALSE(
-      attempter_.omaha_request_params_->update_check_count_wait_enabled());
-  EXPECT_FALSE(
-      attempter_.omaha_request_params_->wall_clock_based_wait_enabled());
-  // Check that scattering is turned off too.
-  EXPECT_EQ(0, attempter_.scatter_factor_.InSeconds());
-}
-
-TEST_F(UpdateAttempterTest, StagingOffIfInteractive) {
-  loop_.PostTask(FROM_HERE,
-                 base::Bind(&UpdateAttempterTest::StagingOffIfInteractiveStart,
-                            base::Unretained(this)));
-  loop_.Run();
-}
-
-void UpdateAttempterTest::StagingOffIfInteractiveStart() {
-  // Tests that staging is turned off when an interactive update is requested.
-  FakeSystemState::Get()->fake_hardware()->SetIsOOBEComplete(Time::UnixEpoch());
-  SetUpStagingTest(kValidStagingSchedule);
-
-  attempter_.Update({.interactive = true});
-  CheckStagingOff();
-
-  ScheduleQuitMainLoop();
-}
-
-TEST_F(UpdateAttempterTest, StagingOffIfOobe) {
-  loop_.PostTask(FROM_HERE,
-                 base::Bind(&UpdateAttempterTest::StagingOffIfOobeStart,
-                            base::Unretained(this)));
-  loop_.Run();
-}
-
-void UpdateAttempterTest::StagingOffIfOobeStart() {
-  // Tests that staging is turned off if OOBE hasn't been completed.
-  FakeSystemState::Get()->fake_hardware()->SetIsOOBEEnabled(true);
-  FakeSystemState::Get()->fake_hardware()->UnsetIsOOBEComplete();
-  SetUpStagingTest(kValidStagingSchedule);
-
-  attempter_.Update({.interactive = true});
-  CheckStagingOff();
-
-  ScheduleQuitMainLoop();
-}
-
-// Checks that we only report daily metrics at most every 24 hours.
-TEST_F(UpdateAttempterTest, ReportDailyMetrics) {
-  auto* fake_clock = FakeSystemState::Get()->fake_clock();
-  Time epoch = Time::FromInternalValue(0);
-  fake_clock->SetWallclockTime(epoch);
-
-  // If there is no kPrefsDailyMetricsLastReportedAt state variable,
-  // we should report.
-  EXPECT_TRUE(attempter_.CheckAndReportDailyMetrics());
-  // We should not report again if no time has passed.
-  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
-
-  // We should not report if only 10 hours has passed.
-  fake_clock->SetWallclockTime(epoch + TimeDelta::FromHours(10));
-  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
-
-  // We should not report if only 24 hours - 1 sec has passed.
-  fake_clock->SetWallclockTime(epoch + TimeDelta::FromHours(24) -
-                               TimeDelta::FromSeconds(1));
-  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
-
-  // We should report if 24 hours has passed.
-  fake_clock->SetWallclockTime(epoch + TimeDelta::FromHours(24));
-  EXPECT_TRUE(attempter_.CheckAndReportDailyMetrics());
-
-  // But then we should not report again..
-  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
-
-  // .. until another 24 hours has passed
-  fake_clock->SetWallclockTime(epoch + TimeDelta::FromHours(47));
-  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
-  fake_clock->SetWallclockTime(epoch + TimeDelta::FromHours(48));
-  EXPECT_TRUE(attempter_.CheckAndReportDailyMetrics());
-  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
-
-  // .. and another 24 hours
-  fake_clock->SetWallclockTime(epoch + TimeDelta::FromHours(71));
-  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
-  fake_clock->SetWallclockTime(epoch + TimeDelta::FromHours(72));
-  EXPECT_TRUE(attempter_.CheckAndReportDailyMetrics());
-  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
-
-  // If the span between time of reporting and present time is
-  // negative, we report. This is in order to reset the timestamp and
-  // avoid an edge condition whereby a distant point in the future is
-  // in the state variable resulting in us never ever reporting again.
-  fake_clock->SetWallclockTime(epoch + TimeDelta::FromHours(71));
-  EXPECT_TRUE(attempter_.CheckAndReportDailyMetrics());
-  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
-
-  // In this case we should not update until the clock reads 71 + 24 = 95.
-  // Check that.
-  fake_clock->SetWallclockTime(epoch + TimeDelta::FromHours(94));
-  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
-  fake_clock->SetWallclockTime(epoch + TimeDelta::FromHours(95));
-  EXPECT_TRUE(attempter_.CheckAndReportDailyMetrics());
-  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
-}
-
-TEST_F(UpdateAttempterTest, BootTimeInUpdateMarkerFile) {
-  FakeSystemState::Get()->fake_clock()->SetBootTime(Time::FromTimeT(42));
-  attempter_.Init();
-
-  Time boot_time;
-  EXPECT_FALSE(attempter_.GetBootTimeAtUpdate(&boot_time));
-
-  attempter_.WriteUpdateCompletedMarker();
-
-  EXPECT_TRUE(attempter_.GetBootTimeAtUpdate(&boot_time));
-  EXPECT_EQ(boot_time.ToTimeT(), 42);
-}
-
-TEST_F(UpdateAttempterTest, AnyUpdateSourceAllowedUnofficial) {
-  FakeSystemState::Get()->fake_hardware()->SetIsOfficialBuild(false);
-  EXPECT_TRUE(attempter_.IsAnyUpdateSourceAllowed());
-}
-
-TEST_F(UpdateAttempterTest, AnyUpdateSourceAllowedOfficialDevmode) {
-  FakeSystemState::Get()->fake_hardware()->SetIsOfficialBuild(true);
-  FakeSystemState::Get()->fake_hardware()->SetAreDevFeaturesEnabled(true);
-  EXPECT_TRUE(attempter_.IsAnyUpdateSourceAllowed());
-}
-
-TEST_F(UpdateAttempterTest, AnyUpdateSourceDisallowedOfficialNormal) {
-  FakeSystemState::Get()->fake_hardware()->SetIsOfficialBuild(true);
-  FakeSystemState::Get()->fake_hardware()->SetAreDevFeaturesEnabled(false);
-  EXPECT_FALSE(attempter_.IsAnyUpdateSourceAllowed());
-}
-
-// TODO(kimjae): Follow testing pattern with params for |CheckForInstall()|.
-// When adding, remove older tests related to |CheckForInstall()|.
-TEST_F(UpdateAttempterTest, CheckForInstallNotIdleFails) {
-  for (const auto status : kNonIdleUpdateStatuses) {
-    // GIVEN a non-idle status.
-    attempter_.status_ = status;
-
-    EXPECT_FALSE(attempter_.CheckForInstall({}, ""));
-  }
-}
-
-TEST_F(UpdateAttempterTest, CheckForUpdateNotIdleFails) {
-  for (const auto status : kNonIdleUpdateStatuses) {
-    // GIVEN a non-idle status.
-    cfu_params_.status = status;
-
-    // THEN |ScheduleUpdates()| should not be called.
-    cfu_params_.should_schedule_updates_be_called = false;
-    // THEN result should indicate failure.
-    cfu_params_.expected_result = false;
-
-    TestCheckForUpdate();
-  }
-}
-
-TEST_F(UpdateAttempterTest, CheckForUpdateOfficalBuildClearsSource) {
-  // GIVEN a official build.
-
-  // THEN we except forced app version + forced omaha url to be cleared.
-
-  TestCheckForUpdate();
-}
-
-TEST_F(UpdateAttempterTest, CheckForUpdateUnofficialBuildChangesSource) {
-  // GIVEN a nonofficial build with dev features enabled.
-  cfu_params_.is_official_build = false;
-  cfu_params_.are_dev_features_enabled = true;
-
-  // THEN the forced app version + forced omaha url changes based on input.
-  cfu_params_.expected_forced_app_version = cfu_params_.app_version;
-  cfu_params_.expected_forced_omaha_url = cfu_params_.omaha_url;
-
-  TestCheckForUpdate();
-}
-
-TEST_F(UpdateAttempterTest, CheckForUpdateOfficialBuildScheduledAUTest) {
-  // GIVEN a scheduled autest omaha url.
-  cfu_params_.omaha_url = "autest-scheduled";
-
-  // THEN forced app version is cleared.
-  // THEN forced omaha url changes to default constant.
-  cfu_params_.expected_forced_omaha_url = constants::kOmahaDefaultAUTestURL;
-
-  TestCheckForUpdate();
-}
-
-TEST_F(UpdateAttempterTest, CheckForUpdateUnofficialBuildScheduledAUTest) {
-  // GIVEN a scheduled autest omaha url.
-  cfu_params_.omaha_url = "autest-scheduled";
-  // GIVEN a nonofficial build with dev features enabled.
-  cfu_params_.is_official_build = false;
-  cfu_params_.are_dev_features_enabled = true;
-
-  // THEN forced app version changes based on input.
-  cfu_params_.expected_forced_app_version = cfu_params_.app_version;
-  // THEN forced omaha url changes to default constant.
-  cfu_params_.expected_forced_omaha_url = constants::kOmahaDefaultAUTestURL;
-
-  TestCheckForUpdate();
-}
-
-TEST_F(UpdateAttempterTest, CheckForUpdateOfficialBuildAUTest) {
-  // GIVEN a autest omaha url.
-  cfu_params_.omaha_url = "autest";
-
-  // THEN forced app version is cleared.
-  // THEN forced omaha url changes to default constant.
-  cfu_params_.expected_forced_omaha_url = constants::kOmahaDefaultAUTestURL;
-
-  TestCheckForUpdate();
-}
-
-TEST_F(UpdateAttempterTest, CheckForUpdateUnofficialBuildAUTest) {
-  // GIVEN a autest omha url.
-  cfu_params_.omaha_url = "autest";
-  // GIVEN a nonofficial build with dev features enabled.
-  cfu_params_.is_official_build = false;
-  cfu_params_.are_dev_features_enabled = true;
-
-  // THEN forced app version changes based on input.
-  cfu_params_.expected_forced_app_version = cfu_params_.app_version;
-  // THEN forced omaha url changes to default constant.
-  cfu_params_.expected_forced_omaha_url = constants::kOmahaDefaultAUTestURL;
-
-  TestCheckForUpdate();
-}
-
-TEST_F(UpdateAttempterTest,
-       CheckForUpdateNonInteractiveOfficialBuildScheduledAUTest) {
-  // GIVEN a scheduled autest omaha url.
-  cfu_params_.omaha_url = "autest-scheduled";
-  // GIVEN a noninteractive update.
-  cfu_params_.flags = UpdateAttemptFlags::kFlagNonInteractive;
-
-  // THEN forced app version is cleared.
-  // THEN forced omaha url changes to default constant.
-  cfu_params_.expected_forced_omaha_url = constants::kOmahaDefaultAUTestURL;
-
-  TestCheckForUpdate();
-}
-
-TEST_F(UpdateAttempterTest,
-       CheckForUpdateNonInteractiveUnofficialBuildScheduledAUTest) {
-  // GIVEN a scheduled autest omaha url.
-  cfu_params_.omaha_url = "autest-scheduled";
-  // GIVEN a noninteractive update.
-  cfu_params_.flags = UpdateAttemptFlags::kFlagNonInteractive;
-  // GIVEN a nonofficial build with dev features enabled.
-  cfu_params_.is_official_build = false;
-  cfu_params_.are_dev_features_enabled = true;
-
-  // THEN forced app version changes based on input.
-  cfu_params_.expected_forced_app_version = cfu_params_.app_version;
-  // THEN forced omaha url changes to default constant.
-  cfu_params_.expected_forced_omaha_url = constants::kOmahaDefaultAUTestURL;
-
-  TestCheckForUpdate();
-}
-
-TEST_F(UpdateAttempterTest, CheckForUpdateNonInteractiveOfficialBuildAUTest) {
-  // GIVEN a autest omaha url.
-  cfu_params_.omaha_url = "autest";
-  // GIVEN a noninteractive update.
-  cfu_params_.flags = UpdateAttemptFlags::kFlagNonInteractive;
-
-  // THEN forced app version is cleared.
-  // THEN forced omaha url changes to default constant.
-  cfu_params_.expected_forced_omaha_url = constants::kOmahaDefaultAUTestURL;
-
-  TestCheckForUpdate();
-}
-
-TEST_F(UpdateAttempterTest, CheckForUpdateNonInteractiveUnofficialBuildAUTest) {
-  // GIVEN a autest omaha url.
-  cfu_params_.omaha_url = "autest";
-  // GIVEN a noninteractive update.
-  cfu_params_.flags = UpdateAttemptFlags::kFlagNonInteractive;
-  // GIVEN a nonofficial build with dev features enabled.
-  cfu_params_.is_official_build = false;
-  cfu_params_.are_dev_features_enabled = true;
-
-  // THEN forced app version changes based on input.
-  cfu_params_.expected_forced_app_version = cfu_params_.app_version;
-  // THEN forced omaha url changes to default constant.
-  cfu_params_.expected_forced_omaha_url = constants::kOmahaDefaultAUTestURL;
-
-  TestCheckForUpdate();
-}
-
-TEST_F(UpdateAttempterTest, CheckForUpdateMissingForcedCallback1) {
-  // GIVEN a official build.
-  // GIVEN forced callback is not set.
-  attempter_.set_forced_update_pending_callback(nullptr);
-
-  // THEN we except forced app version + forced omaha url to be cleared.
-  // THEN |ScheduleUpdates()| should not be called.
-  cfu_params_.should_schedule_updates_be_called = false;
-
-  TestCheckForUpdate();
-}
-
-TEST_F(UpdateAttempterTest, CheckForUpdateMissingForcedCallback2) {
-  // GIVEN a nonofficial build with dev features enabled.
-  cfu_params_.is_official_build = false;
-  cfu_params_.are_dev_features_enabled = true;
-  // GIVEN forced callback is not set.
-  attempter_.set_forced_update_pending_callback(nullptr);
-
-  // THEN the forced app version + forced omaha url changes based on input.
-  cfu_params_.expected_forced_app_version = cfu_params_.app_version;
-  cfu_params_.expected_forced_omaha_url = cfu_params_.omaha_url;
-  // THEN |ScheduleUpdates()| should not be called.
-  cfu_params_.should_schedule_updates_be_called = false;
-
-  TestCheckForUpdate();
-}
-
-TEST_F(UpdateAttempterTest, CheckForInstallTest) {
-  FakeSystemState::Get()->fake_hardware()->SetIsOfficialBuild(true);
-  FakeSystemState::Get()->fake_hardware()->SetAreDevFeaturesEnabled(false);
-  attempter_.CheckForInstall({}, "autest");
-  EXPECT_EQ(constants::kOmahaDefaultAUTestURL, attempter_.forced_omaha_url());
-
-  attempter_.CheckForInstall({}, "autest-scheduled");
-  EXPECT_EQ(constants::kOmahaDefaultAUTestURL, attempter_.forced_omaha_url());
-
-  attempter_.CheckForInstall({}, "http://omaha.phishing");
-  EXPECT_EQ("", attempter_.forced_omaha_url());
-}
-
-TEST_F(UpdateAttempterTest, InstallSetsStatusIdle) {
-  attempter_.CheckForInstall({}, "http://foo.bar");
-  attempter_.status_ = UpdateStatus::DOWNLOADING;
-  EXPECT_TRUE(attempter_.is_install_);
-  attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
-  UpdateEngineStatus status;
-  attempter_.GetStatus(&status);
-  // Should set status to idle after an install operation.
-  EXPECT_EQ(UpdateStatus::IDLE, status.status);
-}
-
-TEST_F(UpdateAttempterTest, RollbackAfterInstall) {
-  attempter_.is_install_ = true;
-  attempter_.Rollback(false);
-  EXPECT_FALSE(attempter_.is_install_);
-}
-
-TEST_F(UpdateAttempterTest, UpdateAfterInstall) {
-  attempter_.is_install_ = true;
-  attempter_.CheckForUpdate("", "", UpdateAttemptFlags::kNone);
-  EXPECT_FALSE(attempter_.is_install_);
-}
-
-TEST_F(UpdateAttempterTest, TargetVersionPrefixSetAndReset) {
-  UpdateCheckParams params;
-  attempter_.CalculateUpdateParams({.target_version_prefix = "1234"});
-  EXPECT_EQ("1234",
-            FakeSystemState::Get()->request_params()->target_version_prefix());
-
-  attempter_.CalculateUpdateParams({});
-  EXPECT_TRUE(FakeSystemState::Get()
-                  ->request_params()
-                  ->target_version_prefix()
-                  .empty());
-}
-
-TEST_F(UpdateAttempterTest, TargetChannelHintSetAndReset) {
-  attempter_.CalculateUpdateParams({.lts_tag = "hint"});
-  EXPECT_EQ("hint", FakeSystemState::Get()->request_params()->lts_tag());
-
-  attempter_.CalculateUpdateParams({});
-  EXPECT_TRUE(FakeSystemState::Get()->request_params()->lts_tag().empty());
-}
-
-TEST_F(UpdateAttempterTest, RollbackAllowedSetAndReset) {
-  attempter_.CalculateUpdateParams({
-      .target_version_prefix = "1234",
-      .rollback_allowed = true,
-      .rollback_allowed_milestones = 4,
-  });
-  EXPECT_TRUE(FakeSystemState::Get()->request_params()->rollback_allowed());
-  EXPECT_EQ(
-      4,
-      FakeSystemState::Get()->request_params()->rollback_allowed_milestones());
-
-  attempter_.CalculateUpdateParams({
-      .target_version_prefix = "1234",
-      .rollback_allowed_milestones = 4,
-  });
-  EXPECT_FALSE(FakeSystemState::Get()->request_params()->rollback_allowed());
-  EXPECT_EQ(
-      4,
-      FakeSystemState::Get()->request_params()->rollback_allowed_milestones());
-}
-
-TEST_F(UpdateAttempterTest, ChannelDowngradeNoRollback) {
-  base::ScopedTempDir tempdir;
-  ASSERT_TRUE(tempdir.CreateUniqueTempDir());
-  FakeSystemState::Get()->request_params()->set_root(tempdir.GetPath().value());
-  attempter_.CalculateUpdateParams({
-      .target_channel = "stable-channel",
-  });
-  EXPECT_FALSE(
-      FakeSystemState::Get()->request_params()->is_powerwash_allowed());
-}
-
-TEST_F(UpdateAttempterTest, ChannelDowngradeRollback) {
-  base::ScopedTempDir tempdir;
-  ASSERT_TRUE(tempdir.CreateUniqueTempDir());
-  FakeSystemState::Get()->request_params()->set_root(tempdir.GetPath().value());
-  attempter_.CalculateUpdateParams({
-      .rollback_on_channel_downgrade = true,
-      .target_channel = "stable-channel",
-  });
-  EXPECT_TRUE(FakeSystemState::Get()->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;
-  response_action.install_plan_.version = "a.b.c.d";
-  response_action.install_plan_.payloads.push_back(
-      {.size = 1234ULL, .type = InstallPayloadType::kFull});
-  // Inform the UpdateAttempter that the OmahaResponseHandlerAction has
-  // completed, with the deferred-update error code.
-  attempter_.ActionCompleted(
-      nullptr, &response_action, ErrorCode::kOmahaUpdateDeferredPerPolicy);
-  {
-    UpdateEngineStatus status;
-    attempter_.GetStatus(&status);
-    EXPECT_EQ(UpdateStatus::UPDATE_AVAILABLE, status.status);
-    EXPECT_TRUE(attempter_.install_plan_);
-    EXPECT_EQ(attempter_.install_plan_->version, status.new_version);
-    EXPECT_EQ(attempter_.install_plan_->payloads[0].size,
-              status.new_size_bytes);
-  }
-  // An "error" event should have been created to tell Omaha that the update is
-  // being deferred.
-  EXPECT_TRUE(nullptr != attempter_.error_event_);
-  EXPECT_EQ(OmahaEvent::kTypeUpdateComplete, attempter_.error_event_->type);
-  EXPECT_EQ(OmahaEvent::kResultUpdateDeferred, attempter_.error_event_->result);
-  ErrorCode expected_code = static_cast<ErrorCode>(
-      static_cast<int>(ErrorCode::kOmahaUpdateDeferredPerPolicy) |
-      static_cast<int>(ErrorCode::kTestOmahaUrlFlag));
-  EXPECT_EQ(expected_code, attempter_.error_event_->error_code);
-  // End the processing
-  attempter_.ProcessingDone(nullptr, ErrorCode::kOmahaUpdateDeferredPerPolicy);
-  // Validate the state of the attempter.
-  {
-    UpdateEngineStatus status;
-    attempter_.GetStatus(&status);
-    EXPECT_EQ(UpdateStatus::REPORTING_ERROR_EVENT, status.status);
-    EXPECT_EQ(response_action.install_plan_.version, status.new_version);
-    EXPECT_EQ(response_action.install_plan_.payloads[0].size,
-              status.new_size_bytes);
-  }
-}
-
-TEST_F(UpdateAttempterTest, UpdateIsNotRunningWhenUpdateAvailable) {
-  // Default construction for |waiting_for_scheduled_check_| is false.
-  EXPECT_FALSE(attempter_.IsBusyOrUpdateScheduled());
-  // Verify in-progress update with UPDATE_AVAILABLE is running
-  attempter_.status_ = UpdateStatus::UPDATE_AVAILABLE;
-  EXPECT_TRUE(attempter_.IsBusyOrUpdateScheduled());
-}
-
-TEST_F(UpdateAttempterTest, UpdateAttemptFlagsCachedAtUpdateStart) {
-  attempter_.SetUpdateAttemptFlags(UpdateAttemptFlags::kFlagRestrictDownload);
-
-  UpdateCheckParams params = {.updates_enabled = true};
-  attempter_.OnUpdateScheduled(EvalStatus::kSucceeded, params);
-
-  EXPECT_EQ(UpdateAttemptFlags::kFlagRestrictDownload,
-            attempter_.GetCurrentUpdateAttemptFlags());
-}
-
-TEST_F(UpdateAttempterTest, RollbackNotAllowed) {
-  UpdateCheckParams params = {.updates_enabled = true,
-                              .rollback_allowed = false};
-  attempter_.OnUpdateScheduled(EvalStatus::kSucceeded, params);
-  EXPECT_FALSE(FakeSystemState::Get()->request_params()->rollback_allowed());
-}
-
-TEST_F(UpdateAttempterTest, RollbackAllowed) {
-  UpdateCheckParams params = {.updates_enabled = true,
-                              .rollback_allowed = true};
-  attempter_.OnUpdateScheduled(EvalStatus::kSucceeded, params);
-  EXPECT_TRUE(FakeSystemState::Get()->request_params()->rollback_allowed());
-}
-
-TEST_F(UpdateAttempterTest, InteractiveUpdateUsesPassedRestrictions) {
-  attempter_.SetUpdateAttemptFlags(UpdateAttemptFlags::kFlagRestrictDownload);
-
-  attempter_.CheckForUpdate("", "", UpdateAttemptFlags::kNone);
-  EXPECT_EQ(UpdateAttemptFlags::kNone,
-            attempter_.GetCurrentUpdateAttemptFlags());
-}
-
-TEST_F(UpdateAttempterTest, NonInteractiveUpdateUsesSetRestrictions) {
-  attempter_.SetUpdateAttemptFlags(UpdateAttemptFlags::kNone);
-
-  // This tests that when CheckForUpdate() is called with the non-interactive
-  // flag set, that it doesn't change the current UpdateAttemptFlags.
-  attempter_.CheckForUpdate("",
-                            "",
-                            UpdateAttemptFlags::kFlagNonInteractive |
-                                UpdateAttemptFlags::kFlagRestrictDownload);
-  EXPECT_EQ(UpdateAttemptFlags::kNone,
-            attempter_.GetCurrentUpdateAttemptFlags());
-}
-
-void UpdateAttempterTest::ResetRollbackHappenedStart(bool is_consumer,
-                                                     bool is_policy_loaded,
-                                                     bool expected_reset) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_payload_state(),
-              GetRollbackHappened())
-      .WillRepeatedly(Return(true));
-  auto mock_policy_provider =
-      std::make_unique<NiceMock<policy::MockPolicyProvider>>();
-  EXPECT_CALL(*mock_policy_provider, IsConsumerDevice())
-      .WillRepeatedly(Return(is_consumer));
-  EXPECT_CALL(*mock_policy_provider, device_policy_is_loaded())
-      .WillRepeatedly(Return(is_policy_loaded));
-  const policy::MockDevicePolicy device_policy;
-  EXPECT_CALL(*mock_policy_provider, GetDevicePolicy())
-      .WillRepeatedly(ReturnRef(device_policy));
-  EXPECT_CALL(*FakeSystemState::Get()->mock_payload_state(),
-              SetRollbackHappened(false))
-      .Times(expected_reset ? 1 : 0);
-  attempter_.policy_provider_ = std::move(mock_policy_provider);
-  attempter_.Update({});
-  ScheduleQuitMainLoop();
-}
-
-TEST_F(UpdateAttempterTest, ResetRollbackHappenedOobe) {
-  loop_.PostTask(FROM_HERE,
-                 base::Bind(&UpdateAttempterTest::ResetRollbackHappenedStart,
-                            base::Unretained(this),
-                            /*is_consumer=*/false,
-                            /*is_policy_loaded=*/false,
-                            /*expected_reset=*/false));
-  loop_.Run();
-}
-
-TEST_F(UpdateAttempterTest, ResetRollbackHappenedConsumer) {
-  loop_.PostTask(FROM_HERE,
-                 base::Bind(&UpdateAttempterTest::ResetRollbackHappenedStart,
-                            base::Unretained(this),
-                            /*is_consumer=*/true,
-                            /*is_policy_loaded=*/false,
-                            /*expected_reset=*/true));
-  loop_.Run();
-}
-
-TEST_F(UpdateAttempterTest, ResetRollbackHappenedEnterprise) {
-  loop_.PostTask(FROM_HERE,
-                 base::Bind(&UpdateAttempterTest::ResetRollbackHappenedStart,
-                            base::Unretained(this),
-                            /*is_consumer=*/false,
-                            /*is_policy_loaded=*/true,
-                            /*expected_reset=*/true));
-  loop_.Run();
-}
-
-TEST_F(UpdateAttempterTest, SetRollbackHappenedRollback) {
-  attempter_.install_plan_.reset(new InstallPlan);
-  attempter_.install_plan_->is_rollback = true;
-
-  EXPECT_CALL(*FakeSystemState::Get()->mock_payload_state(),
-              SetRollbackHappened(true))
-      .Times(1);
-  attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
-}
-
-TEST_F(UpdateAttempterTest, SetRollbackHappenedNotRollback) {
-  attempter_.install_plan_.reset(new InstallPlan);
-  attempter_.install_plan_->is_rollback = false;
-
-  EXPECT_CALL(*FakeSystemState::Get()->mock_payload_state(),
-              SetRollbackHappened(true))
-      .Times(0);
-  attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
-}
-
-TEST_F(UpdateAttempterTest, RollbackMetricsRollbackSuccess) {
-  attempter_.install_plan_.reset(new InstallPlan);
-  attempter_.install_plan_->is_rollback = true;
-  attempter_.install_plan_->version = kRollbackVersion;
-
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
-              ReportEnterpriseRollbackMetrics(true, kRollbackVersion))
-      .Times(1);
-  attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
-}
-
-TEST_F(UpdateAttempterTest, RollbackMetricsNotRollbackSuccess) {
-  attempter_.install_plan_.reset(new InstallPlan);
-  attempter_.install_plan_->is_rollback = false;
-  attempter_.install_plan_->version = kRollbackVersion;
-
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
-              ReportEnterpriseRollbackMetrics(_, _))
-      .Times(0);
-  attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
-}
-
-TEST_F(UpdateAttempterTest, RollbackMetricsRollbackFailure) {
-  attempter_.install_plan_.reset(new InstallPlan);
-  attempter_.install_plan_->is_rollback = true;
-  attempter_.install_plan_->version = kRollbackVersion;
-
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
-              ReportEnterpriseRollbackMetrics(false, kRollbackVersion))
-      .Times(1);
-  MockAction action;
-  attempter_.CreatePendingErrorEvent(&action, ErrorCode::kRollbackNotPossible);
-  attempter_.ProcessingDone(nullptr, ErrorCode::kRollbackNotPossible);
-}
-
-TEST_F(UpdateAttempterTest, RollbackMetricsNotRollbackFailure) {
-  attempter_.install_plan_.reset(new InstallPlan);
-  attempter_.install_plan_->is_rollback = false;
-  attempter_.install_plan_->version = kRollbackVersion;
-
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
-              ReportEnterpriseRollbackMetrics(_, _))
-      .Times(0);
-  MockAction action;
-  attempter_.CreatePendingErrorEvent(&action, ErrorCode::kRollbackNotPossible);
-  attempter_.ProcessingDone(nullptr, ErrorCode::kRollbackNotPossible);
-}
-
-TEST_F(UpdateAttempterTest, TimeToUpdateAppliedMetricFailure) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
-              ReportEnterpriseUpdateSeenToDownloadDays(_, _))
-      .Times(0);
-  attempter_.ProcessingDone(nullptr, ErrorCode::kOmahaUpdateDeferredPerPolicy);
-}
-
-TEST_F(UpdateAttempterTest, TimeToUpdateAppliedOnNonEnterprise) {
-  auto device_policy = std::make_unique<policy::MockDevicePolicy>();
-  FakeSystemState::Get()->set_device_policy(device_policy.get());
-  // Make device policy return that this is not enterprise enrolled
-  EXPECT_CALL(*device_policy, IsEnterpriseEnrolled()).WillOnce(Return(false));
-
-  // Ensure that the metric is not recorded.
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
-              ReportEnterpriseUpdateSeenToDownloadDays(_, _))
-      .Times(0);
-  attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
-}
-
-TEST_F(UpdateAttempterTest,
-       TimeToUpdateAppliedWithTimeRestrictionMetricSuccess) {
-  constexpr int kDaysToUpdate = 15;
-  auto device_policy = std::make_unique<policy::MockDevicePolicy>();
-  FakeSystemState::Get()->set_device_policy(device_policy.get());
-  // Make device policy return that this is enterprise enrolled
-  EXPECT_CALL(*device_policy, IsEnterpriseEnrolled()).WillOnce(Return(true));
-  // Pretend that there's a time restriction policy in place
-  EXPECT_CALL(*device_policy, GetDisallowedTimeIntervals(_))
-      .WillOnce(Return(true));
-
-  Time update_first_seen_at = Time::Now();
-  FakeSystemState::Get()->fake_prefs()->SetInt64(
-      kPrefsUpdateFirstSeenAt, update_first_seen_at.ToInternalValue());
-
-  Time update_finished_at =
-      update_first_seen_at + TimeDelta::FromDays(kDaysToUpdate);
-  FakeSystemState::Get()->fake_clock()->SetWallclockTime(update_finished_at);
-
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
-              ReportEnterpriseUpdateSeenToDownloadDays(true, kDaysToUpdate))
-      .Times(1);
-  attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
-}
-
-TEST_F(UpdateAttempterTest,
-       TimeToUpdateAppliedWithoutTimeRestrictionMetricSuccess) {
-  constexpr int kDaysToUpdate = 15;
-  auto device_policy = std::make_unique<policy::MockDevicePolicy>();
-  FakeSystemState::Get()->set_device_policy(device_policy.get());
-  // Make device policy return that this is enterprise enrolled
-  EXPECT_CALL(*device_policy, IsEnterpriseEnrolled()).WillOnce(Return(true));
-  // Pretend that there's no time restriction policy in place
-  EXPECT_CALL(*device_policy, GetDisallowedTimeIntervals(_))
-      .WillOnce(Return(false));
-
-  Time update_first_seen_at = Time::Now();
-  FakeSystemState::Get()->fake_prefs()->SetInt64(
-      kPrefsUpdateFirstSeenAt, update_first_seen_at.ToInternalValue());
-
-  Time update_finished_at =
-      update_first_seen_at + TimeDelta::FromDays(kDaysToUpdate);
-  FakeSystemState::Get()->fake_clock()->SetWallclockTime(update_finished_at);
-
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
-              ReportEnterpriseUpdateSeenToDownloadDays(false, kDaysToUpdate))
-      .Times(1);
-  attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
-}
-
-TEST_F(UpdateAttempterTest, ProcessingDoneUpdated) {
-  // GIVEN an update finished.
-
-  // THEN update_engine should call update completion.
-  pd_params_.should_update_completed_be_called = true;
-  // THEN need reboot since update applied.
-  pd_params_.expected_exit_status = UpdateStatus::UPDATED_NEED_REBOOT;
-  // THEN install indication should be false.
-
-  TestProcessingDone();
-}
-
-TEST_F(UpdateAttempterTest, ProcessingDoneUpdatedDlcFilter) {
-  // GIVEN an update finished.
-  // GIVEN DLC |AppParams| list.
-  auto dlc_1 = "dlc_1", dlc_2 = "dlc_2";
-  pd_params_.dlc_apps_params = {{dlc_1, {.name = dlc_1, .updated = false}},
-                                {dlc_2, {.name = dlc_2}}};
-
-  // THEN update_engine should call update completion.
-  pd_params_.should_update_completed_be_called = true;
-  pd_params_.args_to_update_completed = {dlc_2};
-  // THEN need reboot since update applied.
-  pd_params_.expected_exit_status = UpdateStatus::UPDATED_NEED_REBOOT;
-  // THEN install indication should be false.
-
-  TestProcessingDone();
-}
-
-TEST_F(UpdateAttempterTest, ProcessingDoneInstalled) {
-  // GIVEN an install finished.
-  pd_params_.is_install = true;
-
-  // THEN update_engine should call install completion.
-  pd_params_.should_install_completed_be_called = true;
-  // THEN go idle.
-  // THEN install indication should be false.
-
-  TestProcessingDone();
-}
-
-TEST_F(UpdateAttempterTest, ProcessingDoneInstalledDlcFilter) {
-  // GIVEN an install finished.
-  pd_params_.is_install = true;
-  // GIVEN DLC |AppParams| list.
-  auto dlc_1 = "dlc_1", dlc_2 = "dlc_2";
-  pd_params_.dlc_apps_params = {{dlc_1, {.name = dlc_1, .updated = false}},
-                                {dlc_2, {.name = dlc_2}}};
-
-  // THEN update_engine should call install completion.
-  pd_params_.should_install_completed_be_called = true;
-  pd_params_.args_to_install_completed = {dlc_2};
-  // THEN go idle.
-  // THEN install indication should be false.
-
-  TestProcessingDone();
-}
-
-TEST_F(UpdateAttempterTest, ProcessingDoneInstallReportingError) {
-  // GIVEN an install finished.
-  pd_params_.is_install = true;
-  // GIVEN a reporting error occurred.
-  pd_params_.status = UpdateStatus::REPORTING_ERROR_EVENT;
-
-  // THEN update_engine should not call install completion.
-  // THEN go idle.
-  // THEN install indication should be false.
-
-  TestProcessingDone();
-}
-
-TEST_F(UpdateAttempterTest, ProcessingDoneNoUpdate) {
-  // GIVEN an update finished.
-  // GIVEN an action error occured.
-  pd_params_.code = ErrorCode::kNoUpdate;
-
-  // THEN update_engine should not call update completion.
-  // THEN go idle.
-  // THEN install indication should be false.
-
-  TestProcessingDone();
-}
-
-TEST_F(UpdateAttempterTest, ProcessingDoneNoInstall) {
-  // GIVEN an install finished.
-  pd_params_.is_install = true;
-  // GIVEN an action error occured.
-  pd_params_.code = ErrorCode::kNoUpdate;
-
-  // THEN update_engine should not call install completion.
-  // THEN go idle.
-  // THEN install indication should be false.
-
-  TestProcessingDone();
-}
-
-TEST_F(UpdateAttempterTest, ProcessingDoneUpdateError) {
-  // GIVEN an update finished.
-  // GIVEN an action error occured.
-  pd_params_.code = ErrorCode::kError;
-  // GIVEN an event error is set.
-  attempter_.error_event_.reset(new OmahaEvent(OmahaEvent::kTypeUpdateComplete,
-                                               OmahaEvent::kResultError,
-                                               ErrorCode::kError));
-
-  // THEN indicate a error event.
-  pd_params_.expected_exit_status = UpdateStatus::REPORTING_ERROR_EVENT;
-  // THEN install indication should be false.
-
-  // THEN update_engine should not call update completion.
-  // THEN expect critical actions of |ScheduleErrorEventAction()|.
-  EXPECT_CALL(*processor_, EnqueueAction(Pointee(_))).Times(1);
-  EXPECT_CALL(*processor_, StartProcessing()).Times(1);
-  // THEN |ScheduleUpdates()| will be called next |ProcessingDone()| so skip.
-  pd_params_.should_schedule_updates_be_called = false;
-
-  TestProcessingDone();
-}
-
-TEST_F(UpdateAttempterTest, ProcessingDoneInstallError) {
-  // GIVEN an install finished.
-  pd_params_.is_install = true;
-  // GIVEN an action error occured.
-  pd_params_.code = ErrorCode::kError;
-  // GIVEN an event error is set.
-  attempter_.error_event_.reset(new OmahaEvent(OmahaEvent::kTypeUpdateComplete,
-                                               OmahaEvent::kResultError,
-                                               ErrorCode::kError));
-
-  // THEN indicate a error event.
-  pd_params_.expected_exit_status = UpdateStatus::REPORTING_ERROR_EVENT;
-  // THEN install indication should be false.
-
-  // THEN update_engine should not call install completion.
-  // THEN expect critical actions of |ScheduleErrorEventAction()|.
-  EXPECT_CALL(*processor_, EnqueueAction(Pointee(_))).Times(1);
-  EXPECT_CALL(*processor_, StartProcessing()).Times(1);
-  // THEN |ScheduleUpdates()| will be called next |ProcessingDone()| so skip.
-  pd_params_.should_schedule_updates_be_called = false;
-
-  TestProcessingDone();
-}
-
-TEST_F(UpdateAttempterTest, QuickFixTokenWhenDeviceIsEnterpriseEnrolled) {
-  attempter_.CalculateUpdateParams({.quick_fix_build_token = "token"});
-  EXPECT_EQ("token",
-            FakeSystemState::Get()->request_params()->autoupdate_token());
-
-  attempter_.CalculateUpdateParams({});
-  EXPECT_TRUE(
-      FakeSystemState::Get()->request_params()->autoupdate_token().empty());
-}
-
-TEST_F(UpdateAttempterTest, ScheduleUpdateSpamHandlerTest) {
-  EXPECT_CALL(mock_update_manager_, AsyncPolicyRequestUpdateCheckAllowed(_, _))
-      .Times(1);
-  EXPECT_TRUE(attempter_.ScheduleUpdates());
-  // Now there is an update scheduled which means that all subsequent
-  // |ScheduleUpdates()| should fail.
-  EXPECT_FALSE(attempter_.ScheduleUpdates());
-  EXPECT_FALSE(attempter_.ScheduleUpdates());
-  EXPECT_FALSE(attempter_.ScheduleUpdates());
-}
-
-// Critical tests to always make sure that an update is scheduled. The following
-// unittest(s) try and cover the correctness in synergy between
-// |UpdateAttempter| and |UpdateManager|. Also it is good to remember the
-// actions that happen in the flow when |UpdateAttempter| get callbacked on
-// |OnUpdateScheduled()| -> (various cases which leads to) -> |ProcessingDone()|
-void UpdateAttempterTest::TestOnUpdateScheduled() {
-  // Setup
-  attempter_.SetWaitingForScheduledCheck(true);
-  attempter_.DisableUpdate();
-  attempter_.DisableScheduleUpdates();
-
-  // Invocation
-  attempter_.OnUpdateScheduled(ous_params_.status, ous_params_.params);
-
-  // Verify
-  EXPECT_EQ(ous_params_.exit_status, attempter_.status());
-  EXPECT_EQ(ous_params_.should_schedule_updates_be_called,
-            attempter_.WasScheduleUpdatesCalled());
-  EXPECT_EQ(ous_params_.should_update_be_called, attempter_.WasUpdateCalled());
-}
-
-TEST_F(UpdateAttempterTest, OnUpdatesScheduledFailed) {
-  // GIVEN failed status.
-
-  // THEN update should be scheduled.
-  ous_params_.should_schedule_updates_be_called = true;
-
-  TestOnUpdateScheduled();
-}
-
-TEST_F(UpdateAttempterTest, OnUpdatesScheduledAskMeAgainLater) {
-  // GIVEN ask me again later status.
-  ous_params_.status = EvalStatus::kAskMeAgainLater;
-
-  // THEN update should be scheduled.
-  ous_params_.should_schedule_updates_be_called = true;
-
-  TestOnUpdateScheduled();
-}
-
-TEST_F(UpdateAttempterTest, OnUpdatesScheduledContinue) {
-  // GIVEN continue status.
-  ous_params_.status = EvalStatus::kContinue;
-
-  // THEN update should be scheduled.
-  ous_params_.should_schedule_updates_be_called = true;
-
-  TestOnUpdateScheduled();
-}
-
-TEST_F(UpdateAttempterTest, OnUpdatesScheduledSucceededButUpdateDisabledFails) {
-  // GIVEN updates disabled.
-  ous_params_.params = {.updates_enabled = false};
-  // GIVEN succeeded status.
-  ous_params_.status = EvalStatus::kSucceeded;
-
-  // THEN update should not be scheduled.
-
-  TestOnUpdateScheduled();
-}
-
-TEST_F(UpdateAttempterTest, OnUpdatesScheduledSucceeded) {
-  // GIVEN updates enabled.
-  ous_params_.params = {.updates_enabled = true};
-  // GIVEN succeeded status.
-  ous_params_.status = EvalStatus::kSucceeded;
-
-  // THEN update should be called indicating status change.
-  ous_params_.exit_status = UpdateStatus::CHECKING_FOR_UPDATE;
-  ous_params_.should_update_be_called = true;
-
-  TestOnUpdateScheduled();
-}
-
-TEST_F(UpdateAttempterTest, IsEnterpriseRollbackInGetStatusDefault) {
-  UpdateEngineStatus status;
-  attempter_.GetStatus(&status);
-  EXPECT_FALSE(status.is_enterprise_rollback);
-}
-
-TEST_F(UpdateAttempterTest, IsEnterpriseRollbackInGetStatusFalse) {
-  attempter_.install_plan_.reset(new InstallPlan);
-  attempter_.install_plan_->is_rollback = false;
-
-  UpdateEngineStatus status;
-  attempter_.GetStatus(&status);
-  EXPECT_FALSE(status.is_enterprise_rollback);
-}
-
-TEST_F(UpdateAttempterTest, IsEnterpriseRollbackInGetStatusTrue) {
-  attempter_.install_plan_.reset(new InstallPlan);
-  attempter_.install_plan_->is_rollback = true;
-
-  UpdateEngineStatus status;
-  attempter_.GetStatus(&status);
-  EXPECT_TRUE(status.is_enterprise_rollback);
-}
-
-TEST_F(UpdateAttempterTest, PowerwashInGetStatusDefault) {
-  UpdateEngineStatus status;
-  attempter_.GetStatus(&status);
-  EXPECT_FALSE(status.will_powerwash_after_reboot);
-}
-
-TEST_F(UpdateAttempterTest, PowerwashInGetStatusTrueBecausePowerwashRequired) {
-  attempter_.install_plan_.reset(new InstallPlan);
-  attempter_.install_plan_->powerwash_required = true;
-
-  UpdateEngineStatus status;
-  attempter_.GetStatus(&status);
-  EXPECT_TRUE(status.will_powerwash_after_reboot);
-}
-
-TEST_F(UpdateAttempterTest, PowerwashInGetStatusTrueBecauseRollback) {
-  attempter_.install_plan_.reset(new InstallPlan);
-  attempter_.install_plan_->is_rollback = true;
-
-  UpdateEngineStatus status;
-  attempter_.GetStatus(&status);
-  EXPECT_TRUE(status.will_powerwash_after_reboot);
-}
-
-TEST_F(UpdateAttempterTest, FutureEolTest) {
-  EolDate eol_date = std::numeric_limits<int64_t>::max();
-  EXPECT_TRUE(prefs_->SetString(kPrefsOmahaEolDate, EolDateToString(eol_date)));
-  UpdateEngineStatus status;
-  attempter_.GetStatus(&status);
-  EXPECT_EQ(eol_date, status.eol_date);
-}
-
-TEST_F(UpdateAttempterTest, PastEolTest) {
-  EolDate eol_date = 1;
-  EXPECT_TRUE(prefs_->SetString(kPrefsOmahaEolDate, EolDateToString(eol_date)));
-  UpdateEngineStatus status;
-  attempter_.GetStatus(&status);
-  EXPECT_EQ(eol_date, status.eol_date);
-}
-
-TEST_F(UpdateAttempterTest, MissingEolTest) {
-  UpdateEngineStatus status;
-  attempter_.GetStatus(&status);
-  EXPECT_EQ(kEolDateInvalid, status.eol_date);
-}
-
-TEST_F(UpdateAttempterTest, CalculateDlcParamsInstallTest) {
-  string dlc_id = "dlc0";
-  attempter_.is_install_ = true;
-  attempter_.dlc_ids_ = {dlc_id};
-  attempter_.CalculateDlcParams();
-
-  OmahaRequestParams* params = FakeSystemState::Get()->request_params();
-  EXPECT_EQ(1, params->dlc_apps_params().count(params->GetDlcAppId(dlc_id)));
-  OmahaRequestParams::AppParams dlc_app_params =
-      params->dlc_apps_params().at(params->GetDlcAppId(dlc_id));
-  EXPECT_STREQ(dlc_id.c_str(), dlc_app_params.name.c_str());
-  EXPECT_EQ(false, dlc_app_params.send_ping);
-  // When the DLC gets installed, a ping is not sent, therefore we don't store
-  // the values sent by Omaha.
-  auto last_active_key = PrefsInterface::CreateSubKey(
-      {kDlcPrefsSubDir, dlc_id, kPrefsPingLastActive});
-  EXPECT_FALSE(FakeSystemState::Get()->prefs()->Exists(last_active_key));
-  auto last_rollcall_key = PrefsInterface::CreateSubKey(
-      {kDlcPrefsSubDir, dlc_id, kPrefsPingLastRollcall});
-  EXPECT_FALSE(FakeSystemState::Get()->prefs()->Exists(last_rollcall_key));
-}
-
-TEST_F(UpdateAttempterTest, CalculateDlcParamsNoPrefFilesTest) {
-  string dlc_id = "dlc0";
-  EXPECT_CALL(mock_dlcservice_, GetDlcsToUpdate(_))
-      .WillOnce(
-          DoAll(SetArgPointee<0>(std::vector<string>({dlc_id})), Return(true)));
-
-  attempter_.is_install_ = false;
-  attempter_.CalculateDlcParams();
-
-  OmahaRequestParams* params = FakeSystemState::Get()->request_params();
-  EXPECT_EQ(1, params->dlc_apps_params().count(params->GetDlcAppId(dlc_id)));
-  OmahaRequestParams::AppParams dlc_app_params =
-      params->dlc_apps_params().at(params->GetDlcAppId(dlc_id));
-  EXPECT_STREQ(dlc_id.c_str(), dlc_app_params.name.c_str());
-
-  EXPECT_EQ(true, dlc_app_params.send_ping);
-  EXPECT_EQ(0, dlc_app_params.ping_active);
-  EXPECT_EQ(-1, dlc_app_params.ping_date_last_active);
-  EXPECT_EQ(-1, dlc_app_params.ping_date_last_rollcall);
-}
-
-TEST_F(UpdateAttempterTest, CalculateDlcParamsNonParseableValuesTest) {
-  string dlc_id = "dlc0";
-  MemoryPrefs prefs;
-  FakeSystemState::Get()->set_prefs(&prefs);
-  EXPECT_CALL(mock_dlcservice_, GetDlcsToUpdate(_))
-      .WillOnce(
-          DoAll(SetArgPointee<0>(std::vector<string>({dlc_id})), Return(true)));
-
-  // Write non numeric values in the metadata files.
-  auto active_key =
-      PrefsInterface::CreateSubKey({kDlcPrefsSubDir, dlc_id, kPrefsPingActive});
-  auto last_active_key = PrefsInterface::CreateSubKey(
-      {kDlcPrefsSubDir, dlc_id, kPrefsPingLastActive});
-  auto last_rollcall_key = PrefsInterface::CreateSubKey(
-      {kDlcPrefsSubDir, dlc_id, kPrefsPingLastRollcall});
-  FakeSystemState::Get()->prefs()->SetString(active_key, "z2yz");
-  FakeSystemState::Get()->prefs()->SetString(last_active_key, "z2yz");
-  FakeSystemState::Get()->prefs()->SetString(last_rollcall_key, "z2yz");
-  attempter_.is_install_ = false;
-  attempter_.CalculateDlcParams();
-
-  OmahaRequestParams* params = FakeSystemState::Get()->request_params();
-  EXPECT_EQ(1, params->dlc_apps_params().count(params->GetDlcAppId(dlc_id)));
-  OmahaRequestParams::AppParams dlc_app_params =
-      params->dlc_apps_params().at(params->GetDlcAppId(dlc_id));
-  EXPECT_STREQ(dlc_id.c_str(), dlc_app_params.name.c_str());
-
-  EXPECT_EQ(true, dlc_app_params.send_ping);
-  EXPECT_EQ(0, dlc_app_params.ping_active);
-  EXPECT_EQ(-2, dlc_app_params.ping_date_last_active);
-  EXPECT_EQ(-2, dlc_app_params.ping_date_last_rollcall);
-}
-
-TEST_F(UpdateAttempterTest, CalculateDlcParamsValidValuesTest) {
-  string dlc_id = "dlc0";
-  EXPECT_CALL(mock_dlcservice_, GetDlcsToUpdate(_))
-      .WillOnce(
-          DoAll(SetArgPointee<0>(std::vector<string>({dlc_id})), Return(true)));
-
-  // Write numeric values in the metadata files.
-  auto active_key =
-      PrefsInterface::CreateSubKey({kDlcPrefsSubDir, dlc_id, kPrefsPingActive});
-  auto last_active_key = PrefsInterface::CreateSubKey(
-      {kDlcPrefsSubDir, dlc_id, kPrefsPingLastActive});
-  auto last_rollcall_key = PrefsInterface::CreateSubKey(
-      {kDlcPrefsSubDir, dlc_id, kPrefsPingLastRollcall});
-
-  FakeSystemState::Get()->prefs()->SetInt64(active_key, 1);
-  FakeSystemState::Get()->prefs()->SetInt64(last_active_key, 78);
-  FakeSystemState::Get()->prefs()->SetInt64(last_rollcall_key, 99);
-  attempter_.is_install_ = false;
-  attempter_.CalculateDlcParams();
-
-  OmahaRequestParams* params = FakeSystemState::Get()->request_params();
-  EXPECT_EQ(1, params->dlc_apps_params().count(params->GetDlcAppId(dlc_id)));
-  OmahaRequestParams::AppParams dlc_app_params =
-      params->dlc_apps_params().at(params->GetDlcAppId(dlc_id));
-  EXPECT_STREQ(dlc_id.c_str(), dlc_app_params.name.c_str());
-
-  EXPECT_EQ(true, dlc_app_params.send_ping);
-  EXPECT_EQ(1, dlc_app_params.ping_active);
-  EXPECT_EQ(78, dlc_app_params.ping_date_last_active);
-  EXPECT_EQ(99, dlc_app_params.ping_date_last_rollcall);
-}
-
-TEST_F(UpdateAttempterTest, CalculateDlcParamsRemoveStaleMetadata) {
-  string dlc_id = "dlc0";
-  auto active_key =
-      PrefsInterface::CreateSubKey({kDlcPrefsSubDir, dlc_id, kPrefsPingActive});
-  auto last_active_key = PrefsInterface::CreateSubKey(
-      {kDlcPrefsSubDir, dlc_id, kPrefsPingLastActive});
-  auto last_rollcall_key = PrefsInterface::CreateSubKey(
-      {kDlcPrefsSubDir, dlc_id, kPrefsPingLastRollcall});
-  FakeSystemState::Get()->prefs()->SetInt64(active_key, kPingInactiveValue);
-  FakeSystemState::Get()->prefs()->SetInt64(last_active_key, 0);
-  FakeSystemState::Get()->prefs()->SetInt64(last_rollcall_key, 0);
-  EXPECT_TRUE(FakeSystemState::Get()->prefs()->Exists(active_key));
-  EXPECT_TRUE(FakeSystemState::Get()->prefs()->Exists(last_active_key));
-  EXPECT_TRUE(FakeSystemState::Get()->prefs()->Exists(last_rollcall_key));
-
-  attempter_.dlc_ids_ = {dlc_id};
-  attempter_.is_install_ = true;
-  attempter_.CalculateDlcParams();
-
-  EXPECT_FALSE(FakeSystemState::Get()->prefs()->Exists(last_active_key));
-  EXPECT_FALSE(FakeSystemState::Get()->prefs()->Exists(last_rollcall_key));
-  // Active key is set on install.
-  EXPECT_TRUE(FakeSystemState::Get()->prefs()->Exists(active_key));
-  int64_t temp_int;
-  EXPECT_TRUE(FakeSystemState::Get()->prefs()->GetInt64(active_key, &temp_int));
-  EXPECT_EQ(temp_int, kPingActiveValue);
-}
-
-TEST_F(UpdateAttempterTest, SetDlcActiveValue) {
-  string dlc_id = "dlc0";
-  attempter_.SetDlcActiveValue(true, dlc_id);
-  int64_t temp_int;
-  auto active_key =
-      PrefsInterface::CreateSubKey({kDlcPrefsSubDir, dlc_id, kPrefsPingActive});
-  EXPECT_TRUE(FakeSystemState::Get()->prefs()->Exists(active_key));
-  EXPECT_TRUE(FakeSystemState::Get()->prefs()->GetInt64(active_key, &temp_int));
-  EXPECT_EQ(temp_int, kPingActiveValue);
-}
-
-TEST_F(UpdateAttempterTest, SetDlcInactive) {
-  string dlc_id = "dlc0";
-  auto sub_keys = {
-      kPrefsPingActive, kPrefsPingLastActive, kPrefsPingLastRollcall};
-  for (auto& sub_key : sub_keys) {
-    auto key = PrefsInterface::CreateSubKey({kDlcPrefsSubDir, dlc_id, sub_key});
-    FakeSystemState::Get()->prefs()->SetInt64(key, 1);
-    EXPECT_TRUE(FakeSystemState::Get()->prefs()->Exists(key));
-  }
-  attempter_.SetDlcActiveValue(false, dlc_id);
-  for (auto& sub_key : sub_keys) {
-    auto key = PrefsInterface::CreateSubKey({kDlcPrefsSubDir, dlc_id, sub_key});
-    EXPECT_FALSE(FakeSystemState::Get()->prefs()->Exists(key));
-  }
-}
-
-TEST_F(UpdateAttempterTest, GetSuccessfulDlcIds) {
-  auto dlc_1 = "1", dlc_2 = "2", dlc_3 = "3";
-  attempter_.omaha_request_params_->set_dlc_apps_params(
-      {{dlc_1, {.name = dlc_1, .updated = false}},
-       {dlc_2, {.name = dlc_2}},
-       {dlc_3, {.name = dlc_3, .updated = false}}});
-  EXPECT_THAT(attempter_.GetSuccessfulDlcIds(), ElementsAre(dlc_2));
-}
-
-TEST_F(UpdateAttempterTest, MoveToPrefs) {
-  string key1 = kPrefsLastActivePingDay;
-  string key2 = kPrefsPingLastRollcall;
-
-  FakePrefs fake_prefs;
-  EXPECT_TRUE(fake_prefs.SetString(key2, "current-rollcall"));
-  FakeSystemState::Get()->set_prefs(&fake_prefs);
-
-  FakePrefs powerwash_safe_prefs;
-  EXPECT_TRUE(powerwash_safe_prefs.SetString(key1, "powerwash-last-active"));
-  EXPECT_TRUE(powerwash_safe_prefs.SetString(key2, "powerwash-last-rollcall"));
-  FakeSystemState::Get()->set_powerwash_safe_prefs(&powerwash_safe_prefs);
-
-  attempter_.Init();
-  attempter_.MoveToPrefs({key1, key2});
-
-  string pref_value_1;
-  fake_prefs.GetString(key1, &pref_value_1);
-  EXPECT_EQ(pref_value_1, "powerwash-last-active");
-  // Do not overwrite if value already exists.
-  string pref_value_2;
-  fake_prefs.GetString(key2, &pref_value_2);
-  EXPECT_EQ(pref_value_2, "current-rollcall");
-
-  // Make sure keys are deleted from powerwash safe prefs regardless of whether
-  // they are written to prefs.
-  EXPECT_FALSE(FakeSystemState::Get()->powerwash_safe_prefs()->Exists(key1));
-  EXPECT_FALSE(FakeSystemState::Get()->powerwash_safe_prefs()->Exists(key2));
-}
-
-}  // namespace chromeos_update_engine
diff --git a/daemon.cc b/daemon.cc
new file mode 100644
index 0000000..d42344a
--- /dev/null
+++ b/daemon.cc
@@ -0,0 +1,117 @@
+//
+// 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/daemon.h"
+
+#include <sysexits.h>
+
+#include <base/bind.h>
+#include <base/location.h>
+#if USE_BINDER
+#include <binderwrapper/binder_wrapper.h>
+#endif  // USE_BINDER
+
+#if USE_OMAHA
+#include "update_engine/real_system_state.h"
+#else  // !USE_OMAHA
+#include "update_engine/daemon_state_android.h"
+#endif  // USE_OMAHA
+
+namespace chromeos_update_engine {
+
+int UpdateEngineDaemon::OnInit() {
+  // Register the |subprocess_| singleton with this Daemon as the signal
+  // handler.
+  subprocess_.Init(this);
+
+  int exit_code = Daemon::OnInit();
+  if (exit_code != EX_OK)
+    return exit_code;
+
+#if USE_BINDER
+  android::BinderWrapper::Create();
+  binder_watcher_.Init();
+#endif  // USE_BINDER
+
+#if USE_OMAHA
+  // Initialize update engine global state but continue if something fails.
+  // TODO(deymo): Move the daemon_state_ initialization to a factory method
+  // avoiding the explicit re-usage of the |bus| instance, shared between
+  // D-Bus service and D-Bus client calls.
+  RealSystemState* real_system_state = new RealSystemState();
+  daemon_state_.reset(real_system_state);
+  LOG_IF(ERROR, !real_system_state->Initialize())
+      << "Failed to initialize system state.";
+#else  // !USE_OMAHA
+  DaemonStateAndroid* daemon_state_android = new DaemonStateAndroid();
+  daemon_state_.reset(daemon_state_android);
+  LOG_IF(ERROR, !daemon_state_android->Initialize())
+      << "Failed to initialize system state.";
+#endif  // USE_OMAHA
+
+#if USE_BINDER
+  // Create the Binder Service.
+#if USE_OMAHA
+  binder_service_ = new BinderUpdateEngineBrilloService{real_system_state};
+#else   // !USE_OMAHA
+  binder_service_ = new BinderUpdateEngineAndroidService{
+      daemon_state_android->service_delegate()};
+#endif  // USE_OMAHA
+  auto binder_wrapper = android::BinderWrapper::Get();
+  if (!binder_wrapper->RegisterService(binder_service_->ServiceName(),
+                                       binder_service_)) {
+    LOG(ERROR) << "Failed to register binder service.";
+  }
+
+  daemon_state_->AddObserver(binder_service_.get());
+#endif  // USE_BINDER
+
+#if USE_DBUS
+  // Create the DBus service.
+  dbus_adaptor_.reset(new UpdateEngineAdaptor(real_system_state));
+  daemon_state_->AddObserver(dbus_adaptor_.get());
+
+  dbus_adaptor_->RegisterAsync(base::Bind(&UpdateEngineDaemon::OnDBusRegistered,
+                                          base::Unretained(this)));
+  LOG(INFO) << "Waiting for DBus object to be registered.";
+#else   // !USE_DBUS
+  daemon_state_->StartUpdater();
+#endif  // USE_DBUS
+  return EX_OK;
+}
+
+#if USE_DBUS
+void UpdateEngineDaemon::OnDBusRegistered(bool succeeded) {
+  if (!succeeded) {
+    LOG(ERROR) << "Registering the UpdateEngineAdaptor";
+    QuitWithExitCode(1);
+    return;
+  }
+
+  // Take ownership of the service now that everything is initialized. We need
+  // to this now and not before to avoid exposing a well known DBus service
+  // path that doesn't have the service it is supposed to implement.
+  if (!dbus_adaptor_->RequestOwnership()) {
+    LOG(ERROR) << "Unable to take ownership of the DBus service, is there "
+               << "other update_engine daemon running?";
+    QuitWithExitCode(1);
+    return;
+  }
+  daemon_state_->StartUpdater();
+}
+#endif  // USE_DBUS
+
+}  // namespace chromeos_update_engine
diff --git a/daemon.h b/daemon.h
new file mode 100644
index 0000000..c10bb28
--- /dev/null
+++ b/daemon.h
@@ -0,0 +1,87 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_DAEMON_H_
+#define UPDATE_ENGINE_DAEMON_H_
+
+#include <memory>
+#include <string>
+
+#if USE_BINDER
+#include <brillo/binder_watcher.h>
+#endif  // USE_BINDER
+#include <brillo/daemons/daemon.h>
+
+#if USE_BINDER
+#if USE_OMAHA
+#include "update_engine/binder_service_brillo.h"
+#else  // !USE_OMAHA
+#include "update_engine/binder_service_android.h"
+#endif  // USE_OMAHA
+#endif  // USE_BINDER
+#include "update_engine/common/subprocess.h"
+#include "update_engine/daemon_state_interface.h"
+#if USE_DBUS
+#include "update_engine/dbus_service.h"
+#endif  // USE_DBUS
+
+namespace chromeos_update_engine {
+
+class UpdateEngineDaemon : public brillo::Daemon {
+ public:
+  UpdateEngineDaemon() = default;
+
+ protected:
+  int OnInit() override;
+
+ private:
+#if USE_DBUS
+  // Run from the main loop when the |dbus_adaptor_| object is registered. At
+  // this point we can request ownership of the DBus service name and continue
+  // initialization.
+  void OnDBusRegistered(bool succeeded);
+
+  // Main D-Bus service adaptor.
+  std::unique_ptr<UpdateEngineAdaptor> dbus_adaptor_;
+#endif  // USE_DBUS
+
+  // The Subprocess singleton class requires a brillo::MessageLoop in the
+  // current thread, so we need to initialize it from this class instead of
+  // the main() function.
+  Subprocess subprocess_;
+
+#if USE_BINDER
+  brillo::BinderWatcher binder_watcher_;
+#endif  // USE_BINDER
+
+#if USE_BINDER
+#if USE_OMAHA
+  android::sp<BinderUpdateEngineBrilloService> binder_service_;
+#else  // !USE_OMAHA
+  android::sp<BinderUpdateEngineAndroidService> binder_service_;
+#endif  // USE_OMAHA
+#endif  // USE_BINDER
+
+  // The daemon state with all the required daemon classes for the configured
+  // platform.
+  std::unique_ptr<DaemonStateInterface> daemon_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(UpdateEngineDaemon);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_DAEMON_H_
diff --git a/aosp/daemon_state_android.cc b/daemon_state_android.cc
similarity index 74%
rename from aosp/daemon_state_android.cc
rename to daemon_state_android.cc
index da49080..c9c09b8 100644
--- a/aosp/daemon_state_android.cc
+++ b/daemon_state_android.cc
@@ -14,16 +14,15 @@
 // limitations under the License.
 //
 
-#include "update_engine/aosp/daemon_state_android.h"
+#include "update_engine/daemon_state_android.h"
 
 #include <base/logging.h>
 
-#include "update_engine/aosp/apex_handler_android.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 {
 
@@ -46,17 +45,17 @@
 
   // Initialize prefs.
   base::FilePath non_volatile_path;
+  // TODO(deymo): Fall back to in-memory prefs if there's no physical directory
+  // available.
   if (!hardware_->GetNonVolatileDirectory(&non_volatile_path)) {
-    prefs_.reset(new MemoryPrefs());
-    LOG(WARNING)
-        << "Could not get a non-volatile directory, fall back to memory prefs";
-  } else {
-    Prefs* prefs = new Prefs();
-    prefs_.reset(prefs);
-    if (!prefs->Init(non_volatile_path.Append(kPrefsSubDirectory))) {
-      LOG(ERROR) << "Failed to initialize preferences.";
-      return false;
-    }
+    LOG(ERROR) << "Failed to get a non-volatile directory.";
+    return false;
+  }
+  Prefs* prefs = new Prefs();
+  prefs_.reset(prefs);
+  if (!prefs->Init(non_volatile_path.Append(kPrefsSubDirectory))) {
+    LOG(ERROR) << "Failed to initialize preferences.";
+    return false;
   }
 
   // The CertificateChecker singleton is used by the update attempter.
@@ -65,11 +64,8 @@
   certificate_checker_->Init();
 
   // Initialize the UpdateAttempter before the UpdateManager.
-  update_attempter_.reset(new UpdateAttempterAndroid(this,
-                                                     prefs_.get(),
-                                                     boot_control_.get(),
-                                                     hardware_.get(),
-                                                     CreateApexHandler()));
+  update_attempter_.reset(new UpdateAttempterAndroid(
+      this, prefs_.get(), boot_control_.get(), hardware_.get()));
 
   return true;
 }
diff --git a/aosp/daemon_state_android.h b/daemon_state_android.h
similarity index 84%
rename from aosp/daemon_state_android.h
rename to daemon_state_android.h
index dea3a23..928a14e 100644
--- a/aosp/daemon_state_android.h
+++ b/daemon_state_android.h
@@ -14,20 +14,20 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_AOSP_DAEMON_STATE_ANDROID_H_
-#define UPDATE_ENGINE_AOSP_DAEMON_STATE_ANDROID_H_
+#ifndef UPDATE_ENGINE_DAEMON_STATE_ANDROID_H_
+#define UPDATE_ENGINE_DAEMON_STATE_ANDROID_H_
 
 #include <memory>
 #include <set>
 
-#include "update_engine/aosp/service_delegate_android_interface.h"
-#include "update_engine/aosp/update_attempter_android.h"
 #include "update_engine/certificate_checker.h"
 #include "update_engine/common/boot_control_interface.h"
-#include "update_engine/common/daemon_state_interface.h"
 #include "update_engine/common/hardware_interface.h"
 #include "update_engine/common/prefs_interface.h"
-#include "update_engine/common/service_observer_interface.h"
+#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"
 
 namespace chromeos_update_engine {
 
@@ -73,4 +73,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_AOSP_DAEMON_STATE_ANDROID_H_
+#endif  // UPDATE_ENGINE_DAEMON_STATE_ANDROID_H_
diff --git a/common/daemon_state_interface.h b/daemon_state_interface.h
similarity index 82%
rename from common/daemon_state_interface.h
rename to daemon_state_interface.h
index 831e38b..2356816 100644
--- a/common/daemon_state_interface.h
+++ b/daemon_state_interface.h
@@ -14,11 +14,12 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_COMMON_DAEMON_STATE_INTERFACE_H_
-#define UPDATE_ENGINE_COMMON_DAEMON_STATE_INTERFACE_H_
+#ifndef UPDATE_ENGINE_DAEMON_STATE_INTERFACE_H_
+#define UPDATE_ENGINE_DAEMON_STATE_INTERFACE_H_
 
-#include "update_engine/common/service_observer_interface.h"
+#include "update_engine/service_observer_interface.h"
 
+#include <memory>
 #include <set>
 
 namespace chromeos_update_engine {
@@ -41,10 +42,8 @@
 
  protected:
   DaemonStateInterface() = default;
-
-  DISALLOW_COPY_AND_ASSIGN(DaemonStateInterface);
 };
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_COMMON_DAEMON_STATE_INTERFACE_H_
+#endif  // UPDATE_ENGINE_DAEMON_STATE_INTERFACE_H_
diff --git a/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml b/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
index ac2f021..f81d4ed 100644
--- a/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
+++ b/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
@@ -1,19 +1,4 @@
 <?xml version="1.0" encoding="utf-8" ?>
-<!--
-  Copyright (C) 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-!-->
 <node name="/org/chromium/UpdateEngine">
   <interface name="org.chromium.UpdateEngineInterface">
     <annotation name="org.freedesktop.DBus.GLib.CSymbol"
@@ -35,12 +20,7 @@
       <arg type="i" name="flags" direction="in" />
     </method>
     <method name="AttemptInstall">
-      <arg type="s" name="omaha_url" direction="in" />
-      <arg type="as" name="dlc_ids" direction="in">
-        <tp:docstring>
-          The list of DLC IDs that needs to be installed.
-        </tp:docstring>
-      </arg>
+      <arg type="s" name="dlc_request" direction="in" />
     </method>
     <method name="AttemptRollback">
       <arg type="b" name="powerwash" direction="in" />
@@ -50,26 +30,12 @@
     </method>
     <method name="ResetStatus">
     </method>
-    <method name="SetDlcActiveValue">
-      <arg type="b" name="is_active" direction="in">
-        <tp:docstring>
-          If the DLC is being set to active or inactive.
-        </tp:docstring>
-      </arg>
-      <arg type="s" name="dlc_id" direction="in">
-        <tp:docstring>
-          The ID of the DLC module that will be set to active/inactive.
-        </tp:docstring>
-      </arg>
-    </method>
-    <method name="GetStatusAdvanced">
-      <arg type="ay" name="status" direction="out">
-        <tp:docstring>
-          The current status serialized in a protobuf.
-        </tp:docstring>
-        <annotation name="org.chromium.DBus.Argument.ProtobufClass"
-                    value="update_engine::StatusResult"/>
-      </arg>
+    <method name="GetStatus">
+      <arg type="x" name="last_checked_time" direction="out" />
+      <arg type="d" name="progress" direction="out" />
+      <arg type="s" name="current_operation" direction="out" />
+      <arg type="s" name="new_version" direction="out" />
+      <arg type="x" name="new_size" direction="out" />
     </method>
     <method name="RebootIfNeeded">
     </method>
@@ -114,14 +80,12 @@
     <method name="GetDurationSinceUpdate">
       <arg type="x" name="usec_wallclock" direction="out" />
     </method>
-    <signal name="StatusUpdateAdvanced">
-      <arg type="ay" name="status" direction="out">
-        <tp:docstring>
-          The current status serialized in a protobuf.
-        </tp:docstring>
-        <annotation name="org.chromium.DBus.Argument.ProtobufClass"
-                    value="update_engine::StatusResult"/>
-      </arg>
+    <signal name="StatusUpdate">
+      <arg type="x" name="last_checked_time" />
+      <arg type="d" name="progress" />
+      <arg type="s" name="current_operation" />
+      <arg type="s" name="new_version" />
+      <arg type="x" name="new_size" />
     </signal>
     <method name="GetPrevVersion">
       <arg type="s" name="prev_version" direction="out" />
@@ -132,5 +96,8 @@
     <method name="GetLastAttemptError">
       <arg type="i" name="last_attempt_error" direction="out" />
     </method>
+    <method name="GetEolStatus">
+      <arg type="i" name="eol_status" direction="out" />
+    </method>
   </interface>
 </node>
diff --git a/cros/dbus_connection.cc b/dbus_connection.cc
similarity index 96%
rename from cros/dbus_connection.cc
rename to dbus_connection.cc
index 6808bae..cf17ec9 100644
--- a/cros/dbus_connection.cc
+++ b/dbus_connection.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/dbus_connection.h"
+#include "update_engine/dbus_connection.h"
 
 #include <base/time/time.h>
 
diff --git a/cros/dbus_connection.h b/dbus_connection.h
similarity index 87%
rename from cros/dbus_connection.h
rename to dbus_connection.h
index 8f0d6f1..c3205ba 100644
--- a/cros/dbus_connection.h
+++ b/dbus_connection.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_DBUS_CONNECTION_H_
-#define UPDATE_ENGINE_CROS_DBUS_CONNECTION_H_
+#ifndef UPDATE_ENGINE_DBUS_CONNECTION_H_
+#define UPDATE_ENGINE_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_CROS_DBUS_CONNECTION_H_
+#endif  // UPDATE_ENGINE_DBUS_CONNECTION_H_
diff --git a/cros/dbus_service.cc b/dbus_service.cc
similarity index 72%
rename from cros/dbus_service.cc
rename to dbus_service.cc
index 1eb7b3c..7296053 100644
--- a/cros/dbus_service.cc
+++ b/dbus_service.cc
@@ -14,15 +14,15 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/dbus_service.h"
+#include "update_engine/dbus_service.h"
 
 #include <string>
 #include <vector>
 
 #include <update_engine/dbus-constants.h>
+#include <update_engine/proto_bindings/update_engine.pb.h>
 
-#include "update_engine/cros/dbus_connection.h"
-#include "update_engine/proto_bindings/update_engine.pb.h"
+#include "update_engine/dbus_connection.h"
 #include "update_engine/update_status_utils.h"
 
 namespace chromeos_update_engine {
@@ -31,29 +31,10 @@
 using chromeos_update_engine::UpdateEngineService;
 using std::string;
 using std::vector;
-using update_engine::Operation;
-using update_engine::StatusResult;
 using update_engine::UpdateEngineStatus;
 
-namespace {
-// Converts the internal |UpdateEngineStatus| to the protobuf |StatusResult|.
-void ConvertToStatusResult(const UpdateEngineStatus& ue_status,
-                           StatusResult* out_status) {
-  out_status->set_last_checked_time(ue_status.last_checked_time);
-  out_status->set_progress(ue_status.progress);
-  out_status->set_current_operation(static_cast<Operation>(ue_status.status));
-  out_status->set_new_version(ue_status.new_version);
-  out_status->set_new_size(ue_status.new_size_bytes);
-  out_status->set_is_enterprise_rollback(ue_status.is_enterprise_rollback);
-  out_status->set_is_install(ue_status.is_install);
-  out_status->set_eol_date(ue_status.eol_date);
-  out_status->set_will_powerwash_after_reboot(
-      ue_status.will_powerwash_after_reboot);
-}
-}  // namespace
-
-DBusUpdateEngineService::DBusUpdateEngineService()
-    : common_(new UpdateEngineService()) {}
+DBusUpdateEngineService::DBusUpdateEngineService(SystemState* system_state)
+    : common_(new UpdateEngineService{system_state}) {}
 
 // org::chromium::UpdateEngineInterfaceInterface methods implementation.
 
@@ -82,9 +63,26 @@
 }
 
 bool DBusUpdateEngineService::AttemptInstall(ErrorPtr* error,
-                                             const string& in_omaha_url,
-                                             const vector<string>& dlc_ids) {
-  return common_->AttemptInstall(error, in_omaha_url, dlc_ids);
+                                             const string& dlc_request) {
+  // Parse the raw parameters into protobuf.
+  DlcParameters dlc_parameters;
+  if (!dlc_parameters.ParseFromString(dlc_request)) {
+    *error = brillo::Error::Create(
+        FROM_HERE, "update_engine", "INTERNAL", "parameters are invalid.");
+    return false;
+  }
+  // Extract fields from the protobuf.
+  vector<string> dlc_module_ids;
+  for (const auto& dlc_info : dlc_parameters.dlc_infos()) {
+    if (dlc_info.dlc_id().empty()) {
+      *error = brillo::Error::Create(
+          FROM_HERE, "update_engine", "INTERNAL", "parameters are invalid.");
+      return false;
+    }
+    dlc_module_ids.push_back(dlc_info.dlc_id());
+  }
+  return common_->AttemptInstall(
+      error, dlc_parameters.omaha_url(), dlc_module_ids);
 }
 
 bool DBusUpdateEngineService::AttemptRollback(ErrorPtr* error,
@@ -101,20 +99,21 @@
   return common_->ResetStatus(error);
 }
 
-bool DBusUpdateEngineService::SetDlcActiveValue(brillo::ErrorPtr* error,
-                                                bool is_active,
-                                                const string& dlc_id) {
-  return common_->SetDlcActiveValue(error, is_active, dlc_id);
-}
-
-bool DBusUpdateEngineService::GetStatusAdvanced(ErrorPtr* error,
-                                                StatusResult* out_status) {
+bool DBusUpdateEngineService::GetStatus(ErrorPtr* error,
+                                        int64_t* out_last_checked_time,
+                                        double* out_progress,
+                                        string* out_current_operation,
+                                        string* out_new_version,
+                                        int64_t* out_new_size) {
   UpdateEngineStatus status;
   if (!common_->GetStatus(error, &status)) {
     return false;
   }
-
-  ConvertToStatusResult(status, out_status);
+  *out_last_checked_time = status.last_checked_time;
+  *out_progress = status.progress;
+  *out_current_operation = UpdateStatusToString(status.status);
+  *out_new_version = status.new_version;
+  *out_new_size = status.new_size_bytes;
   return true;
 }
 
@@ -192,10 +191,15 @@
   return common_->GetLastAttemptError(error, out_last_attempt_error);
 }
 
-UpdateEngineAdaptor::UpdateEngineAdaptor()
+bool DBusUpdateEngineService::GetEolStatus(ErrorPtr* error,
+                                           int32_t* out_eol_status) {
+  return common_->GetEolStatus(error, out_eol_status);
+}
+
+UpdateEngineAdaptor::UpdateEngineAdaptor(SystemState* system_state)
     : org::chromium::UpdateEngineInterfaceAdaptor(&dbus_service_),
       bus_(DBusConnection::Get()->GetDBus()),
-      dbus_service_(),
+      dbus_service_(system_state),
       dbus_object_(nullptr,
                    bus_,
                    dbus::ObjectPath(update_engine::kUpdateEngineServicePath)) {}
@@ -213,11 +217,11 @@
 
 void UpdateEngineAdaptor::SendStatusUpdate(
     const UpdateEngineStatus& update_engine_status) {
-  StatusResult status;
-  ConvertToStatusResult(update_engine_status, &status);
-
-  // Send |StatusUpdateAdvanced| signal.
-  SendStatusUpdateAdvancedSignal(status);
+  SendStatusUpdateSignal(update_engine_status.last_checked_time,
+                         update_engine_status.progress,
+                         UpdateStatusToString(update_engine_status.status),
+                         update_engine_status.new_version,
+                         update_engine_status.new_size_bytes);
 }
 
 }  // namespace chromeos_update_engine
diff --git a/cros/dbus_service.h b/dbus_service.h
similarity index 86%
rename from cros/dbus_service.h
rename to dbus_service.h
index 3ad6589..134461b 100644
--- a/cros/dbus_service.h
+++ b/dbus_service.h
@@ -14,22 +14,20 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_DBUS_SERVICE_H_
-#define UPDATE_ENGINE_CROS_DBUS_SERVICE_H_
+#ifndef UPDATE_ENGINE_DBUS_SERVICE_H_
+#define UPDATE_ENGINE_DBUS_SERVICE_H_
 
 #include <inttypes.h>
 
 #include <memory>
 #include <string>
-#include <vector>
 
 #include <base/memory/ref_counted.h>
 #include <brillo/errors/error.h>
-#include <update_engine/proto_bindings/update_engine.pb.h>
 
-#include "update_engine/common/service_observer_interface.h"
-#include "update_engine/cros/common_service.h"
-#include "update_engine/cros/update_attempter.h"
+#include "update_engine/common_service.h"
+#include "update_engine/service_observer_interface.h"
+#include "update_engine/update_attempter.h"
 
 #include "dbus_bindings/org.chromium.UpdateEngineInterface.h"
 
@@ -38,7 +36,7 @@
 class DBusUpdateEngineService
     : public org::chromium::UpdateEngineInterfaceInterface {
  public:
-  DBusUpdateEngineService();
+  explicit DBusUpdateEngineService(SystemState* system_state);
   virtual ~DBusUpdateEngineService() = default;
 
   // Implementation of org::chromium::UpdateEngineInterfaceInterface.
@@ -52,8 +50,7 @@
                               int32_t in_flags_as_int) override;
 
   bool AttemptInstall(brillo::ErrorPtr* error,
-                      const std::string& in_omaha_url,
-                      const std::vector<std::string>& dlc_ids) override;
+                      const std::string& dlc_request) override;
 
   bool AttemptRollback(brillo::ErrorPtr* error, bool in_powerwash) override;
 
@@ -65,17 +62,15 @@
   // update. This is used for development only.
   bool ResetStatus(brillo::ErrorPtr* error) override;
 
-  // Sets the DLC as active or inactive. When set to active, the ping metadata
-  // for the DLC is updated accordingly. When set to inactive, the metadata
-  // for the DLC is deleted.
-  bool SetDlcActiveValue(brillo::ErrorPtr* error,
-                         bool is_active,
-                         const std::string& dlc_id) override;
-
-  // Similar to Above, but returns a protobuffer instead. In the future it will
-  // have more features and is easily extendable.
-  bool GetStatusAdvanced(brillo::ErrorPtr* error,
-                         update_engine::StatusResult* out_status) override;
+  // Returns the current status of the Update Engine. If an update is in
+  // progress, the number of operations, size to download and overall progress
+  // is reported.
+  bool GetStatus(brillo::ErrorPtr* error,
+                 int64_t* out_last_checked_time,
+                 double* out_progress,
+                 std::string* out_current_operation,
+                 std::string* out_new_version,
+                 int64_t* out_new_size) override;
 
   // Reboots the device if an update is applied and a reboot is required.
   bool RebootIfNeeded(brillo::ErrorPtr* error) override;
@@ -155,6 +150,9 @@
   bool GetLastAttemptError(brillo::ErrorPtr* error,
                            int32_t* out_last_attempt_error) override;
 
+  // Returns the current end-of-life status of the device in |out_eol_status|.
+  bool GetEolStatus(brillo::ErrorPtr* error, int32_t* out_eol_status) override;
+
  private:
   std::unique_ptr<UpdateEngineService> common_;
 };
@@ -165,7 +163,7 @@
 class UpdateEngineAdaptor : public org::chromium::UpdateEngineInterfaceAdaptor,
                             public ServiceObserverInterface {
  public:
-  UpdateEngineAdaptor();
+  explicit UpdateEngineAdaptor(SystemState* system_state);
   ~UpdateEngineAdaptor() = default;
 
   // Register the DBus object with the update engine service asynchronously.
@@ -191,4 +189,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_DBUS_SERVICE_H_
+#endif  // UPDATE_ENGINE_DBUS_SERVICE_H_
diff --git a/cros/dbus_test_utils.h b/dbus_test_utils.h
similarity index 81%
rename from cros/dbus_test_utils.h
rename to dbus_test_utils.h
index 1116c52..b3748ce 100644
--- a/cros/dbus_test_utils.h
+++ b/dbus_test_utils.h
@@ -14,13 +14,11 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_DBUS_TEST_UTILS_H_
-#define UPDATE_ENGINE_CROS_DBUS_TEST_UTILS_H_
+#ifndef UPDATE_ENGINE_DBUS_TEST_UTILS_H_
+#define UPDATE_ENGINE_DBUS_TEST_UTILS_H_
 
-#include <memory>
 #include <set>
 #include <string>
-#include <utility>
 
 #include <base/bind.h>
 #include <brillo/message_loops/message_loop.h>
@@ -29,13 +27,13 @@
 namespace chromeos_update_engine {
 namespace dbus_test_utils {
 
-#define MOCK_SIGNAL_HANDLER_EXPECT_SIGNAL_HANDLER(                             \
-    mock_signal_handler, mock_proxy, signal)                                   \
-  do {                                                                         \
-    EXPECT_CALL((mock_proxy),                                                  \
-                DoRegister##signal##SignalHandler(::testing::_, ::testing::_)) \
-        .WillOnce(::chromeos_update_engine::dbus_test_utils::GrabCallbacks(    \
-            &(mock_signal_handler)));                                          \
+#define MOCK_SIGNAL_HANDLER_EXPECT_SIGNAL_HANDLER(                           \
+    mock_signal_handler, mock_proxy, signal)                                 \
+  do {                                                                       \
+    EXPECT_CALL((mock_proxy),                                                \
+                Register##signal##SignalHandler(::testing::_, ::testing::_)) \
+        .WillOnce(::chromeos_update_engine::dbus_test_utils::GrabCallbacks(  \
+            &(mock_signal_handler)));                                        \
   } while (false)
 
 template <typename T>
@@ -54,10 +52,10 @@
 
   void GrabCallbacks(
       const base::Callback<T>& signal_callback,
-      dbus::ObjectProxy::OnConnectedCallback* on_connected_callback) {
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) {
     signal_callback_.reset(new base::Callback<T>(signal_callback));
-    on_connected_callback_.reset(new dbus::ObjectProxy::OnConnectedCallback(
-        std::move(*on_connected_callback)));
+    on_connected_callback_.reset(
+        new dbus::ObjectProxy::OnConnectedCallback(on_connected_callback));
     // Notify from the main loop that the callback was connected.
     callback_connected_task_ = brillo::MessageLoop::current()->PostTask(
         FROM_HERE,
@@ -68,7 +66,7 @@
  private:
   void OnCallbackConnected() {
     callback_connected_task_ = brillo::MessageLoop::kTaskIdNull;
-    std::move(*on_connected_callback_).Run("", "", true);
+    on_connected_callback_->Run("", "", true);
   }
 
   brillo::MessageLoop::TaskId callback_connected_task_{
@@ -88,4 +86,4 @@
 }  // namespace dbus_test_utils
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_DBUS_TEST_UTILS_H_
+#endif  // UPDATE_ENGINE_DBUS_TEST_UTILS_H_
diff --git a/dlcservice_chromeos.cc b/dlcservice_chromeos.cc
new file mode 100644
index 0000000..e95f08f
--- /dev/null
+++ b/dlcservice_chromeos.cc
@@ -0,0 +1,54 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/dlcservice_chromeos.h"
+
+#include <dlcservice/dbus-proxies.h>
+#include <dlcservice/proto_bindings/dlcservice.pb.h>
+
+#include "update_engine/dbus_connection.h"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+std::unique_ptr<DlcServiceInterface> CreateDlcService() {
+  return std::make_unique<DlcServiceChromeOS>();
+}
+
+bool DlcServiceChromeOS::GetInstalled(vector<string>* dlc_module_ids) {
+  if (!dlc_module_ids)
+    return false;
+  org::chromium::DlcServiceInterfaceProxy dlcservice_proxy(
+      DBusConnection::Get()->GetDBus());
+  string dlc_module_list_str;
+  if (!dlcservice_proxy.GetInstalled(&dlc_module_list_str, nullptr)) {
+    LOG(ERROR) << "dlcservice does not return installed DLC module list.";
+    return false;
+  }
+  dlcservice::DlcModuleList dlc_module_list;
+  if (!dlc_module_list.ParseFromString(dlc_module_list_str)) {
+    LOG(ERROR) << "Errors parsing DlcModuleList protobuf.";
+    return false;
+  }
+  for (const auto& dlc_module_info : dlc_module_list.dlc_module_infos()) {
+    dlc_module_ids->emplace_back(dlc_module_info.dlc_id());
+  }
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/dlcservice_chromeos.h b/dlcservice_chromeos.h
new file mode 100644
index 0000000..8d103c1
--- /dev/null
+++ b/dlcservice_chromeos.h
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_DLCSERVICE_CHROMEOS_H_
+#define UPDATE_ENGINE_DLCSERVICE_CHROMEOS_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "update_engine/common/dlcservice_interface.h"
+
+namespace chromeos_update_engine {
+
+// The Chrome OS implementation of the DlcServiceInterface. This interface
+// interacts with dlcservice via D-Bus.
+class DlcServiceChromeOS : public DlcServiceInterface {
+ public:
+  DlcServiceChromeOS() = default;
+  ~DlcServiceChromeOS() = default;
+
+  // BootControlInterface overrides.
+  bool GetInstalled(std::vector<std::string>* dlc_module_ids) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DlcServiceChromeOS);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_DLCSERVICE_CHROMEOS_H_
diff --git a/download_action.cc b/download_action.cc
deleted file mode 100644
index 62a8423..0000000
--- a/download_action.cc
+++ /dev/null
@@ -1,281 +0,0 @@
-//
-// Copyright (C) 2011 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/download_action.h"
-
-#include <errno.h>
-
-#include <algorithm>
-#include <string>
-
-#include <base/files/file_path.h>
-#include <base/metrics/statistics_recorder.h>
-#include <base/strings/stringprintf.h>
-
-#include "update_engine/common/action_pipe.h"
-#include "update_engine/common/boot_control_interface.h"
-#include "update_engine/common/error_code_utils.h"
-#include "update_engine/common/multi_range_http_fetcher.h"
-#include "update_engine/common/prefs_interface.h"
-#include "update_engine/common/utils.h"
-
-using base::FilePath;
-using std::string;
-
-namespace chromeos_update_engine {
-
-DownloadAction::DownloadAction(PrefsInterface* prefs,
-                               BootControlInterface* boot_control,
-                               HardwareInterface* hardware,
-                               HttpFetcher* http_fetcher,
-                               bool interactive)
-    : prefs_(prefs),
-      boot_control_(boot_control),
-      hardware_(hardware),
-      http_fetcher_(new MultiRangeHttpFetcher(http_fetcher)),
-      interactive_(interactive),
-      code_(ErrorCode::kSuccess),
-      delegate_(nullptr) {}
-
-DownloadAction::~DownloadAction() {}
-
-void DownloadAction::PerformAction() {
-  http_fetcher_->set_delegate(this);
-
-  // Get the InstallPlan and read it
-  CHECK(HasInputObject());
-  install_plan_ = GetInputObject();
-  install_plan_.Dump();
-
-  bytes_received_ = 0;
-  bytes_received_previous_payloads_ = 0;
-  bytes_total_ = 0;
-  for (const auto& payload : install_plan_.payloads)
-    bytes_total_ += payload.size;
-
-  if (install_plan_.is_resume) {
-    int64_t payload_index = 0;
-    if (prefs_->GetInt64(kPrefsUpdateStatePayloadIndex, &payload_index) &&
-        static_cast<size_t>(payload_index) < install_plan_.payloads.size()) {
-      // Save the index for the resume payload before downloading any previous
-      // payload, otherwise it will be overwritten.
-      resume_payload_index_ = payload_index;
-      for (int i = 0; i < payload_index; i++)
-        install_plan_.payloads[i].already_applied = true;
-    }
-  }
-  CHECK_GE(install_plan_.payloads.size(), 1UL);
-  if (!payload_)
-    payload_ = &install_plan_.payloads[0];
-
-  LOG(INFO) << "Marking new slot as unbootable";
-  if (!boot_control_->MarkSlotUnbootable(install_plan_.target_slot)) {
-    LOG(WARNING) << "Unable to mark new slot "
-                 << BootControlInterface::SlotName(install_plan_.target_slot)
-                 << ". Proceeding with the update anyway.";
-  }
-
-  StartDownloading();
-}
-
-bool DownloadAction::LoadCachedManifest(int64_t manifest_size) {
-  std::string cached_manifest_bytes;
-  if (!prefs_->GetString(kPrefsManifestBytes, &cached_manifest_bytes) ||
-      cached_manifest_bytes.size() <= 0) {
-    LOG(INFO) << "Cached Manifest data not found";
-    return false;
-  }
-  if (static_cast<int64_t>(cached_manifest_bytes.size()) != manifest_size) {
-    LOG(WARNING) << "Cached metadata has unexpected size: "
-                 << cached_manifest_bytes.size() << " vs. " << manifest_size;
-    return false;
-  }
-
-  ErrorCode error;
-  const bool success =
-      delta_performer_->Write(
-          cached_manifest_bytes.data(), cached_manifest_bytes.size(), &error) &&
-      delta_performer_->IsManifestValid();
-  if (success) {
-    LOG(INFO) << "Successfully parsed cached manifest";
-  } else {
-    // If parsing of cached data failed, fall back to fetch them using HTTP
-    LOG(WARNING) << "Cached manifest data fails to load, error code:"
-                 << static_cast<int>(error) << "," << error;
-  }
-  return success;
-}
-
-void DownloadAction::StartDownloading() {
-  download_active_ = true;
-  http_fetcher_->ClearRanges();
-
-  if (delta_performer_ != nullptr) {
-    LOG(INFO) << "Using writer for test.";
-  } else {
-    delta_performer_.reset(new DeltaPerformer(prefs_,
-                                              boot_control_,
-                                              hardware_,
-                                              delegate_,
-                                              &install_plan_,
-                                              payload_,
-                                              interactive_));
-  }
-
-  if (install_plan_.is_resume &&
-      payload_ == &install_plan_.payloads[resume_payload_index_]) {
-    // Resuming an update so parse the cached manifest first
-    int64_t manifest_metadata_size = 0;
-    int64_t manifest_signature_size = 0;
-    prefs_->GetInt64(kPrefsManifestMetadataSize, &manifest_metadata_size);
-    prefs_->GetInt64(kPrefsManifestSignatureSize, &manifest_signature_size);
-
-    // TODO(zhangkelvin) Add unittest for success and fallback route
-    if (!LoadCachedManifest(manifest_metadata_size + manifest_signature_size)) {
-      if (delta_performer_) {
-        // Create a new DeltaPerformer to reset all its state
-        delta_performer_ = std::make_unique<DeltaPerformer>(prefs_,
-                                                            boot_control_,
-                                                            hardware_,
-                                                            delegate_,
-                                                            &install_plan_,
-                                                            payload_,
-                                                            interactive_);
-      }
-      http_fetcher_->AddRange(base_offset_,
-                              manifest_metadata_size + manifest_signature_size);
-    }
-
-    // If there're remaining unprocessed data blobs, fetch them. Be careful
-    // not to request data beyond the end of the payload to avoid 416 HTTP
-    // response error codes.
-    int64_t next_data_offset = 0;
-    prefs_->GetInt64(kPrefsUpdateStateNextDataOffset, &next_data_offset);
-    uint64_t resume_offset =
-        manifest_metadata_size + manifest_signature_size + next_data_offset;
-    if (!payload_->size) {
-      http_fetcher_->AddRange(base_offset_ + resume_offset);
-    } else if (resume_offset < payload_->size) {
-      http_fetcher_->AddRange(base_offset_ + resume_offset,
-                              payload_->size - resume_offset);
-    }
-  } else {
-    if (payload_->size) {
-      http_fetcher_->AddRange(base_offset_, payload_->size);
-    } else {
-      // If no payload size is passed we assume we read until the end of the
-      // stream.
-      http_fetcher_->AddRange(base_offset_);
-    }
-  }
-
-  http_fetcher_->BeginTransfer(install_plan_.download_url);
-}
-
-void DownloadAction::SuspendAction() {
-  http_fetcher_->Pause();
-}
-
-void DownloadAction::ResumeAction() {
-  http_fetcher_->Unpause();
-}
-
-void DownloadAction::TerminateProcessing() {
-  if (delta_performer_) {
-    delta_performer_->Close();
-    delta_performer_.reset();
-  }
-  download_active_ = false;
-  // Terminates the transfer. The action is terminated, if necessary, when the
-  // TransferTerminated callback is received.
-  http_fetcher_->TerminateTransfer();
-}
-
-void DownloadAction::SeekToOffset(off_t offset) {
-  bytes_received_ = offset;
-}
-
-bool DownloadAction::ReceivedBytes(HttpFetcher* fetcher,
-                                   const void* bytes,
-                                   size_t length) {
-  bytes_received_ += length;
-  uint64_t bytes_downloaded_total =
-      bytes_received_previous_payloads_ + bytes_received_;
-  if (delegate_ && download_active_) {
-    delegate_->BytesReceived(length, bytes_downloaded_total, bytes_total_);
-  }
-  if (delta_performer_ && !delta_performer_->Write(bytes, length, &code_)) {
-    if (code_ != ErrorCode::kSuccess) {
-      LOG(ERROR) << "Error " << utils::ErrorCodeToString(code_) << " (" << code_
-                 << ") in DeltaPerformer's Write method when "
-                 << "processing the received payload -- Terminating processing";
-    }
-    // Don't tell the action processor that the action is complete until we get
-    // the TransferTerminated callback. Otherwise, this and the HTTP fetcher
-    // objects may get destroyed before all callbacks are complete.
-    TerminateProcessing();
-    return false;
-  }
-
-  return true;
-}
-
-void DownloadAction::TransferComplete(HttpFetcher* fetcher, bool successful) {
-  if (delta_performer_) {
-    LOG_IF(WARNING, delta_performer_->Close() != 0)
-        << "Error closing the writer.";
-  }
-  download_active_ = false;
-  ErrorCode code =
-      successful ? ErrorCode::kSuccess : ErrorCode::kDownloadTransferError;
-  if (code == ErrorCode::kSuccess) {
-    if (delta_performer_ && !payload_->already_applied)
-      code = delta_performer_->VerifyPayload(payload_->hash, payload_->size);
-    if (code == ErrorCode::kSuccess) {
-      CHECK_EQ(install_plan_.payloads.size(), 1UL);
-      // All payloads have been applied and verified.
-      if (delegate_)
-        delegate_->DownloadComplete();
-
-      // Log UpdateEngine.DownloadAction.* histograms to help diagnose
-      // long-blocking operations.
-      std::string histogram_output;
-      base::StatisticsRecorder::WriteGraph("UpdateEngine.DownloadAction.",
-                                           &histogram_output);
-      LOG(INFO) << histogram_output;
-    } else {
-      LOG(ERROR) << "Download of " << install_plan_.download_url
-                 << " failed due to payload verification error.";
-    }
-  }
-
-  // Write the path to the output pipe if we're successful.
-  if (code == ErrorCode::kSuccess && HasOutputPipe())
-    SetOutputObject(install_plan_);
-  processor_->ActionComplete(this, code);
-}
-
-void DownloadAction::TransferTerminated(HttpFetcher* fetcher) {
-  if (code_ != ErrorCode::kSuccess) {
-    processor_->ActionComplete(this, code_);
-  } else if (payload_->already_applied) {
-    LOG(INFO) << "TransferTerminated with ErrorCode::kSuccess when the current "
-                 "payload has already applied, treating as TransferComplete.";
-    TransferComplete(fetcher, true);
-  }
-}
-
-}  // namespace chromeos_update_engine
diff --git a/download_action_android_unittest.cc b/download_action_android_unittest.cc
deleted file mode 100644
index fef2d24..0000000
--- a/download_action_android_unittest.cc
+++ /dev/null
@@ -1,180 +0,0 @@
-//
-// 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 <unistd.h>
-#include <cstdint>
-#include <memory>
-
-#include <gmock/gmock.h>
-#include <gmock/gmock-actions.h>
-#include <gmock/gmock-function-mocker.h>
-#include <gmock/gmock-spec-builders.h>
-#include <gtest/gtest.h>
-
-#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/fake_hardware.h"
-#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/test_utils.h"
-#include "update_engine/common/utils.h"
-#include "update_engine/payload_consumer/install_plan.h"
-#include "update_engine/payload_consumer/payload_constants.h"
-#include "update_engine/payload_generator/annotated_operation.h"
-#include "update_engine/payload_generator/payload_file.h"
-#include "update_engine/payload_generator/payload_signer.h"
-
-namespace chromeos_update_engine {
-using testing::_;
-using testing::DoAll;
-using testing::Return;
-using testing::SetArgPointee;
-
-extern const char* kUnittestPrivateKeyPath;
-extern const char* kUnittestPublicKeyPath;
-
-class DownloadActionTest : public ::testing::Test {
- public:
-  static constexpr int64_t METADATA_SIZE = 1024;
-  static constexpr int64_t SIGNATURE_SIZE = 256;
-  std::shared_ptr<ActionPipe<InstallPlan>> action_pipe{
-      new ActionPipe<InstallPlan>()};
-};
-
-TEST_F(DownloadActionTest, CacheManifestInvalid) {
-  std::string data(METADATA_SIZE + SIGNATURE_SIZE, '-');
-  MockPrefs prefs;
-  EXPECT_CALL(prefs, GetInt64(kPrefsUpdateStatePayloadIndex, _))
-      .WillRepeatedly(DoAll(SetArgPointee<1>(0L), Return(true)));
-  EXPECT_CALL(prefs, GetInt64(kPrefsManifestMetadataSize, _))
-      .WillRepeatedly(DoAll(SetArgPointee<1>(METADATA_SIZE), Return(true)));
-  EXPECT_CALL(prefs, GetInt64(kPrefsManifestSignatureSize, _))
-      .WillRepeatedly(DoAll(SetArgPointee<1>(SIGNATURE_SIZE), Return(true)));
-  EXPECT_CALL(prefs, GetInt64(kPrefsUpdateStateNextDataOffset, _))
-      .WillRepeatedly(DoAll(SetArgPointee<1>(0L), Return(true)));
-  EXPECT_CALL(prefs, GetString(kPrefsManifestBytes, _))
-      .WillRepeatedly(DoAll(SetArgPointee<1>(data), Return(true)));
-
-  BootControlStub boot_control;
-  MockHttpFetcher* http_fetcher =
-      new MockHttpFetcher(data.data(), data.size(), nullptr);
-  http_fetcher->set_delay(false);
-  InstallPlan install_plan;
-  auto& payload = install_plan.payloads.emplace_back();
-  install_plan.download_url = "http://fake_url.invalid";
-  payload.size = data.size();
-  payload.payload_urls.emplace_back("http://fake_url.invalid");
-  install_plan.is_resume = true;
-  action_pipe->set_contents(install_plan);
-
-  // takes ownership of passed in HttpFetcher
-  auto download_action = std::make_unique<DownloadAction>(
-      &prefs, &boot_control, nullptr, http_fetcher, false /* interactive */);
-  download_action->set_in_pipe(action_pipe);
-  MockActionProcessor mock_processor;
-  download_action->SetProcessor(&mock_processor);
-  download_action->PerformAction();
-  ASSERT_EQ(download_action->http_fetcher()->GetBytesDownloaded(), data.size());
-}
-
-TEST_F(DownloadActionTest, CacheManifestValid) {
-  // Create a valid manifest
-  PayloadGenerationConfig config;
-  config.version.major = kMaxSupportedMajorPayloadVersion;
-  config.version.minor = kMaxSupportedMinorPayloadVersion;
-
-  PayloadFile payload_file;
-  ASSERT_TRUE(payload_file.Init(config));
-  PartitionConfig partition_config{"system"};
-  ScopedTempFile partition_file("part-system-XXXXXX", true);
-  ftruncate(partition_file.fd(), 4096);
-  partition_config.size = 4096;
-  partition_config.path = partition_file.path();
-  ASSERT_TRUE(
-      payload_file.AddPartition(partition_config, partition_config, {}, {}, 0));
-  ScopedTempFile blob_file("Blob-XXXXXX");
-  ScopedTempFile manifest_file("Manifest-XXXXXX");
-  uint64_t metadata_size;
-  std::string private_key =
-      test_utils::GetBuildArtifactsPath(kUnittestPrivateKeyPath);
-  payload_file.WritePayload(
-      manifest_file.path(), blob_file.path(), private_key, &metadata_size);
-  uint64_t signature_blob_length = 0;
-  ASSERT_TRUE(PayloadSigner::SignatureBlobLength({private_key},
-                                                 &signature_blob_length));
-  std::string data;
-  ASSERT_TRUE(utils::ReadFile(manifest_file.path(), &data));
-  data.resize(metadata_size + signature_blob_length);
-
-  // Setup the prefs so that manifest is cached
-  MockPrefs prefs;
-  EXPECT_CALL(prefs, GetInt64(kPrefsUpdateStatePayloadIndex, _))
-      .WillRepeatedly(DoAll(SetArgPointee<1>(0L), Return(true)));
-  EXPECT_CALL(prefs, GetInt64(kPrefsManifestMetadataSize, _))
-      .WillRepeatedly(DoAll(SetArgPointee<1>(metadata_size), Return(true)));
-  EXPECT_CALL(prefs, GetInt64(kPrefsManifestSignatureSize, _))
-      .WillRepeatedly(
-          DoAll(SetArgPointee<1>(signature_blob_length), Return(true)));
-  EXPECT_CALL(prefs, GetInt64(kPrefsUpdateStateNextDataOffset, _))
-      .WillRepeatedly(DoAll(SetArgPointee<1>(0L), Return(true)));
-  EXPECT_CALL(prefs, GetString(kPrefsManifestBytes, _))
-      .WillRepeatedly(DoAll(SetArgPointee<1>(data), Return(true)));
-  EXPECT_CALL(prefs, GetInt64(kPrefsUpdateStateNextOperation, _))
-      .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(true)));
-  EXPECT_CALL(prefs, GetInt64(kPrefsUpdateStatePayloadIndex, _))
-      .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(true)));
-
-  BootControlStub boot_control;
-  MockHttpFetcher* http_fetcher =
-      new MockHttpFetcher(data.data(), data.size(), nullptr);
-  http_fetcher->set_delay(false);
-  InstallPlan install_plan;
-  auto& payload = install_plan.payloads.emplace_back();
-  install_plan.download_url = "http://fake_url.invalid";
-  payload.size = data.size();
-  payload.payload_urls.emplace_back("http://fake_url.invalid");
-  install_plan.is_resume = true;
-  auto& install_part = install_plan.partitions.emplace_back();
-  install_part.source_path = partition_file.path();
-  install_part.target_path = partition_file.path();
-  action_pipe->set_contents(install_plan);
-
-  FakeHardware hardware;
-  // takes ownership of passed in HttpFetcher
-  auto download_action = std::make_unique<DownloadAction>(
-      &prefs, &boot_control, &hardware, http_fetcher, false /* interactive */);
-
-  auto delta_performer = std::make_unique<DeltaPerformer>(&prefs,
-                                                          &boot_control,
-                                                          &hardware,
-                                                          nullptr,
-                                                          &install_plan,
-                                                          &payload,
-                                                          false);
-  delta_performer->set_public_key_path(kUnittestPublicKeyPath);
-  download_action->SetTestFileWriter(std::move(delta_performer));
-  download_action->set_in_pipe(action_pipe);
-  MockActionProcessor mock_processor;
-  download_action->SetProcessor(&mock_processor);
-  download_action->PerformAction();
-
-  // Manifest is cached, so no data should be downloaded from http fetcher.
-  ASSERT_EQ(download_action->http_fetcher()->GetBytesDownloaded(), 0UL);
-}
-}  // namespace chromeos_update_engine
diff --git a/aosp/dynamic_partition_control_android.cc b/dynamic_partition_control_android.cc
similarity index 67%
rename from aosp/dynamic_partition_control_android.cc
rename to dynamic_partition_control_android.cc
index 538b57c..ecd6252 100644
--- a/aosp/dynamic_partition_control_android.cc
+++ b/dynamic_partition_control_android.cc
@@ -14,17 +14,13 @@
 // limitations under the License.
 //
 
-#include "update_engine/aosp/dynamic_partition_control_android.h"
+#include "update_engine/dynamic_partition_control_android.h"
 
-#include <algorithm>
 #include <chrono>  // NOLINT(build/c++11) - using libsnapshot / liblp API
-#include <cstdint>
 #include <map>
 #include <memory>
 #include <set>
 #include <string>
-#include <string_view>
-#include <utility>
 #include <vector>
 
 #include <android-base/properties.h>
@@ -32,25 +28,18 @@
 #include <base/files/file_util.h>
 #include <base/logging.h>
 #include <base/strings/string_util.h>
-#include <base/strings/stringprintf.h>
 #include <bootloader_message/bootloader_message.h>
 #include <fs_mgr.h>
 #include <fs_mgr_dm_linear.h>
 #include <fs_mgr_overlayfs.h>
 #include <libavb/libavb.h>
 #include <libdm/dm.h>
-#include <liblp/liblp.h>
-#include <libsnapshot/cow_writer.h>
 #include <libsnapshot/snapshot.h>
-#include <libsnapshot/snapshot_stub.h>
 
-#include "update_engine/aosp/cleanup_previous_update_action.h"
-#include "update_engine/aosp/dynamic_partition_utils.h"
+#include "update_engine/cleanup_previous_update_action.h"
 #include "update_engine/common/boot_control_interface.h"
-#include "update_engine/common/dynamic_partition_control_interface.h"
-#include "update_engine/common/platform_constants.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/payload_consumer/cow_writer_file_descriptor.h"
+#include "update_engine/dynamic_partition_utils.h"
 #include "update_engine/payload_consumer/delta_performer.h"
 
 using android::base::GetBoolProperty;
@@ -69,9 +58,7 @@
 using android::snapshot::OptimizeSourceCopyOperation;
 using android::snapshot::Return;
 using android::snapshot::SnapshotManager;
-using android::snapshot::SnapshotManagerStub;
 using android::snapshot::UpdateState;
-using base::StringPrintf;
 
 namespace chromeos_update_engine {
 
@@ -80,14 +67,6 @@
     "ro.boot.dynamic_partitions_retrofit";
 constexpr char kVirtualAbEnabled[] = "ro.virtual_ab.enabled";
 constexpr char kVirtualAbRetrofit[] = "ro.virtual_ab.retrofit";
-constexpr char kVirtualAbCompressionEnabled[] =
-    "ro.virtual_ab.compression.enabled";
-
-// Currently, android doesn't have a retrofit prop for VAB Compression. However,
-// struct FeatureFlag forces us to determine if a feature is 'retrofit'. So this
-// is here just to simplify code. Replace it with real retrofit prop name once
-// there is one.
-constexpr char kVirtualAbCompressionRetrofit[] = "";
 constexpr char kPostinstallFstabPrefix[] = "ro.postinstall.fstab.prefix";
 // Map timeout for dynamic partitions.
 constexpr std::chrono::milliseconds kMapTimeout{1000};
@@ -95,15 +74,19 @@
 // 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();
 }
 
 static FeatureFlag GetFeatureFlag(const char* enable_prop,
                                   const char* retrofit_prop) {
-  // Default retrofit to false if retrofit_prop is empty.
-  bool retrofit = retrofit_prop && retrofit_prop[0] != '\0' &&
-                  GetBoolProperty(retrofit_prop, false);
+  bool retrofit = GetBoolProperty(retrofit_prop, false);
   bool enabled = GetBoolProperty(enable_prop, false);
   if (retrofit && !enabled) {
     LOG(ERROR) << retrofit_prop << " is true but " << enable_prop
@@ -119,20 +102,14 @@
   return FeatureFlag(FeatureFlag::Value::NONE);
 }
 
-DynamicPartitionControlAndroid::DynamicPartitionControlAndroid(
-    uint32_t source_slot)
+DynamicPartitionControlAndroid::DynamicPartitionControlAndroid()
     : dynamic_partitions_(
           GetFeatureFlag(kUseDynamicPartitions, kRetrfoitDynamicPartitions)),
-      virtual_ab_(GetFeatureFlag(kVirtualAbEnabled, kVirtualAbRetrofit)),
-      virtual_ab_compression_(GetFeatureFlag(kVirtualAbCompressionEnabled,
-                                             kVirtualAbCompressionRetrofit)),
-      source_slot_(source_slot) {
+      virtual_ab_(GetFeatureFlag(kVirtualAbEnabled, kVirtualAbRetrofit)) {
   if (GetVirtualAbFeatureFlag().IsEnabled()) {
     snapshot_ = SnapshotManager::New();
-  } else {
-    snapshot_ = SnapshotManagerStub::New();
+    CHECK(snapshot_ != nullptr) << "Cannot initialize SnapshotManager.";
   }
-  CHECK(snapshot_ != nullptr) << "Cannot initialize SnapshotManager.";
 }
 
 FeatureFlag DynamicPartitionControlAndroid::GetDynamicPartitionsFeatureFlag() {
@@ -143,15 +120,6 @@
   return virtual_ab_;
 }
 
-FeatureFlag
-DynamicPartitionControlAndroid::GetVirtualAbCompressionFeatureFlag() {
-  if constexpr (constants::kIsRecovery) {
-    // Don't attempt VABC in recovery
-    return FeatureFlag(FeatureFlag::Value::NONE);
-  }
-  return virtual_ab_compression_;
-}
-
 bool DynamicPartitionControlAndroid::OptimizeOperation(
     const std::string& partition_name,
     const InstallOperation& operation,
@@ -285,10 +253,9 @@
   return true;
 }
 
-bool DynamicPartitionControlAndroid::UnmapAllPartitions() {
-  snapshot_->UnmapAllSnapshots();
+void DynamicPartitionControlAndroid::UnmapAllPartitions() {
   if (mapped_devices_.empty()) {
-    return false;
+    return;
   }
   // UnmapPartitionOnDeviceMapper removes objects from mapped_devices_, hence
   // a copy is needed for the loop.
@@ -297,7 +264,6 @@
   for (const auto& partition_name : mapped) {
     ignore_result(UnmapPartitionOnDeviceMapper(partition_name));
   }
-  return true;
 }
 
 void DynamicPartitionControlAndroid::Cleanup() {
@@ -321,16 +287,9 @@
 
 std::unique_ptr<MetadataBuilder>
 DynamicPartitionControlAndroid::LoadMetadataBuilder(
-    const std::string& super_device, uint32_t slot) {
-  auto builder = MetadataBuilder::New(PartitionOpener(), super_device, slot);
-  if (builder == nullptr) {
-    LOG(WARNING) << "No metadata slot " << BootControlInterface::SlotName(slot)
-                 << " in " << super_device;
-    return nullptr;
-  }
-  LOG(INFO) << "Loaded metadata from slot "
-            << BootControlInterface::SlotName(slot) << " in " << super_device;
-  return builder;
+    const std::string& super_device, uint32_t source_slot) {
+  return LoadMetadataBuilder(
+      super_device, source_slot, BootControlInterface::kInvalidSlot);
 }
 
 std::unique_ptr<MetadataBuilder>
@@ -338,19 +297,26 @@
     const std::string& super_device,
     uint32_t source_slot,
     uint32_t target_slot) {
-  bool always_keep_source_slot = !target_supports_snapshot_;
-  auto builder = MetadataBuilder::NewForUpdate(PartitionOpener(),
-                                               super_device,
-                                               source_slot,
-                                               target_slot,
-                                               always_keep_source_slot);
+  std::unique_ptr<MetadataBuilder> builder;
+  if (target_slot == BootControlInterface::kInvalidSlot) {
+    builder =
+        MetadataBuilder::New(PartitionOpener(), super_device, source_slot);
+  } else {
+    bool always_keep_source_slot = !target_supports_snapshot_;
+    builder = MetadataBuilder::NewForUpdate(PartitionOpener(),
+                                            super_device,
+                                            source_slot,
+                                            target_slot,
+                                            always_keep_source_slot);
+  }
+
   if (builder == nullptr) {
     LOG(WARNING) << "No metadata slot "
                  << BootControlInterface::SlotName(source_slot) << " in "
                  << super_device;
     return nullptr;
   }
-  LOG(INFO) << "Created metadata for new update from slot "
+  LOG(INFO) << "Loaded metadata from slot "
             << BootControlInterface::SlotName(source_slot) << " in "
             << super_device;
   return builder;
@@ -462,17 +428,17 @@
     return false;
   }
 
-  if (!SetTargetBuildVars(manifest)) {
-    return false;
-  }
-
   // Although the current build supports dynamic partitions, the given payload
   // doesn't use it for target partitions. This could happen when applying a
   // retrofit update. Skip updating the partition metadata for the target slot.
+  is_target_dynamic_ = !manifest.dynamic_partition_metadata().groups().empty();
   if (!is_target_dynamic_) {
     return true;
   }
 
+  target_supports_snapshot_ =
+      manifest.dynamic_partition_metadata().snapshot_enabled();
+
   if (!update)
     return true;
 
@@ -523,60 +489,8 @@
     }
   }
 
-  // TODO(xunchang) support partial update on non VAB enabled devices.
-  TEST_AND_RETURN_FALSE(PrepareDynamicPartitionsForUpdate(
-      source_slot, target_slot, manifest, delete_source));
-
-  if (required_size != nullptr) {
-    *required_size = 0;
-  }
-  return true;
-}
-
-bool DynamicPartitionControlAndroid::SetTargetBuildVars(
-    const DeltaArchiveManifest& manifest) {
-  // Precondition: current build supports dynamic partition.
-  CHECK(GetDynamicPartitionsFeatureFlag().IsEnabled());
-
-  bool is_target_dynamic =
-      !manifest.dynamic_partition_metadata().groups().empty();
-  bool target_supports_snapshot =
-      manifest.dynamic_partition_metadata().snapshot_enabled();
-
-  if (manifest.partial_update()) {
-    // Partial updates requires DAP. On partial updates that does not involve
-    // dynamic partitions, groups() can be empty, so also assume
-    // is_target_dynamic in this case. This assumption should be safe because we
-    // also check target_supports_snapshot below, which presumably also implies
-    // target build supports dynamic partition.
-    if (!is_target_dynamic) {
-      LOG(INFO) << "Assuming target build supports dynamic partitions for "
-                   "partial updates.";
-      is_target_dynamic = true;
-    }
-
-    // Partial updates requires Virtual A/B. Double check that both current
-    // build and target build supports Virtual A/B.
-    if (!GetVirtualAbFeatureFlag().IsEnabled()) {
-      LOG(ERROR) << "Partial update cannot be applied on a device that does "
-                    "not support snapshots.";
-      return false;
-    }
-    if (!target_supports_snapshot) {
-      LOG(ERROR) << "Cannot apply partial update to a build that does not "
-                    "support snapshots.";
-      return false;
-    }
-  }
-
-  // Store the flags.
-  is_target_dynamic_ = is_target_dynamic;
-  // If !is_target_dynamic_, leave target_supports_snapshot_ unset because
-  // snapshots would not work without dynamic partition.
-  if (is_target_dynamic_) {
-    target_supports_snapshot_ = target_supports_snapshot;
-  }
-  return true;
+  return PrepareDynamicPartitionsForUpdate(
+      source_slot, target_slot, manifest, delete_source);
 }
 
 namespace {
@@ -807,7 +721,9 @@
   }
 
   std::string device_dir_str;
-  TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
+  if (!GetDeviceDir(&device_dir_str)) {
+    return false;
+  }
   base::FilePath device_dir(device_dir_str);
   auto source_device =
       device_dir.Append(GetSuperPartitionName(source_slot)).value();
@@ -824,125 +740,21 @@
         DeleteSourcePartitions(builder.get(), source_slot, manifest));
   }
 
-  TEST_AND_RETURN_FALSE(
-      UpdatePartitionMetadata(builder.get(), target_slot, manifest));
+  if (!UpdatePartitionMetadata(builder.get(), target_slot, manifest)) {
+    return false;
+  }
 
   auto target_device =
       device_dir.Append(GetSuperPartitionName(target_slot)).value();
   return StoreMetadata(target_device, builder.get(), target_slot);
 }
 
-DynamicPartitionControlAndroid::SpaceLimit
-DynamicPartitionControlAndroid::GetSpaceLimit(bool use_snapshot) {
-  // On device retrofitting dynamic partitions, allocatable_space = "super",
-  // where "super" is the sum of all block devices for that slot. Since block
-  // devices are dedicated for the corresponding slot, there's no need to halve
-  // the allocatable space.
-  if (GetDynamicPartitionsFeatureFlag().IsRetrofit())
-    return SpaceLimit::ERROR_IF_EXCEEDED_SUPER;
-
-  // On device launching dynamic partitions w/o VAB, regardless of recovery
-  // sideload, super partition must be big enough to hold both A and B slots of
-  // groups. Hence,
-  // allocatable_space = super / 2
-  if (!GetVirtualAbFeatureFlag().IsEnabled())
-    return SpaceLimit::ERROR_IF_EXCEEDED_HALF_OF_SUPER;
-
-  // Source build supports VAB. Super partition must be big enough to hold
-  // one slot of groups (ERROR_IF_EXCEEDED_SUPER). However, there are cases
-  // where additional warning messages needs to be written.
-
-  // If using snapshot updates, implying that target build also uses VAB,
-  // allocatable_space = super
-  if (use_snapshot)
-    return SpaceLimit::ERROR_IF_EXCEEDED_SUPER;
-
-  // Source build supports VAB but not using snapshot updates. There are
-  // several cases, as listed below.
-  // Sideloading: allocatable_space = super.
-  if (IsRecovery())
-    return SpaceLimit::ERROR_IF_EXCEEDED_SUPER;
-
-  // On launch VAB device, this implies secondary payload.
-  // Technically, we don't have to check anything, but sum(groups) < super
-  // still applies.
-  if (!GetVirtualAbFeatureFlag().IsRetrofit())
-    return SpaceLimit::ERROR_IF_EXCEEDED_SUPER;
-
-  // On retrofit VAB device, either of the following:
-  // - downgrading: allocatable_space = super / 2
-  // - secondary payload: don't check anything
-  // These two cases are indistinguishable,
-  // hence emit warning if sum(groups) > super / 2
-  return SpaceLimit::WARN_IF_EXCEEDED_HALF_OF_SUPER;
-}
-
-bool DynamicPartitionControlAndroid::CheckSuperPartitionAllocatableSpace(
-    android::fs_mgr::MetadataBuilder* builder,
-    const DeltaArchiveManifest& manifest,
-    bool use_snapshot) {
-  uint64_t sum_groups = 0;
-  for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
-    sum_groups += group.size();
-  }
-
-  uint64_t full_space = builder->AllocatableSpace();
-  uint64_t half_space = full_space / 2;
-  constexpr const char* fmt =
-      "The maximum size of all groups for the target slot (%" PRIu64
-      ") has exceeded %sallocatable space for dynamic partitions %" PRIu64 ".";
-  switch (GetSpaceLimit(use_snapshot)) {
-    case SpaceLimit::ERROR_IF_EXCEEDED_HALF_OF_SUPER: {
-      if (sum_groups > half_space) {
-        LOG(ERROR) << StringPrintf(fmt, sum_groups, "HALF OF ", half_space);
-        return false;
-      }
-      // If test passes, it implies that the following two conditions also pass.
-      break;
-    }
-    case SpaceLimit::WARN_IF_EXCEEDED_HALF_OF_SUPER: {
-      if (sum_groups > half_space) {
-        LOG(WARNING) << StringPrintf(fmt, sum_groups, "HALF OF ", half_space)
-                     << " This is allowed for downgrade or secondary OTA on "
-                        "retrofit VAB device.";
-      }
-      // still check sum(groups) < super
-      [[fallthrough]];
-    }
-    case SpaceLimit::ERROR_IF_EXCEEDED_SUPER: {
-      if (sum_groups > full_space) {
-        LOG(ERROR) << base::StringPrintf(fmt, sum_groups, "", full_space);
-        return false;
-      }
-      break;
-    }
-  }
-
-  return true;
-}
-
 bool DynamicPartitionControlAndroid::PrepareSnapshotPartitionsForUpdate(
     uint32_t source_slot,
     uint32_t target_slot,
     const DeltaArchiveManifest& manifest,
     uint64_t* required_size) {
   TEST_AND_RETURN_FALSE(ExpectMetadataMounted());
-
-  std::string device_dir_str;
-  TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
-  base::FilePath device_dir(device_dir_str);
-  auto super_device =
-      device_dir.Append(GetSuperPartitionName(source_slot)).value();
-  auto builder = LoadMetadataBuilder(super_device, source_slot);
-  if (builder == nullptr) {
-    LOG(ERROR) << "No metadata at "
-               << BootControlInterface::SlotName(source_slot);
-    return false;
-  }
-
-  TEST_AND_RETURN_FALSE(
-      CheckSuperPartitionAllocatableSpace(builder.get(), manifest, true));
-
   if (!snapshot_->BeginUpdate()) {
     LOG(ERROR) << "Cannot begin new update.";
     return false;
@@ -968,18 +780,6 @@
     MetadataBuilder* builder,
     uint32_t target_slot,
     const DeltaArchiveManifest& manifest) {
-  // Check preconditions.
-  if (GetVirtualAbFeatureFlag().IsEnabled()) {
-    CHECK(!target_supports_snapshot_ || IsRecovery())
-        << "Must use snapshot on VAB device when target build supports VAB and "
-           "not sideloading.";
-    LOG_IF(INFO, !target_supports_snapshot_)
-        << "Not using snapshot on VAB device because target build does not "
-           "support snapshot. Secondary or downgrade OTA?";
-    LOG_IF(INFO, IsRecovery())
-        << "Not using snapshot on VAB device because sideloading.";
-  }
-
   // If applying downgrade from Virtual A/B to non-Virtual A/B, the left-over
   // COW group needs to be deleted to ensure there are enough space to create
   // target partitions.
@@ -988,8 +788,24 @@
   const std::string target_suffix = SlotSuffixForSlotNumber(target_slot);
   DeleteGroupsWithSuffix(builder, target_suffix);
 
-  TEST_AND_RETURN_FALSE(
-      CheckSuperPartitionAllocatableSpace(builder, manifest, false));
+  uint64_t total_size = 0;
+  for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
+    total_size += group.size();
+  }
+
+  std::string expr;
+  uint64_t allocatable_space = builder->AllocatableSpace();
+  if (!GetDynamicPartitionsFeatureFlag().IsRetrofit()) {
+    allocatable_space /= 2;
+    expr = "half of ";
+  }
+  if (total_size > allocatable_space) {
+    LOG(ERROR) << "The maximum size of all groups with suffix " << target_suffix
+               << " (" << total_size << ") has exceeded " << expr
+               << "allocatable space for dynamic partitions "
+               << allocatable_space << ".";
+    return false;
+  }
 
   // name of partition(e.g. "system") -> size in bytes
   std::map<std::string, uint64_t> partition_sizes;
@@ -1033,12 +849,6 @@
                    << " to size " << partition_size << ". Not enough space?";
         return false;
       }
-      if (p->size() < partition_size) {
-        LOG(ERROR) << "Partition " << partition_name_suffix
-                   << " was expected to have size " << partition_size
-                   << ", but instead has size " << p->size();
-        return false;
-      }
       LOG(INFO) << "Added partition " << partition_name_suffix << " to group "
                 << group_name_suffix << " with size " << partition_size;
     }
@@ -1064,99 +874,38 @@
     const std::string& partition_name,
     uint32_t slot,
     uint32_t current_slot,
-    bool not_in_payload,
-    std::string* device,
-    bool* is_dynamic) {
-  auto partition_dev =
-      GetPartitionDevice(partition_name, slot, current_slot, not_in_payload);
-  if (!partition_dev.has_value()) {
-    return false;
-  }
-  if (device) {
-    *device = std::move(partition_dev->rw_device_path);
-  }
-  if (is_dynamic) {
-    *is_dynamic = partition_dev->is_dynamic;
-  }
-  return true;
-}
-
-bool DynamicPartitionControlAndroid::GetPartitionDevice(
-    const std::string& partition_name,
-    uint32_t slot,
-    uint32_t current_slot,
     std::string* device) {
-  return GetPartitionDevice(
-      partition_name, slot, current_slot, false, device, nullptr);
-}
-
-static std::string GetStaticDevicePath(
-    const base::FilePath& device_dir,
-    const std::string& partition_name_suffixed) {
-  base::FilePath path = device_dir.Append(partition_name_suffixed);
-  return path.value();
-}
-
-std::optional<PartitionDevice>
-DynamicPartitionControlAndroid::GetPartitionDevice(
-    const std::string& partition_name,
-    uint32_t slot,
-    uint32_t current_slot,
-    bool not_in_payload) {
-  std::string device_dir_str;
-  if (!GetDeviceDir(&device_dir_str)) {
-    LOG(ERROR) << "Failed to GetDeviceDir()";
-    return {};
-  }
-  const base::FilePath device_dir(device_dir_str);
-  // When VABC is enabled, we can't get device path for dynamic partitions in
-  // target slot.
   const auto& partition_name_suffix =
       partition_name + SlotSuffixForSlotNumber(slot);
-  if (UpdateUsesSnapshotCompression() && slot != current_slot &&
-      IsDynamicPartition(partition_name, slot)) {
-    return {
-        {.readonly_device_path = base::FilePath{std::string{VABC_DEVICE_DIR}}
-                                     .Append(partition_name_suffix)
-                                     .value(),
-         .is_dynamic = true}};
-  }
+  std::string device_dir_str;
+  TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
+  base::FilePath device_dir(device_dir_str);
 
   // When looking up target partition devices, treat them as static if the
   // current payload doesn't encode them as dynamic partitions. This may happen
   // when applying a retrofit update on top of a dynamic-partitions-enabled
   // build.
-  std::string device;
   if (GetDynamicPartitionsFeatureFlag().IsEnabled() &&
       (slot == current_slot || is_target_dynamic_)) {
-    switch (GetDynamicPartitionDevice(device_dir,
-                                      partition_name_suffix,
-                                      slot,
-                                      current_slot,
-                                      not_in_payload,
-                                      &device)) {
+    switch (GetDynamicPartitionDevice(
+        device_dir, partition_name_suffix, slot, current_slot, device)) {
       case DynamicPartitionDeviceStatus::SUCCESS:
-        return {{.rw_device_path = device,
-                 .readonly_device_path = device,
-                 .is_dynamic = true}};
-
+        return true;
       case DynamicPartitionDeviceStatus::TRY_STATIC:
         break;
       case DynamicPartitionDeviceStatus::ERROR:  // fallthrough
       default:
-        return {};
+        return false;
     }
   }
-  // Try static partitions.
-  auto static_path = GetStaticDevicePath(device_dir, partition_name_suffix);
-  if (!DeviceExists(static_path)) {
-    LOG(ERROR) << "Device file " << static_path << " does not exist.";
-    return {};
+  base::FilePath path = device_dir.Append(partition_name_suffix);
+  if (!DeviceExists(path.value())) {
+    LOG(ERROR) << "Device file " << path.value() << " does not exist.";
+    return false;
   }
 
-  return {{.rw_device_path = static_path,
-           .readonly_device_path = static_path,
-           .is_dynamic = false}};
+  *device = path.value();
+  return true;
 }
 
 bool DynamicPartitionControlAndroid::IsSuperBlockDevice(
@@ -1175,7 +924,6 @@
     const std::string& partition_name_suffix,
     uint32_t slot,
     uint32_t current_slot,
-    bool not_in_payload,
     std::string* device) {
   std::string super_device =
       device_dir.Append(GetSuperPartitionName(slot)).value();
@@ -1215,7 +963,7 @@
     }
   }
 
-  bool force_writable = (slot != current_slot) && !not_in_payload;
+  bool force_writable = slot != current_slot;
   if (MapPartitionOnDeviceMapper(
           super_device, partition_name_suffix, slot, force_writable, device)) {
     return DynamicPartitionDeviceStatus::SUCCESS;
@@ -1229,7 +977,7 @@
 }
 
 bool DynamicPartitionControlAndroid::IsRecovery() {
-  return constants::kIsRecovery;
+  return kIsRecovery;
 }
 
 static bool IsIncrementalUpdate(const DeltaArchiveManifest& manifest) {
@@ -1257,7 +1005,7 @@
 
   LOG(INFO) << "Will overwrite existing partitions. Slot "
             << BootControlInterface::SlotName(source_slot)
-            << " may be unbootable until update finishes!";
+            << "may be unbootable until update finishes!";
   const std::string source_suffix = SlotSuffixForSlotNumber(source_slot);
   DeleteGroupsWithSuffix(builder, source_suffix);
 
@@ -1301,76 +1049,6 @@
   return true;
 }
 
-bool DynamicPartitionControlAndroid::ListDynamicPartitionsForSlot(
-    uint32_t slot,
-    uint32_t current_slot,
-    std::vector<std::string>* partitions) {
-  CHECK(slot == source_slot_ || target_slot_ != UINT32_MAX)
-      << " source slot: " << source_slot_ << " target slot: " << target_slot_
-      << " slot: " << slot
-      << " attempting to query dynamic partition metadata for target slot "
-         "before PreparePartitionForUpdate() is called. The "
-         "metadata in target slot isn't valid until "
-         "PreparePartitionForUpdate() is called, contining execution would "
-         "likely cause problems.";
-  bool slot_enables_dynamic_partitions =
-      GetDynamicPartitionsFeatureFlag().IsEnabled();
-  // Check if the target slot has dynamic partitions, this may happen when
-  // applying a retrofit package.
-  if (slot != current_slot) {
-    slot_enables_dynamic_partitions =
-        slot_enables_dynamic_partitions && is_target_dynamic_;
-  }
-
-  if (!slot_enables_dynamic_partitions) {
-    LOG(INFO) << "Dynamic partition is not enabled for slot " << slot;
-    return true;
-  }
-
-  std::string device_dir_str;
-  TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
-  base::FilePath device_dir(device_dir_str);
-  auto super_device = device_dir.Append(GetSuperPartitionName(slot)).value();
-  auto builder = LoadMetadataBuilder(super_device, slot);
-  TEST_AND_RETURN_FALSE(builder != nullptr);
-
-  std::vector<std::string> result;
-  auto suffix = SlotSuffixForSlotNumber(slot);
-  for (const auto& group : builder->ListGroups()) {
-    for (const auto& partition : builder->ListPartitionsInGroup(group)) {
-      std::string_view partition_name = partition->name();
-      if (!android::base::ConsumeSuffix(&partition_name, suffix)) {
-        continue;
-      }
-      result.emplace_back(partition_name);
-    }
-  }
-  *partitions = std::move(result);
-  return true;
-}
-
-bool DynamicPartitionControlAndroid::VerifyExtentsForUntouchedPartitions(
-    uint32_t source_slot,
-    uint32_t target_slot,
-    const std::vector<std::string>& partitions) {
-  std::string device_dir_str;
-  TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
-  base::FilePath device_dir(device_dir_str);
-
-  auto source_super_device =
-      device_dir.Append(GetSuperPartitionName(source_slot)).value();
-  auto source_builder = LoadMetadataBuilder(source_super_device, source_slot);
-  TEST_AND_RETURN_FALSE(source_builder != nullptr);
-
-  auto target_super_device =
-      device_dir.Append(GetSuperPartitionName(target_slot)).value();
-  auto target_builder = LoadMetadataBuilder(target_super_device, target_slot);
-  TEST_AND_RETURN_FALSE(target_builder != nullptr);
-
-  return MetadataBuilder::VerifyExtentsAgainstSourceMetadata(
-      *source_builder, source_slot, *target_builder, target_slot, partitions);
-}
-
 bool DynamicPartitionControlAndroid::ExpectMetadataMounted() {
   // No need to mount metadata for non-Virtual A/B devices.
   if (!GetVirtualAbFeatureFlag().IsEnabled()) {
@@ -1398,80 +1076,4 @@
   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::OpenCowFd(
-    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;
-  }
-  if (!cow_writer->InitializeAppend(kEndOfInstallLabel)) {
-    return nullptr;
-  }
-  return std::make_shared<CowWriterFileDescriptor>(std::move(cow_writer));
-}
-
-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, uint32_t slot) {
-  if (slot >= dynamic_partition_list_.size()) {
-    LOG(ERROR) << "Seeing unexpected slot # " << slot << " currently assuming "
-               << dynamic_partition_list_.size() << " slots";
-    return false;
-  }
-  auto& dynamic_partition_list = dynamic_partition_list_[slot];
-  if (dynamic_partition_list.empty() &&
-      GetDynamicPartitionsFeatureFlag().IsEnabled()) {
-    // Use the DAP config of the target slot.
-    CHECK(ListDynamicPartitionsForSlot(
-        slot, source_slot_, &dynamic_partition_list));
-  }
-  return std::find(dynamic_partition_list.begin(),
-                   dynamic_partition_list.end(),
-                   partition_name) != dynamic_partition_list.end();
-}
-
-bool DynamicPartitionControlAndroid::UpdateUsesSnapshotCompression() {
-  return GetVirtualAbFeatureFlag().IsEnabled() &&
-         snapshot_->UpdateUsesCompression();
-}
-
 }  // namespace chromeos_update_engine
diff --git a/aosp/dynamic_partition_control_android.h b/dynamic_partition_control_android.h
similarity index 68%
rename from aosp/dynamic_partition_control_android.h
rename to dynamic_partition_control_android.h
index df91401..8ad7593 100644
--- a/aosp/dynamic_partition_control_android.h
+++ b/dynamic_partition_control_android.h
@@ -14,19 +14,16 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_CONTROL_ANDROID_H_
-#define UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_CONTROL_ANDROID_H_
+#ifndef UPDATE_ENGINE_DYNAMIC_PARTITION_CONTROL_ANDROID_H_
+#define UPDATE_ENGINE_DYNAMIC_PARTITION_CONTROL_ANDROID_H_
 
 #include <memory>
 #include <set>
 #include <string>
-#include <string_view>
-#include <vector>
 
 #include <base/files/file_util.h>
 #include <libsnapshot/auto_device.h>
 #include <libsnapshot/snapshot.h>
-#include <libsnapshot/snapshot_writer.h>
 
 #include "update_engine/common/dynamic_partition_control_interface.h"
 
@@ -34,16 +31,10 @@
 
 class DynamicPartitionControlAndroid : public DynamicPartitionControlInterface {
  public:
-  // A directory where all partitions mapped by VABC is expected to be found.
-  // Per earlier discussion with VAB team, this directory is unlikely to change.
-  // So we declare it as a constant here.
-  static constexpr std::string_view VABC_DEVICE_DIR = "/dev/block/mapper/";
-  explicit DynamicPartitionControlAndroid(uint32_t source_slot);
+  DynamicPartitionControlAndroid();
   ~DynamicPartitionControlAndroid();
-
   FeatureFlag GetDynamicPartitionsFeatureFlag() override;
   FeatureFlag GetVirtualAbFeatureFlag() override;
-  FeatureFlag GetVirtualAbCompressionFeatureFlag() override;
   bool OptimizeOperation(const std::string& partition_name,
                          const InstallOperation& operation,
                          InstallOperation* optimized) override;
@@ -62,58 +53,16 @@
 
   bool ResetUpdate(PrefsInterface* prefs) override;
 
-  bool ListDynamicPartitionsForSlot(
-      uint32_t slot,
-      uint32_t current_slot,
-      std::vector<std::string>* partitions) override;
-
-  bool VerifyExtentsForUntouchedPartitions(
-      uint32_t source_slot,
-      uint32_t target_slot,
-      const std::vector<std::string>& partitions) override;
-
-  bool GetDeviceDir(std::string* path) override;
-
   // Return the device for partition |partition_name| at slot |slot|.
   // |current_slot| should be set to the current active slot.
   // Note: this function is only used by BootControl*::GetPartitionDevice.
   // Other callers should prefer BootControl*::GetPartitionDevice over
   // BootControl*::GetDynamicPartitionControl()->GetPartitionDevice().
-  std::optional<PartitionDevice> GetPartitionDevice(
-      const std::string& partition_name,
-      uint32_t slot,
-      uint32_t current_slot,
-      bool not_in_payload);
-  // Deprecated, please use GetPartitionDevice(string, uint32_t, uint32_t);
-  // TODO(zhangkelvin) Remove below deprecated APIs.
-  bool GetPartitionDevice(const std::string& partition_name,
-                          uint32_t slot,
-                          uint32_t current_slot,
-                          bool not_in_payload,
-                          std::string* device,
-                          bool* is_dynamic);
-
   bool GetPartitionDevice(const std::string& partition_name,
                           uint32_t slot,
                           uint32_t current_slot,
                           std::string* device);
 
-  // Partition name is expected to be unsuffixed. e.g. system, vendor
-  // Return an interface to write to a snapshoted partition.
-  std::unique_ptr<android::snapshot::ISnapshotWriter> OpenCowWriter(
-      const std::string& unsuffixed_partition_name,
-      const std::optional<std::string>& source_path,
-      bool is_append) override;
-  FileDescriptorPtr OpenCowFd(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, uint32_t slot) override;
-
-  bool UpdateUsesSnapshotCompression() override;
-
  protected:
   // These functions are exposed for testing.
 
@@ -123,14 +72,16 @@
   virtual bool UnmapPartitionOnDeviceMapper(
       const std::string& target_partition_name);
 
-  // Retrieves metadata from |super_device| at slot |slot|.
-  virtual std::unique_ptr<android::fs_mgr::MetadataBuilder> LoadMetadataBuilder(
-      const std::string& super_device, uint32_t slot);
-
-  // Retrieves metadata from |super_device| at slot |source_slot|. And
-  // modifies the metadata so that during updates, the metadata can be written
-  // to |target_slot|. In particular, on retrofit devices, the returned
+  // Retrieve metadata from |super_device| at slot |source_slot|.
+  //
+  // If |target_slot| != kInvalidSlot, before returning the metadata, this
+  // function modifies the metadata so that during updates, the metadata can be
+  // written to |target_slot|. In particular, on retrofit devices, the returned
   // metadata automatically includes block devices at |target_slot|.
+  //
+  // If |target_slot| == kInvalidSlot, this function returns metadata at
+  // |source_slot| without modifying it. This is the same as
+  // LoadMetadataBuilder(const std::string&, uint32_t).
   virtual std::unique_ptr<android::fs_mgr::MetadataBuilder> LoadMetadataBuilder(
       const std::string& super_device,
       uint32_t source_slot,
@@ -169,6 +120,13 @@
   virtual bool GetDmDevicePathByName(const std::string& name,
                                      std::string* path);
 
+  // Retrieve metadata from |super_device| at slot |source_slot|.
+  virtual std::unique_ptr<android::fs_mgr::MetadataBuilder> LoadMetadataBuilder(
+      const std::string& super_device, uint32_t source_slot);
+
+  // Return a possible location for devices listed by name.
+  virtual bool GetDeviceDir(std::string* path);
+
   // Return the name of the super partition (which stores super partition
   // metadata) for a given slot.
   virtual std::string GetSuperPartitionName(uint32_t slot);
@@ -215,68 +173,38 @@
   virtual bool EraseSystemOtherAvbFooter(uint32_t source_slot,
                                          uint32_t target_slot);
 
-  // Helper for PreparePartitionsForUpdate. Used for devices with dynamic
-  // partitions updating without snapshots.
-  // If |delete_source| is set, source partitions are deleted before resizing
-  // target partitions (using DeleteSourcePartitions).
-  virtual bool PrepareDynamicPartitionsForUpdate(
-      uint32_t source_slot,
-      uint32_t target_slot,
-      const DeltaArchiveManifest& manifest,
-      bool delete_source);
-
-  bool MapAllPartitions() override;
-
-  void SetSourceSlot(uint32_t slot) { source_slot_ = slot; }
-  void SetTargetSlot(uint32_t slot) { target_slot_ = slot; }
-
  private:
   friend class DynamicPartitionControlAndroidTest;
-  friend class SnapshotPartitionTestP;
 
-  std::optional<base::FilePath> GetSuperDevice();
-
+  void UnmapAllPartitions();
   bool MapPartitionInternal(const std::string& super_device,
                             const std::string& target_partition_name,
                             uint32_t slot,
                             bool force_writable,
                             std::string* path);
 
-  // Update |builder| according to |partition_metadata|.
-  // - In Android mode, this is only called when the device
-  //   does not have Virtual A/B.
-  // - When sideloading, this maybe called as a fallback path if CoW cannot
-  //   be created.
+  // Update |builder| according to |partition_metadata|, assuming the device
+  // does not have Virtual A/B.
   bool UpdatePartitionMetadata(android::fs_mgr::MetadataBuilder* builder,
                                uint32_t target_slot,
                                const DeltaArchiveManifest& manifest);
 
-  // Helper for PreparePartitionsForUpdate. Used for snapshotted partitions
-  // for Virtual A/B update.
+  // Helper for PreparePartitionsForUpdate. Used for devices with dynamic
+  // partitions updating without snapshots.
+  // If |delete_source| is set, source partitions are deleted before resizing
+  // target partitions (using DeleteSourcePartitions).
+  bool PrepareDynamicPartitionsForUpdate(uint32_t source_slot,
+                                         uint32_t target_slot,
+                                         const DeltaArchiveManifest& manifest,
+                                         bool delete_source);
+
+  // Helper for PreparePartitionsForUpdate. Used for snapshotted partitions for
+  // Virtual A/B update.
   bool PrepareSnapshotPartitionsForUpdate(uint32_t source_slot,
                                           uint32_t target_slot,
                                           const DeltaArchiveManifest& manifest,
                                           uint64_t* required_size);
 
-  enum SpaceLimit {
-    // Most restricted: if sum(groups) > super / 2, error
-    ERROR_IF_EXCEEDED_HALF_OF_SUPER,
-    // Implies ERROR_IF_EXCEEDED_SUPER; then, if sum(groups) > super / 2, warn
-    WARN_IF_EXCEEDED_HALF_OF_SUPER,
-    // Least restricted: if sum(groups) > super, error
-    ERROR_IF_EXCEEDED_SUPER,
-  };
-  // Helper of CheckSuperPartitionAllocatableSpace. Determine limit for groups
-  // and partitions.
-  SpaceLimit GetSpaceLimit(bool use_snapshot);
-
-  // Returns true if the allocatable space in super partition is larger than
-  // the size of dynamic partition groups in the manifest.
-  bool CheckSuperPartitionAllocatableSpace(
-      android::fs_mgr::MetadataBuilder* builder,
-      const DeltaArchiveManifest& manifest,
-      bool use_snapshot);
-
   enum class DynamicPartitionDeviceStatus {
     SUCCESS,
     ERROR,
@@ -292,7 +220,6 @@
       const std::string& partition_name_suffix,
       uint32_t slot,
       uint32_t current_slot,
-      bool not_in_payload,
       std::string* device);
 
   // Return true if |partition_name_suffix| is a block device of
@@ -309,20 +236,17 @@
   // Returns true if metadata is expected to be mounted, false otherwise.
   // Note that it returns false on non-Virtual A/B devices.
   //
-  // Almost all functions of SnapshotManager depends on metadata being
-  // mounted.
+  // Almost all functions of SnapshotManager depends on metadata being mounted.
   // - In Android mode for Virtual A/B devices, assume it is mounted. If not,
   //   let caller fails when calling into SnapshotManager.
-  // - In recovery for Virtual A/B devices, it is possible that metadata is
-  // not
+  // - In recovery for Virtual A/B devices, it is possible that metadata is not
   //   formatted, hence it cannot be mounted. Caller should not call into
   //   SnapshotManager.
-  // - On non-Virtual A/B devices, updates do not depend on metadata
-  // partition.
+  // - On non-Virtual A/B devices, updates do not depend on metadata partition.
   //   Caller should not call into SnapshotManager.
   //
-  // This function does NOT mount metadata partition. Use
-  // EnsureMetadataMounted to mount metadata partition.
+  // This function does NOT mount metadata partition. Use EnsureMetadataMounted
+  // to mount metadata partition.
   bool ExpectMetadataMounted();
 
   // Ensure /metadata is mounted. Returns true if successful, false otherwise.
@@ -331,28 +255,21 @@
   // doing anything.
   bool EnsureMetadataMounted();
 
-  // Set boolean flags related to target build. This includes flags like
-  // target_supports_snapshot_ and is_target_dynamic_.
-  bool SetTargetBuildVars(const DeltaArchiveManifest& manifest);
-
   std::set<std::string> mapped_devices_;
   const FeatureFlag dynamic_partitions_;
   const FeatureFlag virtual_ab_;
-  const FeatureFlag virtual_ab_compression_;
-  std::unique_ptr<android::snapshot::ISnapshotManager> snapshot_;
+  std::unique_ptr<android::snapshot::SnapshotManager> snapshot_;
   std::unique_ptr<android::snapshot::AutoDevice> metadata_device_;
   bool target_supports_snapshot_ = false;
   // Whether the target partitions should be loaded as dynamic partitions. Set
   // by PreparePartitionsForUpdate() per each update.
   bool is_target_dynamic_ = false;
-
   uint32_t source_slot_ = UINT32_MAX;
   uint32_t target_slot_ = UINT32_MAX;
-  std::vector<std::vector<std::string>> dynamic_partition_list_{2UL};
 
   DISALLOW_COPY_AND_ASSIGN(DynamicPartitionControlAndroid);
 };
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_CONTROL_ANDROID_H_
+#endif  // UPDATE_ENGINE_DYNAMIC_PARTITION_CONTROL_ANDROID_H_
diff --git a/aosp/dynamic_partition_control_android_unittest.cc b/dynamic_partition_control_android_unittest.cc
similarity index 75%
rename from aosp/dynamic_partition_control_android_unittest.cc
rename to dynamic_partition_control_android_unittest.cc
index 6f1d4ef..2081918 100644
--- a/aosp/dynamic_partition_control_android_unittest.cc
+++ b/dynamic_partition_control_android_unittest.cc
@@ -14,9 +14,8 @@
 // limitations under the License.
 //
 
-#include "update_engine/aosp/dynamic_partition_control_android.h"
+#include "update_engine/dynamic_partition_control_android.h"
 
-#include <algorithm>
 #include <set>
 #include <vector>
 
@@ -25,21 +24,19 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <libavb/libavb.h>
-#include <libsnapshot/mock_snapshot.h>
 
-#include "update_engine/aosp/dynamic_partition_test_utils.h"
-#include "update_engine/aosp/mock_dynamic_partition_control_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;
 using testing::AnyOf;
-using testing::AtLeast;
 using testing::Invoke;
 using testing::NiceMock;
 using testing::Not;
@@ -57,10 +54,7 @@
         .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
     ON_CALL(dynamicControl(), GetVirtualAbFeatureFlag())
         .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::NONE)));
-    ON_CALL(dynamicControl(), GetVirtualAbCompressionFeatureFlag())
-        .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::NONE)));
-    ON_CALL(dynamicControl(), UpdateUsesSnapshotCompression())
-        .WillByDefault(Return(false));
+
     ON_CALL(dynamicControl(), GetDeviceDir(_))
         .WillByDefault(Invoke([](auto path) {
           *path = kFakeDevicePath;
@@ -78,17 +72,6 @@
 
     ON_CALL(dynamicControl(), EraseSystemOtherAvbFooter(_, _))
         .WillByDefault(Return(true));
-
-    ON_CALL(dynamicControl(), IsRecovery()).WillByDefault(Return(false));
-
-    ON_CALL(dynamicControl(), PrepareDynamicPartitionsForUpdate(_, _, _, _))
-        .WillByDefault(Invoke([&](uint32_t source_slot,
-                                  uint32_t target_slot,
-                                  const DeltaArchiveManifest& manifest,
-                                  bool delete_source) {
-          return dynamicControl().RealPrepareDynamicPartitionsForUpdate(
-              source_slot, target_slot, manifest, delete_source);
-        }));
   }
 
   // Return the mocked DynamicPartitionControlInterface.
@@ -117,24 +100,13 @@
   // |slot|.
   void SetMetadata(uint32_t slot,
                    const PartitionSuffixSizes& sizes,
-                   uint32_t partition_attr = 0,
-                   uint64_t super_size = kDefaultSuperSize) {
-    EXPECT_CALL(dynamicControl(),
-                LoadMetadataBuilder(GetSuperDevice(slot), slot))
-        .Times(AnyNumber())
-        .WillRepeatedly(Invoke([=](auto, auto) {
-          return NewFakeMetadata(PartitionSuffixSizesToManifest(sizes),
-                                 partition_attr,
-                                 super_size);
-        }));
-
+                   uint32_t partition_attr = 0) {
     EXPECT_CALL(dynamicControl(),
                 LoadMetadataBuilder(GetSuperDevice(slot), slot, _))
         .Times(AnyNumber())
-        .WillRepeatedly(Invoke([=](auto, auto, auto) {
+        .WillRepeatedly(Invoke([sizes, partition_attr](auto, auto, auto) {
           return NewFakeMetadata(PartitionSuffixSizesToManifest(sizes),
-                                 partition_attr,
-                                 super_size);
+                                 partition_attr);
         }));
   }
 
@@ -222,8 +194,6 @@
   void SetUp() override {
     DynamicPartitionControlAndroidTest::SetUp();
     SetSlots(GetParam());
-    dynamicControl().SetSourceSlot(source());
-    dynamicControl().SetTargetSlot(target());
   }
 };
 
@@ -393,92 +363,6 @@
   EXPECT_EQ(GetDevice(T("bar")), bar_device);
 }
 
-TEST_P(DynamicPartitionControlAndroidTestP, GetMountableDevicePath) {
-  ON_CALL(dynamicControl(), GetDynamicPartitionsFeatureFlag())
-      .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
-  ON_CALL(dynamicControl(), GetVirtualAbFeatureFlag())
-      .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
-  ON_CALL(dynamicControl(), GetVirtualAbCompressionFeatureFlag())
-      .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::NONE)));
-  ON_CALL(dynamicControl(), UpdateUsesSnapshotCompression())
-      .WillByDefault(Return(false));
-  ON_CALL(dynamicControl(), IsDynamicPartition(_, _))
-      .WillByDefault(Return(true));
-
-  EXPECT_CALL(dynamicControl(),
-              DeviceExists(AnyOf(GetDevice(S("vendor")),
-                                 GetDevice(T("vendor")),
-                                 GetDevice(S("system")),
-                                 GetDevice(T("system")))))
-      .WillRepeatedly(Return(true));
-  EXPECT_CALL(
-      dynamicControl(),
-      GetState(AnyOf(S("vendor"), T("vendor"), S("system"), T("system"))))
-      .WillRepeatedly(Return(DmDeviceState::ACTIVE));
-
-  SetMetadata(source(), {{S("system"), 2_GiB}, {S("vendor"), 1_GiB}});
-  SetMetadata(target(), {{T("system"), 2_GiB}, {T("vendor"), 1_GiB}});
-  std::string device;
-  ASSERT_TRUE(dynamicControl().GetPartitionDevice(
-      "system", source(), source(), &device));
-  ASSERT_EQ(GetDmDevice(S("system")), device);
-
-  ASSERT_TRUE(dynamicControl().GetPartitionDevice(
-      "system", target(), source(), &device));
-  ASSERT_EQ(GetDevice(T("system")), device);
-
-  // If VABC is disabled, mountable device path should be same as device path.
-  auto device_info =
-      dynamicControl().GetPartitionDevice("system", target(), source(), false);
-  ASSERT_TRUE(device_info.has_value());
-  ASSERT_EQ(device_info->readonly_device_path, device);
-}
-
-TEST_P(DynamicPartitionControlAndroidTestP, GetMountableDevicePathVABC) {
-  ON_CALL(dynamicControl(), GetDynamicPartitionsFeatureFlag())
-      .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
-  ON_CALL(dynamicControl(), GetVirtualAbFeatureFlag())
-      .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
-  ON_CALL(dynamicControl(), GetVirtualAbCompressionFeatureFlag())
-      .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
-  ON_CALL(dynamicControl(), UpdateUsesSnapshotCompression())
-      .WillByDefault(Return(true));
-  EXPECT_CALL(dynamicControl(), IsDynamicPartition(_, _))
-      .Times(AtLeast(1))
-      .WillRepeatedly(Return(true));
-
-  EXPECT_CALL(dynamicControl(),
-              DeviceExists(AnyOf(GetDevice(S("vendor")),
-                                 GetDevice(T("vendor")),
-                                 GetDevice(S("system")),
-                                 GetDevice(T("system")))))
-      .WillRepeatedly(Return(true));
-  EXPECT_CALL(
-      dynamicControl(),
-      GetState(AnyOf(S("vendor"), T("vendor"), S("system"), T("system"))))
-      .WillRepeatedly(Return(DmDeviceState::ACTIVE));
-
-  SetMetadata(source(), {{S("system"), 2_GiB}, {S("vendor"), 1_GiB}});
-  SetMetadata(target(), {{T("system"), 2_GiB}, {T("vendor"), 1_GiB}});
-
-  std::string device;
-  ASSERT_TRUE(dynamicControl().GetPartitionDevice(
-      "system", source(), source(), &device));
-  ASSERT_EQ(GetDmDevice(S("system")), device);
-
-  ASSERT_TRUE(dynamicControl().GetPartitionDevice(
-      "system", target(), source(), &device));
-  ASSERT_EQ("", device);
-
-  auto device_info =
-      dynamicControl().GetPartitionDevice("system", target(), source(), false);
-  ASSERT_TRUE(device_info.has_value());
-  base::FilePath vabc_device_dir{
-      std::string{DynamicPartitionControlAndroid::VABC_DEVICE_DIR}};
-  ASSERT_EQ(device_info->readonly_device_path,
-            vabc_device_dir.Append(T("system")).value());
-}
-
 TEST_P(DynamicPartitionControlAndroidTestP,
        GetPartitionDeviceWhenResumingUpdate) {
   // Static partition bar_{a,b} exists.
@@ -885,11 +769,11 @@
 }
 
 TEST_F(DynamicPartitionControlAndroidTest, IsAvbNotEnabledInFstab) {
+  // clang-format off
   std::string fstab_content =
-      "system /postinstall ext4 ro,nosuid,nodev,noexec "
-      "slotselect_other,logical\n"
-      "/dev/block/by-name/system /postinstall ext4 "
-      "ro,nosuid,nodev,noexec slotselect_other\n";
+      "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
   ScopedTempFile fstab;
   ASSERT_TRUE(test_utils::WriteFileString(fstab.path(), fstab_content));
   ASSERT_THAT(dynamicControl().RealIsAvbEnabledInFstab(fstab.path()),
@@ -897,9 +781,10 @@
 }
 
 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";
+      "system /postinstall ext4 ro,nosuid,nodev,noexec slotselect_other,logical,avb_keys=/foo\n";  // NOLINT(whitespace/line_length)
+  // clang-format on
   ScopedTempFile fstab;
   ASSERT_TRUE(test_utils::WriteFileString(fstab.path(), fstab_content));
   ASSERT_THAT(dynamicControl().RealIsAvbEnabledInFstab(fstab.path()),
@@ -1007,123 +892,4 @@
   ASSERT_EQ(new_expected, device_content);
 }
 
-class FakeAutoDevice : public android::snapshot::AutoDevice {
- public:
-  FakeAutoDevice() : AutoDevice("") {}
-};
-
-class SnapshotPartitionTestP : public DynamicPartitionControlAndroidTestP {
- public:
-  void SetUp() override {
-    DynamicPartitionControlAndroidTestP::SetUp();
-    ON_CALL(dynamicControl(), GetVirtualAbFeatureFlag())
-        .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
-
-    snapshot_ = new NiceMock<MockSnapshotManager>();
-    dynamicControl().snapshot_.reset(snapshot_);  // takes ownership
-    EXPECT_CALL(*snapshot_, BeginUpdate()).WillOnce(Return(true));
-    EXPECT_CALL(*snapshot_, EnsureMetadataMounted())
-        .WillRepeatedly(
-            Invoke([]() { return std::make_unique<FakeAutoDevice>(); }));
-
-    manifest_ =
-        PartitionSizesToManifest({{"system", 3_GiB}, {"vendor", 1_GiB}});
-  }
-  void ExpectCreateUpdateSnapshots(android::snapshot::Return val) {
-    manifest_.mutable_dynamic_partition_metadata()->set_snapshot_enabled(true);
-    EXPECT_CALL(*snapshot_, CreateUpdateSnapshots(_))
-        .WillRepeatedly(Invoke([&, val](const auto& manifest) {
-          // Deep comparison requires full protobuf library. Comparing the
-          // pointers are sufficient.
-          EXPECT_EQ(&manifest_, &manifest);
-          LOG(WARNING) << "CreateUpdateSnapshots returning " << val.string();
-          return val;
-        }));
-  }
-  bool PreparePartitionsForUpdate(uint64_t* required_size) {
-    return dynamicControl().PreparePartitionsForUpdate(
-        source(), target(), manifest_, true /* update */, required_size);
-  }
-  MockSnapshotManager* snapshot_ = nullptr;
-  DeltaArchiveManifest manifest_;
-};
-
-// Test happy path of PreparePartitionsForUpdate on a Virtual A/B device.
-TEST_P(SnapshotPartitionTestP, PreparePartitions) {
-  ExpectCreateUpdateSnapshots(android::snapshot::Return::Ok());
-  SetMetadata(source(), {});
-  uint64_t required_size = 0;
-  EXPECT_TRUE(PreparePartitionsForUpdate(&required_size));
-  EXPECT_EQ(0u, required_size);
-}
-
-// Test that if not enough space, required size returned by SnapshotManager is
-// passed up.
-TEST_P(SnapshotPartitionTestP, PreparePartitionsNoSpace) {
-  ExpectCreateUpdateSnapshots(android::snapshot::Return::NoSpace(1_GiB));
-  uint64_t required_size = 0;
-
-  SetMetadata(source(), {});
-  EXPECT_FALSE(PreparePartitionsForUpdate(&required_size));
-  EXPECT_EQ(1_GiB, required_size);
-}
-
-// Test that in recovery, use empty space in super partition for a snapshot
-// update first.
-TEST_P(SnapshotPartitionTestP, RecoveryUseSuperEmpty) {
-  ExpectCreateUpdateSnapshots(android::snapshot::Return::Ok());
-  EXPECT_CALL(dynamicControl(), IsRecovery()).WillRepeatedly(Return(true));
-
-  // Metadata is needed to perform super partition size check.
-  SetMetadata(source(), {});
-
-  // Must not call PrepareDynamicPartitionsForUpdate if
-  // PrepareSnapshotPartitionsForUpdate succeeds.
-  EXPECT_CALL(dynamicControl(), PrepareDynamicPartitionsForUpdate(_, _, _, _))
-      .Times(0);
-  uint64_t required_size = 0;
-  EXPECT_TRUE(PreparePartitionsForUpdate(&required_size));
-  EXPECT_EQ(0u, required_size);
-}
-
-// Test that in recovery, if CreateUpdateSnapshots throws an error, try
-// the flashing path for full updates.
-TEST_P(SnapshotPartitionTestP, RecoveryErrorShouldDeleteSource) {
-  // Expectation on PreparePartitionsForUpdate
-  ExpectCreateUpdateSnapshots(android::snapshot::Return::NoSpace(1_GiB));
-  EXPECT_CALL(dynamicControl(), IsRecovery()).WillRepeatedly(Return(true));
-  EXPECT_CALL(*snapshot_, CancelUpdate()).WillOnce(Return(true));
-  EXPECT_CALL(dynamicControl(), PrepareDynamicPartitionsForUpdate(_, _, _, _))
-      .WillRepeatedly(Invoke([&](auto source_slot,
-                                 auto target_slot,
-                                 const auto& manifest,
-                                 auto delete_source) {
-        EXPECT_EQ(source(), source_slot);
-        EXPECT_EQ(target(), target_slot);
-        // Deep comparison requires full protobuf library. Comparing the
-        // pointers are sufficient.
-        EXPECT_EQ(&manifest_, &manifest);
-        EXPECT_TRUE(delete_source);
-        return dynamicControl().RealPrepareDynamicPartitionsForUpdate(
-            source_slot, target_slot, manifest, delete_source);
-      }));
-  // Only one slot of space in super
-  uint64_t super_size = kDefaultGroupSize + 1_MiB;
-  // Expectation on PrepareDynamicPartitionsForUpdate
-  SetMetadata(
-      source(), {{S("system"), 2_GiB}, {S("vendor"), 1_GiB}}, 0, super_size);
-  ExpectUnmap({T("system"), T("vendor")});
-  // Expect that the source partitions aren't present in target super
-  // metadata.
-  ExpectStoreMetadata({{T("system"), 3_GiB}, {T("vendor"), 1_GiB}});
-
-  uint64_t required_size = 0;
-  EXPECT_TRUE(PreparePartitionsForUpdate(&required_size));
-  EXPECT_EQ(0u, required_size);
-}
-
-INSTANTIATE_TEST_CASE_P(DynamicPartitionControlAndroidTest,
-                        SnapshotPartitionTestP,
-                        testing::Values(TestParam{0, 1}, TestParam{1, 0}));
-
 }  // namespace chromeos_update_engine
diff --git a/aosp/dynamic_partition_test_utils.h b/dynamic_partition_test_utils.h
similarity index 94%
rename from aosp/dynamic_partition_test_utils.h
rename to dynamic_partition_test_utils.h
index c518382..70a176b 100644
--- a/aosp/dynamic_partition_test_utils.h
+++ b/dynamic_partition_test_utils.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_TEST_UTILS_H_
-#define UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_TEST_UTILS_H_
+#ifndef UPDATE_ENGINE_DYNAMIC_PARTITION_TEST_UTILS_H_
+#define UPDATE_ENGINE_DYNAMIC_PARTITION_TEST_UTILS_H_
 
 #include <stdint.h>
 
@@ -47,7 +47,7 @@
 
 constexpr const uint32_t kMaxNumSlots = 2;
 constexpr const char* kSlotSuffixes[kMaxNumSlots] = {"_a", "_b"};
-constexpr std::string_view kFakeDevicePath = "/fake/dev/path/";
+constexpr const char* kFakeDevicePath = "/fake/dev/path/";
 constexpr const char* kFakeDmDevicePath = "/fake/dm/dev/path/";
 constexpr const uint32_t kFakeMetadataSize = 65536;
 constexpr const char* kDefaultGroup = "foo";
@@ -112,7 +112,7 @@
 }
 
 inline std::string GetDevice(const std::string& name) {
-  return std::string(kFakeDevicePath) + name;
+  return kFakeDevicePath + name;
 }
 
 inline std::string GetDmDevice(const std::string& name) {
@@ -175,11 +175,9 @@
 }
 
 inline std::unique_ptr<MetadataBuilder> NewFakeMetadata(
-    const DeltaArchiveManifest& manifest,
-    uint32_t partition_attr = 0,
-    uint64_t super_size = kDefaultSuperSize) {
+    const DeltaArchiveManifest& manifest, uint32_t partition_attr = 0) {
   auto builder =
-      MetadataBuilder::New(super_size, kFakeMetadataSize, kMaxNumSlots);
+      MetadataBuilder::New(kDefaultSuperSize, kFakeMetadataSize, kMaxNumSlots);
   for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
     EXPECT_TRUE(builder->AddGroup(group.name(), group.size()));
     for (const auto& partition_name : group.partition_names()) {
@@ -285,4 +283,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_TEST_UTILS_H_
+#endif  // UPDATE_ENGINE_DYNAMIC_PARTITION_TEST_UTILS_H_
diff --git a/aosp/dynamic_partition_utils.cc b/dynamic_partition_utils.cc
similarity index 95%
rename from aosp/dynamic_partition_utils.cc
rename to dynamic_partition_utils.cc
index 6b77a45..f9bd886 100644
--- a/aosp/dynamic_partition_utils.cc
+++ b/dynamic_partition_utils.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/aosp/dynamic_partition_utils.h"
+#include "update_engine/dynamic_partition_utils.h"
 
 #include <vector>
 
diff --git a/aosp/dynamic_partition_utils.h b/dynamic_partition_utils.h
similarity index 85%
rename from aosp/dynamic_partition_utils.h
rename to dynamic_partition_utils.h
index 5a51d5e..09fce00 100644
--- a/aosp/dynamic_partition_utils.h
+++ b/dynamic_partition_utils.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_UTILS_H_
-#define UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_UTILS_H_
+#ifndef UPDATE_ENGINE_DYNAMIC_PARTITION_UTILS_H_
+#define UPDATE_ENGINE_DYNAMIC_PARTITION_UTILS_H_
 
 #include <string>
 
@@ -30,4 +30,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_UTILS_H_
+#endif  // UPDATE_ENGINE_DYNAMIC_PARTITION_UTILS_H_
diff --git a/cros/fake_p2p_manager.h b/fake_p2p_manager.h
similarity index 94%
rename from cros/fake_p2p_manager.h
rename to fake_p2p_manager.h
index 1011b7e..1f8ae95 100644
--- a/cros/fake_p2p_manager.h
+++ b/fake_p2p_manager.h
@@ -14,12 +14,12 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_FAKE_P2P_MANAGER_H_
-#define UPDATE_ENGINE_CROS_FAKE_P2P_MANAGER_H_
+#ifndef UPDATE_ENGINE_FAKE_P2P_MANAGER_H_
+#define UPDATE_ENGINE_FAKE_P2P_MANAGER_H_
 
 #include <string>
 
-#include "update_engine/cros/p2p_manager.h"
+#include "update_engine/p2p_manager.h"
 
 namespace chromeos_update_engine {
 
@@ -109,4 +109,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_FAKE_P2P_MANAGER_H_
+#endif  // UPDATE_ENGINE_FAKE_P2P_MANAGER_H_
diff --git a/cros/fake_p2p_manager_configuration.h b/fake_p2p_manager_configuration.h
similarity index 93%
rename from cros/fake_p2p_manager_configuration.h
rename to fake_p2p_manager_configuration.h
index 8d50ac8..f5b0e80 100644
--- a/cros/fake_p2p_manager_configuration.h
+++ b/fake_p2p_manager_configuration.h
@@ -14,10 +14,10 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_FAKE_P2P_MANAGER_CONFIGURATION_H_
-#define UPDATE_ENGINE_CROS_FAKE_P2P_MANAGER_CONFIGURATION_H_
+#ifndef UPDATE_ENGINE_FAKE_P2P_MANAGER_CONFIGURATION_H_
+#define UPDATE_ENGINE_FAKE_P2P_MANAGER_CONFIGURATION_H_
 
-#include "update_engine/cros/p2p_manager.h"
+#include "update_engine/p2p_manager.h"
 
 #include <string>
 #include <vector>
@@ -99,4 +99,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_FAKE_P2P_MANAGER_CONFIGURATION_H_
+#endif  // UPDATE_ENGINE_FAKE_P2P_MANAGER_CONFIGURATION_H_
diff --git a/cros/fake_shill_proxy.cc b/fake_shill_proxy.cc
similarity index 96%
rename from cros/fake_shill_proxy.cc
rename to fake_shill_proxy.cc
index 2d05a6b..de96511 100644
--- a/cros/fake_shill_proxy.cc
+++ b/fake_shill_proxy.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/fake_shill_proxy.h"
+#include "update_engine/fake_shill_proxy.h"
 
 #include <utility>
 
diff --git a/cros/fake_shill_proxy.h b/fake_shill_proxy.h
similarity index 90%
rename from cros/fake_shill_proxy.h
rename to fake_shill_proxy.h
index 8c15a9d..ae17eaa 100644
--- a/cros/fake_shill_proxy.h
+++ b/fake_shill_proxy.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_FAKE_SHILL_PROXY_H_
-#define UPDATE_ENGINE_CROS_FAKE_SHILL_PROXY_H_
+#ifndef UPDATE_ENGINE_FAKE_SHILL_PROXY_H_
+#define UPDATE_ENGINE_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/cros/shill_proxy_interface.h"
+#include "update_engine/shill_proxy_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -63,4 +63,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_FAKE_SHILL_PROXY_H_
+#endif  // UPDATE_ENGINE_FAKE_SHILL_PROXY_H_
diff --git a/cros/fake_system_state.cc b/fake_system_state.cc
similarity index 81%
rename from cros/fake_system_state.cc
rename to fake_system_state.cc
index 7673b1d..1bfcafa 100644
--- a/cros/fake_system_state.cc
+++ b/fake_system_state.cc
@@ -14,20 +14,22 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/fake_system_state.h"
+#include "update_engine/fake_system_state.h"
 
 namespace chromeos_update_engine {
 
 // Mock the SystemStateInterface so that we could lie that
 // OOBE is completed even when there's no such marker file, etc.
 FakeSystemState::FakeSystemState()
-    : mock_update_attempter_(nullptr),
+    : mock_update_attempter_(this, nullptr),
+      mock_request_params_(this),
+      fake_update_manager_(&fake_clock_),
       clock_(&fake_clock_),
       connection_manager_(&mock_connection_manager_),
       hardware_(&fake_hardware_),
       metrics_reporter_(&mock_metrics_reporter_),
-      prefs_(&fake_prefs_),
-      powerwash_safe_prefs_(&fake_powerwash_safe_prefs_),
+      prefs_(&mock_prefs_),
+      powerwash_safe_prefs_(&mock_powerwash_safe_prefs_),
       payload_state_(&mock_payload_state_),
       update_attempter_(&mock_update_attempter_),
       request_params_(&mock_request_params_),
@@ -35,7 +37,7 @@
       update_manager_(&fake_update_manager_),
       device_policy_(nullptr),
       fake_system_rebooted_(false) {
-  mock_payload_state_.Initialize();
+  mock_payload_state_.Initialize(this);
 }
 
 }  // namespace chromeos_update_engine
diff --git a/cros/fake_system_state.h b/fake_system_state.h
similarity index 86%
rename from cros/fake_system_state.h
rename to fake_system_state.h
index da36306..24b1eec 100644
--- a/cros/fake_system_state.h
+++ b/fake_system_state.h
@@ -14,10 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_FAKE_SYSTEM_STATE_H_
-#define UPDATE_ENGINE_CROS_FAKE_SYSTEM_STATE_H_
-
-#include <memory>
+#ifndef UPDATE_ENGINE_FAKE_SYSTEM_STATE_H_
+#define UPDATE_ENGINE_FAKE_SYSTEM_STATE_H_
 
 #include <base/logging.h>
 #include <gmock/gmock.h>
@@ -27,16 +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/fake_prefs.h"
-#include "update_engine/common/mock_metrics_reporter.h"
 #include "update_engine/common/mock_prefs.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/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/update_manager/fake_update_manager.h"
 
 namespace chromeos_update_engine {
@@ -45,15 +42,7 @@
 // OOBE is completed even when there's no such marker file, etc.
 class FakeSystemState : public SystemState {
  public:
-  static void CreateInstance() {
-    static std::unique_ptr<FakeSystemState> system_state;
-    system_state.reset(new FakeSystemState());
-    g_pointer_ = system_state.get();
-  }
-
-  static FakeSystemState* Get() {
-    return reinterpret_cast<FakeSystemState*>(g_pointer_);
-  }
+  FakeSystemState();
 
   // Base class overrides. All getters return the current implementation of
   // various members, either the default (fake/mock) or the one set to override
@@ -207,16 +196,6 @@
     return &fake_hardware_;
   }
 
-  inline FakePrefs* fake_prefs() {
-    CHECK(prefs_ == &fake_prefs_);
-    return &fake_prefs_;
-  }
-
-  inline FakePrefs* fake_powerwash_safe_prefs() {
-    CHECK(powerwash_safe_prefs_ == &fake_powerwash_safe_prefs_);
-    return &fake_powerwash_safe_prefs_;
-  }
-
   inline testing::NiceMock<MockMetricsReporter>* mock_metrics_reporter() {
     CHECK(metrics_reporter_ == &mock_metrics_reporter_);
     return &mock_metrics_reporter_;
@@ -258,18 +237,11 @@
   }
 
  private:
-  // Don't allow for direct initialization of this class.
-  FakeSystemState();
-
   // Default mock/fake implementations (owned).
-  chromeos_update_manager::FakeUpdateManager fake_update_manager_;
   FakeBootControl fake_boot_control_;
   FakeClock fake_clock_;
-  FakeHardware fake_hardware_;
-  FakePrefs fake_prefs_;
-  FakePrefs fake_powerwash_safe_prefs_;
-
   testing::NiceMock<MockConnectionManager> mock_connection_manager_;
+  FakeHardware fake_hardware_;
   testing::NiceMock<MockMetricsReporter> mock_metrics_reporter_;
   testing::NiceMock<MockPrefs> mock_prefs_;
   testing::NiceMock<MockPrefs> mock_powerwash_safe_prefs_;
@@ -277,6 +249,7 @@
   testing::NiceMock<MockUpdateAttempter> mock_update_attempter_;
   testing::NiceMock<MockOmahaRequestParams> mock_request_params_;
   testing::NiceMock<MockP2PManager> mock_p2p_manager_;
+  chromeos_update_manager::FakeUpdateManager fake_update_manager_;
   testing::NiceMock<MockPowerManager> mock_power_manager_;
 
   // Pointers to objects that client code can override. They are initialized to
@@ -305,4 +278,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_FAKE_SYSTEM_STATE_H_
+#endif  // UPDATE_ENGINE_FAKE_SYSTEM_STATE_H_
diff --git a/hardware_android.cc b/hardware_android.cc
new file mode 100644
index 0000000..068468b
--- /dev/null
+++ b/hardware_android.cc
@@ -0,0 +1,220 @@
+//
+// 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/hardware_android.h"
+
+#include <sys/types.h>
+
+#include <memory>
+
+#include <android-base/properties.h>
+#include <base/files/file_util.h>
+#include <bootloader_message/bootloader_message.h>
+
+#include "update_engine/common/hardware.h"
+#include "update_engine/common/platform_constants.h"
+
+using android::base::GetBoolProperty;
+using android::base::GetIntProperty;
+using android::base::GetProperty;
+using std::string;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+// Android properties that identify the hardware and potentially non-updatable
+// parts of the bootloader (such as the bootloader version and the baseband
+// version).
+const char 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";
+const char kPropBuildDateUTC[] = "ro.build.date.utc";
+
+}  // namespace
+
+namespace hardware {
+
+// Factory defined in hardware.h.
+std::unique_ptr<HardwareInterface> CreateHardware() {
+  return std::make_unique<HardwareAndroid>();
+}
+
+}  // namespace hardware
+
+// In Android there are normally three kinds of builds: eng, userdebug and user.
+// These builds target respectively a developer build, a debuggable version of
+// the final product and the pristine final product the end user will run.
+// Apart from the ro.build.type property name, they differ in the following
+// properties that characterize the builds:
+// * eng builds: ro.secure=0 and ro.debuggable=1
+// * userdebug builds: ro.secure=1 and ro.debuggable=1
+// * user builds: ro.secure=1 and ro.debuggable=0
+//
+// See IsOfficialBuild() and IsNormalMode() for the meaning of these options in
+// Android.
+
+bool HardwareAndroid::IsOfficialBuild() const {
+  // We run an official build iff ro.secure == 1, because we expect the build to
+  // behave like the end user product and check for updates. Note that while
+  // developers are able to build "official builds" by just running "make user",
+  // that will only result in a more restrictive environment. The important part
+  // is that we don't produce and push "non-official" builds to the end user.
+  //
+  // In case of a non-bool value, we take the most restrictive option and
+  // assume we are in an official-build.
+  return GetBoolProperty("ro.secure", true);
+}
+
+bool HardwareAndroid::IsNormalBootMode() const {
+  // We are running in "dev-mode" iff ro.debuggable == 1. In dev-mode the
+  // update_engine will allow extra developers options, such as providing a
+  // different update URL. In case of error, we assume the build is in
+  // normal-mode.
+  return !GetBoolProperty("ro.debuggable", false);
+}
+
+bool HardwareAndroid::AreDevFeaturesEnabled() const {
+  return !IsNormalBootMode();
+}
+
+bool HardwareAndroid::IsOOBEEnabled() const {
+  // No OOBE flow blocking updates for Android-based boards.
+  return false;
+}
+
+bool HardwareAndroid::IsOOBEComplete(base::Time* out_time_of_oobe) const {
+  LOG(WARNING) << "OOBE is not enabled but IsOOBEComplete() called.";
+  if (out_time_of_oobe)
+    *out_time_of_oobe = base::Time();
+  return true;
+}
+
+string HardwareAndroid::GetHardwareClass() const {
+  auto manufacturer = GetProperty(kPropProductManufacturer, "");
+  auto sku = GetProperty(kPropBootHardwareSKU, "");
+  auto revision = GetProperty(kPropBootRevision, "");
+
+  return manufacturer + ":" + sku + ":" + revision;
+}
+
+string HardwareAndroid::GetFirmwareVersion() const {
+  return GetProperty(kPropBootBootloader, "");
+}
+
+string HardwareAndroid::GetECVersion() const {
+  return GetProperty(kPropBootBaseband, "");
+}
+
+int HardwareAndroid::GetMinKernelKeyVersion() const {
+  LOG(WARNING) << "STUB: No Kernel key version is available.";
+  return -1;
+}
+
+int HardwareAndroid::GetMinFirmwareKeyVersion() const {
+  LOG(WARNING) << "STUB: No Firmware key version is available.";
+  return -1;
+}
+
+int HardwareAndroid::GetMaxFirmwareKeyRollforward() const {
+  LOG(WARNING) << "STUB: Getting firmware_max_rollforward is not supported.";
+  return -1;
+}
+
+bool HardwareAndroid::SetMaxFirmwareKeyRollforward(
+    int firmware_max_rollforward) {
+  LOG(WARNING) << "STUB: Setting firmware_max_rollforward is not supported.";
+  return false;
+}
+
+bool HardwareAndroid::SetMaxKernelKeyRollforward(int kernel_max_rollforward) {
+  LOG(WARNING) << "STUB: Setting kernel_max_rollforward is not supported.";
+  return false;
+}
+
+int HardwareAndroid::GetPowerwashCount() const {
+  LOG(WARNING) << "STUB: Assuming no factory reset was performed.";
+  return 0;
+}
+
+bool HardwareAndroid::SchedulePowerwash(bool is_rollback) {
+  LOG(INFO) << "Scheduling a powerwash to BCB.";
+  LOG_IF(WARNING, is_rollback) << "is_rollback was true but isn't supported.";
+  string err;
+  if (!update_bootloader_message({"--wipe_data", "--reason=wipe_data_from_ota"},
+                                 &err)) {
+    LOG(ERROR) << "Failed to update bootloader message: " << err;
+    return false;
+  }
+  return true;
+}
+
+bool HardwareAndroid::CancelPowerwash() {
+  string err;
+  if (!clear_bootloader_message(&err)) {
+    LOG(ERROR) << "Failed to clear bootloader message: " << err;
+    return false;
+  }
+  return true;
+}
+
+bool HardwareAndroid::GetNonVolatileDirectory(base::FilePath* path) const {
+  base::FilePath local_path(constants::kNonVolatileDirectory);
+  if (!base::PathExists(local_path)) {
+    LOG(ERROR) << "Non-volatile directory not found: " << local_path.value();
+    return false;
+  }
+  *path = local_path;
+  return true;
+}
+
+bool HardwareAndroid::GetPowerwashSafeDirectory(base::FilePath* path) const {
+  // On Android, we don't have a directory persisted across powerwash.
+  return false;
+}
+
+int64_t HardwareAndroid::GetBuildTimestamp() const {
+  return GetIntProperty<int64_t>(kPropBuildDateUTC, 0);
+}
+
+// Returns true if the device runs an userdebug build, and explicitly allows OTA
+// downgrade.
+bool HardwareAndroid::AllowDowngrade() const {
+  return GetBoolProperty("ro.ota.allow_downgrade", false) &&
+         GetBoolProperty("ro.debuggable", false);
+}
+
+bool HardwareAndroid::GetFirstActiveOmahaPingSent() const {
+  LOG(WARNING) << "STUB: Assuming first active omaha was never set.";
+  return false;
+}
+
+bool HardwareAndroid::SetFirstActiveOmahaPingSent() {
+  LOG(WARNING) << "STUB: Assuming first active omaha is set.";
+  // We will set it true, so its failure doesn't cause escalation.
+  return true;
+}
+
+void HardwareAndroid::SetWarmReset(bool warm_reset) {
+  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;
+  }
+}
+
+}  // namespace chromeos_update_engine
diff --git a/aosp/hardware_android.h b/hardware_android.h
similarity index 72%
rename from aosp/hardware_android.h
rename to hardware_android.h
index d20e8df..145a936 100644
--- a/aosp/hardware_android.h
+++ b/hardware_android.h
@@ -14,23 +14,21 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_AOSP_HARDWARE_ANDROID_H_
-#define UPDATE_ENGINE_AOSP_HARDWARE_ANDROID_H_
+#ifndef UPDATE_ENGINE_HARDWARE_ANDROID_H_
+#define UPDATE_ENGINE_HARDWARE_ANDROID_H_
 
 #include <string>
-#include <string_view>
 
 #include <base/macros.h>
 #include <base/time/time.h>
 
-#include "update_engine/common/error_code.h"
 #include "update_engine/common/hardware.h"
 #include "update_engine/common/hardware_interface.h"
 
 namespace chromeos_update_engine {
 
 // Implements the real interface with the hardware in the Android platform.
-class HardwareAndroid : public HardwareInterface {
+class HardwareAndroid final : public HardwareInterface {
  public:
   HardwareAndroid() = default;
   ~HardwareAndroid() override = default;
@@ -42,14 +40,15 @@
   bool IsOOBEEnabled() const override;
   bool IsOOBEComplete(base::Time* out_time_of_oobe) const override;
   std::string GetHardwareClass() const override;
-  std::string GetDeviceRequisition() const override;
+  std::string GetFirmwareVersion() const override;
+  std::string GetECVersion() const override;
   int GetMinKernelKeyVersion() const override;
   int GetMinFirmwareKeyVersion() const override;
   int GetMaxFirmwareKeyRollforward() const override;
   bool SetMaxFirmwareKeyRollforward(int firmware_max_rollforward) override;
   bool SetMaxKernelKeyRollforward(int kernel_max_rollforward) override;
   int GetPowerwashCount() const override;
-  bool SchedulePowerwash(bool save_rollback_data) override;
+  bool SchedulePowerwash(bool is_rollback) override;
   bool CancelPowerwash() override;
   bool GetNonVolatileDirectory(base::FilePath* path) const override;
   bool GetPowerwashSafeDirectory(base::FilePath* path) const override;
@@ -58,14 +57,6 @@
   bool GetFirstActiveOmahaPingSent() const override;
   bool SetFirstActiveOmahaPingSent() override;
   void SetWarmReset(bool warm_reset) override;
-  void SetVbmetaDigestForInactiveSlot(bool reset) override;
-  [[nodiscard]] std::string GetVersionForLogging(
-      const std::string& partition_name) const override;
-  [[nodiscard]] ErrorCode IsPartitionUpdateValid(
-      const std::string& partition_name,
-      const std::string& new_version) const override;
-  [[nodiscard]] const char* GetPartitionMountOptions(
-      const std::string& partition_name) const override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(HardwareAndroid);
@@ -73,4 +64,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_AOSP_HARDWARE_ANDROID_H_
+#endif  // UPDATE_ENGINE_HARDWARE_ANDROID_H_
diff --git a/cros/hardware_chromeos.cc b/hardware_chromeos.cc
similarity index 77%
rename from cros/hardware_chromeos.cc
rename to hardware_chromeos.cc
index ad0a64d..a49375e 100644
--- a/cros/hardware_chromeos.cc
+++ b/hardware_chromeos.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/hardware_chromeos.h"
+#include "update_engine/hardware_chromeos.h"
 
 #include <utility>
 
@@ -37,10 +37,7 @@
 #include "update_engine/common/platform_constants.h"
 #include "update_engine/common/subprocess.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/cros/dbus_connection.h"
-#if USE_CFM
-#include "update_engine/cros/requisition_util.h"
-#endif
+#include "update_engine/dbus_connection.h"
 
 using std::string;
 using std::vector;
@@ -50,7 +47,7 @@
 const char kOOBECompletedMarker[] = "/home/chronos/.oobe_completed";
 
 // The stateful directory used by update_engine to store powerwash-safe files.
-// The files stored here must be added to the powerwash script allowlist.
+// The files stored here must be whitelisted in the powerwash scripts.
 const char kPowerwashSafeDirectory[] =
     "/mnt/stateful_partition/unencrypted/preserve";
 
@@ -64,11 +61,6 @@
 const char kPowerwashMarkerFile[] =
     "/mnt/stateful_partition/factory_install_reset";
 
-// The name of the marker file used to trigger a save of rollback data
-// during the next shutdown.
-const char kRollbackSaveMarkerFile[] =
-    "/mnt/stateful_partition/.save_rollback_data";
-
 // The contents of the powerwash marker file for the non-rollback case.
 const char kPowerwashCommand[] = "safe fast keepimg reason=update_engine\n";
 
@@ -175,13 +167,22 @@
   return ReadValueFromCrosSystem("hwid");
 }
 
-string HardwareChromeOS::GetDeviceRequisition() const {
-#if USE_CFM
-  const char* kLocalStatePath = "/home/chronos/Local State";
-  return ReadDeviceRequisition(base::FilePath(kLocalStatePath));
-#else
-  return "";
-#endif
+string HardwareChromeOS::GetFirmwareVersion() const {
+  return ReadValueFromCrosSystem("fwid");
+}
+
+string HardwareChromeOS::GetECVersion() const {
+  string input_line;
+  int exit_code = 0;
+  vector<string> cmd = {"/usr/sbin/mosys", "-k", "ec", "info"};
+
+  bool success = Subprocess::SynchronousExec(cmd, &exit_code, &input_line);
+  if (!success || exit_code) {
+    LOG(ERROR) << "Unable to read ec info from mosys (" << exit_code << ")";
+    return "";
+  }
+
+  return utils::ParseECVersion(input_line);
 }
 
 int HardwareChromeOS::GetMinKernelKeyVersion() const {
@@ -225,25 +226,15 @@
   return powerwash_count;
 }
 
-bool HardwareChromeOS::SchedulePowerwash(bool save_rollback_data) {
-  if (save_rollback_data) {
-    if (!utils::WriteFile(kRollbackSaveMarkerFile, nullptr, 0)) {
-      PLOG(ERROR) << "Error in creating rollback save marker file: "
-                  << kRollbackSaveMarkerFile << ". Rollback will not"
-                  << " preserve any data.";
-    } else {
-      LOG(INFO) << "Rollback data save has been scheduled on next shutdown.";
-    }
-  }
-
+bool HardwareChromeOS::SchedulePowerwash(bool is_rollback) {
   const char* powerwash_command =
-      save_rollback_data ? kRollbackPowerwashCommand : kPowerwashCommand;
+      is_rollback ? kRollbackPowerwashCommand : kPowerwashCommand;
   bool result = utils::WriteFile(
       kPowerwashMarkerFile, powerwash_command, strlen(powerwash_command));
   if (result) {
     LOG(INFO) << "Created " << kPowerwashMarkerFile
-              << " to powerwash on next reboot ("
-              << "save_rollback_data=" << save_rollback_data << ")";
+              << " to powerwash on next reboot (is_rollback=" << is_rollback
+              << ")";
   } else {
     PLOG(ERROR) << "Error in creating powerwash marker file: "
                 << kPowerwashMarkerFile;
@@ -253,7 +244,7 @@
 }
 
 bool HardwareChromeOS::CancelPowerwash() {
-  bool result = base::DeleteFile(base::FilePath(kPowerwashMarkerFile));
+  bool result = base::DeleteFile(base::FilePath(kPowerwashMarkerFile), false);
 
   if (result) {
     LOG(INFO) << "Successfully deleted the powerwash marker file : "
@@ -263,11 +254,6 @@
                 << kPowerwashMarkerFile;
   }
 
-  // Delete the rollback save marker file if it existed.
-  if (!base::DeleteFile(base::FilePath(kRollbackSaveMarkerFile))) {
-    PLOG(ERROR) << "Could not remove rollback save marker";
-  }
-
   return result;
 }
 
@@ -305,11 +291,17 @@
 }
 
 bool HardwareChromeOS::GetFirstActiveOmahaPingSent() const {
+  int exit_code = 0;
   string active_ping_str;
-  if (!utils::GetVpdValue(kActivePingKey, &active_ping_str)) {
+  vector<string> cmd = {"vpd_get_value", kActivePingKey};
+  if (!Subprocess::SynchronousExec(cmd, &exit_code, &active_ping_str) ||
+      exit_code) {
+    LOG(ERROR) << "Failed to get vpd key for " << kActivePingKey
+               << " with exit code: " << exit_code;
     return false;
   }
 
+  base::TrimWhitespaceASCII(active_ping_str, base::TRIM_ALL, &active_ping_str);
   int active_ping;
   if (active_ping_str.empty() ||
       !base::StringToInt(active_ping_str, &active_ping)) {
@@ -321,51 +313,26 @@
 
 bool HardwareChromeOS::SetFirstActiveOmahaPingSent() {
   int exit_code = 0;
-  string output, error;
+  string output;
   vector<string> vpd_set_cmd = {
       "vpd", "-i", "RW_VPD", "-s", string(kActivePingKey) + "=1"};
-  if (!Subprocess::SynchronousExec(vpd_set_cmd, &exit_code, &output, &error) ||
+  if (!Subprocess::SynchronousExec(vpd_set_cmd, &exit_code, &output) ||
       exit_code) {
     LOG(ERROR) << "Failed to set vpd key for " << kActivePingKey
-               << " with exit code: " << exit_code << " with output: " << output
-               << " and error: " << error;
+               << " with exit code: " << exit_code << " with error: " << output;
     return false;
-  } else if (!error.empty()) {
-    LOG(INFO) << "vpd succeeded but with error logs: " << error;
   }
 
   vector<string> vpd_dump_cmd = {"dump_vpd_log", "--force"};
-  if (!Subprocess::SynchronousExec(vpd_dump_cmd, &exit_code, &output, &error) ||
+  if (!Subprocess::SynchronousExec(vpd_dump_cmd, &exit_code, &output) ||
       exit_code) {
     LOG(ERROR) << "Failed to cache " << kActivePingKey << " using dump_vpd_log"
-               << " with exit code: " << exit_code << " with output: " << output
-               << " and error: " << error;
+               << " with exit code: " << exit_code << " with error: " << output;
     return false;
-  } else if (!error.empty()) {
-    LOG(INFO) << "dump_vpd_log succeeded but with error logs: " << error;
   }
   return true;
 }
 
 void HardwareChromeOS::SetWarmReset(bool warm_reset) {}
 
-void HardwareChromeOS::SetVbmetaDigestForInactiveSlot(bool reset) {}
-
-std::string HardwareChromeOS::GetVersionForLogging(
-    const std::string& partition_name) const {
-  // TODO(zhangkelvin) Implement per-partition timestamp for Chrome OS.
-  return "";
-}
-
-ErrorCode HardwareChromeOS::IsPartitionUpdateValid(
-    const std::string& partition_name, const std::string& new_version) const {
-  // TODO(zhangkelvin) Implement per-partition timestamp for Chrome OS.
-  return ErrorCode::kSuccess;
-}
-
-const char* HardwareChromeOS::GetPartitionMountOptions(
-    const std::string& partition_name) const {
-  return "";
-}
-
 }  // namespace chromeos_update_engine
diff --git a/cros/hardware_chromeos.h b/hardware_chromeos.h
similarity index 79%
rename from cros/hardware_chromeos.h
rename to hardware_chromeos.h
index a64f804..2bea989 100644
--- a/cros/hardware_chromeos.h
+++ b/hardware_chromeos.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_HARDWARE_CHROMEOS_H_
-#define UPDATE_ENGINE_CROS_HARDWARE_CHROMEOS_H_
+#ifndef UPDATE_ENGINE_HARDWARE_CHROMEOS_H_
+#define UPDATE_ENGINE_HARDWARE_CHROMEOS_H_
 
 #include <memory>
 #include <string>
@@ -25,7 +25,6 @@
 #include <base/time/time.h>
 #include <debugd/dbus-proxies.h>
 
-#include "update_engine/common/error_code.h"
 #include "update_engine/common/hardware_interface.h"
 
 namespace chromeos_update_engine {
@@ -46,14 +45,15 @@
   bool IsOOBEEnabled() const override;
   bool IsOOBEComplete(base::Time* out_time_of_oobe) const override;
   std::string GetHardwareClass() const override;
-  std::string GetDeviceRequisition() const override;
+  std::string GetFirmwareVersion() const override;
+  std::string GetECVersion() const override;
   int GetMinKernelKeyVersion() const override;
   int GetMinFirmwareKeyVersion() const override;
   int GetMaxFirmwareKeyRollforward() const override;
   bool SetMaxFirmwareKeyRollforward(int firmware_max_rollforward) override;
   bool SetMaxKernelKeyRollforward(int kernel_max_rollforward) override;
   int GetPowerwashCount() const override;
-  bool SchedulePowerwash(bool save_rollback_data) override;
+  bool SchedulePowerwash(bool is_rollback) override;
   bool CancelPowerwash() override;
   bool GetNonVolatileDirectory(base::FilePath* path) const override;
   bool GetPowerwashSafeDirectory(base::FilePath* path) const override;
@@ -62,14 +62,6 @@
   bool GetFirstActiveOmahaPingSent() const override;
   bool SetFirstActiveOmahaPingSent() override;
   void SetWarmReset(bool warm_reset) override;
-  void SetVbmetaDigestForInactiveSlot(bool reset) override;
-  std::string GetVersionForLogging(
-      const std::string& partition_name) const override;
-  ErrorCode IsPartitionUpdateValid(
-      const std::string& partition_name,
-      const std::string& new_version) const override;
-  const char* GetPartitionMountOptions(
-      const std::string& partition_name) const override;
 
  private:
   friend class HardwareChromeOSTest;
@@ -88,4 +80,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_HARDWARE_CHROMEOS_H_
+#endif  // UPDATE_ENGINE_HARDWARE_CHROMEOS_H_
diff --git a/cros/hardware_chromeos_unittest.cc b/hardware_chromeos_unittest.cc
similarity index 97%
rename from cros/hardware_chromeos_unittest.cc
rename to hardware_chromeos_unittest.cc
index 50bced6..162dec4 100644
--- a/cros/hardware_chromeos_unittest.cc
+++ b/hardware_chromeos_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/hardware_chromeos.h"
+#include "update_engine/hardware_chromeos.h"
 
 #include <memory>
 
diff --git a/cros/image_properties.h b/image_properties.h
similarity index 84%
rename from cros/image_properties.h
rename to image_properties.h
index 1297547..49fe82f 100644
--- a/cros/image_properties.h
+++ b/image_properties.h
@@ -18,22 +18,28 @@
 // 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_CROS_IMAGE_PROPERTIES_H_
-#define UPDATE_ENGINE_CROS_IMAGE_PROPERTIES_H_
+#ifndef UPDATE_ENGINE_IMAGE_PROPERTIES_H_
+#define UPDATE_ENGINE_IMAGE_PROPERTIES_H_
 
 #include <string>
 
 namespace chromeos_update_engine {
 
+class SystemState;
+
 // The read-only system properties of the running image.
 struct ImageProperties {
   // The product id of the image used for all channels, except canary.
   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;
@@ -75,15 +81,16 @@
 // Loads all the image properties from the running system. In case of error
 // loading any of these properties from the read-only system image a default
 // value may be returned instead.
-ImageProperties LoadImageProperties();
+ImageProperties LoadImageProperties(SystemState* system_state);
 
 // Loads the mutable image properties from the stateful partition if found or
 // the system image otherwise.
-MutableImageProperties LoadMutableImageProperties();
+MutableImageProperties LoadMutableImageProperties(SystemState* system_state);
 
 // Stores the mutable image properties in the stateful partition. Returns
 // whether the operation succeeded.
-bool StoreMutableImageProperties(const MutableImageProperties& properties);
+bool StoreMutableImageProperties(SystemState* system_state,
+                                 const MutableImageProperties& properties);
 
 // Logs the image properties.
 void LogImageProperties();
@@ -96,4 +103,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_IMAGE_PROPERTIES_H_
+#endif  // UPDATE_ENGINE_IMAGE_PROPERTIES_H_
diff --git a/image_properties_android.cc b/image_properties_android.cc
new file mode 100644
index 0000000..2d418b3
--- /dev/null
+++ b/image_properties_android.cc
@@ -0,0 +1,246 @@
+//
+// 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
new file mode 100644
index 0000000..607284a
--- /dev/null
+++ b/image_properties_android_unittest.cc
@@ -0,0 +1,123 @@
+//
+// 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/cros/image_properties_chromeos.cc b/image_properties_chromeos.cc
similarity index 93%
rename from cros/image_properties_chromeos.cc
rename to image_properties_chromeos.cc
index 79155b5..5ab8f05 100644
--- a/cros/image_properties_chromeos.cc
+++ b/image_properties_chromeos.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/image_properties.h"
+#include "update_engine/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 {
 
@@ -86,7 +86,7 @@
 }
 }  // namespace test
 
-ImageProperties LoadImageProperties() {
+ImageProperties LoadImageProperties(SystemState* system_state) {
   ImageProperties result;
 
   brillo::KeyValueStore lsb_release;
@@ -97,7 +97,7 @@
   // In dev-mode and unofficial build we can override the image properties set
   // in the system image with the ones from the stateful partition, except the
   // channel of the current image.
-  HardwareInterface* const hardware = SystemState::Get()->hardware();
+  HardwareInterface* const hardware = system_state->hardware();
   if (!hardware->IsOfficialBuild() || !hardware->IsNormalBootMode())
     LoadLsbRelease(LsbReleaseSource::kStateful, &lsb_release);
 
@@ -124,7 +124,7 @@
   return result;
 }
 
-MutableImageProperties LoadMutableImageProperties() {
+MutableImageProperties LoadMutableImageProperties(SystemState* system_state) {
   MutableImageProperties result;
   brillo::KeyValueStore lsb_release;
   LoadLsbRelease(LsbReleaseSource::kSystem, &lsb_release);
@@ -137,7 +137,8 @@
   return result;
 }
 
-bool StoreMutableImageProperties(const MutableImageProperties& properties) {
+bool StoreMutableImageProperties(SystemState* system_state,
+                                 const MutableImageProperties& properties) {
   brillo::KeyValueStore lsb_release;
   LoadLsbRelease(LsbReleaseSource::kStateful, &lsb_release);
   lsb_release.SetString(kLsbReleaseUpdateChannelKey, properties.target_channel);
diff --git a/cros/image_properties_chromeos_unittest.cc b/image_properties_chromeos_unittest.cc
similarity index 84%
rename from cros/image_properties_chromeos_unittest.cc
rename to image_properties_chromeos_unittest.cc
index 497554e..d9ed688 100644
--- a/cros/image_properties_chromeos_unittest.cc
+++ b/image_properties_chromeos_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/image_properties.h"
+#include "update_engine/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/cros/fake_system_state.h"
+#include "update_engine/fake_system_state.h"
 
 using chromeos_update_engine::test_utils::WriteFileString;
 using std::string;
@@ -40,15 +40,16 @@
     EXPECT_TRUE(base::CreateDirectory(base::FilePath(
         tempdir_.GetPath().value() + kStatefulPartition + "/etc")));
     test::SetImagePropertiesRootPrefix(tempdir_.GetPath().value().c_str());
-    FakeSystemState::CreateInstance();
     SetLockDown(false);
   }
 
   void SetLockDown(bool locked_down) {
-    FakeSystemState::Get()->fake_hardware()->SetIsOfficialBuild(locked_down);
-    FakeSystemState::Get()->fake_hardware()->SetIsNormalBootMode(locked_down);
+    fake_system_state_.fake_hardware()->SetIsOfficialBuild(locked_down);
+    fake_system_state_.fake_hardware()->SetIsNormalBootMode(locked_down);
   }
 
+  FakeSystemState fake_system_state_;
+
   base::ScopedTempDir tempdir_;
 };
 
@@ -60,7 +61,7 @@
                       "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
                       "CHROMEOS_RELEASE_TRACK=dev-channel\n"
                       "CHROMEOS_AUSERVER=http://www.google.com"));
-  ImageProperties props = LoadImageProperties();
+  ImageProperties props = LoadImageProperties(&fake_system_state_);
   EXPECT_EQ("arm-generic", props.board);
   EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", props.product_id);
   EXPECT_EQ("0.2.2.3", props.version);
@@ -72,7 +73,7 @@
   ASSERT_TRUE(WriteFileString(
       tempdir_.GetPath().Append("etc/lsb-release").value(),
       "CHROMEOS_RELEASE_APPID={58c35cef-9d30-476e-9098-ce20377d535d}"));
-  ImageProperties props = LoadImageProperties();
+  ImageProperties props = LoadImageProperties(&fake_system_state_);
   EXPECT_EQ("{58c35cef-9d30-476e-9098-ce20377d535d}", props.product_id);
 }
 
@@ -81,12 +82,12 @@
       WriteFileString(tempdir_.GetPath().Append("etc/lsb-release").value(),
                       "CHROMEOS_RELEASE_FOO=CHROMEOS_RELEASE_VERSION=1.2.3.4\n"
                       "CHROMEOS_RELEASE_VERSION=0.2.2.3"));
-  ImageProperties props = LoadImageProperties();
+  ImageProperties props = LoadImageProperties(&fake_system_state_);
   EXPECT_EQ("0.2.2.3", props.version);
 }
 
 TEST_F(ImagePropertiesTest, MissingVersionTest) {
-  ImageProperties props = LoadImageProperties();
+  ImageProperties props = LoadImageProperties(&fake_system_state_);
   EXPECT_EQ("", props.version);
 }
 
@@ -102,11 +103,12 @@
       "CHROMEOS_RELEASE_BOARD=x86-generic\n"
       "CHROMEOS_RELEASE_TRACK=beta-channel\n"
       "CHROMEOS_AUSERVER=https://www.google.com"));
-  ImageProperties props = LoadImageProperties();
+  ImageProperties props = LoadImageProperties(&fake_system_state_);
   EXPECT_EQ("x86-generic", props.board);
   EXPECT_EQ("dev-channel", props.current_channel);
   EXPECT_EQ("https://www.google.com", props.omaha_url);
-  MutableImageProperties mutable_props = LoadMutableImageProperties();
+  MutableImageProperties mutable_props =
+      LoadMutableImageProperties(&fake_system_state_);
   EXPECT_EQ("beta-channel", mutable_props.target_channel);
 }
 
@@ -123,11 +125,12 @@
       "CHROMEOS_RELEASE_TRACK=stable-channel\n"
       "CHROMEOS_AUSERVER=http://www.google.com"));
   SetLockDown(true);
-  ImageProperties props = LoadImageProperties();
+  ImageProperties props = LoadImageProperties(&fake_system_state_);
   EXPECT_EQ("arm-generic", props.board);
   EXPECT_EQ("dev-channel", props.current_channel);
   EXPECT_EQ("https://www.google.com", props.omaha_url);
-  MutableImageProperties mutable_props = LoadMutableImageProperties();
+  MutableImageProperties mutable_props =
+      LoadMutableImageProperties(&fake_system_state_);
   EXPECT_EQ("stable-channel", mutable_props.target_channel);
 }
 
@@ -138,7 +141,7 @@
                       "CHROMEOS_BOARD_APPID=b\n"
                       "CHROMEOS_CANARY_APPID=c\n"
                       "CHROMEOS_RELEASE_TRACK=stable-channel\n"));
-  ImageProperties props = LoadImageProperties();
+  ImageProperties props = LoadImageProperties(&fake_system_state_);
   EXPECT_EQ("stable-channel", props.current_channel);
   EXPECT_EQ("b", props.product_id);
 }
@@ -150,7 +153,7 @@
                       "CHROMEOS_BOARD_APPID=b\n"
                       "CHROMEOS_CANARY_APPID=c\n"
                       "CHROMEOS_RELEASE_TRACK=canary-channel\n"));
-  ImageProperties props = LoadImageProperties();
+  ImageProperties props = LoadImageProperties(&fake_system_state_);
   EXPECT_EQ("canary-channel", props.current_channel);
   EXPECT_EQ("c", props.canary_product_id);
 }
@@ -161,7 +164,7 @@
                       "CHROMEOS_RELEASE_APPID=r\n"
                       "CHROMEOS_CANARY_APPID=c\n"
                       "CHROMEOS_RELEASE_TRACK=stable-channel\n"));
-  ImageProperties props = LoadImageProperties();
+  ImageProperties props = LoadImageProperties(&fake_system_state_);
   EXPECT_EQ("stable-channel", props.current_channel);
   EXPECT_EQ("r", props.product_id);
 }
diff --git a/init/update-engine.conf b/init/update-engine.conf
index 36c89d7..d3681db 100644
--- a/init/update-engine.conf
+++ b/init/update-engine.conf
@@ -25,7 +25,6 @@
 # The default is 10 failures every 5 seconds, but even if we crash early, it is
 # hard to catch that. So here we set the crash rate as 10 failures every 20
 # seconds which will include the default and more.
-respawn
 respawn limit 10 20
 
 expect fork
@@ -37,17 +36,7 @@
 # Put update_engine process in its own cgroup.
 # Default cpu.shares is 1024.
 post-start script
-  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"
+  cgroup_dir="/sys/fs/cgroup/cpu/${UPSTART_JOB}"
+  mkdir -p "${cgroup_dir}"
+  echo $(status | cut -f 4 -d ' ') > "${cgroup_dir}/tasks"
 end script
diff --git a/libcurl_http_fetcher.cc b/libcurl_http_fetcher.cc
index 1599aac..ce3475d 100644
--- a/libcurl_http_fetcher.cc
+++ b/libcurl_http_fetcher.cc
@@ -16,8 +16,6 @@
 
 #include "update_engine/libcurl_http_fetcher.h"
 
-#include <netinet/in.h>
-#include <resolv.h>
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -28,10 +26,8 @@
 #include <base/format_macros.h>
 #include <base/location.h>
 #include <base/logging.h>
-#include <base/strings/string_split.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
-#include <base/threading/thread_task_runner_handle.h>
 
 #ifdef __ANDROID__
 #include <cutils/qtaguid.h>
@@ -79,11 +75,18 @@
 #ifdef __ANDROID__
   qtaguid_untagSocket(item);
 #endif  // __ANDROID__
-
   LibcurlHttpFetcher* fetcher = static_cast<LibcurlHttpFetcher*>(clientp);
   // Stop watching the socket before closing it.
-  for (size_t t = 0; t < base::size(fetcher->fd_controller_maps_); ++t) {
-    fetcher->fd_controller_maps_[t].erase(item);
+  for (size_t t = 0; t < arraysize(fetcher->fd_task_maps_); ++t) {
+    const auto fd_task_pair = fetcher->fd_task_maps_[t].find(item);
+    if (fd_task_pair != fetcher->fd_task_maps_[t].end()) {
+      if (!MessageLoop::current()->CancelTask(fd_task_pair->second)) {
+        LOG(WARNING) << "Error canceling the watch task "
+                     << fd_task_pair->second << " for "
+                     << (t ? "writing" : "reading") << " the fd " << item;
+      }
+      fetcher->fd_task_maps_[t].erase(item);
+    }
   }
 
   // Documentation for this callback says to return 0 on success or 1 on error.
@@ -266,11 +269,11 @@
     } else if (base::StartsWith(
                    url_, "https://", base::CompareCase::INSENSITIVE_ASCII)) {
       SetCurlOptionsForHttps();
-#ifdef __ANDROID__
+#if !USE_OMAHA
     } else if (base::StartsWith(
                    url_, "file://", base::CompareCase::INSENSITIVE_ASCII)) {
       SetCurlOptionsForFile();
-#endif  // __ANDROID__
+#endif
     } else {
       LOG(ERROR) << "Received invalid URI: " << url_;
       // Lock down to no protocol supported for the transfer.
@@ -302,7 +305,6 @@
   LOG(INFO) << "Setting up curl options for HTTPS";
   CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_SSL_VERIFYPEER, 1), CURLE_OK);
   CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_SSL_VERIFYHOST, 2), CURLE_OK);
-  CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_CAINFO, nullptr), CURLE_OK);
   CHECK_EQ(curl_easy_setopt(
                curl_handle_, CURLOPT_CAPATH, constants::kCACertificatesPath),
            CURLE_OK);
@@ -389,37 +391,6 @@
   extra_headers_[base::ToLowerASCII(header_name)] = header_line;
 }
 
-// Inputs: header_name, header_value
-// Example:
-//   extra_headers_ = { {"foo":"foo: 123"}, {"bar":"bar:"} }
-//   string tmp = "gibberish";
-//   Case 1:
-//     GetHeader("foo", &tmp) -> tmp = "123", return true.
-//   Case 2:
-//     GetHeader("bar", &tmp) -> tmp = "", return true.
-//   Case 3:
-//     GetHeader("moo", &tmp) -> tmp = "", return false.
-bool LibcurlHttpFetcher::GetHeader(const string& header_name,
-                                   string* header_value) const {
-  // Initially clear |header_value| to handle both success and failures without
-  // leaving |header_value| in a unclear state.
-  header_value->clear();
-  auto header_key = base::ToLowerASCII(header_name);
-  auto header_line_itr = extra_headers_.find(header_key);
-  // If the |header_name| was never set, indicate so by returning false.
-  if (header_line_itr == extra_headers_.end())
-    return false;
-  // From |SetHeader()| the check for |header_name| to not include ":" is
-  // verified, so finding the first index of ":" is a safe operation.
-  auto header_line = header_line_itr->second;
-  *header_value = header_line.substr(header_line.find(':') + 1);
-  // The following is neccessary to remove the leading ' ' before the header
-  // value that was place only if |header_value| passed to |SetHeader()| was
-  // a non-empty string.
-  header_value->erase(0, 1);
-  return true;
-}
-
 void LibcurlHttpFetcher::CurlPerformOnce() {
   CHECK(transfer_in_progress_);
   int running_handles = 0;
@@ -435,18 +406,6 @@
     }
   }
 
-  // When retcode is not |CURLM_OK| at this point, libcurl has an internal error
-  // that it is less likely to recover from (libcurl bug, out-of-memory, etc.).
-  // In case of an update check, we send UMA metrics and log the error.
-  if (is_update_check_ &&
-      (retcode == CURLM_OUT_OF_MEMORY || retcode == CURLM_INTERNAL_ERROR)) {
-    auxiliary_error_code_ = ErrorCode::kInternalLibCurlError;
-    LOG(ERROR) << "curl_multi_perform is in an unrecoverable error condition: "
-               << retcode;
-  } else if (retcode != CURLM_OK) {
-    LOG(ERROR) << "curl_multi_perform returns error: " << retcode;
-  }
-
   // If the transfer completes while paused, we should ignore the failure once
   // the fetcher is unpaused.
   if (running_handles == 0 && transfer_paused_ && !ignore_failure_) {
@@ -458,18 +417,6 @@
     // 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.
-    //
-    // 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,
-          base::Bind(&LibcurlHttpFetcher::CurlPerformOnce,
-                     base::Unretained(this)),
-          TimeDelta::FromSeconds(1));
-      return;
-    }
     SetupMessageLoopSources();
     return;
   }
@@ -481,35 +428,13 @@
   if (http_response_code_) {
     LOG(INFO) << "HTTP response code: " << http_response_code_;
     no_network_retry_count_ = 0;
-    unresolved_host_state_machine_.UpdateState(false);
   } else {
     LOG(ERROR) << "Unable to get http response code.";
-    CURLcode curl_code = GetCurlCode();
-    LOG(ERROR) << "Return code for the transfer: " << curl_code;
-    if (curl_code == CURLE_COULDNT_RESOLVE_HOST) {
-      LOG(ERROR) << "libcurl can not resolve host.";
-      unresolved_host_state_machine_.UpdateState(true);
-      auxiliary_error_code_ = ErrorCode::kUnresolvedHostError;
-    }
   }
 
   // we're done!
   CleanUp();
 
-  if (unresolved_host_state_machine_.GetState() ==
-      UnresolvedHostStateMachine::State::kRetry) {
-    // Based on
-    // https://curl.haxx.se/docs/todo.html#updated_DNS_server_while_running,
-    // update_engine process should call res_init() and unconditionally retry.
-    res_init();
-    no_network_max_retries_++;
-    LOG(INFO) << "Will retry after reloading resolv.conf because last attempt "
-                 "failed to resolve host.";
-  } else if (unresolved_host_state_machine_.GetState() ==
-             UnresolvedHostStateMachine::State::kRetriedSuccess) {
-    auxiliary_error_code_ = ErrorCode::kUnresolvedHostRecovered;
-  }
-
   // TODO(petkov): This temporary code tries to deal with the case where the
   // update engine performs an update check while the network is not ready
   // (e.g., right after resume). Longer term, we should check if the network
@@ -690,15 +615,15 @@
 
   // We should iterate through all file descriptors up to libcurl's fd_max or
   // the highest one we're tracking, whichever is larger.
-  for (size_t t = 0; t < base::size(fd_controller_maps_); ++t) {
-    if (!fd_controller_maps_[t].empty())
-      fd_max = max(fd_max, fd_controller_maps_[t].rbegin()->first);
+  for (size_t t = 0; t < arraysize(fd_task_maps_); ++t) {
+    if (!fd_task_maps_[t].empty())
+      fd_max = max(fd_max, fd_task_maps_[t].rbegin()->first);
   }
 
   // For each fd, if we're not tracking it, track it. If we are tracking it, but
   // libcurl doesn't care about it anymore, stop tracking it. After this loop,
-  // there should be exactly as many tasks scheduled in
-  // fd_controller_maps_[0|1] as there are read/write fds that we're tracking.
+  // there should be exactly as many tasks scheduled in fd_task_maps_[0|1] as
+  // there are read/write fds that we're tracking.
   for (int fd = 0; fd <= fd_max; ++fd) {
     // Note that fd_exc is unused in the current version of libcurl so is_exc
     // should always be false.
@@ -707,14 +632,21 @@
         is_exc || (FD_ISSET(fd, &fd_read) != 0),  // track 0 -- read
         is_exc || (FD_ISSET(fd, &fd_write) != 0)  // track 1 -- write
     };
+    MessageLoop::WatchMode watch_modes[2] = {
+        MessageLoop::WatchMode::kWatchRead,
+        MessageLoop::WatchMode::kWatchWrite,
+    };
 
-    for (size_t t = 0; t < base::size(fd_controller_maps_); ++t) {
-      bool tracked =
-          fd_controller_maps_[t].find(fd) != fd_controller_maps_[t].end();
+    for (size_t t = 0; t < arraysize(fd_task_maps_); ++t) {
+      auto fd_task_it = fd_task_maps_[t].find(fd);
+      bool tracked = fd_task_it != fd_task_maps_[t].end();
 
       if (!must_track[t]) {
         // If we have an outstanding io_channel, remove it.
-        fd_controller_maps_[t].erase(fd);
+        if (tracked) {
+          MessageLoop::current()->CancelTask(fd_task_it->second);
+          fd_task_maps_[t].erase(fd_task_it);
+        }
         continue;
       }
 
@@ -723,21 +655,14 @@
         continue;
 
       // Track a new fd.
-      switch (t) {
-        case 0:  // Read
-          fd_controller_maps_[t][fd] =
-              base::FileDescriptorWatcher::WatchReadable(
-                  fd,
-                  base::BindRepeating(&LibcurlHttpFetcher::CurlPerformOnce,
-                                      base::Unretained(this)));
-          break;
-        case 1:  // Write
-          fd_controller_maps_[t][fd] =
-              base::FileDescriptorWatcher::WatchWritable(
-                  fd,
-                  base::BindRepeating(&LibcurlHttpFetcher::CurlPerformOnce,
-                                      base::Unretained(this)));
-      }
+      fd_task_maps_[t][fd] = MessageLoop::current()->WatchFileDescriptor(
+          FROM_HERE,
+          fd,
+          watch_modes[t],
+          true,  // persistent
+          base::Bind(&LibcurlHttpFetcher::CurlPerformOnce,
+                     base::Unretained(this)));
+
       static int io_counter = 0;
       io_counter++;
       if (io_counter % 50 == 0) {
@@ -789,8 +714,15 @@
   MessageLoop::current()->CancelTask(timeout_id_);
   timeout_id_ = MessageLoop::kTaskIdNull;
 
-  for (size_t t = 0; t < base::size(fd_controller_maps_); ++t) {
-    fd_controller_maps_[t].clear();
+  for (size_t t = 0; t < arraysize(fd_task_maps_); ++t) {
+    for (const auto& fd_taks_pair : fd_task_maps_[t]) {
+      if (!MessageLoop::current()->CancelTask(fd_taks_pair.second)) {
+        LOG(WARNING) << "Error canceling the watch task " << fd_taks_pair.second
+                     << " for " << (t ? "writing" : "reading") << " the fd "
+                     << fd_taks_pair.first;
+      }
+    }
+    fd_task_maps_[t].clear();
   }
 
   if (curl_http_headers_) {
@@ -823,66 +755,6 @@
                                CURLINFO_RESPONSE_CODE,
                                &http_response_code) == CURLE_OK) {
     http_response_code_ = static_cast<int>(http_response_code);
-  } else {
-    LOG(ERROR) << "Unable to get http response code from curl_easy_getinfo";
-  }
-}
-
-CURLcode LibcurlHttpFetcher::GetCurlCode() {
-  CURLcode curl_code = CURLE_OK;
-  while (true) {
-    // Repeated calls to |curl_multi_info_read| will return a new struct each
-    // time, until a NULL is returned as a signal that there is no more to get
-    // at this point.
-    int msgs_in_queue;
-    CURLMsg* curl_msg =
-        curl_multi_info_read(curl_multi_handle_, &msgs_in_queue);
-    if (curl_msg == nullptr)
-      break;
-    // When |curl_msg| is |CURLMSG_DONE|, a transfer of an easy handle is done,
-    // and then data contains the return code for this transfer.
-    if (curl_msg->msg == CURLMSG_DONE) {
-      // Make sure |curl_multi_handle_| has one and only one easy handle
-      // |curl_handle_|.
-      CHECK_EQ(curl_handle_, curl_msg->easy_handle);
-      // Transfer return code reference:
-      // https://curl.haxx.se/libcurl/c/libcurl-errors.html
-      curl_code = curl_msg->data.result;
-    }
-  }
-
-  // Gets connection error if exists.
-  long connect_error = 0;  // NOLINT(runtime/int) - curl needs long.
-  CURLcode res =
-      curl_easy_getinfo(curl_handle_, CURLINFO_OS_ERRNO, &connect_error);
-  if (res == CURLE_OK && connect_error) {
-    LOG(ERROR) << "Connect error code from the OS: " << connect_error;
-  }
-
-  return curl_code;
-}
-
-void UnresolvedHostStateMachine::UpdateState(bool failed_to_resolve_host) {
-  switch (state_) {
-    case State::kInit:
-      if (failed_to_resolve_host) {
-        state_ = State::kRetry;
-      }
-      break;
-    case State::kRetry:
-      if (failed_to_resolve_host) {
-        state_ = State::kNotRetry;
-      } else {
-        state_ = State::kRetriedSuccess;
-      }
-      break;
-    case State::kNotRetry:
-      break;
-    case State::kRetriedSuccess:
-      break;
-    default:
-      NOTREACHED();
-      break;
   }
 }
 
diff --git a/libcurl_http_fetcher.h b/libcurl_http_fetcher.h
index 4e91b69..25a2df3 100644
--- a/libcurl_http_fetcher.h
+++ b/libcurl_http_fetcher.h
@@ -24,7 +24,6 @@
 
 #include <curl/curl.h>
 
-#include <base/files/file_descriptor_watcher_posix.h>
 #include <base/logging.h>
 #include <base/macros.h>
 #include <brillo/message_loops/message_loop.h>
@@ -38,48 +37,6 @@
 
 namespace chromeos_update_engine {
 
-// |UnresolvedHostStateMachine| is a representation of internal state machine of
-// |LibcurlHttpFetcher|.
-class UnresolvedHostStateMachine {
- public:
-  UnresolvedHostStateMachine() = default;
-  enum class State {
-    kInit = 0,
-    kRetry = 1,
-    kRetriedSuccess = 2,
-    kNotRetry = 3,
-  };
-
-  State GetState() { return state_; }
-
-  // Updates the following internal state machine:
-  //
-  // |kInit|
-  //   |
-  //   |
-  //   \/
-  // (Try, host Unresolved)
-  //   |
-  //   |
-  //   \/
-  // |kRetry| --> (Retry, host resolved)
-  //   |                                  |
-  //   |                                  |
-  //   \/                                 \/
-  // (Retry, host Unresolved)    |kRetriedSuccess|
-  //   |
-  //   |
-  //   \/
-  // |kNotRetry|
-  //
-  void UpdateState(bool failed_to_resolve_host);
-
- private:
-  State state_ = {State::kInit};
-
-  DISALLOW_COPY_AND_ASSIGN(UnresolvedHostStateMachine);
-};
-
 class LibcurlHttpFetcher : public HttpFetcher {
  public:
   LibcurlHttpFetcher(ProxyResolver* proxy_resolver,
@@ -104,9 +61,6 @@
   void SetHeader(const std::string& header_name,
                  const std::string& header_value) override;
 
-  bool GetHeader(const std::string& header_name,
-                 std::string* header_value) const override;
-
   // Suspend the transfer by calling curl_easy_pause(CURLPAUSE_ALL).
   void Pause() override;
 
@@ -131,8 +85,6 @@
     no_network_max_retries_ = retries;
   }
 
-  int get_no_network_max_retries() { return no_network_max_retries_; }
-
   void set_server_to_check(ServerToCheck server_to_check) {
     server_to_check_ = server_to_check;
   }
@@ -154,13 +106,7 @@
     max_retry_count_ = max_retry_count;
   }
 
-  void set_is_update_check(bool is_update_check) {
-    is_update_check_ = is_update_check;
-  }
-
  private:
-  FRIEND_TEST(LibcurlHttpFetcherTest, HostResolvedTest);
-
   // libcurl's CURLOPT_CLOSESOCKETFUNCTION callback function. Called when
   // closing a socket created with the CURLOPT_OPENSOCKETFUNCTION callback.
   static int LibcurlCloseSocketCallback(void* clientp, curl_socket_t item);
@@ -170,10 +116,7 @@
   void ProxiesResolved();
 
   // Asks libcurl for the http response code and stores it in the object.
-  virtual void GetHttpResponseCode();
-
-  // Returns the last |CURLcode|.
-  CURLcode GetCurlCode();
+  void GetHttpResponseCode();
 
   // Checks whether stored HTTP response is within the success range.
   inline bool IsHttpResponseSuccess() {
@@ -218,7 +161,7 @@
   }
 
   // Cleans up the following if they are non-null:
-  // curl(m) handles, fd_controller_maps_(fd_task_maps_), timeout_id_.
+  // curl(m) handles, fd_task_maps_, timeout_id_.
   void CleanUp();
 
   // Force terminate the transfer. This will invoke the delegate's (if any)
@@ -255,8 +198,7 @@
   // the message loop. libcurl may open/close descriptors and switch their
   // directions so maintain two separate lists so that watch conditions can be
   // set appropriately.
-  std::map<int, std::unique_ptr<base::FileDescriptorWatcher::Controller>>
-      fd_controller_maps_[2];
+  std::map<int, brillo::MessageLoop::TaskId> fd_task_maps_[2];
 
   // The TaskId of the timer we're waiting on. kTaskIdNull if we are not waiting
   // on it.
@@ -323,12 +265,6 @@
   // ServerToCheck::kNone.
   ServerToCheck server_to_check_{ServerToCheck::kNone};
 
-  // True if this object is for update check.
-  bool is_update_check_{false};
-
-  // Internal state machine.
-  UnresolvedHostStateMachine unresolved_host_state_machine_;
-
   int low_speed_limit_bps_{kDownloadLowSpeedLimitBps};
   int low_speed_time_seconds_{kDownloadLowSpeedTimeSeconds};
   int connect_timeout_seconds_{kDownloadConnectTimeoutSeconds};
diff --git a/libcurl_http_fetcher_unittest.cc b/libcurl_http_fetcher_unittest.cc
deleted file mode 100644
index 5d67570..0000000
--- a/libcurl_http_fetcher_unittest.cc
+++ /dev/null
@@ -1,205 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include "update_engine/libcurl_http_fetcher.h"
-
-#include <string>
-
-#include <brillo/message_loops/fake_message_loop.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include "update_engine/common/fake_hardware.h"
-#include "update_engine/common/mock_proxy_resolver.h"
-#include "update_engine/mock_libcurl_http_fetcher.h"
-
-using std::string;
-
-namespace chromeos_update_engine {
-
-namespace {
-constexpr char kHeaderName[] = "X-Goog-Test-Header";
-}
-
-class LibcurlHttpFetcherTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    loop_.SetAsCurrent();
-    fake_hardware_.SetIsOfficialBuild(true);
-    fake_hardware_.SetIsOOBEEnabled(false);
-  }
-
-  brillo::FakeMessageLoop loop_{nullptr};
-  FakeHardware fake_hardware_;
-  MockLibcurlHttpFetcher libcurl_fetcher_{nullptr, &fake_hardware_};
-  UnresolvedHostStateMachine state_machine_;
-};
-
-TEST_F(LibcurlHttpFetcherTest, GetEmptyHeaderValueTest) {
-  const string header_value = "";
-  string actual_header_value;
-  libcurl_fetcher_.SetHeader(kHeaderName, header_value);
-  EXPECT_TRUE(libcurl_fetcher_.GetHeader(kHeaderName, &actual_header_value));
-  EXPECT_EQ("", actual_header_value);
-}
-
-TEST_F(LibcurlHttpFetcherTest, GetHeaderTest) {
-  const string header_value = "This-is-value 123";
-  string actual_header_value;
-  libcurl_fetcher_.SetHeader(kHeaderName, header_value);
-  EXPECT_TRUE(libcurl_fetcher_.GetHeader(kHeaderName, &actual_header_value));
-  EXPECT_EQ(header_value, actual_header_value);
-}
-
-TEST_F(LibcurlHttpFetcherTest, GetNonExistentHeaderValueTest) {
-  string actual_header_value;
-  // Skip |SetHeaader()| call.
-  EXPECT_FALSE(libcurl_fetcher_.GetHeader(kHeaderName, &actual_header_value));
-  // Even after a failed |GetHeaderValue()|, enforce that the passed pointer to
-  // modifiable string was cleared to be empty.
-  EXPECT_EQ("", actual_header_value);
-}
-
-TEST_F(LibcurlHttpFetcherTest, GetHeaderEdgeCaseTest) {
-  const string header_value = "\a\b\t\v\f\r\\ edge:-case: \a\b\t\v\f\r\\";
-  string actual_header_value;
-  libcurl_fetcher_.SetHeader(kHeaderName, header_value);
-  EXPECT_TRUE(libcurl_fetcher_.GetHeader(kHeaderName, &actual_header_value));
-  EXPECT_EQ(header_value, actual_header_value);
-}
-
-TEST_F(LibcurlHttpFetcherTest, InvalidURLTest) {
-  int no_network_max_retries = 1;
-  libcurl_fetcher_.set_no_network_max_retries(no_network_max_retries);
-
-  libcurl_fetcher_.BeginTransfer("not-a-URL");
-  while (loop_.PendingTasks()) {
-    loop_.RunOnce(true);
-  }
-
-  EXPECT_EQ(libcurl_fetcher_.get_no_network_max_retries(),
-            no_network_max_retries);
-}
-
-TEST_F(LibcurlHttpFetcherTest, CouldNotResolveHostTest) {
-  int no_network_max_retries = 1;
-  libcurl_fetcher_.set_no_network_max_retries(no_network_max_retries);
-
-  libcurl_fetcher_.BeginTransfer("https://An-uNres0lvable-uRl.invalid");
-
-  // 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.
-  while (loop_.PendingTasks() &&
-         libcurl_fetcher_.GetAuxiliaryErrorCode() == ErrorCode::kSuccess) {
-    loop_.RunOnce(true);
-  }
-
-  EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(),
-            ErrorCode::kUnresolvedHostError);
-
-  while (loop_.PendingTasks()) {
-    loop_.RunOnce(true);
-  }
-  // The auxilary error code should've have been changed.
-  EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(),
-            ErrorCode::kUnresolvedHostError);
-
-  // If libcurl fails to resolve the name, we call res_init() to reload
-  // resolv.conf and retry exactly once more. See crbug.com/982813 for details.
-  EXPECT_EQ(libcurl_fetcher_.get_no_network_max_retries(),
-            no_network_max_retries + 1);
-}
-
-TEST_F(LibcurlHttpFetcherTest, HostResolvedTest) {
-  int no_network_max_retries = 2;
-  libcurl_fetcher_.set_no_network_max_retries(no_network_max_retries);
-
-  // This test actually sends request to internet but according to
-  // https://tools.ietf.org/html/rfc2606#section-2, .invalid domain names are
-  // reserved and sure to be invalid. Ideally we should mock libcurl or
-  // reorganize LibcurlHttpFetcher so the part that sends request can be mocked
-  // easily.
-  // TODO(xiaochu) Refactor LibcurlHttpFetcher (and its relates) so it's
-  // easier to mock the part that depends on internet connectivity.
-  libcurl_fetcher_.BeginTransfer("https://An-uNres0lvable-uRl.invalid");
-
-  // 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.
-  while (loop_.PendingTasks() &&
-         libcurl_fetcher_.GetAuxiliaryErrorCode() == ErrorCode::kSuccess) {
-    loop_.RunOnce(true);
-  }
-
-  EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(),
-            ErrorCode::kUnresolvedHostError);
-
-  // The second time, it will resolve, with error code 200 but we set the
-  // download size be smaller than the transfer size so it will retry again.
-  EXPECT_CALL(libcurl_fetcher_, GetHttpResponseCode())
-      .WillOnce(testing::Invoke(
-          [this]() { libcurl_fetcher_.http_response_code_ = 200; }))
-      .WillRepeatedly(testing::Invoke(
-          [this]() { libcurl_fetcher_.http_response_code_ = 0; }));
-  libcurl_fetcher_.transfer_size_ = 10;
-
-  // 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.
-  while (loop_.PendingTasks() && libcurl_fetcher_.GetAuxiliaryErrorCode() ==
-                                     ErrorCode::kUnresolvedHostError) {
-    loop_.RunOnce(true);
-  }
-
-  EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(),
-            ErrorCode::kUnresolvedHostRecovered);
-
-  while (loop_.PendingTasks()) {
-    loop_.RunOnce(true);
-  }
-  // The auxilary error code should not have been changed.
-  EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(),
-            ErrorCode::kUnresolvedHostRecovered);
-
-  // If libcurl fails to resolve the name, we call res_init() to reload
-  // resolv.conf and retry exactly once more. See crbug.com/982813 for details.
-  EXPECT_EQ(libcurl_fetcher_.get_no_network_max_retries(),
-            no_network_max_retries + 1);
-}
-
-TEST_F(LibcurlHttpFetcherTest, HttpFetcherStateMachineRetryFailedTest) {
-  state_machine_.UpdateState(true);
-  state_machine_.UpdateState(true);
-  EXPECT_EQ(state_machine_.GetState(),
-            UnresolvedHostStateMachine::State::kNotRetry);
-}
-
-TEST_F(LibcurlHttpFetcherTest, HttpFetcherStateMachineRetrySucceedTest) {
-  state_machine_.UpdateState(true);
-  state_machine_.UpdateState(false);
-  EXPECT_EQ(state_machine_.GetState(),
-            UnresolvedHostStateMachine::State::kRetriedSuccess);
-}
-
-TEST_F(LibcurlHttpFetcherTest, HttpFetcherStateMachineNoRetryTest) {
-  state_machine_.UpdateState(false);
-  state_machine_.UpdateState(false);
-  EXPECT_EQ(state_machine_.GetState(),
-            UnresolvedHostStateMachine::State::kInit);
-}
-
-}  // namespace chromeos_update_engine
diff --git a/client-headers/libupdate_engine-client-test.pc.in b/libupdate_engine-client-test.pc.in
similarity index 100%
rename from client-headers/libupdate_engine-client-test.pc.in
rename to libupdate_engine-client-test.pc.in
diff --git a/client-headers/libupdate_engine-client.pc.in b/libupdate_engine-client.pc.in
similarity index 100%
rename from client-headers/libupdate_engine-client.pc.in
rename to libupdate_engine-client.pc.in
diff --git a/cros/logging.cc b/logging.cc
similarity index 93%
rename from cros/logging.cc
rename to logging.cc
index 8b6c556..6320e36 100644
--- a/cros/logging.cc
+++ b/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;
 
@@ -46,7 +46,7 @@
     base::ReplaceFile(
         base::FilePath(symlink_path), base::FilePath(log_path), nullptr);
   }
-  base::DeletePathRecursively(base::FilePath(symlink_path));
+  base::DeleteFile(base::FilePath(symlink_path), true);
   if (symlink(log_path.c_str(), symlink_path.c_str()) == -1) {
     PLOG(ERROR) << "Unable to create symlink " << symlink_path
                 << " pointing at " << log_path;
@@ -79,11 +79,7 @@
   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/common/logging.h b/logging.h
similarity index 100%
rename from common/logging.h
rename to logging.h
diff --git a/aosp/logging_android.cc b/logging_android.cc
similarity index 94%
rename from aosp/logging_android.cc
rename to logging_android.cc
index 5ccf7bc..0219075 100644
--- a/aosp/logging_android.cc
+++ b/logging_android.cc
@@ -240,16 +240,9 @@
   std::string_view sv = str_newline;
   ignore_result(android::base::ConsumeSuffix(&sv, "\n"));
   std::string str(sv.data(), sv.size());
-
-  if (priority == ANDROID_LOG_FATAL) {
-    // Abort the program for priority FATAL. __android_log_assert will log the
-    // message to stderr and CombinedLogger.
-    __android_log_assert(nullptr, nullptr, "%s", str.c_str());
-  } else {
-    // This will eventually be redirected to CombinedLogger.
-    // Use nullptr as tag so that liblog infers log tag from getprogname().
-    __android_log_write(priority, nullptr /* tag */, str.c_str());
-  }
+  // This will eventually be redirected to CombinedLogger.
+  // Use nullptr as tag so that liblog infers log tag from getprogname().
+  __android_log_write(priority, nullptr /* tag */, str.c_str());
   return true;
 }
 
diff --git a/main.cc b/main.cc
index a23a08b..4377a15 100644
--- a/main.cc
+++ b/main.cc
@@ -23,11 +23,10 @@
 #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.h"
+#include "update_engine/logging.h"
 
 using std::string;
 
@@ -64,8 +63,8 @@
   // Done _after_ log file creation.
   umask(S_IRWXG | S_IRWXO);
 
-  auto daemon = chromeos_update_engine::DaemonBase::CreateInstance();
-  int exit_code = daemon->Run();
+  chromeos_update_engine::UpdateEngineDaemon update_engine_daemon;
+  int exit_code = update_engine_daemon.Run();
 
   chromeos_update_engine::Subprocess::Get().FlushBufferedLogsAtExit();
 
diff --git a/common/metrics_constants.h b/metrics_constants.h
similarity index 94%
rename from common/metrics_constants.h
rename to metrics_constants.h
index b7633b9..137143a 100644
--- a/common/metrics_constants.h
+++ b/metrics_constants.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_COMMON_METRICS_CONSTANTS_H_
-#define UPDATE_ENGINE_COMMON_METRICS_CONSTANTS_H_
+#ifndef UPDATE_ENGINE_METRICS_CONSTANTS_H_
+#define UPDATE_ENGINE_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
@@ -119,12 +119,12 @@
   kUnknown = 0,           // Unknown.
   kEthernet = 1,          // Ethernet.
   kWifi = 2,              // Wireless.
+  kWimax = 3,             // WiMax.
+  kBluetooth = 4,         // Bluetooth.
   kCellular = 5,          // Cellular.
   kTetheredEthernet = 6,  // Tethered (Ethernet).
   kTetheredWifi = 7,      // Tethered (Wifi).
   kDisconnected = 8,      // Disconnected.
-  // deprecated: kWimax = 3,
-  // deprecated: kBluetooth = 4,
 
   kNumConstants,
   kUnset = -1
@@ -144,4 +144,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_COMMON_METRICS_CONSTANTS_H_
+#endif  // UPDATE_ENGINE_METRICS_CONSTANTS_H_
diff --git a/aosp/metrics_reporter_android.cc b/metrics_reporter_android.cc
similarity index 80%
rename from aosp/metrics_reporter_android.cc
rename to metrics_reporter_android.cc
index a324fab..d8fa6e5 100644
--- a/aosp/metrics_reporter_android.cc
+++ b/metrics_reporter_android.cc
@@ -14,12 +14,10 @@
 // limitations under the License.
 //
 
-#include "update_engine/aosp/metrics_reporter_android.h"
+#include "update_engine/metrics_reporter_android.h"
 
 #include <stdint.h>
 
-#include <algorithm>
-#include <any>
 #include <memory>
 #include <string>
 
@@ -32,7 +30,6 @@
 #include <statslog.h>
 
 #include "update_engine/common/constants.h"
-#include "update_engine/payload_consumer/install_plan.h"
 
 using android::fs_mgr::GetPartitionGroupName;
 using android::fs_mgr::LpMetadata;
@@ -51,38 +48,20 @@
 int32_t GetStatsdEnumValue(int32_t value) {
   return kMetricsReporterEnumOffset + value;
 }
-
-bool IsHashTreeEnabled(
-    const chromeos_update_engine::InstallPlan* install_plan) {
-  return std::any_of(
-      install_plan->partitions.begin(),
-      install_plan->partitions.end(),
-      [](const auto& partition) { return partition.hash_tree_size > 0; });
-}
-
-bool IsFECEnabled(const chromeos_update_engine::InstallPlan* install_plan) {
-  return std::any_of(
-      install_plan->partitions.begin(),
-      install_plan->partitions.end(),
-      [](const auto& partition) { return partition.fec_size > 0; });
-}
-
 }  // namespace
 
 namespace chromeos_update_engine {
 
 namespace metrics {
 
-std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter(
-    DynamicPartitionControlInterface* dynamic_partition_control,
-    const InstallPlan* install_plan) {
-  return std::make_unique<MetricsReporterAndroid>(dynamic_partition_control,
-                                                  install_plan);
+std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter() {
+  return std::make_unique<MetricsReporterAndroid>();
 }
 
 }  // namespace metrics
 
 void MetricsReporterAndroid::ReportUpdateAttemptMetrics(
+    SystemState* /* system_state */,
     int attempt_number,
     PayloadType payload_type,
     base::TimeDelta duration,
@@ -127,11 +106,6 @@
     }
   }
 
-  bool vab_compression_enabled = android::base::GetBoolProperty(
-      "ro.virtual_ab.compression.enabled", false);
-  bool vab_compression_used =
-      dynamic_partition_control_->UpdateUsesSnapshotCompression();
-
   android::util::stats_write(
       android::util::UPDATE_ENGINE_UPDATE_ATTEMPT_REPORTED,
       attempt_number,
@@ -144,9 +118,7 @@
       android::base::GetProperty("ro.build.fingerprint", "").c_str(),
       super_partition_size_bytes,
       slot_size_bytes,
-      super_free_space,
-      vab_compression_enabled,
-      vab_compression_used);
+      super_free_space);
 }
 
 void MetricsReporterAndroid::ReportUpdateAttemptDownloadMetrics(
@@ -185,9 +157,7 @@
       static_cast<int32_t>(total_bytes_downloaded),
       static_cast<int32_t>(download_overhead_percentage),
       static_cast<int32_t>(total_duration.InMinutes()),
-      static_cast<int32_t>(reboot_count),
-      IsHashTreeEnabled(install_plan_),
-      IsFECEnabled(install_plan_));
+      static_cast<int32_t>(reboot_count));
 }
 
 void MetricsReporterAndroid::ReportAbnormallyTerminatedUpdateAttemptMetrics() {
diff --git a/aosp/metrics_reporter_android.h b/metrics_reporter_android.h
similarity index 81%
rename from aosp/metrics_reporter_android.h
rename to metrics_reporter_android.h
index aeb579a..e320c12 100644
--- a/aosp/metrics_reporter_android.h
+++ b/metrics_reporter_android.h
@@ -14,28 +14,25 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_AOSP_METRICS_REPORTER_ANDROID_H_
-#define UPDATE_ENGINE_AOSP_METRICS_REPORTER_ANDROID_H_
+#ifndef UPDATE_ENGINE_METRICS_REPORTER_ANDROID_H_
+#define UPDATE_ENGINE_METRICS_REPORTER_ANDROID_H_
 
 #include <string>
 
 #include "update_engine/common/error_code.h"
-#include "update_engine/common/metrics_constants.h"
-#include "update_engine/common/metrics_reporter_interface.h"
-#include "update_engine/payload_consumer/install_plan.h"
+#include "update_engine/metrics_constants.h"
+#include "update_engine/metrics_reporter_interface.h"
 
 namespace chromeos_update_engine {
 
 class MetricsReporterAndroid : public MetricsReporterInterface {
  public:
-  explicit MetricsReporterAndroid(
-      DynamicPartitionControlInterface* dynamic_partition_control,
-      const InstallPlan* install_plan)
-      : dynamic_partition_control_(dynamic_partition_control),
-        install_plan_(install_plan) {}
+  MetricsReporterAndroid() = default;
 
   ~MetricsReporterAndroid() override = default;
 
+  void Initialize() override {}
+
   void ReportRollbackMetrics(metrics::RollbackResult result) override {}
 
   void ReportEnterpriseRollbackMetrics(
@@ -44,11 +41,13 @@
   void ReportDailyMetrics(base::TimeDelta os_age) override {}
 
   void ReportUpdateCheckMetrics(
+      SystemState* system_state,
       metrics::CheckResult result,
       metrics::CheckReaction reaction,
       metrics::DownloadErrorCode download_error_code) override {}
 
-  void ReportUpdateAttemptMetrics(int attempt_number,
+  void ReportUpdateAttemptMetrics(SystemState* system_state,
+                                  int attempt_number,
                                   PayloadType payload_type,
                                   base::TimeDelta duration,
                                   base::TimeDelta duration_uptime,
@@ -96,12 +95,9 @@
       bool has_time_restriction_policy, int time_to_update_days) override {}
 
  private:
-  DynamicPartitionControlInterface* dynamic_partition_control_{};
-  const InstallPlan* install_plan_{};
-
   DISALLOW_COPY_AND_ASSIGN(MetricsReporterAndroid);
 };
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_AOSP_METRICS_REPORTER_ANDROID_H_
+#endif  // UPDATE_ENGINE_METRICS_REPORTER_ANDROID_H_
diff --git a/common/metrics_reporter_interface.h b/metrics_reporter_interface.h
similarity index 94%
rename from common/metrics_reporter_interface.h
rename to metrics_reporter_interface.h
index a7a91a5..fce8bfd 100644
--- a/common/metrics_reporter_interface.h
+++ b/metrics_reporter_interface.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_COMMON_METRICS_REPORTER_INTERFACE_H_
-#define UPDATE_ENGINE_COMMON_METRICS_REPORTER_INTERFACE_H_
+#ifndef UPDATE_ENGINE_METRICS_REPORTER_INTERFACE_H_
+#define UPDATE_ENGINE_METRICS_REPORTER_INTERFACE_H_
 
 #include <memory>
 #include <string>
@@ -23,20 +23,27 @@
 #include <base/time/time.h>
 
 #include "update_engine/common/constants.h"
-#include "update_engine/common/dynamic_partition_control_interface.h"
 #include "update_engine/common/error_code.h"
-#include "update_engine/common/metrics_constants.h"
-#include "update_engine/payload_consumer/install_plan.h"
+#include "update_engine/metrics_constants.h"
+#include "update_engine/system_state.h"
 
 namespace chromeos_update_engine {
 
 enum class ServerToCheck;
 enum class CertificateCheckResult;
 
+namespace metrics {
+
+std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter();
+
+}  // namespace metrics
+
 class MetricsReporterInterface {
  public:
   virtual ~MetricsReporterInterface() = default;
 
+  virtual void Initialize() = 0;
+
   // Helper function to report metrics related to user-initiated rollback. The
   // following metrics are reported:
   //
@@ -87,6 +94,7 @@
   // if it's set, |kMetricCheckRollbackTargetVersion| reports the same, but only
   // if rollback is also allowed using enterprise policy.
   virtual void ReportUpdateCheckMetrics(
+      SystemState* system_state,
       metrics::CheckResult result,
       metrics::CheckReaction reaction,
       metrics::DownloadErrorCode download_error_code) = 0;
@@ -114,7 +122,8 @@
   // |kMetricAttemptTimeSinceLastAttemptUptimeMinutes| metrics are
   // automatically calculated and reported by maintaining persistent and
   // process-local state variables.
-  virtual void ReportUpdateAttemptMetrics(int attempt_number,
+  virtual void ReportUpdateAttemptMetrics(SystemState* system_state,
+                                          int attempt_number,
                                           PayloadType payload_type,
                                           base::TimeDelta duration,
                                           base::TimeDelta duration_uptime,
@@ -235,14 +244,6 @@
       bool has_time_restriction_policy, int time_to_update_days) = 0;
 };
 
-namespace metrics {
-
-std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter(
-    DynamicPartitionControlInterface* dynamic_partition_control,
-    const InstallPlan* install_plan);
-
-}  // namespace metrics
-
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_COMMON_METRICS_REPORTER_INTERFACE_H_
+#endif  // UPDATE_ENGINE_METRICS_REPORTER_INTERFACE_H_
diff --git a/cros/metrics_reporter_omaha.cc b/metrics_reporter_omaha.cc
similarity index 80%
rename from cros/metrics_reporter_omaha.cc
rename to metrics_reporter_omaha.cc
index 69cdb19..14819d8 100644
--- a/cros/metrics_reporter_omaha.cc
+++ b/metrics_reporter_omaha.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/metrics_reporter_omaha.h"
+#include "update_engine/metrics_reporter_omaha.h"
 
 #include <memory>
 
@@ -22,18 +22,18 @@
 #include <base/strings/string_number_conversions.h>
 #include <metrics/metrics_library.h>
 
+#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.
@@ -135,8 +135,7 @@
     "UpdateEngine.InstallDateProvisioningSource";
 const char kMetricTimeToRebootMinutes[] = "UpdateEngine.TimeToRebootMinutes";
 
-std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter(
-    DynamicPartitionControlInterface* dynamic_partition_control) {
+std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter() {
   return std::make_unique<MetricsReporterOmaha>();
 }
 
@@ -145,8 +144,14 @@
 MetricsReporterOmaha::MetricsReporterOmaha()
     : metrics_lib_(new MetricsLibrary()) {}
 
+void MetricsReporterOmaha::Initialize() {
+  metrics_lib_->Init();
+}
+
 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
@@ -155,6 +160,7 @@
 }
 
 void MetricsReporterOmaha::ReportUpdateCheckMetrics(
+    SystemState* system_state,
     metrics::CheckResult result,
     metrics::CheckReaction reaction,
     metrics::DownloadErrorCode download_error_code) {
@@ -166,24 +172,31 @@
     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 (WallclockDurationHelper(kPrefsMetricsCheckLastReportingTime,
-                              &time_since_last)) {
+  if (metrics_utils::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
@@ -193,8 +206,11 @@
 
   base::TimeDelta uptime_since_last;
   static int64_t uptime_since_last_storage = 0;
-  if (MonotonicDurationHelper(&uptime_since_last_storage, &uptime_since_last)) {
+  if (metrics_utils::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
@@ -203,15 +219,19 @@
   }
 
   // First section of target version specified for the update.
-  if (SystemState::Get()->request_params()) {
+  if (system_state && system_state->request_params()) {
     string target_version =
-        SystemState::Get()->request_params()->target_version_prefix();
+        system_state->request_params()->target_version_prefix();
     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 (SystemState::Get()->request_params()->rollback_allowed()) {
+      if (system_state->request_params()->rollback_allowed()) {
         metric = metrics::kMetricCheckRollbackTargetVersion;
+        LOG(INFO) << "Sending " << value << " for metric " << metric
+                  << " (sparse)";
         metrics_lib_->SendSparseToUMA(metric, value);
       }
     }
@@ -223,6 +243,8 @@
   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),
@@ -230,6 +252,7 @@
 }
 
 void MetricsReporterOmaha::ReportUpdateAttemptMetrics(
+    SystemState* system_state,
     int attempt_number,
     PayloadType payload_type,
     base::TimeDelta duration,
@@ -238,6 +261,7 @@
     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
@@ -245,9 +269,13 @@
                           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
@@ -255,6 +283,8 @@
                           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
@@ -263,6 +293,7 @@
 
   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
@@ -270,6 +301,8 @@
                           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),
@@ -280,9 +313,13 @@
   }
 
   base::TimeDelta time_since_last;
-  if (WallclockDurationHelper(kPrefsMetricsAttemptLastReportingTime,
-                              &time_since_last)) {
+  if (metrics_utils::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
@@ -292,8 +329,11 @@
 
   static int64_t uptime_since_last_storage = 0;
   base::TimeDelta uptime_since_last;
-  if (MonotonicDurationHelper(&uptime_since_last_storage, &uptime_since_last)) {
+  if (metrics_utils::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
@@ -311,6 +351,8 @@
   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
@@ -319,6 +361,8 @@
 
   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
@@ -326,15 +370,20 @@
                           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),
@@ -354,6 +403,7 @@
     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
@@ -383,6 +433,7 @@
     }
 
     if (mbs > 0) {
+      LOG(INFO) << "Uploading " << mbs << " (MiBs) for metric " << metric;
       metrics_lib_->SendToUMA(metric,
                               mbs,
                               0,     // min: 0 MiB
@@ -392,6 +443,8 @@
   }
 
   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
@@ -399,6 +452,8 @@
                           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
@@ -406,6 +461,8 @@
                           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
@@ -413,6 +470,8 @@
                           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
@@ -420,6 +479,8 @@
                           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
@@ -427,6 +488,8 @@
                           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
@@ -435,6 +498,8 @@
 
   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,
@@ -442,8 +507,11 @@
                           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
@@ -455,6 +523,7 @@
     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));
 }
@@ -465,6 +534,7 @@
   string metric = metrics::kMetricEnterpriseRollbackSuccess;
   if (!success)
     metric = metrics::kMetricEnterpriseRollbackFailure;
+  LOG(INFO) << "Sending " << value << " for metric " << metric;
   metrics_lib_->SendSparseToUMA(metric, value);
 }
 
@@ -481,6 +551,8 @@
     case ServerToCheck::kNone:
       return;
   }
+  LOG(INFO) << "Uploading " << static_cast<int>(result) << " for metric "
+            << metric;
   metrics_lib_->SendEnumToUMA(
       metric,
       static_cast<int>(result),
@@ -494,6 +566,9 @@
                           1,   // min value
                           50,  // max value
                           kNumDefaultUmaBuckets);
+
+  LOG(INFO) << "Uploading " << target_attempt << " (count) for metric "
+            << metric;
 }
 
 void MetricsReporterOmaha::ReportTimeToReboot(int time_to_reboot_minutes) {
@@ -503,6 +578,9 @@
                           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,
@@ -514,6 +592,7 @@
 
 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));
@@ -525,14 +604,18 @@
     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);
 }
 
@@ -542,6 +625,7 @@
       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,
@@ -550,44 +634,4 @@
                           50);     // num_buckets
 }
 
-bool MetricsReporterOmaha::WallclockDurationHelper(
-    const std::string& state_variable_key,
-    TimeDelta* out_duration) {
-  bool ret = false;
-  Time now = SystemState::Get()->clock()->GetWallclockTime();
-  int64_t stored_value;
-  if (SystemState::Get()->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 (!SystemState::Get()->prefs()->SetInt64(state_variable_key,
-                                             now.ToInternalValue())) {
-    LOG(ERROR) << "Error storing time-stamp in " << state_variable_key;
-  }
-
-  return ret;
-}
-
-bool MetricsReporterOmaha::MonotonicDurationHelper(int64_t* storage,
-                                                   TimeDelta* out_duration) {
-  bool ret = false;
-  Time now = SystemState::Get()->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/cros/metrics_reporter_omaha.h b/metrics_reporter_omaha.h
similarity index 80%
rename from cros/metrics_reporter_omaha.h
rename to metrics_reporter_omaha.h
index b6ffcce..5680dec 100644
--- a/cros/metrics_reporter_omaha.h
+++ b/metrics_reporter_omaha.h
@@ -14,21 +14,21 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_METRICS_REPORTER_OMAHA_H_
-#define UPDATE_ENGINE_CROS_METRICS_REPORTER_OMAHA_H_
+#ifndef UPDATE_ENGINE_METRICS_REPORTER_OMAHA_H_
+#define UPDATE_ENGINE_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/common/metrics_constants.h"
-#include "update_engine/common/metrics_reporter_interface.h"
+#include "update_engine/metrics_constants.h"
+#include "update_engine/metrics_reporter_interface.h"
+#include "update_engine/system_state.h"
 
 namespace chromeos_update_engine {
 
@@ -108,6 +108,8 @@
 
   ~MetricsReporterOmaha() override = default;
 
+  void Initialize() override;
+
   void ReportRollbackMetrics(metrics::RollbackResult result) override;
 
   void ReportEnterpriseRollbackMetrics(
@@ -116,11 +118,13 @@
   void ReportDailyMetrics(base::TimeDelta os_age) override;
 
   void ReportUpdateCheckMetrics(
+      SystemState* system_state,
       metrics::CheckResult result,
       metrics::CheckReaction reaction,
       metrics::DownloadErrorCode download_error_code) override;
 
-  void ReportUpdateAttemptMetrics(int attempt_number,
+  void ReportUpdateAttemptMetrics(SystemState* system_state,
+                                  int attempt_number,
                                   PayloadType payload_type,
                                   base::TimeDelta duration,
                                   base::TimeDelta duration_uptime,
@@ -169,28 +173,6 @@
 
  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(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(int64_t* storage, base::TimeDelta* out_duration);
 
   std::unique_ptr<MetricsLibraryInterface> metrics_lib_;
 
@@ -199,4 +181,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_METRICS_REPORTER_OMAHA_H_
+#endif  // UPDATE_ENGINE_METRICS_REPORTER_OMAHA_H_
diff --git a/cros/metrics_reporter_omaha_unittest.cc b/metrics_reporter_omaha_unittest.cc
similarity index 76%
rename from cros/metrics_reporter_omaha_unittest.cc
rename to metrics_reporter_omaha_unittest.cc
index cdc44cd..545d02f 100644
--- a/cros/metrics_reporter_omaha_unittest.cc
+++ b/metrics_reporter_omaha_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/metrics_reporter_omaha.h"
+#include "update_engine/metrics_reporter_omaha.h"
 
 #include <memory>
 #include <string>
@@ -25,7 +25,8 @@
 #include <metrics/metrics_library_mock.h>
 
 #include "update_engine/common/fake_clock.h"
-#include "update_engine/cros/fake_system_state.h"
+#include "update_engine/common/fake_prefs.h"
+#include "update_engine/fake_system_state.h"
 
 using base::TimeDelta;
 using testing::_;
@@ -39,16 +40,12 @@
 
   // Reset the metrics_lib_ to a mock library.
   void SetUp() override {
-    FakeSystemState::CreateInstance();
-    fake_clock_ = FakeSystemState::Get()->fake_clock();
     mock_metrics_lib_ = new testing::NiceMock<MetricsLibraryMock>();
     reporter_.metrics_lib_.reset(mock_metrics_lib_);
   }
 
   testing::NiceMock<MetricsLibraryMock>* mock_metrics_lib_;
   MetricsReporterOmaha reporter_;
-
-  FakeClock* fake_clock_;
 };
 
 TEST_F(MetricsReporterOmahaTest, ReportDailyMetrics) {
@@ -61,9 +58,15 @@
 }
 
 TEST_F(MetricsReporterOmahaTest, ReportUpdateCheckMetrics) {
+  FakeSystemState fake_system_state;
+  FakeClock fake_clock;
+  FakePrefs fake_prefs;
+
   // We need to execute the report twice to test the time since last report.
-  fake_clock_->SetWallclockTime(base::Time::FromInternalValue(1000000));
-  fake_clock_->SetMonotonicTime(base::Time::FromInternalValue(1000000));
+  fake_system_state.set_clock(&fake_clock);
+  fake_system_state.set_prefs(&fake_prefs);
+  fake_clock.SetWallclockTime(base::Time::FromInternalValue(1000000));
+  fake_clock.SetMonotonicTime(base::Time::FromInternalValue(1000000));
 
   metrics::CheckResult result = metrics::CheckResult::kUpdateAvailable;
   metrics::CheckReaction reaction = metrics::CheckReaction::kIgnored;
@@ -101,20 +104,24 @@
           metrics::kMetricCheckTimeSinceLastCheckUptimeMinutes, 1, _, _, _))
       .Times(1);
 
-  reporter_.ReportUpdateCheckMetrics(result, reaction, error_code);
+  reporter_.ReportUpdateCheckMetrics(
+      &fake_system_state, result, reaction, error_code);
 
   // Advance the clock by 1 minute and report the same metrics again.
-  fake_clock_->SetWallclockTime(base::Time::FromInternalValue(61000000));
-  fake_clock_->SetMonotonicTime(base::Time::FromInternalValue(61000000));
+  fake_clock.SetWallclockTime(base::Time::FromInternalValue(61000000));
+  fake_clock.SetMonotonicTime(base::Time::FromInternalValue(61000000));
   // Allow rollback
-  reporter_.ReportUpdateCheckMetrics(result, reaction, error_code);
+  reporter_.ReportUpdateCheckMetrics(
+      &fake_system_state, result, reaction, error_code);
 }
 
 TEST_F(MetricsReporterOmahaTest, ReportUpdateCheckMetricsPinned) {
-  OmahaRequestParams params;
+  FakeSystemState fake_system_state;
+
+  OmahaRequestParams params(&fake_system_state);
   params.set_target_version_prefix("10575.");
   params.set_rollback_allowed(false);
-  FakeSystemState::Get()->set_request_params(&params);
+  fake_system_state.set_request_params(&params);
 
   metrics::CheckResult result = metrics::CheckResult::kUpdateAvailable;
   metrics::CheckReaction reaction = metrics::CheckReaction::kIgnored;
@@ -131,14 +138,17 @@
               SendSparseToUMA(metrics::kMetricCheckRollbackTargetVersion, _))
       .Times(0);
 
-  reporter_.ReportUpdateCheckMetrics(result, reaction, error_code);
+  reporter_.ReportUpdateCheckMetrics(
+      &fake_system_state, result, reaction, error_code);
 }
 
 TEST_F(MetricsReporterOmahaTest, ReportUpdateCheckMetricsRollback) {
-  OmahaRequestParams params;
+  FakeSystemState fake_system_state;
+
+  OmahaRequestParams params(&fake_system_state);
   params.set_target_version_prefix("10575.");
   params.set_rollback_allowed(true);
-  FakeSystemState::Get()->set_request_params(&params);
+  fake_system_state.set_request_params(&params);
 
   metrics::CheckResult result = metrics::CheckResult::kUpdateAvailable;
   metrics::CheckReaction reaction = metrics::CheckReaction::kIgnored;
@@ -156,7 +166,8 @@
       SendSparseToUMA(metrics::kMetricCheckRollbackTargetVersion, 10575))
       .Times(1);
 
-  reporter_.ReportUpdateCheckMetrics(result, reaction, error_code);
+  reporter_.ReportUpdateCheckMetrics(
+      &fake_system_state, result, reaction, error_code);
 }
 
 TEST_F(MetricsReporterOmahaTest,
@@ -172,8 +183,14 @@
 }
 
 TEST_F(MetricsReporterOmahaTest, ReportUpdateAttemptMetrics) {
-  fake_clock_->SetWallclockTime(base::Time::FromInternalValue(1000000));
-  fake_clock_->SetMonotonicTime(base::Time::FromInternalValue(1000000));
+  FakeSystemState fake_system_state;
+  FakeClock fake_clock;
+  FakePrefs fake_prefs;
+
+  fake_system_state.set_clock(&fake_clock);
+  fake_system_state.set_prefs(&fake_prefs);
+  fake_clock.SetWallclockTime(base::Time::FromInternalValue(1000000));
+  fake_clock.SetMonotonicTime(base::Time::FromInternalValue(1000000));
 
   int attempt_number = 1;
   PayloadType payload_type = kPayloadTypeFull;
@@ -235,7 +252,8 @@
           metrics::kMetricAttemptTimeSinceLastAttemptUptimeMinutes, 1, _, _, _))
       .Times(1);
 
-  reporter_.ReportUpdateAttemptMetrics(attempt_number,
+  reporter_.ReportUpdateAttemptMetrics(&fake_system_state,
+                                       attempt_number,
                                        payload_type,
                                        duration,
                                        duration_uptime,
@@ -244,9 +262,10 @@
                                        internal_error_code);
 
   // Advance the clock by 1 minute and report the same metrics again.
-  fake_clock_->SetWallclockTime(base::Time::FromInternalValue(61000000));
-  fake_clock_->SetMonotonicTime(base::Time::FromInternalValue(61000000));
-  reporter_.ReportUpdateAttemptMetrics(attempt_number,
+  fake_clock.SetWallclockTime(base::Time::FromInternalValue(61000000));
+  fake_clock.SetMonotonicTime(base::Time::FromInternalValue(61000000));
+  reporter_.ReportUpdateAttemptMetrics(&fake_system_state,
+                                       attempt_number,
                                        payload_type,
                                        duration,
                                        duration_uptime,
@@ -519,89 +538,4 @@
       true /* has_time_restriction_policy */, kDaysToUpdate);
 }
 
-TEST_F(MetricsReporterOmahaTest, WallclockDurationHelper) {
-  base::TimeDelta duration;
-  const std::string state_variable_key = "test-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(state_variable_key, &duration));
-
-  // Next time, we should get zero since the clock didn't advance.
-  EXPECT_TRUE(reporter_.WallclockDurationHelper(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(state_variable_key, &duration));
-  EXPECT_EQ(duration.InSeconds(), 0);
-  EXPECT_TRUE(reporter_.WallclockDurationHelper(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(state_variable_key, &duration));
-  EXPECT_EQ(duration.InSeconds(), 1);
-  EXPECT_TRUE(reporter_.WallclockDurationHelper(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(state_variable_key, &duration));
-  EXPECT_EQ(duration.InSeconds(), 2);
-  EXPECT_TRUE(reporter_.WallclockDurationHelper(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(state_variable_key, &duration));
-  fake_clock_->SetWallclockTime(base::Time::FromInternalValue(4000000));
-  EXPECT_TRUE(reporter_.WallclockDurationHelper(state_variable_key, &duration));
-  EXPECT_EQ(duration.InSeconds(), 1);
-}
-
-TEST_F(MetricsReporterOmahaTest, MonotonicDurationHelper) {
-  int64_t storage = 0;
-  base::TimeDelta duration;
-
-  // 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(&storage, &duration));
-
-  // Next time, we should get zero since the clock didn't advance.
-  EXPECT_TRUE(reporter_.MonotonicDurationHelper(&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(&storage, &duration));
-  EXPECT_EQ(duration.InSeconds(), 0);
-  EXPECT_TRUE(reporter_.MonotonicDurationHelper(&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(&storage, &duration));
-  EXPECT_EQ(duration.InSeconds(), 1);
-  EXPECT_TRUE(reporter_.MonotonicDurationHelper(&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(&storage, &duration));
-  EXPECT_EQ(duration.InSeconds(), 2);
-  EXPECT_TRUE(reporter_.MonotonicDurationHelper(&storage, &duration));
-  EXPECT_EQ(duration.InSeconds(), 0);
-}
-
 }  // namespace chromeos_update_engine
diff --git a/common/metrics_reporter_stub.cc b/metrics_reporter_stub.cc
similarity index 84%
rename from common/metrics_reporter_stub.cc
rename to metrics_reporter_stub.cc
index 61559d9..81664a5 100644
--- a/common/metrics_reporter_stub.cc
+++ b/metrics_reporter_stub.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/common/metrics_reporter_stub.h"
+#include "update_engine/metrics_reporter_stub.h"
 
 #include <memory>
 
@@ -22,9 +22,7 @@
 
 namespace metrics {
 
-std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter(
-    DynamicPartitionControlInterface* dynamic_partition_control,
-    const InstallPlan* install_plan) {
+std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter() {
   return std::make_unique<MetricsReporterStub>();
 }
 
diff --git a/common/metrics_reporter_stub.h b/metrics_reporter_stub.h
similarity index 88%
rename from common/metrics_reporter_stub.h
rename to metrics_reporter_stub.h
index 80cf469..25660b5 100644
--- a/common/metrics_reporter_stub.h
+++ b/metrics_reporter_stub.h
@@ -14,14 +14,14 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_COMMON_METRICS_REPORTER_STUB_H_
-#define UPDATE_ENGINE_COMMON_METRICS_REPORTER_STUB_H_
+#ifndef UPDATE_ENGINE_METRICS_REPORTER_STUB_H_
+#define UPDATE_ENGINE_METRICS_REPORTER_STUB_H_
 
 #include <string>
 
 #include "update_engine/common/error_code.h"
-#include "update_engine/common/metrics_constants.h"
-#include "update_engine/common/metrics_reporter_interface.h"
+#include "update_engine/metrics_constants.h"
+#include "update_engine/metrics_reporter_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -31,6 +31,8 @@
 
   ~MetricsReporterStub() override = default;
 
+  void Initialize() override {}
+
   void ReportRollbackMetrics(metrics::RollbackResult result) override {}
 
   void ReportEnterpriseRollbackMetrics(
@@ -39,11 +41,13 @@
   void ReportDailyMetrics(base::TimeDelta os_age) override {}
 
   void ReportUpdateCheckMetrics(
+      SystemState* system_state,
       metrics::CheckResult result,
       metrics::CheckReaction reaction,
       metrics::DownloadErrorCode download_error_code) override {}
 
-  void ReportUpdateAttemptMetrics(int attempt_number,
+  void ReportUpdateAttemptMetrics(SystemState* system_state,
+                                  int attempt_number,
                                   PayloadType payload_type,
                                   base::TimeDelta duration,
                                   base::TimeDelta duration_uptime,
@@ -96,4 +100,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_COMMON_METRICS_REPORTER_STUB_H_
+#endif  // UPDATE_ENGINE_METRICS_REPORTER_STUB_H_
diff --git a/metrics_utils.cc b/metrics_utils.cc
index 34da5a1..9abc3ef 100644
--- a/metrics_utils.cc
+++ b/metrics_utils.cc
@@ -23,6 +23,7 @@
 #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;
@@ -94,7 +95,6 @@
     case ErrorCode::kPostinstallRunnerError:
     case ErrorCode::kPostinstallBootedFromFirmwareB:
     case ErrorCode::kPostinstallFirmwareRONotUpdatable:
-    case ErrorCode::kPostInstallMountError:
       return metrics::AttemptResult::kPostInstallFailed;
 
     case ErrorCode::kUserCanceled:
@@ -111,6 +111,10 @@
     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:
@@ -120,13 +124,8 @@
     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|.
@@ -189,7 +188,6 @@
     case ErrorCode::kOmahaResponseHandlerError:
     case ErrorCode::kFilesystemCopierError:
     case ErrorCode::kPostinstallRunnerError:
-    case ErrorCode::kPostInstallMountError:
     case ErrorCode::kPayloadMismatchedType:
     case ErrorCode::kInstallDeviceOpenError:
     case ErrorCode::kKernelDeviceOpenError:
@@ -242,7 +240,6 @@
     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
@@ -283,6 +280,12 @@
       else
         return metrics::ConnectionType::kWifi;
 
+    case ConnectionType::kWimax:
+      return metrics::ConnectionType::kWimax;
+
+    case ConnectionType::kBluetooth:
+      return metrics::ConnectionType::kBluetooth;
+
     case ConnectionType::kCellular:
       return metrics::ConnectionType::kCellular;
   }
@@ -294,6 +297,48 @@
   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))
@@ -363,7 +408,8 @@
     return false;
 
   Time system_updated_at = Time::FromInternalValue(stored_value);
-  TimeDelta time_to_reboot = clock->GetMonotonicTime() - system_updated_at;
+  base::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 3aac4e5..8f1aad1 100644
--- a/metrics_utils.h
+++ b/metrics_utils.h
@@ -22,14 +22,16 @@
 #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 {
 
+class SystemState;
+
 namespace metrics_utils {
 
 // Transforms a ErrorCode value into a metrics::DownloadErrorCode.
@@ -48,6 +50,29 @@
 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 93b48fb..e7c4c26 100644
--- a/metrics_utils_unittest.cc
+++ b/metrics_utils_unittest.cc
@@ -18,6 +18,10 @@
 
 #include <gtest/gtest.h>
 
+#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 {
 
@@ -37,6 +41,12 @@
   EXPECT_EQ(
       metrics::ConnectionType::kWifi,
       GetConnectionType(ConnectionType::kWifi, ConnectionTethering::kUnknown));
+  EXPECT_EQ(
+      metrics::ConnectionType::kWimax,
+      GetConnectionType(ConnectionType::kWimax, ConnectionTethering::kUnknown));
+  EXPECT_EQ(metrics::ConnectionType::kBluetooth,
+            GetConnectionType(ConnectionType::kBluetooth,
+                              ConnectionTethering::kUnknown));
   EXPECT_EQ(metrics::ConnectionType::kCellular,
             GetConnectionType(ConnectionType::kCellular,
                               ConnectionTethering::kUnknown));
@@ -70,5 +80,116 @@
       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
new file mode 100644
index 0000000..4e9cb50
--- /dev/null
+++ b/mock_boot_control_hal.h
@@ -0,0 +1,49 @@
+//
+// 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/cros/mock_connection_manager.h b/mock_connection_manager.h
similarity index 85%
rename from cros/mock_connection_manager.h
rename to mock_connection_manager.h
index 899a49b..2fff68c 100644
--- a/cros/mock_connection_manager.h
+++ b/mock_connection_manager.h
@@ -14,12 +14,12 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_MOCK_CONNECTION_MANAGER_H_
-#define UPDATE_ENGINE_CROS_MOCK_CONNECTION_MANAGER_H_
+#ifndef UPDATE_ENGINE_MOCK_CONNECTION_MANAGER_H_
+#define UPDATE_ENGINE_MOCK_CONNECTION_MANAGER_H_
 
 #include <gmock/gmock.h>
 
-#include "update_engine/cros/connection_manager_interface.h"
+#include "update_engine/connection_manager_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -41,4 +41,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_MOCK_CONNECTION_MANAGER_H_
+#endif  // UPDATE_ENGINE_MOCK_CONNECTION_MANAGER_H_
diff --git a/mock_dynamic_partition_control.h b/mock_dynamic_partition_control.h
new file mode 100644
index 0000000..1e4e5fd
--- /dev/null
+++ b/mock_dynamic_partition_control.h
@@ -0,0 +1,110 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <stdint.h>
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include "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 {
+
+class MockDynamicPartitionControl : public DynamicPartitionControlInterface {
+ public:
+  MOCK_METHOD5(MapPartitionOnDeviceMapper,
+               bool(const std::string&,
+                    const std::string&,
+                    uint32_t,
+                    bool,
+                    std::string*));
+  MOCK_METHOD0(Cleanup, void());
+  MOCK_METHOD0(GetDynamicPartitionsFeatureFlag, FeatureFlag());
+  MOCK_METHOD5(
+      PreparePartitionsForUpdate,
+      bool(uint32_t, uint32_t, const DeltaArchiveManifest&, bool, uint64_t*));
+  MOCK_METHOD0(GetVirtualAbFeatureFlag, FeatureFlag());
+  MOCK_METHOD1(FinishUpdate, bool(bool));
+  MOCK_METHOD0(CleanupSuccessfulUpdate, ErrorCode());
+  MOCK_METHOD3(GetCleanupPreviousUpdateAction,
+               std::unique_ptr<AbstractAction>(
+                   BootControlInterface*,
+                   PrefsInterface*,
+                   CleanupPreviousUpdateActionDelegateInterface*));
+};
+
+class MockDynamicPartitionControlAndroid
+    : public DynamicPartitionControlAndroid {
+ public:
+  MOCK_METHOD5(MapPartitionOnDeviceMapper,
+               bool(const std::string&,
+                    const std::string&,
+                    uint32_t,
+                    bool,
+                    std::string*));
+  MOCK_METHOD1(UnmapPartitionOnDeviceMapper, bool(const std::string&));
+  MOCK_METHOD0(Cleanup, void());
+  MOCK_METHOD1(DeviceExists, bool(const std::string&));
+  MOCK_METHOD1(GetState, ::android::dm::DmDeviceState(const std::string&));
+  MOCK_METHOD2(GetDmDevicePathByName, bool(const std::string&, std::string*));
+  MOCK_METHOD3(LoadMetadataBuilder,
+               std::unique_ptr<::android::fs_mgr::MetadataBuilder>(
+                   const std::string&, uint32_t, uint32_t));
+  MOCK_METHOD3(StoreMetadata,
+               bool(const std::string&,
+                    android::fs_mgr::MetadataBuilder*,
+                    uint32_t));
+  MOCK_METHOD1(GetDeviceDir, bool(std::string*));
+  MOCK_METHOD0(GetDynamicPartitionsFeatureFlag, FeatureFlag());
+  MOCK_METHOD1(GetSuperPartitionName, std::string(uint32_t));
+  MOCK_METHOD0(GetVirtualAbFeatureFlag, FeatureFlag());
+  MOCK_METHOD1(FinishUpdate, bool(bool));
+  MOCK_METHOD5(
+      GetSystemOtherPath,
+      bool(uint32_t, uint32_t, const std::string&, std::string*, bool*));
+  MOCK_METHOD2(EraseSystemOtherAvbFooter, bool(uint32_t, uint32_t));
+  MOCK_METHOD0(IsAvbEnabledOnSystemOther, std::optional<bool>());
+
+  void set_fake_mapped_devices(const std::set<std::string>& fake) override {
+    DynamicPartitionControlAndroid::set_fake_mapped_devices(fake);
+  }
+
+  bool RealGetSystemOtherPath(uint32_t source_slot,
+                              uint32_t target_slot,
+                              const std::string& partition_name_suffix,
+                              std::string* path,
+                              bool* should_unmap) {
+    return DynamicPartitionControlAndroid::GetSystemOtherPath(
+        source_slot, target_slot, partition_name_suffix, path, should_unmap);
+  }
+
+  bool RealEraseSystemOtherAvbFooter(uint32_t source_slot,
+                                     uint32_t target_slot) {
+    return DynamicPartitionControlAndroid::EraseSystemOtherAvbFooter(
+        source_slot, target_slot);
+  }
+
+  std::optional<bool> RealIsAvbEnabledInFstab(const std::string& path) {
+    return DynamicPartitionControlAndroid::IsAvbEnabledInFstab(path);
+  }
+};
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/mock_file_writer.h b/mock_file_writer.h
similarity index 100%
rename from payload_consumer/mock_file_writer.h
rename to mock_file_writer.h
diff --git a/mock_libcurl_http_fetcher.h b/mock_libcurl_http_fetcher.h
deleted file mode 100644
index a14f953..0000000
--- a/mock_libcurl_http_fetcher.h
+++ /dev/null
@@ -1,37 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef UPDATE_ENGINE_MOCK_LIBCURL_HTTP_FETCHER_H_
-#define UPDATE_ENGINE_MOCK_LIBCURL_HTTP_FETCHER_H_
-
-#include <gmock/gmock.h>
-
-#include "update_engine/libcurl_http_fetcher.h"
-
-namespace chromeos_update_engine {
-
-class MockLibcurlHttpFetcher : public LibcurlHttpFetcher {
- public:
-  MockLibcurlHttpFetcher(ProxyResolver* proxy_resolver,
-                         HardwareInterface* hardware)
-      : LibcurlHttpFetcher(proxy_resolver, hardware) {}
-
-  MOCK_METHOD0(GetHttpResponseCode, void());
-};
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_MOCK_LIBCURL_HTTP_FETCHER_H_
diff --git a/common/mock_metrics_reporter.h b/mock_metrics_reporter.h
similarity index 87%
rename from common/mock_metrics_reporter.h
rename to mock_metrics_reporter.h
index 1bb1e84..baf3a78 100644
--- a/common/mock_metrics_reporter.h
+++ b/mock_metrics_reporter.h
@@ -14,14 +14,14 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_COMMON_MOCK_METRICS_REPORTER_H_
-#define UPDATE_ENGINE_COMMON_MOCK_METRICS_REPORTER_H_
+#ifndef UPDATE_ENGINE_MOCK_METRICS_REPORTER_H_
+#define UPDATE_ENGINE_MOCK_METRICS_REPORTER_H_
 
 #include <string>
 
 #include <gmock/gmock.h>
 
-#include "update_engine/common/metrics_reporter_interface.h"
+#include "update_engine/metrics_reporter_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -36,13 +36,15 @@
 
   MOCK_METHOD1(ReportDailyMetrics, void(base::TimeDelta os_age));
 
-  MOCK_METHOD3(ReportUpdateCheckMetrics,
-               void(metrics::CheckResult result,
+  MOCK_METHOD4(ReportUpdateCheckMetrics,
+               void(SystemState* system_state,
+                    metrics::CheckResult result,
                     metrics::CheckReaction reaction,
                     metrics::DownloadErrorCode download_error_code));
 
-  MOCK_METHOD7(ReportUpdateAttemptMetrics,
-               void(int attempt_number,
+  MOCK_METHOD8(ReportUpdateAttemptMetrics,
+               void(SystemState* system_state,
+                    int attempt_number,
                     PayloadType payload_type,
                     base::TimeDelta duration,
                     base::TimeDelta duration_uptime,
@@ -94,4 +96,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_COMMON_MOCK_METRICS_REPORTER_H_
+#endif  // UPDATE_ENGINE_MOCK_METRICS_REPORTER_H_
diff --git a/cros/mock_omaha_request_params.h b/mock_omaha_request_params.h
similarity index 89%
rename from cros/mock_omaha_request_params.h
rename to mock_omaha_request_params.h
index 1e21812..41bdc19 100644
--- a/cros/mock_omaha_request_params.h
+++ b/mock_omaha_request_params.h
@@ -14,20 +14,21 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_MOCK_OMAHA_REQUEST_PARAMS_H_
-#define UPDATE_ENGINE_CROS_MOCK_OMAHA_REQUEST_PARAMS_H_
+#ifndef UPDATE_ENGINE_MOCK_OMAHA_REQUEST_PARAMS_H_
+#define UPDATE_ENGINE_MOCK_OMAHA_REQUEST_PARAMS_H_
 
 #include <string>
 
 #include <gmock/gmock.h>
 
-#include "update_engine/cros/omaha_request_params.h"
+#include "update_engine/omaha_request_params.h"
 
 namespace chromeos_update_engine {
 
 class MockOmahaRequestParams : public OmahaRequestParams {
  public:
-  MockOmahaRequestParams() : OmahaRequestParams() {
+  explicit MockOmahaRequestParams(SystemState* system_state)
+      : OmahaRequestParams(system_state) {
     // Delegate all calls to the parent instance by default. This helps the
     // migration from tests using the real RequestParams when they should have
     // use a fake or mock.
@@ -78,4 +79,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_MOCK_OMAHA_REQUEST_PARAMS_H_
+#endif  // UPDATE_ENGINE_MOCK_OMAHA_REQUEST_PARAMS_H_
diff --git a/cros/mock_p2p_manager.h b/mock_p2p_manager.h
similarity index 94%
rename from cros/mock_p2p_manager.h
rename to mock_p2p_manager.h
index 273f7f9..fd67034 100644
--- a/cros/mock_p2p_manager.h
+++ b/mock_p2p_manager.h
@@ -14,12 +14,12 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_MOCK_P2P_MANAGER_H_
-#define UPDATE_ENGINE_CROS_MOCK_P2P_MANAGER_H_
+#ifndef UPDATE_ENGINE_MOCK_P2P_MANAGER_H_
+#define UPDATE_ENGINE_MOCK_P2P_MANAGER_H_
 
 #include <string>
 
-#include "update_engine/cros/fake_p2p_manager.h"
+#include "update_engine/fake_p2p_manager.h"
 
 #include <gmock/gmock.h>
 
@@ -99,4 +99,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_MOCK_P2P_MANAGER_H_
+#endif  // UPDATE_ENGINE_MOCK_P2P_MANAGER_H_
diff --git a/cros/mock_payload_state.h b/mock_payload_state.h
similarity index 90%
rename from cros/mock_payload_state.h
rename to mock_payload_state.h
index 211b96d..ad22de5 100644
--- a/cros/mock_payload_state.h
+++ b/mock_payload_state.h
@@ -14,20 +14,21 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_MOCK_PAYLOAD_STATE_H_
-#define UPDATE_ENGINE_CROS_MOCK_PAYLOAD_STATE_H_
+#ifndef UPDATE_ENGINE_MOCK_PAYLOAD_STATE_H_
+#define UPDATE_ENGINE_MOCK_PAYLOAD_STATE_H_
 
 #include <string>
 
 #include <gmock/gmock.h>
 
-#include "update_engine/cros/payload_state_interface.h"
+#include "update_engine/omaha_request_action.h"
+#include "update_engine/payload_state_interface.h"
 
 namespace chromeos_update_engine {
 
 class MockPayloadState : public PayloadStateInterface {
  public:
-  bool Initialize() { return true; }
+  bool Initialize(SystemState* system_state) { return true; }
 
   // Significant methods.
   MOCK_METHOD1(SetResponse, void(const OmahaResponse& response));
@@ -80,4 +81,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_MOCK_PAYLOAD_STATE_H_
+#endif  // UPDATE_ENGINE_MOCK_PAYLOAD_STATE_H_
diff --git a/cros/mock_power_manager.h b/mock_power_manager.h
similarity index 80%
rename from cros/mock_power_manager.h
rename to mock_power_manager.h
index d4a8682..8363171 100644
--- a/cros/mock_power_manager.h
+++ b/mock_power_manager.h
@@ -14,12 +14,12 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_MOCK_POWER_MANAGER_H_
-#define UPDATE_ENGINE_CROS_MOCK_POWER_MANAGER_H_
+#ifndef UPDATE_ENGINE_MOCK_POWER_MANAGER_H_
+#define UPDATE_ENGINE_MOCK_POWER_MANAGER_H_
 
 #include <gmock/gmock.h>
 
-#include "update_engine/cros/power_manager_interface.h"
+#include "update_engine/power_manager_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -32,4 +32,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_MOCK_POWER_MANAGER_H_
+#endif  // UPDATE_ENGINE_MOCK_POWER_MANAGER_H_
diff --git a/common/mock_service_observer.h b/mock_service_observer.h
similarity index 81%
rename from common/mock_service_observer.h
rename to mock_service_observer.h
index 2c895f9..e434eab 100644
--- a/common/mock_service_observer.h
+++ b/mock_service_observer.h
@@ -14,11 +14,11 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_COMMON_MOCK_SERVICE_OBSERVER_H_
-#define UPDATE_ENGINE_COMMON_MOCK_SERVICE_OBSERVER_H_
+#ifndef UPDATE_ENGINE_MOCK_SERVICE_OBSERVER_H_
+#define UPDATE_ENGINE_MOCK_SERVICE_OBSERVER_H_
 
 #include <gmock/gmock.h>
-#include "update_engine/common/service_observer_interface.h"
+#include "update_engine/service_observer_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -32,4 +32,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_COMMON_MOCK_SERVICE_OBSERVER_H_
+#endif  // UPDATE_ENGINE_MOCK_SERVICE_OBSERVER_H_
diff --git a/cros/mock_update_attempter.h b/mock_update_attempter.h
similarity index 71%
rename from cros/mock_update_attempter.h
rename to mock_update_attempter.h
index be8cfcc..5df5a6b 100644
--- a/cros/mock_update_attempter.h
+++ b/mock_update_attempter.h
@@ -14,13 +14,13 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_MOCK_UPDATE_ATTEMPTER_H_
-#define UPDATE_ENGINE_CROS_MOCK_UPDATE_ATTEMPTER_H_
+#ifndef UPDATE_ENGINE_MOCK_UPDATE_ATTEMPTER_H_
+#define UPDATE_ENGINE_MOCK_UPDATE_ATTEMPTER_H_
 
 #include <string>
 #include <vector>
 
-#include "update_engine/cros/update_attempter.h"
+#include "update_engine/update_attempter.h"
 
 #include <gmock/gmock.h>
 
@@ -30,10 +30,14 @@
  public:
   using UpdateAttempter::UpdateAttempter;
 
-  MOCK_METHOD(void,
-              Update,
-              (const chromeos_update_manager::UpdateCheckParams& params),
-              (override));
+  MOCK_METHOD7(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 obey_proxies,
+                    bool interactive));
 
   MOCK_METHOD1(GetStatus, bool(update_engine::UpdateEngineStatus* out_status));
 
@@ -49,13 +53,9 @@
                     UpdateAttemptFlags flags));
 
   MOCK_METHOD2(CheckForInstall,
-               bool(const std::vector<std::string>& dlc_ids,
+               bool(const std::vector<std::string>& dlc_module_ids,
                     const std::string& omaha_url));
 
-  MOCK_METHOD2(SetDlcActiveValue, bool(bool, const std::string&));
-
-  MOCK_CONST_METHOD0(GetExcluder, ExcluderInterface*(void));
-
   MOCK_METHOD0(RefreshDevicePolicy, void(void));
 
   MOCK_CONST_METHOD0(consecutive_failed_update_checks, unsigned int(void));
@@ -65,4 +65,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_MOCK_UPDATE_ATTEMPTER_H_
+#endif  // UPDATE_ENGINE_MOCK_UPDATE_ATTEMPTER_H_
diff --git a/common/network_selector.h b/network_selector.h
similarity index 80%
rename from common/network_selector.h
rename to network_selector.h
index bfc09c5..22aed8e 100644
--- a/common/network_selector.h
+++ b/network_selector.h
@@ -14,12 +14,12 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_COMMON_NETWORK_SELECTOR_H_
-#define UPDATE_ENGINE_COMMON_NETWORK_SELECTOR_H_
+#ifndef UPDATE_ENGINE_NETWORK_SELECTOR_H_
+#define UPDATE_ENGINE_NETWORK_SELECTOR_H_
 
 #include <memory>
 
-#include "update_engine/common/network_selector_interface.h"
+#include "update_engine/network_selector_interface.h"
 
 namespace chromeos_update_engine {
 namespace network {
@@ -30,4 +30,4 @@
 }  // namespace network
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_COMMON_NETWORK_SELECTOR_H_
+#endif  // UPDATE_ENGINE_NETWORK_SELECTOR_H_
diff --git a/aosp/network_selector_android.cc b/network_selector_android.cc
similarity index 88%
rename from aosp/network_selector_android.cc
rename to network_selector_android.cc
index a7db415..55ba799 100644
--- a/aosp/network_selector_android.cc
+++ b/network_selector_android.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/aosp/network_selector_android.h"
+#include "update_engine/network_selector_android.h"
 
 #include <memory>
 
@@ -25,14 +25,14 @@
 
 namespace network {
 
-// Factory defined in common/network_selector.h.
+// Factory defined in network_selector.h.
 std::unique_ptr<NetworkSelectorInterface> CreateNetworkSelector() {
   return std::make_unique<NetworkSelectorAndroid>();
 }
 
 }  // namespace network
 
-// Defined in common/network_selector_interface.h.
+// Defined in network_selector_interface.h.
 const NetworkId kDefaultNetworkId = NETWORK_UNSPECIFIED;
 
 bool NetworkSelectorAndroid::SetProcessNetwork(NetworkId network_id) {
diff --git a/aosp/network_selector_android.h b/network_selector_android.h
similarity index 81%
rename from aosp/network_selector_android.h
rename to network_selector_android.h
index b79d1b3..135536c 100644
--- a/aosp/network_selector_android.h
+++ b/network_selector_android.h
@@ -14,12 +14,12 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_AOSP_NETWORK_SELECTOR_ANDROID_H_
-#define UPDATE_ENGINE_AOSP_NETWORK_SELECTOR_ANDROID_H_
+#ifndef UPDATE_ENGINE_NETWORK_SELECTOR_ANDROID_H_
+#define UPDATE_ENGINE_NETWORK_SELECTOR_ANDROID_H_
 
 #include <base/macros.h>
 
-#include "update_engine/common/network_selector_interface.h"
+#include "update_engine/network_selector_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -37,4 +37,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_AOSP_NETWORK_SELECTOR_ANDROID_H_
+#endif  // UPDATE_ENGINE_NETWORK_SELECTOR_ANDROID_H_
diff --git a/common/network_selector_interface.h b/network_selector_interface.h
similarity index 88%
rename from common/network_selector_interface.h
rename to network_selector_interface.h
index 42ce32e..bd0948f 100644
--- a/common/network_selector_interface.h
+++ b/network_selector_interface.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_COMMON_NETWORK_SELECTOR_INTERFACE_H_
-#define UPDATE_ENGINE_COMMON_NETWORK_SELECTOR_INTERFACE_H_
+#ifndef UPDATE_ENGINE_NETWORK_SELECTOR_INTERFACE_H_
+#define UPDATE_ENGINE_NETWORK_SELECTOR_INTERFACE_H_
 
 #include <cstdint>
 
@@ -45,4 +45,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_COMMON_NETWORK_SELECTOR_INTERFACE_H_
+#endif  // UPDATE_ENGINE_NETWORK_SELECTOR_INTERFACE_H_
diff --git a/common/network_selector_stub.cc b/network_selector_stub.cc
similarity index 87%
rename from common/network_selector_stub.cc
rename to network_selector_stub.cc
index 24c0e25..67925f4 100644
--- a/common/network_selector_stub.cc
+++ b/network_selector_stub.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/common/network_selector_stub.h"
+#include "update_engine/network_selector_stub.h"
 
 #include <memory>
 
@@ -24,14 +24,14 @@
 
 namespace network {
 
-// Factory defined in common/network_selector.h.
+// Factory defined in network_selector.h.
 std::unique_ptr<NetworkSelectorInterface> CreateNetworkSelector() {
   return std::make_unique<NetworkSelectorStub>();
 }
 
 }  // namespace network
 
-// Defined in common/network_selector_interface.h.
+// Defined in network_selector_interface.h.
 const NetworkId kDefaultNetworkId = 0;
 
 bool NetworkSelectorStub::SetProcessNetwork(NetworkId network_id) {
diff --git a/common/network_selector_stub.h b/network_selector_stub.h
similarity index 81%
rename from common/network_selector_stub.h
rename to network_selector_stub.h
index b32df91..b3f7b48 100644
--- a/common/network_selector_stub.h
+++ b/network_selector_stub.h
@@ -14,12 +14,12 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_COMMON_NETWORK_SELECTOR_STUB_H_
-#define UPDATE_ENGINE_COMMON_NETWORK_SELECTOR_STUB_H_
+#ifndef UPDATE_ENGINE_NETWORK_SELECTOR_STUB_H_
+#define UPDATE_ENGINE_NETWORK_SELECTOR_STUB_H_
 
 #include <base/macros.h>
 
-#include "update_engine/common/network_selector_interface.h"
+#include "update_engine/network_selector_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -37,4 +37,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_COMMON_NETWORK_SELECTOR_STUB_H_
+#endif  // UPDATE_ENGINE_NETWORK_SELECTOR_STUB_H_
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
new file mode 100644
index 0000000..fae9471
--- /dev/null
+++ b/omaha_request_action.cc
@@ -0,0 +1,1997 @@
+//
+// Copyright (C) 2012 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/omaha_request_action.h"
+
+#include <inttypes.h>
+
+#include <limits>
+#include <map>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/logging.h>
+#include <base/rand_util.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <base/time/time.h>
+#include <brillo/key_value_store.h>
+#include <expat.h>
+#include <metrics/metrics_library.h>
+#include <policy/libpolicy.h>
+
+#include "update_engine/common/action_pipe.h"
+#include "update_engine/common/constants.h"
+#include "update_engine/common/hardware_interface.h"
+#include "update_engine/common/hash_calculator.h"
+#include "update_engine/common/platform_constants.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/metrics_utils.h"
+#include "update_engine/omaha_request_params.h"
+#include "update_engine/p2p_manager.h"
+#include "update_engine/payload_state_interface.h"
+
+using base::Time;
+using base::TimeDelta;
+using chromeos_update_manager::kRollforwardInfinity;
+using std::map;
+using std::numeric_limits;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+// List of custom attributes that we interpret in the Omaha response:
+constexpr char kAttrDeadline[] = "deadline";
+constexpr char kAttrDisableP2PForDownloading[] = "DisableP2PForDownloading";
+constexpr char kAttrDisableP2PForSharing[] = "DisableP2PForSharing";
+constexpr char kAttrDisablePayloadBackoff[] = "DisablePayloadBackoff";
+constexpr char kAttrVersion[] = "version";
+// Deprecated: "IsDelta"
+constexpr char kAttrIsDeltaPayload[] = "IsDeltaPayload";
+constexpr char kAttrMaxFailureCountPerUrl[] = "MaxFailureCountPerUrl";
+constexpr char kAttrMaxDaysToScatter[] = "MaxDaysToScatter";
+// Deprecated: "ManifestSignatureRsa"
+// Deprecated: "ManifestSize"
+constexpr char kAttrMetadataSignatureRsa[] = "MetadataSignatureRsa";
+constexpr char kAttrMetadataSize[] = "MetadataSize";
+constexpr char kAttrMoreInfo[] = "MoreInfo";
+constexpr char kAttrNoUpdate[] = "noupdate";
+// Deprecated: "NeedsAdmin"
+constexpr char kAttrPollInterval[] = "PollInterval";
+constexpr char kAttrPowerwash[] = "Powerwash";
+constexpr char kAttrPrompt[] = "Prompt";
+constexpr char kAttrPublicKeyRsa[] = "PublicKeyRsa";
+
+// List of attributes that we interpret in the Omaha response:
+constexpr char kAttrAppId[] = "appid";
+constexpr char kAttrCodeBase[] = "codebase";
+constexpr char kAttrCohort[] = "cohort";
+constexpr char kAttrCohortHint[] = "cohorthint";
+constexpr char kAttrCohortName[] = "cohortname";
+constexpr char kAttrElapsedDays[] = "elapsed_days";
+constexpr char kAttrElapsedSeconds[] = "elapsed_seconds";
+constexpr char kAttrEvent[] = "event";
+constexpr char kAttrHashSha256[] = "hash_sha256";
+// Deprecated: "hash"; Although we still need to pass it from the server for
+// backward compatibility.
+constexpr char kAttrName[] = "name";
+// Deprecated: "sha256"; Although we still need to pass it from the server for
+// backward compatibility.
+constexpr char kAttrSize[] = "size";
+constexpr char kAttrStatus[] = "status";
+
+// List of values that we interpret in the Omaha response:
+constexpr char kValPostInstall[] = "postinstall";
+constexpr char kValNoUpdate[] = "noupdate";
+
+constexpr char kOmahaUpdaterVersion[] = "0.1.0.0";
+
+// X-Goog-Update headers.
+constexpr char kXGoogleUpdateInteractivity[] = "X-Goog-Update-Interactivity";
+constexpr char kXGoogleUpdateAppId[] = "X-Goog-Update-AppId";
+constexpr char kXGoogleUpdateUpdater[] = "X-Goog-Update-Updater";
+
+// updatecheck attributes (without the underscore prefix).
+constexpr char kAttrEol[] = "eol";
+constexpr char kAttrRollback[] = "rollback";
+constexpr char kAttrFirmwareVersion[] = "firmware_version";
+constexpr char kAttrKernelVersion[] = "kernel_version";
+
+namespace {
+
+// Returns an XML ping element attribute assignment with attribute
+// |name| and value |ping_days| if |ping_days| has a value that needs
+// to be sent, or an empty string otherwise.
+string GetPingAttribute(const string& name, int ping_days) {
+  if (ping_days > 0 || ping_days == OmahaRequestAction::kNeverPinged)
+    return base::StringPrintf(" %s=\"%d\"", name.c_str(), ping_days);
+  return "";
+}
+
+// Returns an XML ping element if any of the elapsed days need to be
+// sent, or an empty string otherwise.
+string GetPingXml(int ping_active_days, int ping_roll_call_days) {
+  string ping_active = GetPingAttribute("a", ping_active_days);
+  string ping_roll_call = GetPingAttribute("r", ping_roll_call_days);
+  if (!ping_active.empty() || !ping_roll_call.empty()) {
+    return base::StringPrintf("        <ping active=\"1\"%s%s></ping>\n",
+                              ping_active.c_str(),
+                              ping_roll_call.c_str());
+  }
+  return "";
+}
+
+// Returns an XML that goes into the body of the <app> element of the Omaha
+// request based on the given parameters.
+string GetAppBody(const OmahaEvent* event,
+                  OmahaRequestParams* params,
+                  bool ping_only,
+                  bool include_ping,
+                  bool skip_updatecheck,
+                  int ping_active_days,
+                  int ping_roll_call_days,
+                  PrefsInterface* prefs) {
+  string app_body;
+  if (event == nullptr) {
+    if (include_ping)
+      app_body = GetPingXml(ping_active_days, ping_roll_call_days);
+    if (!ping_only) {
+      if (!skip_updatecheck) {
+        app_body += "        <updatecheck";
+        if (!params->target_version_prefix().empty()) {
+          app_body += base::StringPrintf(
+              " targetversionprefix=\"%s\"",
+              XmlEncodeWithDefault(params->target_version_prefix(), "")
+                  .c_str());
+          // Rollback requires target_version_prefix set.
+          if (params->rollback_allowed()) {
+            app_body += " rollback_allowed=\"true\"";
+          }
+        }
+        app_body += "></updatecheck>\n";
+      }
+
+      // If this is the first update check after a reboot following a previous
+      // update, generate an event containing the previous version number. If
+      // the previous version preference file doesn't exist the event is still
+      // generated with a previous version of 0.0.0.0 -- this is relevant for
+      // older clients or new installs. The previous version event is not sent
+      // for ping-only requests because they come before the client has
+      // rebooted. The previous version event is also not sent if it was already
+      // sent for this new version with a previous updatecheck.
+      string prev_version;
+      if (!prefs->GetString(kPrefsPreviousVersion, &prev_version)) {
+        prev_version = "0.0.0.0";
+      }
+      // We only store a non-empty previous version value after a successful
+      // update in the previous boot. After reporting it back to the server,
+      // we clear the previous version value so it doesn't get reported again.
+      if (!prev_version.empty()) {
+        app_body += base::StringPrintf(
+            "        <event eventtype=\"%d\" eventresult=\"%d\" "
+            "previousversion=\"%s\"></event>\n",
+            OmahaEvent::kTypeRebootedAfterUpdate,
+            OmahaEvent::kResultSuccess,
+            XmlEncodeWithDefault(prev_version, "0.0.0.0").c_str());
+        LOG_IF(WARNING, !prefs->SetString(kPrefsPreviousVersion, ""))
+            << "Unable to reset the previous version.";
+      }
+    }
+  } else {
+    // 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) {
+      error_code = base::StringPrintf(" errorcode=\"%d\"",
+                                      static_cast<int>(event->error_code));
+    }
+    app_body = base::StringPrintf(
+        "        <event eventtype=\"%d\" eventresult=\"%d\"%s></event>\n",
+        event->type,
+        event->result,
+        error_code.c_str());
+  }
+
+  return app_body;
+}
+
+// Returns the cohort* argument to include in the <app> tag for the passed
+// |arg_name| and |prefs_key|, if any. The return value is suitable to
+// concatenate to the list of arguments and includes a space at the end.
+string GetCohortArgXml(PrefsInterface* prefs,
+                       const string arg_name,
+                       const string prefs_key) {
+  // There's nothing wrong with not having a given cohort setting, so we check
+  // existence first to avoid the warning log message.
+  if (!prefs->Exists(prefs_key))
+    return "";
+  string cohort_value;
+  if (!prefs->GetString(prefs_key, &cohort_value) || cohort_value.empty())
+    return "";
+  // This is a sanity check to avoid sending a huge XML file back to Ohama due
+  // to a compromised stateful partition making the update check fail in low
+  // network environments envent after a reboot.
+  if (cohort_value.size() > 1024) {
+    LOG(WARNING) << "The omaha cohort setting " << arg_name
+                 << " has a too big value, which must be an error or an "
+                    "attacker trying to inhibit updates.";
+    return "";
+  }
+
+  string escaped_xml_value;
+  if (!XmlEncode(cohort_value, &escaped_xml_value)) {
+    LOG(WARNING) << "The omaha cohort setting " << arg_name
+                 << " is ASCII-7 invalid, ignoring it.";
+    return "";
+  }
+
+  return base::StringPrintf(
+      "%s=\"%s\" ", arg_name.c_str(), escaped_xml_value.c_str());
+}
+
+struct OmahaAppData {
+  string id;
+  string version;
+  string product_components;
+};
+
+bool IsValidComponentID(const string& id) {
+  for (char c : id) {
+    if (!isalnum(c) && c != '-' && c != '_' && c != '.')
+      return false;
+  }
+  return true;
+}
+
+// Returns an XML that corresponds to the entire <app> node of the Omaha
+// request based on the given parameters.
+string GetAppXml(const OmahaEvent* event,
+                 OmahaRequestParams* params,
+                 const OmahaAppData& app_data,
+                 bool ping_only,
+                 bool include_ping,
+                 bool skip_updatecheck,
+                 int ping_active_days,
+                 int ping_roll_call_days,
+                 int install_date_in_days,
+                 SystemState* system_state) {
+  string app_body = GetAppBody(event,
+                               params,
+                               ping_only,
+                               include_ping,
+                               skip_updatecheck,
+                               ping_active_days,
+                               ping_roll_call_days,
+                               system_state->prefs());
+  string app_versions;
+
+  // If we are downgrading to a more stable channel and we are allowed to do
+  // powerwash, then pass 0.0.0.0 as the version. This is needed to get the
+  // highest-versioned payload on the destination channel.
+  if (params->ShouldPowerwash()) {
+    LOG(INFO) << "Passing OS version as 0.0.0.0 as we are set to powerwash "
+              << "on downgrading to the version in the more stable channel";
+    app_versions = "version=\"0.0.0.0\" from_version=\"" +
+                   XmlEncodeWithDefault(app_data.version, "0.0.0.0") + "\" ";
+  } else {
+    app_versions = "version=\"" +
+                   XmlEncodeWithDefault(app_data.version, "0.0.0.0") + "\" ";
+  }
+
+  string download_channel = params->download_channel();
+  string app_channels =
+      "track=\"" + XmlEncodeWithDefault(download_channel, "") + "\" ";
+  if (params->current_channel() != download_channel) {
+    app_channels += "from_track=\"" +
+                    XmlEncodeWithDefault(params->current_channel(), "") + "\" ";
+  }
+
+  string delta_okay_str = params->delta_okay() ? "true" : "false";
+
+  // If install_date_days is not set (e.g. its value is -1 ), don't
+  // include the attribute.
+  string install_date_in_days_str = "";
+  if (install_date_in_days >= 0) {
+    install_date_in_days_str =
+        base::StringPrintf("installdate=\"%d\" ", install_date_in_days);
+  }
+
+  string app_cohort_args;
+  app_cohort_args +=
+      GetCohortArgXml(system_state->prefs(), "cohort", kPrefsOmahaCohort);
+  app_cohort_args += GetCohortArgXml(
+      system_state->prefs(), "cohorthint", kPrefsOmahaCohortHint);
+  app_cohort_args += GetCohortArgXml(
+      system_state->prefs(), "cohortname", kPrefsOmahaCohortName);
+
+  string fingerprint_arg;
+  if (!params->os_build_fingerprint().empty()) {
+    fingerprint_arg = "fingerprint=\"" +
+                      XmlEncodeWithDefault(params->os_build_fingerprint(), "") +
+                      "\" ";
+  }
+
+  string buildtype_arg;
+  if (!params->os_build_type().empty()) {
+    buildtype_arg = "os_build_type=\"" +
+                    XmlEncodeWithDefault(params->os_build_type(), "") + "\" ";
+  }
+
+  string product_components_args;
+  if (!params->ShouldPowerwash() && !app_data.product_components.empty()) {
+    brillo::KeyValueStore store;
+    if (store.LoadFromString(app_data.product_components)) {
+      for (const string& key : store.GetKeys()) {
+        if (!IsValidComponentID(key)) {
+          LOG(ERROR) << "Invalid component id: " << key;
+          continue;
+        }
+        string version;
+        if (!store.GetString(key, &version)) {
+          LOG(ERROR) << "Failed to get version for " << key
+                     << " in product_components.";
+          continue;
+        }
+        product_components_args +=
+            base::StringPrintf("_%s.version=\"%s\" ",
+                               key.c_str(),
+                               XmlEncodeWithDefault(version, "").c_str());
+      }
+    } else {
+      LOG(ERROR) << "Failed to parse product_components:\n"
+                 << app_data.product_components;
+    }
+  }
+
+  // clang-format off
+  string app_xml = "    <app "
+      "appid=\"" + XmlEncodeWithDefault(app_data.id, "") + "\" " +
+      app_cohort_args +
+      app_versions +
+      app_channels +
+      product_components_args +
+      fingerprint_arg +
+      buildtype_arg +
+      "lang=\"" + XmlEncodeWithDefault(params->app_lang(), "en-US") + "\" " +
+      "board=\"" + XmlEncodeWithDefault(params->os_board(), "") + "\" " +
+      "hardware_class=\"" + XmlEncodeWithDefault(params->hwid(), "") + "\" " +
+      "delta_okay=\"" + delta_okay_str + "\" "
+      "fw_version=\"" + XmlEncodeWithDefault(params->fw_version(), "") + "\" " +
+      "ec_version=\"" + XmlEncodeWithDefault(params->ec_version(), "") + "\" " +
+      install_date_in_days_str +
+      ">\n" +
+         app_body +
+      "    </app>\n";
+  // clang-format on
+  return app_xml;
+}
+
+// Returns an XML that corresponds to the entire <os> node of the Omaha
+// request based on the given parameters.
+string GetOsXml(OmahaRequestParams* params) {
+  string os_xml =
+      "    <os "
+      "version=\"" +
+      XmlEncodeWithDefault(params->os_version(), "") + "\" " + "platform=\"" +
+      XmlEncodeWithDefault(params->os_platform(), "") + "\" " + "sp=\"" +
+      XmlEncodeWithDefault(params->os_sp(), "") +
+      "\">"
+      "</os>\n";
+  return os_xml;
+}
+
+// Returns an XML that corresponds to the entire Omaha request based on the
+// given parameters.
+string GetRequestXml(const OmahaEvent* event,
+                     OmahaRequestParams* params,
+                     bool ping_only,
+                     bool include_ping,
+                     int ping_active_days,
+                     int ping_roll_call_days,
+                     int install_date_in_days,
+                     SystemState* system_state) {
+  string os_xml = GetOsXml(params);
+  OmahaAppData product_app = {
+      .id = params->GetAppId(),
+      .version = params->app_version(),
+      .product_components = params->product_components()};
+  // Skips updatecheck for platform app in case of an install operation.
+  string app_xml = GetAppXml(event,
+                             params,
+                             product_app,
+                             ping_only,
+                             include_ping,
+                             params->is_install(), /* skip_updatecheck */
+                             ping_active_days,
+                             ping_roll_call_days,
+                             install_date_in_days,
+                             system_state);
+  if (!params->system_app_id().empty()) {
+    OmahaAppData system_app = {.id = params->system_app_id(),
+                               .version = params->system_version()};
+    app_xml += GetAppXml(event,
+                         params,
+                         system_app,
+                         ping_only,
+                         include_ping,
+                         false, /* skip_updatecheck */
+                         ping_active_days,
+                         ping_roll_call_days,
+                         install_date_in_days,
+                         system_state);
+  }
+  // Create APP ID according to |dlc_module_id| (sticking the current AppID to
+  // the DLC module ID with an underscode).
+  for (const auto& dlc_module_id : params->dlc_module_ids()) {
+    OmahaAppData dlc_module_app = {
+        .id = params->GetAppId() + "_" + dlc_module_id,
+        .version = params->app_version()};
+    app_xml += GetAppXml(event,
+                         params,
+                         dlc_module_app,
+                         ping_only,
+                         include_ping,
+                         false, /* skip_updatecheck */
+                         ping_active_days,
+                         ping_roll_call_days,
+                         install_date_in_days,
+                         system_state);
+  }
+
+  string install_source = base::StringPrintf(
+      "installsource=\"%s\" ",
+      (params->interactive() ? "ondemandupdate" : "scheduler"));
+
+  string updater_version = XmlEncodeWithDefault(
+      base::StringPrintf(
+          "%s-%s", constants::kOmahaUpdaterID, kOmahaUpdaterVersion),
+      "");
+  string request_xml =
+      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+      "<request protocol=\"3.0\" " +
+      ("version=\"" + updater_version +
+       "\" "
+       "updaterversion=\"" +
+       updater_version + "\" " + install_source + "ismachine=\"1\">\n") +
+      os_xml + app_xml + "</request>\n";
+
+  return request_xml;
+}
+
+}  // namespace
+
+// Struct used for holding data obtained when parsing the XML.
+struct OmahaParserData {
+  explicit OmahaParserData(XML_Parser _xml_parser) : xml_parser(_xml_parser) {}
+
+  // Pointer to the expat XML_Parser object.
+  XML_Parser xml_parser;
+
+  // This is the state of the parser as it's processing the XML.
+  bool failed = false;
+  bool entity_decl = false;
+  string current_path;
+
+  // These are the values extracted from the XML.
+  string updatecheck_poll_interval;
+  map<string, string> updatecheck_attrs;
+  string daystart_elapsed_days;
+  string daystart_elapsed_seconds;
+
+  struct App {
+    string id;
+    vector<string> url_codebase;
+    string manifest_version;
+    map<string, string> action_postinstall_attrs;
+    string updatecheck_status;
+    string cohort;
+    string cohorthint;
+    string cohortname;
+    bool cohort_set = false;
+    bool cohorthint_set = false;
+    bool cohortname_set = false;
+
+    struct Package {
+      string name;
+      string size;
+      string hash;
+    };
+    vector<Package> packages;
+  };
+  vector<App> apps;
+};
+
+namespace {
+
+// Callback function invoked by expat.
+void ParserHandlerStart(void* user_data,
+                        const XML_Char* element,
+                        const XML_Char** attr) {
+  OmahaParserData* data = reinterpret_cast<OmahaParserData*>(user_data);
+
+  if (data->failed)
+    return;
+
+  data->current_path += string("/") + element;
+
+  map<string, string> attrs;
+  if (attr != nullptr) {
+    for (int n = 0; attr[n] != nullptr && attr[n + 1] != nullptr; n += 2) {
+      string key = attr[n];
+      string value = attr[n + 1];
+      attrs[key] = value;
+    }
+  }
+
+  if (data->current_path == "/response/app") {
+    OmahaParserData::App app;
+    if (attrs.find(kAttrAppId) != attrs.end()) {
+      app.id = attrs[kAttrAppId];
+    }
+    if (attrs.find(kAttrCohort) != attrs.end()) {
+      app.cohort_set = true;
+      app.cohort = attrs[kAttrCohort];
+    }
+    if (attrs.find(kAttrCohortHint) != attrs.end()) {
+      app.cohorthint_set = true;
+      app.cohorthint = attrs[kAttrCohortHint];
+    }
+    if (attrs.find(kAttrCohortName) != attrs.end()) {
+      app.cohortname_set = true;
+      app.cohortname = attrs[kAttrCohortName];
+    }
+    data->apps.push_back(std::move(app));
+  } else if (data->current_path == "/response/app/updatecheck") {
+    if (!data->apps.empty())
+      data->apps.back().updatecheck_status = attrs[kAttrStatus];
+    if (data->updatecheck_poll_interval.empty())
+      data->updatecheck_poll_interval = attrs[kAttrPollInterval];
+    // Omaha sends arbitrary key-value pairs as extra attributes starting with
+    // an underscore.
+    for (const auto& attr : attrs) {
+      if (!attr.first.empty() && attr.first[0] == '_')
+        data->updatecheck_attrs[attr.first.substr(1)] = attr.second;
+    }
+  } else if (data->current_path == "/response/daystart") {
+    // Get the install-date.
+    data->daystart_elapsed_days = attrs[kAttrElapsedDays];
+    data->daystart_elapsed_seconds = attrs[kAttrElapsedSeconds];
+  } else if (data->current_path == "/response/app/updatecheck/urls/url") {
+    // Look at all <url> elements.
+    if (!data->apps.empty())
+      data->apps.back().url_codebase.push_back(attrs[kAttrCodeBase]);
+  } else if (data->current_path ==
+             "/response/app/updatecheck/manifest/packages/package") {
+    // Look at all <package> elements.
+    if (!data->apps.empty())
+      data->apps.back().packages.push_back({.name = attrs[kAttrName],
+                                            .size = attrs[kAttrSize],
+                                            .hash = attrs[kAttrHashSha256]});
+  } else if (data->current_path == "/response/app/updatecheck/manifest") {
+    // Get the version.
+    if (!data->apps.empty())
+      data->apps.back().manifest_version = attrs[kAttrVersion];
+  } else if (data->current_path ==
+             "/response/app/updatecheck/manifest/actions/action") {
+    // We only care about the postinstall action.
+    if (attrs[kAttrEvent] == kValPostInstall && !data->apps.empty()) {
+      data->apps.back().action_postinstall_attrs = std::move(attrs);
+    }
+  }
+}
+
+// Callback function invoked by expat.
+void ParserHandlerEnd(void* user_data, const XML_Char* element) {
+  OmahaParserData* data = reinterpret_cast<OmahaParserData*>(user_data);
+  if (data->failed)
+    return;
+
+  const string path_suffix = string("/") + element;
+
+  if (!base::EndsWith(
+          data->current_path, path_suffix, base::CompareCase::SENSITIVE)) {
+    LOG(ERROR) << "Unexpected end element '" << element
+               << "' with current_path='" << data->current_path << "'";
+    data->failed = true;
+    return;
+  }
+  data->current_path.resize(data->current_path.size() - path_suffix.size());
+}
+
+// Callback function invoked by expat.
+//
+// This is called for entity declarations. Since Omaha is guaranteed
+// to never return any XML with entities our course of action is to
+// just stop parsing. This avoids potential resource exhaustion
+// problems AKA the "billion laughs". CVE-2013-0340.
+void ParserHandlerEntityDecl(void* user_data,
+                             const XML_Char* entity_name,
+                             int is_parameter_entity,
+                             const XML_Char* value,
+                             int value_length,
+                             const XML_Char* base,
+                             const XML_Char* system_id,
+                             const XML_Char* public_id,
+                             const XML_Char* notation_name) {
+  OmahaParserData* data = reinterpret_cast<OmahaParserData*>(user_data);
+
+  LOG(ERROR) << "XML entities are not supported. Aborting parsing.";
+  data->failed = true;
+  data->entity_decl = true;
+  XML_StopParser(data->xml_parser, false);
+}
+
+}  // namespace
+
+bool XmlEncode(const string& input, string* output) {
+  if (std::find_if(input.begin(), input.end(), [](const char c) {
+        return c & 0x80;
+      }) != input.end()) {
+    LOG(WARNING) << "Invalid ASCII-7 string passed to the XML encoder:";
+    utils::HexDumpString(input);
+    return false;
+  }
+  output->clear();
+  // We need at least input.size() space in the output, but the code below will
+  // handle it if we need more.
+  output->reserve(input.size());
+  for (char c : input) {
+    switch (c) {
+      case '\"':
+        output->append("&quot;");
+        break;
+      case '\'':
+        output->append("&apos;");
+        break;
+      case '&':
+        output->append("&amp;");
+        break;
+      case '<':
+        output->append("&lt;");
+        break;
+      case '>':
+        output->append("&gt;");
+        break;
+      default:
+        output->push_back(c);
+    }
+  }
+  return true;
+}
+
+string XmlEncodeWithDefault(const string& input, const string& default_value) {
+  string output;
+  if (XmlEncode(input, &output))
+    return output;
+  return default_value;
+}
+
+OmahaRequestAction::OmahaRequestAction(
+    SystemState* system_state,
+    OmahaEvent* event,
+    std::unique_ptr<HttpFetcher> http_fetcher,
+    bool ping_only)
+    : system_state_(system_state),
+      params_(system_state->request_params()),
+      event_(event),
+      http_fetcher_(std::move(http_fetcher)),
+      policy_provider_(std::make_unique<policy::PolicyProvider>()),
+      ping_only_(ping_only),
+      ping_active_days_(0),
+      ping_roll_call_days_(0) {
+  policy_provider_->Reload();
+}
+
+OmahaRequestAction::~OmahaRequestAction() {}
+
+// Calculates the value to use for the ping days parameter.
+int OmahaRequestAction::CalculatePingDays(const string& key) {
+  int days = kNeverPinged;
+  int64_t last_ping = 0;
+  if (system_state_->prefs()->GetInt64(key, &last_ping) && last_ping >= 0) {
+    days = (Time::Now() - Time::FromInternalValue(last_ping)).InDays();
+    if (days < 0) {
+      // If |days| is negative, then the system clock must have jumped
+      // back in time since the ping was sent. Mark the value so that
+      // it doesn't get sent to the server but we still update the
+      // last ping daystart preference. This way the next ping time
+      // will be correct, hopefully.
+      days = kPingTimeJump;
+      LOG(WARNING)
+          << "System clock jumped back in time. Resetting ping daystarts.";
+    }
+  }
+  return days;
+}
+
+void OmahaRequestAction::InitPingDays() {
+  // We send pings only along with update checks, not with events.
+  if (IsEvent()) {
+    return;
+  }
+  // TODO(petkov): Figure a way to distinguish active use pings
+  // vs. roll call pings. Currently, the two pings are identical. A
+  // fix needs to change this code as well as UpdateLastPingDays and ShouldPing.
+  ping_active_days_ = CalculatePingDays(kPrefsLastActivePingDay);
+  ping_roll_call_days_ = CalculatePingDays(kPrefsLastRollCallPingDay);
+}
+
+bool OmahaRequestAction::ShouldPing() const {
+  if (ping_active_days_ == OmahaRequestAction::kNeverPinged &&
+      ping_roll_call_days_ == OmahaRequestAction::kNeverPinged) {
+    int powerwash_count = system_state_->hardware()->GetPowerwashCount();
+    if (powerwash_count > 0) {
+      LOG(INFO) << "Not sending ping with a=-1 r=-1 to omaha because "
+                << "powerwash_count is " << powerwash_count;
+      return false;
+    }
+    if (system_state_->hardware()->GetFirstActiveOmahaPingSent()) {
+      LOG(INFO) << "Not sending ping with a=-1 r=-1 to omaha because "
+                << "the first_active_omaha_ping_sent is true";
+      return false;
+    }
+    return true;
+  }
+  return ping_active_days_ > 0 || ping_roll_call_days_ > 0;
+}
+
+// static
+int OmahaRequestAction::GetInstallDate(SystemState* system_state) {
+  PrefsInterface* prefs = system_state->prefs();
+  if (prefs == nullptr)
+    return -1;
+
+  // If we have the value stored on disk, just return it.
+  int64_t stored_value;
+  if (prefs->GetInt64(kPrefsInstallDateDays, &stored_value)) {
+    // Convert and sanity-check.
+    int install_date_days = static_cast<int>(stored_value);
+    if (install_date_days >= 0)
+      return install_date_days;
+    LOG(ERROR) << "Dropping stored Omaha InstallData since its value num_days="
+               << install_date_days << " looks suspicious.";
+    prefs->Delete(kPrefsInstallDateDays);
+  }
+
+  // Otherwise, if OOBE is not complete then do nothing and wait for
+  // ParseResponse() to call ParseInstallDate() and then
+  // PersistInstallDate() to set the kPrefsInstallDateDays state
+  // variable. Once that is done, we'll then report back in future
+  // Omaha requests.  This works exactly because OOBE triggers an
+  // update check.
+  //
+  // However, if OOBE is complete and the kPrefsInstallDateDays state
+  // variable is not set, there are two possibilities
+  //
+  //   1. The update check in OOBE failed so we never got a response
+  //      from Omaha (no network etc.); or
+  //
+  //   2. OOBE was done on an older version that didn't write to the
+  //      kPrefsInstallDateDays state variable.
+  //
+  // In both cases, we approximate the install date by simply
+  // inspecting the timestamp of when OOBE happened.
+
+  Time time_of_oobe;
+  if (!system_state->hardware()->IsOOBEEnabled() ||
+      !system_state->hardware()->IsOOBEComplete(&time_of_oobe)) {
+    LOG(INFO) << "Not generating Omaha InstallData as we have "
+              << "no prefs file and OOBE is not complete or not enabled.";
+    return -1;
+  }
+
+  int num_days;
+  if (!utils::ConvertToOmahaInstallDate(time_of_oobe, &num_days)) {
+    LOG(ERROR) << "Not generating Omaha InstallData from time of OOBE "
+               << "as its value '" << utils::ToString(time_of_oobe)
+               << "' looks suspicious.";
+    return -1;
+  }
+
+  // Persist this to disk, for future use.
+  if (!OmahaRequestAction::PersistInstallDate(
+          system_state, num_days, kProvisionedFromOOBEMarker))
+    return -1;
+
+  LOG(INFO) << "Set the Omaha InstallDate from OOBE time-stamp to " << num_days
+            << " days";
+
+  return num_days;
+}
+
+void OmahaRequestAction::PerformAction() {
+  http_fetcher_->set_delegate(this);
+  InitPingDays();
+  if (ping_only_ && !ShouldPing()) {
+    processor_->ActionComplete(this, ErrorCode::kSuccess);
+    return;
+  }
+
+  string request_post(GetRequestXml(event_.get(),
+                                    params_,
+                                    ping_only_,
+                                    ShouldPing(),  // include_ping
+                                    ping_active_days_,
+                                    ping_roll_call_days_,
+                                    GetInstallDate(system_state_),
+                                    system_state_));
+
+  // Set X-Goog-Update headers.
+  http_fetcher_->SetHeader(kXGoogleUpdateInteractivity,
+                           params_->interactive() ? "fg" : "bg");
+  http_fetcher_->SetHeader(kXGoogleUpdateAppId, params_->GetAppId());
+  http_fetcher_->SetHeader(
+      kXGoogleUpdateUpdater,
+      base::StringPrintf(
+          "%s-%s", constants::kOmahaUpdaterID, kOmahaUpdaterVersion));
+
+  http_fetcher_->SetPostData(
+      request_post.data(), request_post.size(), kHttpContentTypeTextXml);
+  LOG(INFO) << "Posting an Omaha request to " << params_->update_url();
+  LOG(INFO) << "Request: " << request_post;
+  http_fetcher_->BeginTransfer(params_->update_url());
+}
+
+void OmahaRequestAction::TerminateProcessing() {
+  http_fetcher_->TerminateTransfer();
+}
+
+// We just store the response in the buffer. Once we've received all bytes,
+// we'll look in the buffer and decide what to do.
+bool OmahaRequestAction::ReceivedBytes(HttpFetcher* fetcher,
+                                       const void* bytes,
+                                       size_t length) {
+  const uint8_t* byte_ptr = reinterpret_cast<const uint8_t*>(bytes);
+  response_buffer_.insert(response_buffer_.end(), byte_ptr, byte_ptr + length);
+  return true;
+}
+
+namespace {
+
+// Parses a 64 bit base-10 int from a string and returns it. Returns 0
+// on error. If the string contains "0", that's indistinguishable from
+// error.
+off_t ParseInt(const string& str) {
+  off_t ret = 0;
+  int rc = sscanf(str.c_str(), "%" PRIi64, &ret);  // NOLINT(runtime/printf)
+  if (rc < 1) {
+    // failure
+    return 0;
+  }
+  return ret;
+}
+
+// Parses |str| and returns |true| if, and only if, its value is "true".
+bool ParseBool(const string& str) {
+  return str == "true";
+}
+
+// Update the last ping day preferences based on the server daystart
+// response. Returns true on success, false otherwise.
+bool UpdateLastPingDays(OmahaParserData* parser_data, PrefsInterface* prefs) {
+  int64_t elapsed_seconds = 0;
+  TEST_AND_RETURN_FALSE(base::StringToInt64(
+      parser_data->daystart_elapsed_seconds, &elapsed_seconds));
+  TEST_AND_RETURN_FALSE(elapsed_seconds >= 0);
+
+  // Remember the local time that matches the server's last midnight
+  // time.
+  Time daystart = Time::Now() - TimeDelta::FromSeconds(elapsed_seconds);
+  prefs->SetInt64(kPrefsLastActivePingDay, daystart.ToInternalValue());
+  prefs->SetInt64(kPrefsLastRollCallPingDay, daystart.ToInternalValue());
+  return true;
+}
+
+// Parses the package node in the given XML document and populates
+// |output_object| if valid. Returns true if we should continue the parsing.
+// False otherwise, in which case it sets any error code using |completer|.
+bool ParsePackage(OmahaParserData::App* app,
+                  OmahaResponse* output_object,
+                  ScopedActionCompleter* completer) {
+  if (app->updatecheck_status.empty() ||
+      app->updatecheck_status == kValNoUpdate) {
+    if (!app->packages.empty()) {
+      LOG(ERROR) << "No update in this <app> but <package> is not empty.";
+      completer->set_code(ErrorCode::kOmahaResponseInvalid);
+      return false;
+    }
+    return true;
+  }
+  if (app->packages.empty()) {
+    LOG(ERROR) << "Omaha Response has no packages";
+    completer->set_code(ErrorCode::kOmahaResponseInvalid);
+    return false;
+  }
+  if (app->url_codebase.empty()) {
+    LOG(ERROR) << "No Omaha Response URLs";
+    completer->set_code(ErrorCode::kOmahaResponseInvalid);
+    return false;
+  }
+  LOG(INFO) << "Found " << app->url_codebase.size() << " url(s)";
+  vector<string> metadata_sizes =
+      base::SplitString(app->action_postinstall_attrs[kAttrMetadataSize],
+                        ":",
+                        base::TRIM_WHITESPACE,
+                        base::SPLIT_WANT_ALL);
+  vector<string> metadata_signatures = base::SplitString(
+      app->action_postinstall_attrs[kAttrMetadataSignatureRsa],
+      ":",
+      base::TRIM_WHITESPACE,
+      base::SPLIT_WANT_ALL);
+  vector<string> is_delta_payloads =
+      base::SplitString(app->action_postinstall_attrs[kAttrIsDeltaPayload],
+                        ":",
+                        base::TRIM_WHITESPACE,
+                        base::SPLIT_WANT_ALL);
+  for (size_t i = 0; i < app->packages.size(); i++) {
+    const auto& package = app->packages[i];
+    if (package.name.empty()) {
+      LOG(ERROR) << "Omaha Response has empty package name";
+      completer->set_code(ErrorCode::kOmahaResponseInvalid);
+      return false;
+    }
+    LOG(INFO) << "Found package " << package.name;
+
+    OmahaResponse::Package out_package;
+    for (const string& codebase : app->url_codebase) {
+      if (codebase.empty()) {
+        LOG(ERROR) << "Omaha Response URL has empty codebase";
+        completer->set_code(ErrorCode::kOmahaResponseInvalid);
+        return false;
+      }
+      out_package.payload_urls.push_back(codebase + package.name);
+    }
+    // Parse the payload size.
+    base::StringToUint64(package.size, &out_package.size);
+    if (out_package.size <= 0) {
+      LOG(ERROR) << "Omaha Response has invalid payload size: " << package.size;
+      completer->set_code(ErrorCode::kOmahaResponseInvalid);
+      return false;
+    }
+    LOG(INFO) << "Payload size = " << out_package.size << " bytes";
+
+    if (i < metadata_sizes.size())
+      base::StringToUint64(metadata_sizes[i], &out_package.metadata_size);
+    LOG(INFO) << "Payload metadata size = " << out_package.metadata_size
+              << " bytes";
+
+    if (i < metadata_signatures.size())
+      out_package.metadata_signature = metadata_signatures[i];
+    LOG(INFO) << "Payload metadata signature = "
+              << out_package.metadata_signature;
+
+    out_package.hash = package.hash;
+    if (out_package.hash.empty()) {
+      LOG(ERROR) << "Omaha Response has empty hash_sha256 value";
+      completer->set_code(ErrorCode::kOmahaResponseInvalid);
+      return false;
+    }
+    LOG(INFO) << "Payload hash = " << out_package.hash;
+
+    if (i < is_delta_payloads.size())
+      out_package.is_delta = ParseBool(is_delta_payloads[i]);
+    LOG(INFO) << "Payload is delta = " << utils::ToString(out_package.is_delta);
+
+    output_object->packages.push_back(std::move(out_package));
+  }
+
+  return true;
+}
+
+// Parses the 2 key version strings kernel_version and firmware_version. If the
+// field is not present, or cannot be parsed the values default to 0xffff.
+void ParseRollbackVersions(OmahaParserData* parser_data,
+                           OmahaResponse* output_object) {
+  utils::ParseRollbackKeyVersion(
+      parser_data->updatecheck_attrs[kAttrFirmwareVersion],
+      &output_object->rollback_key_version.firmware_key,
+      &output_object->rollback_key_version.firmware);
+  utils::ParseRollbackKeyVersion(
+      parser_data->updatecheck_attrs[kAttrKernelVersion],
+      &output_object->rollback_key_version.kernel_key,
+      &output_object->rollback_key_version.kernel);
+}
+
+}  // namespace
+
+bool OmahaRequestAction::ParseResponse(OmahaParserData* parser_data,
+                                       OmahaResponse* output_object,
+                                       ScopedActionCompleter* completer) {
+  if (parser_data->apps.empty()) {
+    completer->set_code(ErrorCode::kOmahaResponseInvalid);
+    return false;
+  }
+  LOG(INFO) << "Found " << parser_data->apps.size() << " <app>.";
+
+  // chromium-os:37289: The PollInterval is not supported by Omaha server
+  // currently.  But still keeping this existing code in case we ever decide to
+  // slow down the request rate from the server-side. Note that the PollInterval
+  // is not persisted, so it has to be sent by the server on every response to
+  // guarantee that the scheduler uses this value (otherwise, if the device got
+  // rebooted after the last server-indicated value, it'll revert to the default
+  // value). Also kDefaultMaxUpdateChecks value for the scattering logic is
+  // based on the assumption that we perform an update check every hour so that
+  // the max value of 8 will roughly be equivalent to one work day. If we decide
+  // to use PollInterval permanently, we should update the
+  // max_update_checks_allowed to take PollInterval into account.  Note: The
+  // parsing for PollInterval happens even before parsing of the status because
+  // we may want to specify the PollInterval even when there's no update.
+  base::StringToInt(parser_data->updatecheck_poll_interval,
+                    &output_object->poll_interval);
+
+  // Check for the "elapsed_days" attribute in the "daystart"
+  // element. This is the number of days since Jan 1 2007, 0:00
+  // PST. If we don't have a persisted value of the Omaha InstallDate,
+  // we'll use it to calculate it and then persist it.
+  if (ParseInstallDate(parser_data, output_object) &&
+      !HasInstallDate(system_state_)) {
+    // Since output_object->install_date_days is never negative, the
+    // elapsed_days -> install-date calculation is reduced to simply
+    // rounding down to the nearest number divisible by 7.
+    int remainder = output_object->install_date_days % 7;
+    int install_date_days_rounded =
+        output_object->install_date_days - remainder;
+    if (PersistInstallDate(system_state_,
+                           install_date_days_rounded,
+                           kProvisionedFromOmahaResponse)) {
+      LOG(INFO) << "Set the Omaha InstallDate from Omaha Response to "
+                << install_date_days_rounded << " days";
+    }
+  }
+
+  // We persist the cohorts sent by omaha even if the status is "noupdate".
+  for (const auto& app : parser_data->apps) {
+    if (app.id == params_->GetAppId()) {
+      if (app.cohort_set)
+        PersistCohortData(kPrefsOmahaCohort, app.cohort);
+      if (app.cohorthint_set)
+        PersistCohortData(kPrefsOmahaCohortHint, app.cohorthint);
+      if (app.cohortname_set)
+        PersistCohortData(kPrefsOmahaCohortName, app.cohortname);
+      break;
+    }
+  }
+
+  // Parse the updatecheck attributes.
+  PersistEolStatus(parser_data->updatecheck_attrs);
+  // Rollback-related updatecheck attributes.
+  // Defaults to false if attribute is not present.
+  output_object->is_rollback =
+      ParseBool(parser_data->updatecheck_attrs[kAttrRollback]);
+
+  // Parses the rollback versions of the current image. If the fields do not
+  // exist they default to 0xffff for the 4 key versions.
+  ParseRollbackVersions(parser_data, output_object);
+
+  if (!ParseStatus(parser_data, output_object, completer))
+    return false;
+
+  if (!ParseParams(parser_data, output_object, completer))
+    return false;
+
+  // Package has to be parsed after Params now because ParseParams need to make
+  // sure that postinstall action exists.
+  for (auto& app : parser_data->apps)
+    if (!ParsePackage(&app, output_object, completer))
+      return false;
+
+  return true;
+}
+
+bool OmahaRequestAction::ParseStatus(OmahaParserData* parser_data,
+                                     OmahaResponse* output_object,
+                                     ScopedActionCompleter* completer) {
+  output_object->update_exists = false;
+  for (const auto& app : parser_data->apps) {
+    const string& status = app.updatecheck_status;
+    if (status == kValNoUpdate) {
+      // Don't update if any app has status="noupdate".
+      LOG(INFO) << "No update for <app> " << app.id;
+      output_object->update_exists = false;
+      break;
+    } else if (status == "ok") {
+      auto const& attr_no_update =
+          app.action_postinstall_attrs.find(kAttrNoUpdate);
+      if (attr_no_update != app.action_postinstall_attrs.end() &&
+          attr_no_update->second == "true") {
+        // noupdate="true" in postinstall attributes means it's an update to
+        // self, only update if there's at least one app really have update.
+        LOG(INFO) << "Update to self for <app> " << app.id;
+      } else {
+        LOG(INFO) << "Update for <app> " << app.id;
+        output_object->update_exists = true;
+      }
+    } else if (status.empty() && params_->is_install() &&
+               params_->GetAppId() == app.id) {
+      // Skips the platform app for install operation.
+      LOG(INFO) << "No payload (and ignore) for <app> " << app.id;
+    } else {
+      LOG(ERROR) << "Unknown Omaha response status: " << status;
+      completer->set_code(ErrorCode::kOmahaResponseInvalid);
+      return false;
+    }
+  }
+  if (!output_object->update_exists) {
+    SetOutputObject(*output_object);
+    completer->set_code(ErrorCode::kSuccess);
+  }
+
+  return output_object->update_exists;
+}
+
+bool OmahaRequestAction::ParseParams(OmahaParserData* parser_data,
+                                     OmahaResponse* output_object,
+                                     ScopedActionCompleter* completer) {
+  map<string, string> attrs;
+  for (auto& app : parser_data->apps) {
+    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
+                   << ") that is different than platform app version ("
+                   << params_->app_version() << ")";
+    }
+    if (!app.action_postinstall_attrs.empty() && attrs.empty()) {
+      attrs = app.action_postinstall_attrs;
+    }
+  }
+  if (params_->is_install()) {
+    LOG(INFO) << "Use request version for Install operation.";
+    output_object->version = params_->app_version();
+  }
+  if (output_object->version.empty()) {
+    LOG(ERROR) << "Omaha Response does not have version in manifest!";
+    completer->set_code(ErrorCode::kOmahaResponseInvalid);
+    return false;
+  }
+
+  LOG(INFO) << "Received omaha response to update to version "
+            << output_object->version;
+
+  if (attrs.empty()) {
+    LOG(ERROR) << "Omaha Response has no postinstall event action";
+    completer->set_code(ErrorCode::kOmahaResponseInvalid);
+    return false;
+  }
+
+  // Get the optional properties one by one.
+  output_object->more_info_url = attrs[kAttrMoreInfo];
+  output_object->prompt = ParseBool(attrs[kAttrPrompt]);
+  output_object->deadline = attrs[kAttrDeadline];
+  output_object->max_days_to_scatter = ParseInt(attrs[kAttrMaxDaysToScatter]);
+  output_object->disable_p2p_for_downloading =
+      ParseBool(attrs[kAttrDisableP2PForDownloading]);
+  output_object->disable_p2p_for_sharing =
+      ParseBool(attrs[kAttrDisableP2PForSharing]);
+  output_object->public_key_rsa = attrs[kAttrPublicKeyRsa];
+
+  string max = attrs[kAttrMaxFailureCountPerUrl];
+  if (!base::StringToUint(max, &output_object->max_failure_count_per_url))
+    output_object->max_failure_count_per_url = kDefaultMaxFailureCountPerUrl;
+
+  output_object->disable_payload_backoff =
+      ParseBool(attrs[kAttrDisablePayloadBackoff]);
+  output_object->powerwash_required = ParseBool(attrs[kAttrPowerwash]);
+
+  return true;
+}
+
+// If the transfer was successful, this uses expat to parse the response
+// and fill in the appropriate fields of the output object. Also, notifies
+// the processor that we're done.
+void OmahaRequestAction::TransferComplete(HttpFetcher* fetcher,
+                                          bool successful) {
+  ScopedActionCompleter completer(processor_, this);
+  string current_response(response_buffer_.begin(), response_buffer_.end());
+  LOG(INFO) << "Omaha request response: " << current_response;
+
+  PayloadStateInterface* const payload_state = system_state_->payload_state();
+
+  // Set the max kernel key version based on whether rollback is allowed.
+  SetMaxKernelKeyVersionForRollback();
+
+  // Events are best effort transactions -- assume they always succeed.
+  if (IsEvent()) {
+    CHECK(!HasOutputPipe()) << "No output pipe allowed for event requests.";
+    completer.set_code(ErrorCode::kSuccess);
+    return;
+  }
+
+  if (!successful) {
+    LOG(ERROR) << "Omaha request network transfer failed.";
+    int code = GetHTTPResponseCode();
+    // Makes sure we send sane error values.
+    if (code < 0 || code >= 1000) {
+      code = 999;
+    }
+    completer.set_code(static_cast<ErrorCode>(
+        static_cast<int>(ErrorCode::kOmahaRequestHTTPResponseBase) + code));
+    return;
+  }
+
+  XML_Parser parser = XML_ParserCreate(nullptr);
+  OmahaParserData parser_data(parser);
+  XML_SetUserData(parser, &parser_data);
+  XML_SetElementHandler(parser, ParserHandlerStart, ParserHandlerEnd);
+  XML_SetEntityDeclHandler(parser, ParserHandlerEntityDecl);
+  XML_Status res =
+      XML_Parse(parser,
+                reinterpret_cast<const char*>(response_buffer_.data()),
+                response_buffer_.size(),
+                XML_TRUE);
+
+  if (res != XML_STATUS_OK || parser_data.failed) {
+    LOG(ERROR) << "Omaha response not valid XML: "
+               << XML_ErrorString(XML_GetErrorCode(parser)) << " at line "
+               << XML_GetCurrentLineNumber(parser) << " col "
+               << XML_GetCurrentColumnNumber(parser);
+    XML_ParserFree(parser);
+    ErrorCode error_code = ErrorCode::kOmahaRequestXMLParseError;
+    if (response_buffer_.empty()) {
+      error_code = ErrorCode::kOmahaRequestEmptyResponseError;
+    } else if (parser_data.entity_decl) {
+      error_code = ErrorCode::kOmahaRequestXMLHasEntityDecl;
+    }
+    completer.set_code(error_code);
+    return;
+  }
+  XML_ParserFree(parser);
+
+  // Update the last ping day preferences based on the server daystart response
+  // even if we didn't send a ping. Omaha always includes the daystart in the
+  // response, but log the error if it didn't.
+  LOG_IF(ERROR, !UpdateLastPingDays(&parser_data, system_state_->prefs()))
+      << "Failed to update the last ping day preferences!";
+
+  // Sets first_active_omaha_ping_sent to true (vpd in CrOS). We only do this if
+  // we have got a response from omaha and if its value has never been set to
+  // true before. Failure of this function should be ignored. There should be no
+  // need to check if a=-1 has been sent because older devices have already sent
+  // their a=-1 in the past and we have to set first_active_omaha_ping_sent for
+  // future checks.
+  if (!system_state_->hardware()->GetFirstActiveOmahaPingSent()) {
+    if (!system_state_->hardware()->SetFirstActiveOmahaPingSent()) {
+      system_state_->metrics_reporter()->ReportInternalErrorCode(
+          ErrorCode::kFirstActiveOmahaPingSentPersistenceError);
+    }
+  }
+
+  if (!HasOutputPipe()) {
+    // Just set success to whether or not the http transfer succeeded,
+    // which must be true at this point in the code.
+    completer.set_code(ErrorCode::kSuccess);
+    return;
+  }
+
+  OmahaResponse output_object;
+  if (!ParseResponse(&parser_data, &output_object, &completer))
+    return;
+  output_object.update_exists = true;
+  SetOutputObject(output_object);
+
+  LoadOrPersistUpdateFirstSeenAtPref();
+
+  ErrorCode error = ErrorCode::kSuccess;
+  if (ShouldIgnoreUpdate(output_object, &error)) {
+    // No need to change output_object.update_exists here, since the value
+    // has been output to the pipe.
+    completer.set_code(error);
+    return;
+  }
+
+  // If Omaha says to disable p2p, respect that
+  if (output_object.disable_p2p_for_downloading) {
+    LOG(INFO) << "Forcibly disabling use of p2p for downloading as "
+              << "requested by Omaha.";
+    payload_state->SetUsingP2PForDownloading(false);
+  }
+  if (output_object.disable_p2p_for_sharing) {
+    LOG(INFO) << "Forcibly disabling use of p2p for sharing as "
+              << "requested by Omaha.";
+    payload_state->SetUsingP2PForSharing(false);
+  }
+
+  // Update the payload state with the current response. The payload state
+  // will automatically reset all stale state if this response is different
+  // from what's stored already. We are updating the payload state as late
+  // as possible in this method so that if a new release gets pushed and then
+  // got pulled back due to some issues, we don't want to clear our internal
+  // state unnecessarily.
+  payload_state->SetResponse(output_object);
+
+  // It could be we've already exceeded the deadline for when p2p is
+  // allowed or that we've tried too many times with p2p. Check that.
+  if (payload_state->GetUsingP2PForDownloading()) {
+    payload_state->P2PNewAttempt();
+    if (!payload_state->P2PAttemptAllowed()) {
+      LOG(INFO) << "Forcibly disabling use of p2p for downloading because "
+                << "of previous failures when using p2p.";
+      payload_state->SetUsingP2PForDownloading(false);
+    }
+  }
+
+  // From here on, we'll complete stuff in CompleteProcessing() so
+  // disable |completer| since we'll create a new one in that
+  // function.
+  completer.set_should_complete(false);
+
+  // If we're allowed to use p2p for downloading we do not pay
+  // attention to wall-clock-based waiting if the URL is indeed
+  // available via p2p. Therefore, check if the file is available via
+  // p2p before deferring...
+  if (payload_state->GetUsingP2PForDownloading()) {
+    LookupPayloadViaP2P(output_object);
+  } else {
+    CompleteProcessing();
+  }
+}
+
+void OmahaRequestAction::CompleteProcessing() {
+  ScopedActionCompleter completer(processor_, this);
+  OmahaResponse& output_object = const_cast<OmahaResponse&>(GetOutputObject());
+  PayloadStateInterface* payload_state = system_state_->payload_state();
+
+  if (ShouldDeferDownload(&output_object)) {
+    output_object.update_exists = false;
+    LOG(INFO) << "Ignoring Omaha updates as updates are deferred by policy.";
+    completer.set_code(ErrorCode::kOmahaUpdateDeferredPerPolicy);
+    return;
+  }
+
+  if (payload_state->ShouldBackoffDownload()) {
+    output_object.update_exists = false;
+    LOG(INFO) << "Ignoring Omaha updates in order to backoff our retry "
+              << "attempts";
+    completer.set_code(ErrorCode::kOmahaUpdateDeferredForBackoff);
+    return;
+  }
+  completer.set_code(ErrorCode::kSuccess);
+}
+
+void OmahaRequestAction::OnLookupPayloadViaP2PCompleted(const string& url) {
+  LOG(INFO) << "Lookup complete, p2p-client returned URL '" << url << "'";
+  if (!url.empty()) {
+    system_state_->payload_state()->SetP2PUrl(url);
+  } else {
+    LOG(INFO) << "Forcibly disabling use of p2p for downloading "
+              << "because no suitable peer could be found.";
+    system_state_->payload_state()->SetUsingP2PForDownloading(false);
+  }
+  CompleteProcessing();
+}
+
+void OmahaRequestAction::LookupPayloadViaP2P(const OmahaResponse& response) {
+  // If the device is in the middle of an update, the state variables
+  // kPrefsUpdateStateNextDataOffset, kPrefsUpdateStateNextDataLength
+  // tracks the offset and length of the operation currently in
+  // progress. The offset is based from the end of the manifest which
+  // is kPrefsManifestMetadataSize bytes long.
+  //
+  // To make forward progress and avoid deadlocks, we need to find a
+  // peer that has at least the entire operation we're currently
+  // working on. Otherwise we may end up in a situation where two
+  // devices bounce back and forth downloading from each other,
+  // neither making any forward progress until one of them decides to
+  // stop using p2p (via kMaxP2PAttempts and kMaxP2PAttemptTimeSeconds
+  // safe-guards). See http://crbug.com/297170 for an example)
+  size_t minimum_size = 0;
+  int64_t manifest_metadata_size = 0;
+  int64_t manifest_signature_size = 0;
+  int64_t next_data_offset = 0;
+  int64_t next_data_length = 0;
+  if (system_state_ &&
+      system_state_->prefs()->GetInt64(kPrefsManifestMetadataSize,
+                                       &manifest_metadata_size) &&
+      manifest_metadata_size != -1 &&
+      system_state_->prefs()->GetInt64(kPrefsManifestSignatureSize,
+                                       &manifest_signature_size) &&
+      manifest_signature_size != -1 &&
+      system_state_->prefs()->GetInt64(kPrefsUpdateStateNextDataOffset,
+                                       &next_data_offset) &&
+      next_data_offset != -1 &&
+      system_state_->prefs()->GetInt64(kPrefsUpdateStateNextDataLength,
+                                       &next_data_length)) {
+    minimum_size = manifest_metadata_size + manifest_signature_size +
+                   next_data_offset + next_data_length;
+  }
+
+  // TODO(senj): Fix P2P for multiple package.
+  brillo::Blob raw_hash;
+  if (!base::HexStringToBytes(response.packages[0].hash, &raw_hash))
+    return;
+  string file_id =
+      utils::CalculateP2PFileId(raw_hash, response.packages[0].size);
+  if (system_state_->p2p_manager()) {
+    LOG(INFO) << "Checking if payload is available via p2p, file_id=" << file_id
+              << " minimum_size=" << minimum_size;
+    system_state_->p2p_manager()->LookupUrlForFile(
+        file_id,
+        minimum_size,
+        TimeDelta::FromSeconds(kMaxP2PNetworkWaitTimeSeconds),
+        base::Bind(&OmahaRequestAction::OnLookupPayloadViaP2PCompleted,
+                   base::Unretained(this)));
+  }
+}
+
+bool OmahaRequestAction::ShouldDeferDownload(OmahaResponse* output_object) {
+  if (params_->interactive()) {
+    LOG(INFO) << "Not deferring download because update is interactive.";
+    return false;
+  }
+
+  // If we're using p2p to download _and_ we have a p2p URL, we never
+  // defer the download. This is because the download will always
+  // happen from a peer on the LAN and we've been waiting in line for
+  // our turn.
+  const PayloadStateInterface* payload_state = system_state_->payload_state();
+  if (payload_state->GetUsingP2PForDownloading() &&
+      !payload_state->GetP2PUrl().empty()) {
+    LOG(INFO) << "Download not deferred because download "
+              << "will happen from a local peer (via p2p).";
+    return false;
+  }
+
+  // We should defer the downloads only if we've first satisfied the
+  // wall-clock-based-waiting period and then the update-check-based waiting
+  // period, if required.
+  if (!params_->wall_clock_based_wait_enabled()) {
+    LOG(INFO) << "Wall-clock-based waiting period is not enabled,"
+              << " so no deferring needed.";
+    return false;
+  }
+
+  switch (IsWallClockBasedWaitingSatisfied(output_object)) {
+    case kWallClockWaitNotSatisfied:
+      // We haven't even satisfied the first condition, passing the
+      // wall-clock-based waiting period, so we should defer the downloads
+      // until that happens.
+      LOG(INFO) << "wall-clock-based-wait not satisfied.";
+      return true;
+
+    case kWallClockWaitDoneButUpdateCheckWaitRequired:
+      LOG(INFO) << "wall-clock-based-wait satisfied and "
+                << "update-check-based-wait required.";
+      return !IsUpdateCheckCountBasedWaitingSatisfied();
+
+    case kWallClockWaitDoneAndUpdateCheckWaitNotRequired:
+      // Wall-clock-based waiting period is satisfied, and it's determined
+      // that we do not need the update-check-based wait. so no need to
+      // defer downloads.
+      LOG(INFO) << "wall-clock-based-wait satisfied and "
+                << "update-check-based-wait is not required.";
+      return false;
+
+    default:
+      // Returning false for this default case so we err on the
+      // side of downloading updates than deferring in case of any bugs.
+      NOTREACHED();
+      return false;
+  }
+}
+
+OmahaRequestAction::WallClockWaitResult
+OmahaRequestAction::IsWallClockBasedWaitingSatisfied(
+    OmahaResponse* output_object) {
+  Time update_first_seen_at = LoadOrPersistUpdateFirstSeenAtPref();
+  if (update_first_seen_at == base::Time()) {
+    LOG(INFO) << "Not scattering as UpdateFirstSeenAt value cannot be read or "
+                 "persisted";
+    return kWallClockWaitDoneAndUpdateCheckWaitNotRequired;
+  }
+
+  TimeDelta elapsed_time =
+      system_state_->clock()->GetWallclockTime() - update_first_seen_at;
+  TimeDelta max_scatter_period =
+      TimeDelta::FromDays(output_object->max_days_to_scatter);
+  int64_t staging_wait_time_in_days = 0;
+  // Use staging and its default max value if staging is on.
+  if (system_state_->prefs()->GetInt64(kPrefsWallClockStagingWaitPeriod,
+                                       &staging_wait_time_in_days) &&
+      staging_wait_time_in_days > 0)
+    max_scatter_period = TimeDelta::FromDays(kMaxWaitTimeStagingInDays);
+
+  LOG(INFO) << "Waiting Period = "
+            << utils::FormatSecs(params_->waiting_period().InSeconds())
+            << ", Time Elapsed = "
+            << utils::FormatSecs(elapsed_time.InSeconds())
+            << ", MaxDaysToScatter = " << max_scatter_period.InDays();
+
+  if (!output_object->deadline.empty()) {
+    // The deadline is set for all rules which serve a delta update from a
+    // previous FSI, which means this update will be applied mostly in OOBE
+    // cases. For these cases, we shouldn't scatter so as to finish the OOBE
+    // quickly.
+    LOG(INFO) << "Not scattering as deadline flag is set";
+    return kWallClockWaitDoneAndUpdateCheckWaitNotRequired;
+  }
+
+  if (max_scatter_period.InDays() == 0) {
+    // This means the Omaha rule creator decides that this rule
+    // should not be scattered irrespective of the policy.
+    LOG(INFO) << "Not scattering as MaxDaysToScatter in rule is 0.";
+    return kWallClockWaitDoneAndUpdateCheckWaitNotRequired;
+  }
+
+  if (elapsed_time > max_scatter_period) {
+    // This means we've waited more than the upperbound wait in the rule
+    // from the time we first saw a valid update available to us.
+    // This will prevent update starvation.
+    LOG(INFO) << "Not scattering as we're past the MaxDaysToScatter limit.";
+    return kWallClockWaitDoneAndUpdateCheckWaitNotRequired;
+  }
+
+  // This means we are required to participate in scattering.
+  // See if our turn has arrived now.
+  TimeDelta remaining_wait_time = params_->waiting_period() - elapsed_time;
+  if (remaining_wait_time.InSeconds() <= 0) {
+    // Yes, it's our turn now.
+    LOG(INFO) << "Successfully passed the wall-clock-based-wait.";
+
+    // But we can't download until the update-check-count-based wait is also
+    // satisfied, so mark it as required now if update checks are enabled.
+    return params_->update_check_count_wait_enabled()
+               ? kWallClockWaitDoneButUpdateCheckWaitRequired
+               : kWallClockWaitDoneAndUpdateCheckWaitNotRequired;
+  }
+
+  // Not our turn yet, so we have to wait until our turn to
+  // help scatter the downloads across all clients of the enterprise.
+  LOG(INFO) << "Update deferred for another "
+            << utils::FormatSecs(remaining_wait_time.InSeconds())
+            << " per policy.";
+  return kWallClockWaitNotSatisfied;
+}
+
+bool OmahaRequestAction::IsUpdateCheckCountBasedWaitingSatisfied() {
+  int64_t update_check_count_value;
+
+  if (system_state_->prefs()->Exists(kPrefsUpdateCheckCount)) {
+    if (!system_state_->prefs()->GetInt64(kPrefsUpdateCheckCount,
+                                          &update_check_count_value)) {
+      // We are unable to read the update check count from file for some reason.
+      // So let's proceed anyway so as to not stall the update.
+      LOG(ERROR) << "Unable to read update check count. "
+                 << "Skipping update-check-count-based-wait.";
+      return true;
+    }
+  } else {
+    // This file does not exist. This means we haven't started our update
+    // check count down yet, so this is the right time to start the count down.
+    update_check_count_value =
+        base::RandInt(params_->min_update_checks_needed(),
+                      params_->max_update_checks_allowed());
+
+    LOG(INFO) << "Randomly picked update check count value = "
+              << update_check_count_value;
+
+    // Write out the initial value of update_check_count_value.
+    if (!system_state_->prefs()->SetInt64(kPrefsUpdateCheckCount,
+                                          update_check_count_value)) {
+      // We weren't able to write the update check count file for some reason.
+      // So let's proceed anyway so as to not stall the update.
+      LOG(ERROR) << "Unable to write update check count. "
+                 << "Skipping update-check-count-based-wait.";
+      return true;
+    }
+  }
+
+  if (update_check_count_value == 0) {
+    LOG(INFO) << "Successfully passed the update-check-based-wait.";
+    return true;
+  }
+
+  if (update_check_count_value < 0 ||
+      update_check_count_value > params_->max_update_checks_allowed()) {
+    // We err on the side of skipping scattering logic instead of stalling
+    // a machine from receiving any updates in case of any unexpected state.
+    LOG(ERROR) << "Invalid value for update check count detected. "
+               << "Skipping update-check-count-based-wait.";
+    return true;
+  }
+
+  // Legal value, we need to wait for more update checks to happen
+  // until this becomes 0.
+  LOG(INFO) << "Deferring Omaha updates for another "
+            << update_check_count_value << " update checks per policy";
+  return false;
+}
+
+// static
+bool OmahaRequestAction::ParseInstallDate(OmahaParserData* parser_data,
+                                          OmahaResponse* output_object) {
+  int64_t elapsed_days = 0;
+  if (!base::StringToInt64(parser_data->daystart_elapsed_days, &elapsed_days))
+    return false;
+
+  if (elapsed_days < 0)
+    return false;
+
+  output_object->install_date_days = elapsed_days;
+  return true;
+}
+
+// static
+bool OmahaRequestAction::HasInstallDate(SystemState* system_state) {
+  PrefsInterface* prefs = system_state->prefs();
+  if (prefs == nullptr)
+    return false;
+
+  return prefs->Exists(kPrefsInstallDateDays);
+}
+
+// static
+bool OmahaRequestAction::PersistInstallDate(
+    SystemState* system_state,
+    int install_date_days,
+    InstallDateProvisioningSource source) {
+  TEST_AND_RETURN_FALSE(install_date_days >= 0);
+
+  PrefsInterface* prefs = system_state->prefs();
+  if (prefs == nullptr)
+    return false;
+
+  if (!prefs->SetInt64(kPrefsInstallDateDays, install_date_days))
+    return false;
+
+  system_state->metrics_reporter()->ReportInstallDateProvisioningSource(
+      static_cast<int>(source),  // Sample.
+      kProvisionedMax);          // Maximum.
+  return true;
+}
+
+bool OmahaRequestAction::PersistCohortData(const string& prefs_key,
+                                           const string& new_value) {
+  if (new_value.empty() && system_state_->prefs()->Exists(prefs_key)) {
+    LOG(INFO) << "Removing stored " << prefs_key << " value.";
+    return system_state_->prefs()->Delete(prefs_key);
+  } else if (!new_value.empty()) {
+    LOG(INFO) << "Storing new setting " << prefs_key << " as " << new_value;
+    return system_state_->prefs()->SetString(prefs_key, new_value);
+  }
+  return true;
+}
+
+bool OmahaRequestAction::PersistEolStatus(const map<string, string>& attrs) {
+  auto eol_attr = attrs.find(kAttrEol);
+  if (eol_attr != attrs.end()) {
+    return system_state_->prefs()->SetString(kPrefsOmahaEolStatus,
+                                             eol_attr->second);
+  } else if (system_state_->prefs()->Exists(kPrefsOmahaEolStatus)) {
+    return system_state_->prefs()->Delete(kPrefsOmahaEolStatus);
+  }
+  return true;
+}
+
+void OmahaRequestAction::ActionCompleted(ErrorCode code) {
+  // We only want to report this on "update check".
+  if (ping_only_ || event_ != nullptr)
+    return;
+
+  metrics::CheckResult result = metrics::CheckResult::kUnset;
+  metrics::CheckReaction reaction = metrics::CheckReaction::kUnset;
+  metrics::DownloadErrorCode download_error_code =
+      metrics::DownloadErrorCode::kUnset;
+
+  // Regular update attempt.
+  switch (code) {
+    case ErrorCode::kSuccess:
+      // OK, we parsed the response successfully but that does
+      // necessarily mean that an update is available.
+      if (HasOutputPipe()) {
+        const OmahaResponse& response = GetOutputObject();
+        if (response.update_exists) {
+          result = metrics::CheckResult::kUpdateAvailable;
+          reaction = metrics::CheckReaction::kUpdating;
+        } else {
+          result = metrics::CheckResult::kNoUpdateAvailable;
+        }
+      } else {
+        result = metrics::CheckResult::kNoUpdateAvailable;
+      }
+      break;
+
+    case ErrorCode::kOmahaUpdateIgnoredPerPolicy:
+    case ErrorCode::kOmahaUpdateIgnoredOverCellular:
+      result = metrics::CheckResult::kUpdateAvailable;
+      reaction = metrics::CheckReaction::kIgnored;
+      break;
+
+    case ErrorCode::kOmahaUpdateDeferredPerPolicy:
+      result = metrics::CheckResult::kUpdateAvailable;
+      reaction = metrics::CheckReaction::kDeferring;
+      break;
+
+    case ErrorCode::kOmahaUpdateDeferredForBackoff:
+      result = metrics::CheckResult::kUpdateAvailable;
+      reaction = metrics::CheckReaction::kBackingOff;
+      break;
+
+    default:
+      // We report two flavors of errors, "Download errors" and "Parsing
+      // error". Try to convert to the former and if that doesn't work
+      // we know it's the latter.
+      metrics::DownloadErrorCode tmp_error =
+          metrics_utils::GetDownloadErrorCode(code);
+      if (tmp_error != metrics::DownloadErrorCode::kInputMalformed) {
+        result = metrics::CheckResult::kDownloadError;
+        download_error_code = tmp_error;
+      } else {
+        result = metrics::CheckResult::kParsingError;
+      }
+      break;
+  }
+
+  system_state_->metrics_reporter()->ReportUpdateCheckMetrics(
+      system_state_, result, reaction, download_error_code);
+}
+
+bool OmahaRequestAction::ShouldIgnoreUpdate(const OmahaResponse& response,
+                                            ErrorCode* error) const {
+  // Note: policy decision to not update to a version we rolled back from.
+  string rollback_version =
+      system_state_->payload_state()->GetRollbackVersion();
+  if (!rollback_version.empty()) {
+    LOG(INFO) << "Detected previous rollback from version " << rollback_version;
+    if (rollback_version == response.version) {
+      LOG(INFO) << "Received version that we rolled back from. Ignoring.";
+      *error = ErrorCode::kOmahaUpdateIgnoredPerPolicy;
+      return true;
+    }
+  }
+
+  if (system_state_->hardware()->IsOOBEEnabled() &&
+      !system_state_->hardware()->IsOOBEComplete(nullptr) &&
+      (response.deadline.empty() ||
+       system_state_->payload_state()->GetRollbackHappened()) &&
+      params_->app_version() != "ForcedUpdate") {
+    LOG(INFO) << "Ignoring a non-critical Omaha update before OOBE completion.";
+    *error = ErrorCode::kNonCriticalUpdateInOOBE;
+    return true;
+  }
+
+  if (!IsUpdateAllowedOverCurrentConnection(error, response)) {
+    LOG(INFO) << "Update is not allowed over current connection.";
+    return true;
+  }
+
+  // Note: We could technically delete the UpdateFirstSeenAt state when we
+  // return true. If we do, it'll mean a device has to restart the
+  // UpdateFirstSeenAt and thus help scattering take effect when the AU is
+  // turned on again. On the other hand, it also increases the chance of update
+  // starvation if an admin turns AU on/off more frequently. We choose to err on
+  // the side of preventing starvation at the cost of not applying scattering in
+  // those cases.
+  return false;
+}
+
+bool OmahaRequestAction::IsUpdateAllowedOverCellularByPrefs(
+    const OmahaResponse& response) const {
+  PrefsInterface* prefs = system_state_->prefs();
+
+  if (!prefs) {
+    LOG(INFO) << "Disabling updates over cellular as the preferences are "
+                 "not available.";
+    return false;
+  }
+
+  bool is_allowed;
+
+  if (prefs->Exists(kPrefsUpdateOverCellularPermission) &&
+      prefs->GetBoolean(kPrefsUpdateOverCellularPermission, &is_allowed) &&
+      is_allowed) {
+    LOG(INFO) << "Allowing updates over cellular as permission preference is "
+                 "set to true.";
+    return true;
+  }
+
+  if (!prefs->Exists(kPrefsUpdateOverCellularTargetVersion) ||
+      !prefs->Exists(kPrefsUpdateOverCellularTargetSize)) {
+    LOG(INFO) << "Disabling updates over cellular as permission preference is "
+                 "set to false or does not exist while target does not exist.";
+    return false;
+  }
+
+  std::string target_version;
+  int64_t target_size;
+
+  if (!prefs->GetString(kPrefsUpdateOverCellularTargetVersion,
+                        &target_version) ||
+      !prefs->GetInt64(kPrefsUpdateOverCellularTargetSize, &target_size)) {
+    LOG(INFO) << "Disabling updates over cellular as the target version or "
+                 "size is not accessible.";
+    return false;
+  }
+
+  uint64_t total_packages_size = 0;
+  for (const auto& package : response.packages) {
+    total_packages_size += package.size;
+  }
+  if (target_version == response.version &&
+      static_cast<uint64_t>(target_size) == total_packages_size) {
+    LOG(INFO) << "Allowing updates over cellular as the target matches the"
+                 "omaha response.";
+    return true;
+  } else {
+    LOG(INFO) << "Disabling updates over cellular as the target does not"
+                 "match the omaha response.";
+    return false;
+  }
+}
+
+bool OmahaRequestAction::IsUpdateAllowedOverCurrentConnection(
+    ErrorCode* error, const OmahaResponse& response) const {
+  ConnectionType type;
+  ConnectionTethering tethering;
+  ConnectionManagerInterface* connection_manager =
+      system_state_->connection_manager();
+  if (!connection_manager->GetConnectionProperties(&type, &tethering)) {
+    LOG(INFO) << "We could not determine our connection type. "
+              << "Defaulting to allow updates.";
+    return true;
+  }
+
+  bool is_allowed = connection_manager->IsUpdateAllowedOver(type, tethering);
+  bool is_device_policy_set =
+      connection_manager->IsAllowedConnectionTypesForUpdateSet();
+  // Treats tethered connection as if it is cellular connection.
+  bool is_over_cellular = type == ConnectionType::kCellular ||
+                          tethering == ConnectionTethering::kConfirmed;
+
+  if (!is_over_cellular) {
+    // There's no need to further check user preferences as we are not over
+    // cellular connection.
+    if (!is_allowed)
+      *error = ErrorCode::kOmahaUpdateIgnoredPerPolicy;
+  } else if (is_device_policy_set) {
+    // There's no need to further check user preferences as the device policy
+    // is set regarding updates over cellular.
+    if (!is_allowed)
+      *error = ErrorCode::kOmahaUpdateIgnoredPerPolicy;
+  } else {
+    // Deivce policy is not set, so user preferences overwrite whether to
+    // allow updates over cellular.
+    is_allowed = IsUpdateAllowedOverCellularByPrefs(response);
+    if (!is_allowed)
+      *error = ErrorCode::kOmahaUpdateIgnoredOverCellular;
+  }
+
+  LOG(INFO) << "We are connected via "
+            << connection_utils::StringForConnectionType(type)
+            << ", Updates allowed: " << (is_allowed ? "Yes" : "No");
+  return is_allowed;
+}
+
+bool OmahaRequestAction::IsRollbackEnabled() const {
+  if (policy_provider_->IsConsumerDevice()) {
+    LOG(INFO) << "Rollback is not enabled for consumer devices.";
+    return false;
+  }
+
+  if (!policy_provider_->device_policy_is_loaded()) {
+    LOG(INFO) << "No device policy is loaded. Assuming rollback enabled.";
+    return true;
+  }
+
+  int allowed_milestones;
+  if (!policy_provider_->GetDevicePolicy().GetRollbackAllowedMilestones(
+          &allowed_milestones)) {
+    LOG(INFO) << "RollbackAllowedMilestones policy can't be read. "
+                 "Defaulting to rollback enabled.";
+    return true;
+  }
+
+  LOG(INFO) << "Rollback allows " << allowed_milestones << " milestones.";
+  return allowed_milestones > 0;
+}
+
+void OmahaRequestAction::SetMaxKernelKeyVersionForRollback() const {
+  int max_kernel_rollforward;
+  int min_kernel_version = system_state_->hardware()->GetMinKernelKeyVersion();
+  if (IsRollbackEnabled()) {
+    // If rollback is enabled, set the max kernel key version to the current
+    // kernel key version. This has the effect of freezing kernel key roll
+    // forwards.
+    //
+    // TODO(zentaro): This behavior is temporary, and ensures that no kernel
+    // key roll forward happens until the server side components of rollback
+    // are implemented. Future changes will allow the Omaha server to return
+    // the kernel key version from max_rollback_versions in the past. At that
+    // point the max kernel key version will be set to that value, creating a
+    // sliding window of versions that can be rolled back to.
+    LOG(INFO) << "Rollback is enabled. Setting kernel_max_rollforward to "
+              << min_kernel_version;
+    max_kernel_rollforward = min_kernel_version;
+  } else {
+    // For devices that are not rollback enabled (ie. consumer devices), the
+    // max kernel key version is set to 0xfffffffe, which is logically
+    // infinity. This maintains the previous behavior that that kernel key
+    // versions roll forward each time they are incremented.
+    LOG(INFO) << "Rollback is disabled. Setting kernel_max_rollforward to "
+              << kRollforwardInfinity;
+    max_kernel_rollforward = kRollforwardInfinity;
+  }
+
+  bool max_rollforward_set =
+      system_state_->hardware()->SetMaxKernelKeyRollforward(
+          max_kernel_rollforward);
+  if (!max_rollforward_set) {
+    LOG(ERROR) << "Failed to set kernel_max_rollforward";
+  }
+  // Report metrics
+  system_state_->metrics_reporter()->ReportKeyVersionMetrics(
+      min_kernel_version, max_kernel_rollforward, max_rollforward_set);
+}
+
+base::Time OmahaRequestAction::LoadOrPersistUpdateFirstSeenAtPref() const {
+  Time update_first_seen_at;
+  int64_t update_first_seen_at_int;
+  if (system_state_->prefs()->Exists(kPrefsUpdateFirstSeenAt)) {
+    if (system_state_->prefs()->GetInt64(kPrefsUpdateFirstSeenAt,
+                                         &update_first_seen_at_int)) {
+      // Note: This timestamp could be that of ANY update we saw in the past
+      // (not necessarily this particular update we're considering to apply)
+      // but never got to apply because of some reason (e.g. stop AU policy,
+      // updates being pulled out from Omaha, changes in target version prefix,
+      // new update being rolled out, etc.). But for the purposes of scattering
+      // it doesn't matter which update the timestamp corresponds to. i.e.
+      // the clock starts ticking the first time we see an update and we're
+      // ready to apply when the random wait period is satisfied relative to
+      // that first seen timestamp.
+      update_first_seen_at = Time::FromInternalValue(update_first_seen_at_int);
+      LOG(INFO) << "Using persisted value of UpdateFirstSeenAt: "
+                << utils::ToString(update_first_seen_at);
+    } else {
+      // This seems like an unexpected error where the persisted value exists
+      // but it's not readable for some reason.
+      LOG(INFO) << "UpdateFirstSeenAt value cannot be read";
+      return base::Time();
+    }
+  } else {
+    update_first_seen_at = system_state_->clock()->GetWallclockTime();
+    update_first_seen_at_int = update_first_seen_at.ToInternalValue();
+    if (system_state_->prefs()->SetInt64(kPrefsUpdateFirstSeenAt,
+                                         update_first_seen_at_int)) {
+      LOG(INFO) << "Persisted the new value for UpdateFirstSeenAt: "
+                << utils::ToString(update_first_seen_at);
+    } else {
+      // This seems like an unexpected error where the value cannot be
+      // persisted for some reason.
+      LOG(INFO) << "UpdateFirstSeenAt value "
+                << utils::ToString(update_first_seen_at)
+                << " cannot be persisted";
+      return base::Time();
+    }
+  }
+  return update_first_seen_at;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/cros/omaha_request_action.h b/omaha_request_action.h
similarity index 74%
rename from cros/omaha_request_action.h
rename to omaha_request_action.h
index 4926c7d..8db5fb9 100644
--- a/cros/omaha_request_action.h
+++ b/omaha_request_action.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_OMAHA_REQUEST_ACTION_H_
-#define UPDATE_ENGINE_CROS_OMAHA_REQUEST_ACTION_H_
+#ifndef UPDATE_ENGINE_OMAHA_REQUEST_ACTION_H_
+#define UPDATE_ENGINE_OMAHA_REQUEST_ACTION_H_
 
 #include <fcntl.h>
 #include <sys/stat.h>
@@ -28,14 +28,13 @@
 
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
-#include <base/optional.h>
 #include <brillo/secure_blob.h>
 #include <curl/curl.h>
 
 #include "update_engine/common/action.h"
 #include "update_engine/common/http_fetcher.h"
-#include "update_engine/cros/omaha_request_builder_xml.h"
-#include "update_engine/cros/omaha_response.h"
+#include "update_engine/omaha_response.h"
+#include "update_engine/system_state.h"
 
 // The Omaha Request action makes a request to Omaha and can output
 // the response on the output ActionPipe.
@@ -46,9 +45,60 @@
 
 namespace chromeos_update_engine {
 
+// Encodes XML entities in a given string. Input must be ASCII-7 valid. If
+// the input is invalid, the default value is used instead.
+std::string XmlEncodeWithDefault(const std::string& input,
+                                 const std::string& default_value);
+
+// Escapes text so it can be included as character data and attribute
+// values. The |input| string must be valid ASCII-7, no UTF-8 supported.
+// Returns whether the |input| was valid and escaped properly in |output|.
+bool XmlEncode(const std::string& input, std::string* output);
+
+// This struct encapsulates the Omaha event information. For a
+// complete list of defined event types and results, see
+// http://code.google.com/p/omaha/wiki/ServerProtocol#event
+struct OmahaEvent {
+  // The Type values correspond to EVENT_TYPE values of Omaha.
+  enum Type {
+    kTypeUnknown = 0,
+    kTypeDownloadComplete = 1,
+    kTypeInstallComplete = 2,
+    kTypeUpdateComplete = 3,
+    kTypeUpdateDownloadStarted = 13,
+    kTypeUpdateDownloadFinished = 14,
+    // Chromium OS reserved type sent after the first reboot following an update
+    // completed.
+    kTypeRebootedAfterUpdate = 54,
+  };
+
+  // The Result values correspond to EVENT_RESULT values of Omaha.
+  enum Result {
+    kResultError = 0,
+    kResultSuccess = 1,
+    kResultUpdateDeferred = 9,  // When we ignore/defer updates due to policy.
+  };
+
+  OmahaEvent()
+      : type(kTypeUnknown),
+        result(kResultError),
+        error_code(ErrorCode::kError) {}
+  explicit OmahaEvent(Type in_type)
+      : type(in_type),
+        result(kResultSuccess),
+        error_code(ErrorCode::kSuccess) {}
+  OmahaEvent(Type in_type, Result in_result, ErrorCode in_error_code)
+      : type(in_type), result(in_result), error_code(in_error_code) {}
+
+  Type type;
+  Result result;
+  ErrorCode error_code;
+};
+
 class NoneType;
 class OmahaRequestAction;
 class OmahaRequestParams;
+class PrefsInterface;
 
 // This struct is declared in the .cc file.
 struct OmahaParserData;
@@ -66,13 +116,14 @@
 class OmahaRequestAction : public Action<OmahaRequestAction>,
                            public HttpFetcherDelegate {
  public:
+  static const int kNeverPinged = -1;
   static const int kPingTimeJump = -2;
-  // We choose this value of 3 as a heuristic for a work day in trying
+  // We choose this value of 10 as a heuristic for a work day in trying
   // each URL, assuming we check roughly every 45 mins. This is a good time to
-  // wait so we don't give up the preferred URLs, but allow using the URL that
-  // appears earlier in list for every payload before resorting to the fallback
-  // URLs in the candiate URL list.
-  static const int kDefaultMaxFailureCountPerUrl = 3;
+  // wait - neither too long nor too little - so we don't give up the preferred
+  // URLs that appear earlier in list too quickly before moving on to the
+  // fallback ones.
+  static const int kDefaultMaxFailureCountPerUrl = 10;
 
   // If staging is enabled, set the maximum wait time to 28 days, since that is
   // the predetermined wait time for staging.
@@ -100,10 +151,10 @@
   // OmahaRequestAction(..., new OmahaEvent(...), new WhateverHttpFetcher);
   // or
   // OmahaRequestAction(..., nullptr, new WhateverHttpFetcher);
-  OmahaRequestAction(OmahaEvent* event,
+  OmahaRequestAction(SystemState* system_state,
+                     OmahaEvent* event,
                      std::unique_ptr<HttpFetcher> http_fetcher,
-                     bool ping_only,
-                     const std::string& session_id);
+                     bool ping_only);
   ~OmahaRequestAction() override;
   typedef ActionTraits<OmahaRequestAction>::InputObjectType InputObjectType;
   typedef ActionTraits<OmahaRequestAction>::OutputObjectType OutputObjectType;
@@ -137,9 +188,6 @@
               GetInstallDateWhenOOBECompletedWithValidDate);
   FRIEND_TEST(OmahaRequestActionTest,
               GetInstallDateWhenOOBECompletedDateChanges);
-  friend class UpdateAttempterTest;
-  FRIEND_TEST(UpdateAttempterTest, SessionIdTestEnforceEmptyStrPingOmaha);
-  FRIEND_TEST(UpdateAttempterTest, SessionIdTestConsistencyInUpdateFlow);
 
   // Enumeration used in PersistInstallDate().
   enum InstallDateProvisioningSource {
@@ -154,7 +202,7 @@
   // Gets the install date, expressed as the number of PST8PDT
   // calendar weeks since January 1st 2007, times seven. Returns -1 if
   // unknown. See http://crbug.com/336838 for details about this value.
-  static int GetInstallDate();
+  static int GetInstallDate(SystemState* system_state);
 
   // Parses the Omaha Response in |doc| and sets the
   // |install_date_days| field of |output_object| to the value of the
@@ -165,25 +213,25 @@
 
   // Returns True if the kPrefsInstallDateDays state variable is set,
   // False otherwise.
-  static bool HasInstallDate();
+  static bool HasInstallDate(SystemState* system_state);
 
   // Writes |install_date_days| into the kPrefsInstallDateDays state
   // variable and emits an UMA stat for the |source| used. Returns
   // True if the value was written, False if an error occurred.
-  static bool PersistInstallDate(int install_date_days,
+  static bool PersistInstallDate(SystemState* system_state,
+                                 int install_date_days,
                                  InstallDateProvisioningSource source);
 
-  // Persist the new cohort value received in the XML file in the |prefs_key|
-  // preference file. If the |new_value| is empty, do nothing. If the
-  // |new_value| stores and empty value, the currently stored value will be
-  // deleted. Don't call this function with an empty |new_value| if the value
-  // was not set in the XML, since that would delete the stored value.
-  void PersistCohortData(const std::string& prefs_key,
-                         const base::Optional<std::string>& new_value);
+  // Persist the new cohort* value received in the XML file in the |prefs_key|
+  // preference file. If the |new_value| is empty, the currently stored value
+  // will be deleted. Don't call this function with an empty |new_value| if the
+  // value was not set in the XML, since that would delete the stored value.
+  bool PersistCohortData(const std::string& prefs_key,
+                         const std::string& new_value);
 
-  // Parses and persists the cohorts sent back in the updatecheck tag
-  // attributes.
-  void PersistCohorts(const OmahaParserData& parser_data);
+  // Parse and persist the end-of-life status flag sent back in the updatecheck
+  // tag attributes. The flag will be validated and stored in the Prefs.
+  bool PersistEolStatus(const std::map<std::string, std::string>& attrs);
 
   // If this is an update check request, initializes
   // |ping_active_days_| and |ping_roll_call_days_| to values that may
@@ -198,10 +246,6 @@
   // send to Omaha and thus we should include them in the response.
   bool ShouldPing() const;
 
-  // Process Omaha's response to a ping request and store the results in the DLC
-  // metadata directory.
-  void StorePingReply(const OmahaParserData& parser_data) const;
-
   // Returns true if the download of a new update should be deferred.
   // False if the update can be downloaded.
   bool ShouldDeferDownload(OmahaResponse* output_object);
@@ -281,6 +325,12 @@
   // kPrefsUpdateFirstSeenAt pref and returns it as a base::Time object.
   base::Time LoadOrPersistUpdateFirstSeenAtPref() const;
 
+  // Global system context.
+  SystemState* system_state_;
+
+  // Contains state that is relevant in the processing of the Omaha request.
+  OmahaRequestParams* params_;
+
   // Pointer to the OmahaEvent info. This is an UpdateCheck request if null.
   std::unique_ptr<OmahaEvent> event_;
 
@@ -302,11 +352,9 @@
   int ping_active_days_;
   int ping_roll_call_days_;
 
-  std::string session_id_;
-
   DISALLOW_COPY_AND_ASSIGN(OmahaRequestAction);
 };
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_OMAHA_REQUEST_ACTION_H_
+#endif  // UPDATE_ENGINE_OMAHA_REQUEST_ACTION_H_
diff --git a/cros/omaha_request_action_fuzzer.cc b/omaha_request_action_fuzzer.cc
similarity index 88%
rename from cros/omaha_request_action_fuzzer.cc
rename to omaha_request_action_fuzzer.cc
index 995de8c..6c2f7ca 100644
--- a/cros/omaha_request_action_fuzzer.cc
+++ b/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/cros/fake_system_state.h"
-#include "update_engine/cros/omaha_request_action.h"
+#include "update_engine/fake_system_state.h"
+#include "update_engine/omaha_request_action.h"
 
 class Environment {
  public:
@@ -31,14 +31,14 @@
   brillo::FakeMessageLoop loop(nullptr);
   loop.SetAsCurrent();
 
-  chromeos_update_engine::FakeSystemState::CreateInstance();
+  chromeos_update_engine::FakeSystemState fake_system_state;
   auto omaha_request_action =
       std::make_unique<chromeos_update_engine::OmahaRequestAction>(
+          &fake_system_state,
           nullptr,
           std::make_unique<chromeos_update_engine::MockHttpFetcher>(
               data, size, nullptr),
-          false,
-          "" /* session_id */);
+          false);
   auto collector_action =
       std::make_unique<chromeos_update_engine::ObjectCollectorAction<
           chromeos_update_engine::OmahaResponse>>();
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
new file mode 100644
index 0000000..1786bcc
--- /dev/null
+++ b/omaha_request_action_unittest.cc
@@ -0,0 +1,3147 @@
+//
+// Copyright (C) 2012 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/omaha_request_action.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/memory/ptr_util.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <base/time/time.h>
+#include <brillo/message_loops/fake_message_loop.h>
+#include <brillo/message_loops/message_loop.h>
+#include <brillo/message_loops/message_loop_utils.h>
+#include <gtest/gtest.h>
+#include <policy/libpolicy.h>
+#include <policy/mock_libpolicy.h>
+
+#include "update_engine/common/action_pipe.h"
+#include "update_engine/common/constants.h"
+#include "update_engine/common/fake_prefs.h"
+#include "update_engine/common/hash_calculator.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_params.h"
+#include "update_engine/update_manager/rollback_prefs.h"
+
+using base::Time;
+using base::TimeDelta;
+using chromeos_update_manager::kRollforwardInfinity;
+using std::string;
+using std::vector;
+using testing::_;
+using testing::AllOf;
+using testing::AnyNumber;
+using testing::DoAll;
+using testing::Ge;
+using testing::Le;
+using testing::NiceMock;
+using testing::Return;
+using testing::ReturnPointee;
+using testing::ReturnRef;
+using testing::SaveArg;
+using testing::SetArgPointee;
+
+namespace {
+
+static_assert(kRollforwardInfinity == 0xfffffffe,
+              "Don't change the value of kRollforward infinity unless its "
+              "size has been changed in firmware.");
+
+const char kCurrentVersion[] = "0.1.0.0";
+const char kTestAppId[] = "test-app-id";
+const char kTestAppId2[] = "test-app2-id";
+const char kTestAppIdSkipUpdatecheck[] = "test-app-id-skip-updatecheck";
+
+// This is a helper struct to allow unit tests build an update response with the
+// values they care about.
+struct FakeUpdateResponse {
+  string GetRollbackVersionAttributes() const {
+    return (rollback ? " _rollback=\"true\"" : "") +
+           (!rollback_firmware_version.empty()
+                ? " _firmware_version=\"" + rollback_firmware_version + "\""
+                : "") +
+           (!rollback_kernel_version.empty()
+                ? " _kernel_version=\"" + rollback_kernel_version + "\""
+                : "");
+  }
+
+  string GetNoUpdateResponse() const {
+    string entity_str;
+    if (include_entity)
+      entity_str = "<!DOCTYPE response [<!ENTITY CrOS \"ChromeOS\">]>";
+    return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + entity_str +
+           "<response protocol=\"3.0\">"
+           "<daystart elapsed_seconds=\"100\"/>"
+           "<app appid=\"" +
+           app_id + "\" " +
+           (include_cohorts
+                ? "cohort=\"" + cohort + "\" cohorthint=\"" + cohorthint +
+                      "\" cohortname=\"" + cohortname + "\" "
+                : "") +
+           " status=\"ok\">"
+           "<ping status=\"ok\"/>"
+           "<updatecheck status=\"noupdate\"/></app>" +
+           (multi_app_no_update
+                ? "<app appid=\"" + app_id2 +
+                      "\"><updatecheck status=\"noupdate\"/></app>"
+                : "") +
+           "</response>";
+  }
+
+  string GetUpdateResponse() const {
+    return "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
+           "protocol=\"3.0\">"
+           "<daystart elapsed_seconds=\"100\"" +
+           (elapsed_days.empty() ? ""
+                                 : (" elapsed_days=\"" + elapsed_days + "\"")) +
+           "/>"
+           "<app appid=\"" +
+           app_id + "\" " +
+           (include_cohorts
+                ? "cohort=\"" + cohort + "\" cohorthint=\"" + cohorthint +
+                      "\" cohortname=\"" + cohortname + "\" "
+                : "") +
+           " status=\"ok\">"
+           "<ping status=\"ok\"/><updatecheck status=\"ok\"" +
+           GetRollbackVersionAttributes() + ">" + "<urls><url codebase=\"" +
+           codebase +
+           "\"/></urls>"
+           "<manifest version=\"" +
+           version +
+           "\">"
+           "<packages><package hash=\"not-used\" name=\"" +
+           filename + "\" size=\"" + base::Int64ToString(size) +
+           "\" hash_sha256=\"" + hash + "\"/>" +
+           (multi_package ? "<package name=\"package2\" size=\"222\" "
+                            "hash_sha256=\"hash2\"/>"
+                          : "") +
+           "</packages>"
+           "<actions><action event=\"postinstall\" MetadataSize=\"11" +
+           (multi_package ? ":22" : "") + "\" MoreInfo=\"" + more_info_url +
+           "\" Prompt=\"" + prompt +
+           "\" "
+           "IsDeltaPayload=\"true" +
+           (multi_package ? ":false" : "") +
+           "\" "
+           "MaxDaysToScatter=\"" +
+           max_days_to_scatter +
+           "\" "
+           "sha256=\"not-used\" " +
+           (deadline.empty() ? "" : ("deadline=\"" + deadline + "\" ")) +
+           (disable_p2p_for_downloading ? "DisableP2PForDownloading=\"true\" "
+                                        : "") +
+           (disable_p2p_for_sharing ? "DisableP2PForSharing=\"true\" " : "") +
+           (powerwash ? "Powerwash=\"true\" " : "") +
+           "/></actions></manifest></updatecheck></app>" +
+           (multi_app
+                ? "<app appid=\"" + app_id2 + "\"" +
+                      (include_cohorts ? " cohort=\"cohort2\"" : "") +
+                      "><updatecheck status=\"ok\"><urls><url codebase=\"" +
+                      codebase2 + "\"/></urls><manifest version=\"" + version2 +
+                      "\"><packages>"
+                      "<package name=\"package3\" size=\"333\" "
+                      "hash_sha256=\"hash3\"/></packages>"
+                      "<actions><action event=\"postinstall\" " +
+                      (multi_app_self_update
+                           ? "noupdate=\"true\" IsDeltaPayload=\"true\" "
+                           : "IsDeltaPayload=\"false\" ") +
+                      "MetadataSize=\"33\"/></actions>"
+                      "</manifest></updatecheck></app>"
+                : "") +
+           (multi_app_no_update
+                ? "<app><updatecheck status=\"noupdate\"/></app>"
+                : "") +
+           (multi_app_skip_updatecheck
+                ? "<app appid=\"" + app_id_skip_updatecheck + "\"></app>"
+                : "") +
+           "</response>";
+  }
+
+  // Return the payload URL, which is split in two fields in the XML response.
+  string GetPayloadUrl() { return codebase + filename; }
+
+  string app_id = kTestAppId;
+  string app_id2 = kTestAppId2;
+  string app_id_skip_updatecheck = kTestAppIdSkipUpdatecheck;
+  string current_version = kCurrentVersion;
+  string version = "1.2.3.4";
+  string version2 = "2.3.4.5";
+  string more_info_url = "http://more/info";
+  string prompt = "true";
+  string codebase = "http://code/base/";
+  string codebase2 = "http://code/base/2/";
+  string filename = "file.signed";
+  string hash = "4841534831323334";
+  uint64_t size = 123;
+  string deadline = "";
+  string max_days_to_scatter = "7";
+  string elapsed_days = "42";
+
+  // P2P setting defaults to allowed.
+  bool disable_p2p_for_downloading = false;
+  bool disable_p2p_for_sharing = false;
+
+  bool powerwash = false;
+
+  // Omaha cohorts settings.
+  bool include_cohorts = false;
+  string cohort = "";
+  string cohorthint = "";
+  string cohortname = "";
+
+  // Whether to include the CrOS <!ENTITY> in the XML response.
+  bool include_entity = false;
+
+  // Whether to include more than one app.
+  bool multi_app = false;
+  // Whether to include an app with noupdate="true".
+  bool multi_app_self_update = false;
+  // Whether to include an additional app with status="noupdate".
+  bool multi_app_no_update = false;
+  // Whether to include an additional app with no updatecheck tag.
+  bool multi_app_skip_updatecheck = false;
+  // Whether to include more than one package in an app.
+  bool multi_package = false;
+
+  // Whether the payload is a rollback.
+  bool rollback = false;
+  // The verified boot firmware key version for the rollback image.
+  string rollback_firmware_version = "";
+  // The verified boot kernel key version for the rollback image.
+  string rollback_kernel_version = "";
+};
+
+}  // namespace
+
+namespace chromeos_update_engine {
+
+class OmahaRequestActionTestProcessorDelegate : public ActionProcessorDelegate {
+ public:
+  OmahaRequestActionTestProcessorDelegate()
+      : expected_code_(ErrorCode::kSuccess),
+        interactive_(false),
+        test_http_fetcher_headers_(false) {}
+  ~OmahaRequestActionTestProcessorDelegate() override = default;
+
+  void ProcessingDone(const ActionProcessor* processor,
+                      ErrorCode code) override {
+    brillo::MessageLoop::current()->BreakLoop();
+  }
+
+  void ActionCompleted(ActionProcessor* processor,
+                       AbstractAction* action,
+                       ErrorCode code) override {
+    // Make sure actions always succeed.
+    if (action->Type() == OmahaRequestAction::StaticType()) {
+      EXPECT_EQ(expected_code_, code);
+      // Check that the headers were set in the fetcher during the action. Note
+      // that we set this request as "interactive".
+      auto fetcher = static_cast<const MockHttpFetcher*>(
+          static_cast<OmahaRequestAction*>(action)->http_fetcher_.get());
+
+      if (test_http_fetcher_headers_) {
+        EXPECT_EQ(interactive_ ? "fg" : "bg",
+                  fetcher->GetHeader("X-Goog-Update-Interactivity"));
+        EXPECT_EQ(kTestAppId, fetcher->GetHeader("X-Goog-Update-AppId"));
+        EXPECT_NE("", fetcher->GetHeader("X-Goog-Update-Updater"));
+      }
+      post_data_ = fetcher->post_data();
+    } else if (action->Type() ==
+               ObjectCollectorAction<OmahaResponse>::StaticType()) {
+      EXPECT_EQ(ErrorCode::kSuccess, code);
+      auto collector_action =
+          static_cast<ObjectCollectorAction<OmahaResponse>*>(action);
+      omaha_response_.reset(new OmahaResponse(collector_action->object()));
+      EXPECT_TRUE(omaha_response_);
+    } else {
+      EXPECT_EQ(ErrorCode::kSuccess, code);
+    }
+  }
+  ErrorCode expected_code_;
+  brillo::Blob post_data_;
+  bool interactive_;
+  bool test_http_fetcher_headers_;
+  std::unique_ptr<OmahaResponse> omaha_response_;
+};
+
+class OmahaRequestActionTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    request_params_.set_os_sp("service_pack");
+    request_params_.set_os_board("x86-generic");
+    request_params_.set_app_id(kTestAppId);
+    request_params_.set_app_version(kCurrentVersion);
+    request_params_.set_app_lang("en-US");
+    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");
+    request_params_.set_target_version_prefix("");
+    request_params_.set_rollback_allowed(false);
+    request_params_.set_is_powerwash_allowed(false);
+    request_params_.set_is_install(false);
+    request_params_.set_dlc_module_ids({});
+
+    fake_system_state_.set_request_params(&request_params_);
+    fake_system_state_.set_prefs(&fake_prefs_);
+  }
+
+  // Returns true iff an output response was obtained from the
+  // OmahaRequestAction. |prefs| may be null, in which case a local MockPrefs
+  // is used. |payload_state| may be null, in which case a local mock is used.
+  // |p2p_manager| may be null, in which case a local mock is used.
+  // |connection_manager| may be null, in which case a local mock is used.
+  // out_response may be null. If |fail_http_response_code| is non-negative,
+  // the transfer will fail with that code. |ping_only| is passed through to the
+  // OmahaRequestAction constructor. out_post_data may be null; if non-null, the
+  // post-data received by the mock HttpFetcher is returned.
+  //
+  // The |expected_check_result|, |expected_check_reaction| and
+  // |expected_error_code| parameters are for checking expectations
+  // about reporting UpdateEngine.Check.{Result,Reaction,DownloadError}
+  // UMA statistics. Use the appropriate ::kUnset value to specify that
+  // the given metric should not be reported.
+  bool TestUpdateCheck(const string& http_response,
+                       int fail_http_response_code,
+                       bool ping_only,
+                       bool is_consumer_device,
+                       int rollback_allowed_milestones,
+                       bool is_policy_loaded,
+                       ErrorCode expected_code,
+                       metrics::CheckResult expected_check_result,
+                       metrics::CheckReaction expected_check_reaction,
+                       metrics::DownloadErrorCode expected_download_error_code,
+                       OmahaResponse* out_response,
+                       brillo::Blob* out_post_data);
+
+  // Overload of TestUpdateCheck that does not supply |is_consumer_device| or
+  // |rollback_allowed_milestones| which are only required for rollback tests.
+  bool TestUpdateCheck(const string& http_response,
+                       int fail_http_response_code,
+                       bool ping_only,
+                       ErrorCode expected_code,
+                       metrics::CheckResult expected_check_result,
+                       metrics::CheckReaction expected_check_reaction,
+                       metrics::DownloadErrorCode expected_download_error_code,
+                       OmahaResponse* out_response,
+                       brillo::Blob* out_post_data);
+
+  void TestRollbackCheck(bool is_consumer_device,
+                         int rollback_allowed_milestones,
+                         bool is_policy_loaded,
+                         OmahaResponse* out_response);
+
+  void TestEvent(OmahaEvent* event,
+                 const string& http_response,
+                 brillo::Blob* out_post_data);
+
+  // Runs and checks a ping test. |ping_only| indicates whether it should send
+  // only a ping or also an updatecheck.
+  void PingTest(bool ping_only);
+
+  // InstallDate test helper function.
+  bool InstallDateParseHelper(const string& elapsed_days,
+                              OmahaResponse* response);
+
+  // P2P test helper function.
+  void P2PTest(bool initial_allow_p2p_for_downloading,
+               bool initial_allow_p2p_for_sharing,
+               bool omaha_disable_p2p_for_downloading,
+               bool omaha_disable_p2p_for_sharing,
+               bool payload_state_allow_p2p_attempt,
+               bool expect_p2p_client_lookup,
+               const string& p2p_client_result_url,
+               bool expected_allow_p2p_for_downloading,
+               bool expected_allow_p2p_for_sharing,
+               const string& expected_p2p_url);
+
+  FakeSystemState fake_system_state_;
+  FakeUpdateResponse fake_update_response_;
+  // Used by all tests.
+  OmahaRequestParams request_params_{&fake_system_state_};
+
+  FakePrefs fake_prefs_;
+
+  OmahaRequestActionTestProcessorDelegate delegate_;
+
+  bool test_http_fetcher_headers_{false};
+};
+
+bool OmahaRequestActionTest::TestUpdateCheck(
+    const string& http_response,
+    int fail_http_response_code,
+    bool ping_only,
+    bool is_consumer_device,
+    int rollback_allowed_milestones,
+    bool is_policy_loaded,
+    ErrorCode expected_code,
+    metrics::CheckResult expected_check_result,
+    metrics::CheckReaction expected_check_reaction,
+    metrics::DownloadErrorCode expected_download_error_code,
+    OmahaResponse* out_response,
+    brillo::Blob* out_post_data) {
+  brillo::FakeMessageLoop loop(nullptr);
+  loop.SetAsCurrent();
+  auto fetcher = std::make_unique<MockHttpFetcher>(
+      http_response.data(), http_response.size(), nullptr);
+  if (fail_http_response_code >= 0) {
+    fetcher->FailTransfer(fail_http_response_code);
+  }
+  // This ensures the tests didn't forget to update fake_system_state_ if they
+  // are not using the default request_params_.
+  EXPECT_EQ(&request_params_, fake_system_state_.request_params());
+
+  auto omaha_request_action = std::make_unique<OmahaRequestAction>(
+      &fake_system_state_, nullptr, std::move(fetcher), ping_only);
+
+  auto mock_policy_provider =
+      std::make_unique<NiceMock<policy::MockPolicyProvider>>();
+  EXPECT_CALL(*mock_policy_provider, IsConsumerDevice())
+      .WillRepeatedly(Return(is_consumer_device));
+
+  EXPECT_CALL(*mock_policy_provider, device_policy_is_loaded())
+      .WillRepeatedly(Return(is_policy_loaded));
+
+  const policy::MockDevicePolicy device_policy;
+  const bool get_allowed_milestone_succeeds = rollback_allowed_milestones >= 0;
+  EXPECT_CALL(device_policy, GetRollbackAllowedMilestones(_))
+      .WillRepeatedly(DoAll(SetArgPointee<0>(rollback_allowed_milestones),
+                            Return(get_allowed_milestone_succeeds)));
+
+  EXPECT_CALL(*mock_policy_provider, GetDevicePolicy())
+      .WillRepeatedly(ReturnRef(device_policy));
+  omaha_request_action->policy_provider_ = std::move(mock_policy_provider);
+
+  delegate_.expected_code_ = expected_code;
+  delegate_.interactive_ = request_params_.interactive();
+  delegate_.test_http_fetcher_headers_ = test_http_fetcher_headers_;
+  ActionProcessor processor;
+  processor.set_delegate(&delegate_);
+
+  auto collector_action =
+      std::make_unique<ObjectCollectorAction<OmahaResponse>>();
+  BondActions(omaha_request_action.get(), collector_action.get());
+  processor.EnqueueAction(std::move(omaha_request_action));
+  processor.EnqueueAction(std::move(collector_action));
+
+  EXPECT_CALL(*fake_system_state_.mock_metrics_reporter(),
+              ReportUpdateCheckMetrics(_, _, _, _))
+      .Times(AnyNumber());
+
+  EXPECT_CALL(*fake_system_state_.mock_metrics_reporter(),
+              ReportUpdateCheckMetrics(_,
+                                       expected_check_result,
+                                       expected_check_reaction,
+                                       expected_download_error_code))
+      .Times(ping_only ? 0 : 1);
+
+  loop.PostTask(base::Bind(
+      [](ActionProcessor* processor) { processor->StartProcessing(); },
+      base::Unretained(&processor)));
+  loop.Run();
+  EXPECT_FALSE(loop.PendingTasks());
+  if (delegate_.omaha_response_ && out_response)
+    *out_response = *delegate_.omaha_response_;
+  if (out_post_data)
+    *out_post_data = delegate_.post_data_;
+  return delegate_.omaha_response_ != nullptr;
+}
+
+bool OmahaRequestActionTest::TestUpdateCheck(
+    const string& http_response,
+    int fail_http_response_code,
+    bool ping_only,
+    ErrorCode expected_code,
+    metrics::CheckResult expected_check_result,
+    metrics::CheckReaction expected_check_reaction,
+    metrics::DownloadErrorCode expected_download_error_code,
+    OmahaResponse* out_response,
+    brillo::Blob* out_post_data) {
+  return TestUpdateCheck(http_response,
+                         fail_http_response_code,
+                         ping_only,
+                         true,   // is_consumer_device
+                         0,      // rollback_allowed_milestones
+                         false,  // is_policy_loaded
+                         expected_code,
+                         expected_check_result,
+                         expected_check_reaction,
+                         expected_download_error_code,
+                         out_response,
+                         out_post_data);
+}
+
+void OmahaRequestActionTest::TestRollbackCheck(bool is_consumer_device,
+                                               int rollback_allowed_milestones,
+                                               bool is_policy_loaded,
+                                               OmahaResponse* out_response) {
+  fake_update_response_.deadline = "20101020";
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              is_consumer_device,
+                              rollback_allowed_milestones,
+                              is_policy_loaded,
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              out_response,
+                              nullptr));
+  ASSERT_TRUE(out_response->update_exists);
+}
+
+// Tests Event requests -- they should always succeed. |out_post_data| may be
+// null; if non-null, the post-data received by the mock HttpFetcher is
+// returned.
+void OmahaRequestActionTest::TestEvent(OmahaEvent* event,
+                                       const string& http_response,
+                                       brillo::Blob* out_post_data) {
+  brillo::FakeMessageLoop loop(nullptr);
+  loop.SetAsCurrent();
+
+  auto action = std::make_unique<OmahaRequestAction>(
+      &fake_system_state_,
+      event,
+      std::make_unique<MockHttpFetcher>(
+          http_response.data(), http_response.size(), nullptr),
+      false);
+  ActionProcessor processor;
+  processor.set_delegate(&delegate_);
+  processor.EnqueueAction(std::move(action));
+
+  loop.PostTask(base::Bind(
+      [](ActionProcessor* processor) { processor->StartProcessing(); },
+      base::Unretained(&processor)));
+  loop.Run();
+  EXPECT_FALSE(loop.PendingTasks());
+
+  if (out_post_data)
+    *out_post_data = delegate_.post_data_;
+}
+
+TEST_F(OmahaRequestActionTest, RejectEntities) {
+  OmahaResponse response;
+  fake_update_response_.include_entity = true;
+  ASSERT_FALSE(TestUpdateCheck(fake_update_response_.GetNoUpdateResponse(),
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kOmahaRequestXMLHasEntityDecl,
+                               metrics::CheckResult::kParsingError,
+                               metrics::CheckReaction::kUnset,
+                               metrics::DownloadErrorCode::kUnset,
+                               &response,
+                               nullptr));
+  EXPECT_FALSE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, NoUpdateTest) {
+  OmahaResponse response;
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetNoUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kNoUpdateAvailable,
+                              metrics::CheckReaction::kUnset,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_FALSE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, MultiAppNoUpdateTest) {
+  OmahaResponse response;
+  fake_update_response_.multi_app_no_update = true;
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetNoUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kNoUpdateAvailable,
+                              metrics::CheckReaction::kUnset,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_FALSE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, MultiAppNoPartialUpdateTest) {
+  OmahaResponse response;
+  fake_update_response_.multi_app_no_update = true;
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kNoUpdateAvailable,
+                              metrics::CheckReaction::kUnset,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_FALSE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, NoSelfUpdateTest) {
+  OmahaResponse response;
+  ASSERT_TRUE(TestUpdateCheck(
+      "<response><app><updatecheck status=\"ok\"><manifest><actions><action "
+      "event=\"postinstall\" noupdate=\"true\"/></actions>"
+      "</manifest></updatecheck></app></response>",
+      -1,
+      false,  // ping_only
+      ErrorCode::kSuccess,
+      metrics::CheckResult::kNoUpdateAvailable,
+      metrics::CheckReaction::kUnset,
+      metrics::DownloadErrorCode::kUnset,
+      &response,
+      nullptr));
+  EXPECT_FALSE(response.update_exists);
+}
+
+// Test that all the values in the response are parsed in a normal update
+// response.
+TEST_F(OmahaRequestActionTest, ValidUpdateTest) {
+  OmahaResponse response;
+  fake_update_response_.deadline = "20101020";
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  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);
+  EXPECT_EQ(fake_update_response_.hash, response.packages[0].hash);
+  EXPECT_EQ(fake_update_response_.size, response.packages[0].size);
+  EXPECT_EQ(true, response.packages[0].is_delta);
+  EXPECT_EQ(fake_update_response_.prompt == "true", response.prompt);
+  EXPECT_EQ(fake_update_response_.deadline, response.deadline);
+  EXPECT_FALSE(response.powerwash_required);
+  // Omaha cohort attributes are not set in the response, so they should not be
+  // persisted.
+  EXPECT_FALSE(fake_prefs_.Exists(kPrefsOmahaCohort));
+  EXPECT_FALSE(fake_prefs_.Exists(kPrefsOmahaCohortHint));
+  EXPECT_FALSE(fake_prefs_.Exists(kPrefsOmahaCohortName));
+}
+
+TEST_F(OmahaRequestActionTest, MultiPackageUpdateTest) {
+  OmahaResponse response;
+  fake_update_response_.multi_package = true;
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_TRUE(response.update_exists);
+  EXPECT_EQ(fake_update_response_.version, response.version);
+  EXPECT_EQ(fake_update_response_.GetPayloadUrl(),
+            response.packages[0].payload_urls[0]);
+  EXPECT_EQ(fake_update_response_.codebase + "package2",
+            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(true, response.packages[0].is_delta);
+  EXPECT_EQ(11u, response.packages[0].metadata_size);
+  ASSERT_EQ(2u, response.packages.size());
+  EXPECT_EQ(string("hash2"), response.packages[1].hash);
+  EXPECT_EQ(222u, response.packages[1].size);
+  EXPECT_EQ(22u, response.packages[1].metadata_size);
+  EXPECT_EQ(false, response.packages[1].is_delta);
+}
+
+TEST_F(OmahaRequestActionTest, MultiAppUpdateTest) {
+  OmahaResponse response;
+  fake_update_response_.multi_app = true;
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_TRUE(response.update_exists);
+  EXPECT_EQ(fake_update_response_.version, response.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, MultiAppAndSystemUpdateTest) {
+  OmahaResponse response;
+  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);
+
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  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) {
+  OmahaResponse response;
+  fake_update_response_.multi_app = true;
+  fake_update_response_.multi_app_self_update = true;
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  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);
+  EXPECT_EQ(fake_update_response_.size, response.packages[0].size);
+  EXPECT_EQ(11u, response.packages[0].metadata_size);
+  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(true, response.packages[1].is_delta);
+}
+
+TEST_F(OmahaRequestActionTest, MultiAppMultiPackageUpdateTest) {
+  OmahaResponse response;
+  fake_update_response_.multi_app = true;
+  fake_update_response_.multi_package = true;
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  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",
+            response.packages[1].payload_urls[0]);
+  EXPECT_EQ(fake_update_response_.codebase2 + "package3",
+            response.packages[2].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(3u, response.packages.size());
+  EXPECT_EQ(string("hash2"), response.packages[1].hash);
+  EXPECT_EQ(222u, response.packages[1].size);
+  EXPECT_EQ(22u, response.packages[1].metadata_size);
+  EXPECT_EQ(false, response.packages[1].is_delta);
+  EXPECT_EQ(string("hash3"), response.packages[2].hash);
+  EXPECT_EQ(333u, response.packages[2].size);
+  EXPECT_EQ(33u, response.packages[2].metadata_size);
+  EXPECT_EQ(false, response.packages[2].is_delta);
+}
+
+TEST_F(OmahaRequestActionTest, PowerwashTest) {
+  OmahaResponse response;
+  fake_update_response_.powerwash = true;
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_TRUE(response.update_exists);
+  EXPECT_TRUE(response.powerwash_required);
+}
+
+TEST_F(OmahaRequestActionTest, ExtraHeadersSentInteractiveTest) {
+  OmahaResponse response;
+  request_params_.set_interactive(true);
+  test_http_fetcher_headers_ = true;
+  ASSERT_FALSE(TestUpdateCheck("invalid xml>",
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kOmahaRequestXMLParseError,
+                               metrics::CheckResult::kParsingError,
+                               metrics::CheckReaction::kUnset,
+                               metrics::DownloadErrorCode::kUnset,
+                               &response,
+                               nullptr));
+  EXPECT_FALSE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, ExtraHeadersSentNoInteractiveTest) {
+  OmahaResponse response;
+  request_params_.set_interactive(false);
+  test_http_fetcher_headers_ = true;
+  ASSERT_FALSE(TestUpdateCheck("invalid xml>",
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kOmahaRequestXMLParseError,
+                               metrics::CheckResult::kParsingError,
+                               metrics::CheckReaction::kUnset,
+                               metrics::DownloadErrorCode::kUnset,
+                               &response,
+                               nullptr));
+  EXPECT_FALSE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, ValidUpdateBlockedByConnection) {
+  OmahaResponse response;
+  // Set up a connection manager that doesn't allow a valid update over
+  // the current ethernet connection.
+  MockConnectionManager mock_cm;
+  fake_system_state_.set_connection_manager(&mock_cm);
+
+  EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
+      .WillRepeatedly(DoAll(SetArgPointee<0>(ConnectionType::kEthernet),
+                            SetArgPointee<1>(ConnectionTethering::kUnknown),
+                            Return(true)));
+  EXPECT_CALL(mock_cm, IsUpdateAllowedOver(ConnectionType::kEthernet, _))
+      .WillRepeatedly(Return(false));
+
+  ASSERT_FALSE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kOmahaUpdateIgnoredPerPolicy,
+                               metrics::CheckResult::kUpdateAvailable,
+                               metrics::CheckReaction::kIgnored,
+                               metrics::DownloadErrorCode::kUnset,
+                               &response,
+                               nullptr));
+  EXPECT_FALSE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, ValidUpdateOverCellularAllowedByDevicePolicy) {
+  // This test tests that update over cellular is allowed as device policy
+  // says yes.
+  OmahaResponse response;
+  MockConnectionManager mock_cm;
+
+  fake_system_state_.set_connection_manager(&mock_cm);
+
+  EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
+      .WillRepeatedly(DoAll(SetArgPointee<0>(ConnectionType::kCellular),
+                            SetArgPointee<1>(ConnectionTethering::kUnknown),
+                            Return(true)));
+  EXPECT_CALL(mock_cm, IsAllowedConnectionTypesForUpdateSet())
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(mock_cm, IsUpdateAllowedOver(ConnectionType::kCellular, _))
+      .WillRepeatedly(Return(true));
+
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_TRUE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, ValidUpdateOverCellularBlockedByDevicePolicy) {
+  // This test tests that update over cellular is blocked as device policy
+  // says no.
+  OmahaResponse response;
+  MockConnectionManager mock_cm;
+
+  fake_system_state_.set_connection_manager(&mock_cm);
+
+  EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
+      .WillRepeatedly(DoAll(SetArgPointee<0>(ConnectionType::kCellular),
+                            SetArgPointee<1>(ConnectionTethering::kUnknown),
+                            Return(true)));
+  EXPECT_CALL(mock_cm, IsAllowedConnectionTypesForUpdateSet())
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(mock_cm, IsUpdateAllowedOver(ConnectionType::kCellular, _))
+      .WillRepeatedly(Return(false));
+
+  ASSERT_FALSE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kOmahaUpdateIgnoredPerPolicy,
+                               metrics::CheckResult::kUpdateAvailable,
+                               metrics::CheckReaction::kIgnored,
+                               metrics::DownloadErrorCode::kUnset,
+                               &response,
+                               nullptr));
+  EXPECT_FALSE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest,
+       ValidUpdateOverCellularAllowedByUserPermissionTrue) {
+  // This test tests that, when device policy is not set, update over cellular
+  // is allowed as permission for update over cellular is set to true.
+  OmahaResponse response;
+  MockConnectionManager mock_cm;
+
+  fake_prefs_.SetBoolean(kPrefsUpdateOverCellularPermission, true);
+  fake_system_state_.set_connection_manager(&mock_cm);
+
+  EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
+      .WillRepeatedly(DoAll(SetArgPointee<0>(ConnectionType::kCellular),
+                            SetArgPointee<1>(ConnectionTethering::kUnknown),
+                            Return(true)));
+  EXPECT_CALL(mock_cm, IsAllowedConnectionTypesForUpdateSet())
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(mock_cm, IsUpdateAllowedOver(ConnectionType::kCellular, _))
+      .WillRepeatedly(Return(true));
+
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_TRUE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest,
+       ValidUpdateOverCellularBlockedByUpdateTargetNotMatch) {
+  // This test tests that, when device policy is not set and permission for
+  // update over cellular is set to false or does not exist, update over
+  // cellular is blocked as update target does not match the omaha response.
+  OmahaResponse response;
+  MockConnectionManager mock_cm;
+  // A version different from the version in omaha response.
+  string diff_version = "99.99.99";
+  // A size different from the size in omaha response.
+  int64_t diff_size = 999;
+
+  fake_prefs_.SetString(kPrefsUpdateOverCellularTargetVersion, diff_version);
+  fake_prefs_.SetInt64(kPrefsUpdateOverCellularTargetSize, diff_size);
+  // This test tests cellular (3G) being the only connection type being allowed.
+  fake_system_state_.set_connection_manager(&mock_cm);
+
+  EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
+      .WillRepeatedly(DoAll(SetArgPointee<0>(ConnectionType::kCellular),
+                            SetArgPointee<1>(ConnectionTethering::kUnknown),
+                            Return(true)));
+  EXPECT_CALL(mock_cm, IsAllowedConnectionTypesForUpdateSet())
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(mock_cm, IsUpdateAllowedOver(ConnectionType::kCellular, _))
+      .WillRepeatedly(Return(true));
+
+  ASSERT_FALSE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kOmahaUpdateIgnoredOverCellular,
+                               metrics::CheckResult::kUpdateAvailable,
+                               metrics::CheckReaction::kIgnored,
+                               metrics::DownloadErrorCode::kUnset,
+                               &response,
+                               nullptr));
+  EXPECT_FALSE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest,
+       ValidUpdateOverCellularAllowedByUpdateTargetMatch) {
+  // This test tests that, when device policy is not set and permission for
+  // update over cellular is set to false or does not exist, update over
+  // cellular is allowed as update target matches the omaha response.
+  OmahaResponse response;
+  MockConnectionManager mock_cm;
+  // A version same as the version in omaha response.
+  string new_version = fake_update_response_.version;
+  // A size same as the size in omaha response.
+  int64_t new_size = fake_update_response_.size;
+
+  fake_prefs_.SetString(kPrefsUpdateOverCellularTargetVersion, new_version);
+  fake_prefs_.SetInt64(kPrefsUpdateOverCellularTargetSize, new_size);
+  fake_system_state_.set_connection_manager(&mock_cm);
+
+  EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
+      .WillRepeatedly(DoAll(SetArgPointee<0>(ConnectionType::kCellular),
+                            SetArgPointee<1>(ConnectionTethering::kUnknown),
+                            Return(true)));
+  EXPECT_CALL(mock_cm, IsAllowedConnectionTypesForUpdateSet())
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(mock_cm, IsUpdateAllowedOver(ConnectionType::kCellular, _))
+      .WillRepeatedly(Return(true));
+
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_TRUE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, ValidUpdateBlockedByRollback) {
+  string rollback_version = "1234.0.0";
+  OmahaResponse response;
+
+  MockPayloadState mock_payload_state;
+  fake_system_state_.set_payload_state(&mock_payload_state);
+
+  EXPECT_CALL(mock_payload_state, GetRollbackVersion())
+      .WillRepeatedly(Return(rollback_version));
+
+  fake_update_response_.version = rollback_version;
+  ASSERT_FALSE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kOmahaUpdateIgnoredPerPolicy,
+                               metrics::CheckResult::kUpdateAvailable,
+                               metrics::CheckReaction::kIgnored,
+                               metrics::DownloadErrorCode::kUnset,
+                               &response,
+                               nullptr));
+  EXPECT_FALSE(response.update_exists);
+}
+
+// Verify that update checks called during OOBE will not try to download an
+// update if the response doesn't include the deadline field.
+TEST_F(OmahaRequestActionTest, SkipNonCriticalUpdatesBeforeOOBE) {
+  OmahaResponse response;
+  fake_system_state_.fake_hardware()->UnsetIsOOBEComplete();
+
+  // TODO(senj): set better default value for metrics::checkresult in
+  // OmahaRequestAction::ActionCompleted.
+  ASSERT_FALSE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kNonCriticalUpdateInOOBE,
+                               metrics::CheckResult::kParsingError,
+                               metrics::CheckReaction::kUnset,
+                               metrics::DownloadErrorCode::kUnset,
+                               &response,
+                               nullptr));
+  EXPECT_FALSE(response.update_exists);
+}
+
+// Verify that the IsOOBEComplete() value is ignored when the OOBE flow is not
+// enabled.
+TEST_F(OmahaRequestActionTest, SkipNonCriticalUpdatesBeforeOOBEDisabled) {
+  OmahaResponse response;
+  fake_system_state_.fake_hardware()->UnsetIsOOBEComplete();
+  fake_system_state_.fake_hardware()->SetIsOOBEEnabled(false);
+
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_TRUE(response.update_exists);
+}
+
+// Verify that update checks called during OOBE will still try to download an
+// update if the response includes the deadline field.
+TEST_F(OmahaRequestActionTest, SkipNonCriticalUpdatesBeforeOOBEDeadlineSet) {
+  OmahaResponse response;
+  fake_system_state_.fake_hardware()->UnsetIsOOBEComplete();
+  fake_update_response_.deadline = "20101020";
+
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_TRUE(response.update_exists);
+}
+
+// Verify that update checks called during OOBE will not try to download an
+// update if a rollback happened, even when the response includes the deadline
+// field.
+TEST_F(OmahaRequestActionTest, SkipNonCriticalUpdatesBeforeOOBERollback) {
+  OmahaResponse response;
+  fake_system_state_.fake_hardware()->UnsetIsOOBEComplete();
+  fake_update_response_.deadline = "20101020";
+  EXPECT_CALL(*(fake_system_state_.mock_payload_state()), GetRollbackHappened())
+      .WillOnce(Return(true));
+
+  ASSERT_FALSE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kNonCriticalUpdateInOOBE,
+                               metrics::CheckResult::kParsingError,
+                               metrics::CheckReaction::kUnset,
+                               metrics::DownloadErrorCode::kUnset,
+                               &response,
+                               nullptr));
+  EXPECT_FALSE(response.update_exists);
+}
+
+// Verify that non-critical updates are skipped by reporting the
+// kNonCriticalUpdateInOOBE error code when attempted over cellular network -
+// i.e. when the update would need user permission. Note that reporting
+// kOmahaUpdateIgnoredOverCellular error in this case  might cause undesired UX
+// in OOBE (warning the user about an update that will be skipped).
+TEST_F(OmahaRequestActionTest, SkipNonCriticalUpdatesInOOBEOverCellular) {
+  OmahaResponse response;
+  fake_system_state_.fake_hardware()->UnsetIsOOBEComplete();
+
+  MockConnectionManager mock_cm;
+  fake_system_state_.set_connection_manager(&mock_cm);
+
+  EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
+      .WillRepeatedly(DoAll(SetArgPointee<0>(ConnectionType::kCellular),
+                            SetArgPointee<1>(ConnectionTethering::kUnknown),
+                            Return(true)));
+  EXPECT_CALL(mock_cm, IsAllowedConnectionTypesForUpdateSet())
+      .WillRepeatedly(Return(false));
+
+  ASSERT_FALSE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kNonCriticalUpdateInOOBE,
+                               metrics::CheckResult::kParsingError,
+                               metrics::CheckReaction::kUnset,
+                               metrics::DownloadErrorCode::kUnset,
+                               &response,
+                               nullptr));
+  EXPECT_FALSE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, WallClockBasedWaitAloneCausesScattering) {
+  OmahaResponse response;
+  request_params_.set_wall_clock_based_wait_enabled(true);
+  request_params_.set_update_check_count_wait_enabled(false);
+  request_params_.set_waiting_period(TimeDelta::FromDays(2));
+
+  fake_system_state_.fake_clock()->SetWallclockTime(Time::Now());
+
+  ASSERT_FALSE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kOmahaUpdateDeferredPerPolicy,
+                               metrics::CheckResult::kUpdateAvailable,
+                               metrics::CheckReaction::kDeferring,
+                               metrics::DownloadErrorCode::kUnset,
+                               &response,
+                               nullptr));
+  EXPECT_FALSE(response.update_exists);
+
+  // Verify if we are interactive check we don't defer.
+  request_params_.set_interactive(true);
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_TRUE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, NoWallClockBasedWaitCausesNoScattering) {
+  OmahaResponse response;
+  request_params_.set_wall_clock_based_wait_enabled(false);
+  request_params_.set_waiting_period(TimeDelta::FromDays(2));
+  request_params_.set_update_check_count_wait_enabled(true);
+  request_params_.set_min_update_checks_needed(1);
+  request_params_.set_max_update_checks_allowed(8);
+
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_TRUE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, ZeroMaxDaysToScatterCausesNoScattering) {
+  OmahaResponse response;
+  request_params_.set_wall_clock_based_wait_enabled(true);
+  request_params_.set_waiting_period(TimeDelta::FromDays(2));
+  request_params_.set_update_check_count_wait_enabled(true);
+  request_params_.set_min_update_checks_needed(1);
+  request_params_.set_max_update_checks_allowed(8);
+
+  fake_update_response_.max_days_to_scatter = "0";
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_TRUE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, ZeroUpdateCheckCountCausesNoScattering) {
+  OmahaResponse response;
+  request_params_.set_wall_clock_based_wait_enabled(true);
+  request_params_.set_waiting_period(TimeDelta());
+  request_params_.set_update_check_count_wait_enabled(true);
+  request_params_.set_min_update_checks_needed(0);
+  request_params_.set_max_update_checks_allowed(0);
+
+  fake_system_state_.fake_clock()->SetWallclockTime(Time::Now());
+
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+
+  int64_t count;
+  ASSERT_TRUE(fake_prefs_.GetInt64(kPrefsUpdateCheckCount, &count));
+  ASSERT_EQ(count, 0);
+  EXPECT_TRUE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, NonZeroUpdateCheckCountCausesScattering) {
+  OmahaResponse response;
+  request_params_.set_wall_clock_based_wait_enabled(true);
+  request_params_.set_waiting_period(TimeDelta());
+  request_params_.set_update_check_count_wait_enabled(true);
+  request_params_.set_min_update_checks_needed(1);
+  request_params_.set_max_update_checks_allowed(8);
+
+  fake_system_state_.fake_clock()->SetWallclockTime(Time::Now());
+
+  ASSERT_FALSE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kOmahaUpdateDeferredPerPolicy,
+                               metrics::CheckResult::kUpdateAvailable,
+                               metrics::CheckReaction::kDeferring,
+                               metrics::DownloadErrorCode::kUnset,
+                               &response,
+                               nullptr));
+
+  int64_t count;
+  ASSERT_TRUE(fake_prefs_.GetInt64(kPrefsUpdateCheckCount, &count));
+  ASSERT_GT(count, 0);
+  EXPECT_FALSE(response.update_exists);
+
+  // Verify if we are interactive check we don't defer.
+  request_params_.set_interactive(true);
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_TRUE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, ExistingUpdateCheckCountCausesScattering) {
+  OmahaResponse response;
+  request_params_.set_wall_clock_based_wait_enabled(true);
+  request_params_.set_waiting_period(TimeDelta());
+  request_params_.set_update_check_count_wait_enabled(true);
+  request_params_.set_min_update_checks_needed(1);
+  request_params_.set_max_update_checks_allowed(8);
+
+  fake_system_state_.fake_clock()->SetWallclockTime(Time::Now());
+
+  ASSERT_TRUE(fake_prefs_.SetInt64(kPrefsUpdateCheckCount, 5));
+
+  ASSERT_FALSE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kOmahaUpdateDeferredPerPolicy,
+                               metrics::CheckResult::kUpdateAvailable,
+                               metrics::CheckReaction::kDeferring,
+                               metrics::DownloadErrorCode::kUnset,
+                               &response,
+                               nullptr));
+
+  int64_t count;
+  ASSERT_TRUE(fake_prefs_.GetInt64(kPrefsUpdateCheckCount, &count));
+  // count remains the same, as the decrementing happens in update_attempter
+  // which this test doesn't exercise.
+  ASSERT_EQ(count, 5);
+  EXPECT_FALSE(response.update_exists);
+
+  // Verify if we are interactive check we don't defer.
+  request_params_.set_interactive(true);
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_TRUE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, StagingTurnedOnCausesScattering) {
+  // If staging is on, the value for max days to scatter should be ignored, and
+  // staging's scatter value should be used.
+  OmahaResponse response;
+  request_params_.set_wall_clock_based_wait_enabled(true);
+  request_params_.set_waiting_period(TimeDelta::FromDays(6));
+  request_params_.set_update_check_count_wait_enabled(false);
+
+  fake_system_state_.fake_clock()->SetWallclockTime(Time::Now());
+
+  ASSERT_TRUE(fake_prefs_.SetInt64(kPrefsWallClockStagingWaitPeriod, 6));
+  // This should not prevent scattering due to staging.
+  fake_update_response_.max_days_to_scatter = "0";
+  ASSERT_FALSE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kOmahaUpdateDeferredPerPolicy,
+                               metrics::CheckResult::kUpdateAvailable,
+                               metrics::CheckReaction::kDeferring,
+                               metrics::DownloadErrorCode::kUnset,
+                               &response,
+                               nullptr));
+  EXPECT_FALSE(response.update_exists);
+
+  // Interactive updates should not be affected.
+  request_params_.set_interactive(true);
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_TRUE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, CohortsArePersisted) {
+  OmahaResponse response;
+  fake_update_response_.include_cohorts = true;
+  fake_update_response_.cohort = "s/154454/8479665";
+  fake_update_response_.cohorthint = "please-put-me-on-beta";
+  fake_update_response_.cohortname = "stable";
+
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+
+  string value;
+  EXPECT_TRUE(fake_prefs_.GetString(kPrefsOmahaCohort, &value));
+  EXPECT_EQ(fake_update_response_.cohort, value);
+
+  EXPECT_TRUE(fake_prefs_.GetString(kPrefsOmahaCohortHint, &value));
+  EXPECT_EQ(fake_update_response_.cohorthint, value);
+
+  EXPECT_TRUE(fake_prefs_.GetString(kPrefsOmahaCohortName, &value));
+  EXPECT_EQ(fake_update_response_.cohortname, value);
+}
+
+TEST_F(OmahaRequestActionTest, CohortsAreUpdated) {
+  OmahaResponse response;
+  EXPECT_TRUE(fake_prefs_.SetString(kPrefsOmahaCohort, "old_value"));
+  EXPECT_TRUE(fake_prefs_.SetString(kPrefsOmahaCohortHint, "old_hint"));
+  EXPECT_TRUE(fake_prefs_.SetString(kPrefsOmahaCohortName, "old_name"));
+  fake_update_response_.include_cohorts = true;
+  fake_update_response_.cohort = "s/154454/8479665";
+  fake_update_response_.cohorthint = "please-put-me-on-beta";
+  fake_update_response_.cohortname = "";
+
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+
+  string value;
+  EXPECT_TRUE(fake_prefs_.GetString(kPrefsOmahaCohort, &value));
+  EXPECT_EQ(fake_update_response_.cohort, value);
+
+  EXPECT_TRUE(fake_prefs_.GetString(kPrefsOmahaCohortHint, &value));
+  EXPECT_EQ(fake_update_response_.cohorthint, value);
+
+  EXPECT_FALSE(fake_prefs_.GetString(kPrefsOmahaCohortName, &value));
+}
+
+TEST_F(OmahaRequestActionTest, CohortsAreNotModifiedWhenMissing) {
+  OmahaResponse response;
+  EXPECT_TRUE(fake_prefs_.SetString(kPrefsOmahaCohort, "old_value"));
+
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+
+  string value;
+  EXPECT_TRUE(fake_prefs_.GetString(kPrefsOmahaCohort, &value));
+  EXPECT_EQ("old_value", value);
+
+  EXPECT_FALSE(fake_prefs_.GetString(kPrefsOmahaCohortHint, &value));
+  EXPECT_FALSE(fake_prefs_.GetString(kPrefsOmahaCohortName, &value));
+}
+
+TEST_F(OmahaRequestActionTest, CohortsArePersistedWhenNoUpdate) {
+  OmahaResponse response;
+  fake_update_response_.include_cohorts = true;
+  fake_update_response_.cohort = "s/154454/8479665";
+  fake_update_response_.cohorthint = "please-put-me-on-beta";
+  fake_update_response_.cohortname = "stable";
+
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetNoUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kNoUpdateAvailable,
+                              metrics::CheckReaction::kUnset,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+
+  string value;
+  EXPECT_TRUE(fake_prefs_.GetString(kPrefsOmahaCohort, &value));
+  EXPECT_EQ(fake_update_response_.cohort, value);
+
+  EXPECT_TRUE(fake_prefs_.GetString(kPrefsOmahaCohortHint, &value));
+  EXPECT_EQ(fake_update_response_.cohorthint, value);
+
+  EXPECT_TRUE(fake_prefs_.GetString(kPrefsOmahaCohortName, &value));
+  EXPECT_EQ(fake_update_response_.cohortname, value);
+}
+
+TEST_F(OmahaRequestActionTest, MultiAppCohortTest) {
+  OmahaResponse response;
+  fake_update_response_.multi_app = true;
+  fake_update_response_.include_cohorts = true;
+  fake_update_response_.cohort = "s/154454/8479665";
+  fake_update_response_.cohorthint = "please-put-me-on-beta";
+  fake_update_response_.cohortname = "stable";
+
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+
+  string value;
+  EXPECT_TRUE(fake_prefs_.GetString(kPrefsOmahaCohort, &value));
+  EXPECT_EQ(fake_update_response_.cohort, value);
+
+  EXPECT_TRUE(fake_prefs_.GetString(kPrefsOmahaCohortHint, &value));
+  EXPECT_EQ(fake_update_response_.cohorthint, value);
+
+  EXPECT_TRUE(fake_prefs_.GetString(kPrefsOmahaCohortName, &value));
+  EXPECT_EQ(fake_update_response_.cohortname, value);
+}
+
+TEST_F(OmahaRequestActionTest, NoOutputPipeTest) {
+  const string http_response(fake_update_response_.GetNoUpdateResponse());
+
+  brillo::FakeMessageLoop loop(nullptr);
+  loop.SetAsCurrent();
+
+  auto action = std::make_unique<OmahaRequestAction>(
+      &fake_system_state_,
+      nullptr,
+      std::make_unique<MockHttpFetcher>(
+          http_response.data(), http_response.size(), nullptr),
+      false);
+  ActionProcessor processor;
+  processor.set_delegate(&delegate_);
+  processor.EnqueueAction(std::move(action));
+
+  loop.PostTask(base::Bind(
+      [](ActionProcessor* processor) { processor->StartProcessing(); },
+      base::Unretained(&processor)));
+  loop.Run();
+  EXPECT_FALSE(loop.PendingTasks());
+  EXPECT_FALSE(processor.IsRunning());
+}
+
+TEST_F(OmahaRequestActionTest, InvalidXmlTest) {
+  OmahaResponse response;
+  ASSERT_FALSE(TestUpdateCheck("invalid xml>",
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kOmahaRequestXMLParseError,
+                               metrics::CheckResult::kParsingError,
+                               metrics::CheckReaction::kUnset,
+                               metrics::DownloadErrorCode::kUnset,
+                               &response,
+                               nullptr));
+  EXPECT_FALSE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, EmptyResponseTest) {
+  OmahaResponse response;
+  ASSERT_FALSE(TestUpdateCheck("",
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kOmahaRequestEmptyResponseError,
+                               metrics::CheckResult::kParsingError,
+                               metrics::CheckReaction::kUnset,
+                               metrics::DownloadErrorCode::kUnset,
+                               &response,
+                               nullptr));
+  EXPECT_FALSE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, MissingStatusTest) {
+  OmahaResponse response;
+  ASSERT_FALSE(TestUpdateCheck(
+      "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\">"
+      "<daystart elapsed_seconds=\"100\"/>"
+      "<app appid=\"foo\" status=\"ok\">"
+      "<ping status=\"ok\"/>"
+      "<updatecheck/></app></response>",
+      -1,
+      false,  // ping_only
+      ErrorCode::kOmahaResponseInvalid,
+      metrics::CheckResult::kParsingError,
+      metrics::CheckReaction::kUnset,
+      metrics::DownloadErrorCode::kUnset,
+      &response,
+      nullptr));
+  EXPECT_FALSE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, InvalidStatusTest) {
+  OmahaResponse response;
+  ASSERT_FALSE(TestUpdateCheck(
+      "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\">"
+      "<daystart elapsed_seconds=\"100\"/>"
+      "<app appid=\"foo\" status=\"ok\">"
+      "<ping status=\"ok\"/>"
+      "<updatecheck status=\"InvalidStatusTest\"/></app></response>",
+      -1,
+      false,  // ping_only
+      ErrorCode::kOmahaResponseInvalid,
+      metrics::CheckResult::kParsingError,
+      metrics::CheckReaction::kUnset,
+      metrics::DownloadErrorCode::kUnset,
+      &response,
+      nullptr));
+  EXPECT_FALSE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, MissingNodesetTest) {
+  OmahaResponse response;
+  ASSERT_FALSE(TestUpdateCheck(
+      "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\">"
+      "<daystart elapsed_seconds=\"100\"/>"
+      "<app appid=\"foo\" status=\"ok\">"
+      "<ping status=\"ok\"/>"
+      "</app></response>",
+      -1,
+      false,  // ping_only
+      ErrorCode::kOmahaResponseInvalid,
+      metrics::CheckResult::kParsingError,
+      metrics::CheckReaction::kUnset,
+      metrics::DownloadErrorCode::kUnset,
+      &response,
+      nullptr));
+  EXPECT_FALSE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, MissingFieldTest) {
+  string input_response =
+      "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\">"
+      "<daystart elapsed_seconds=\"100\"/>"
+      // the appid needs to match that in the request params
+      "<app appid=\"" +
+      fake_update_response_.app_id +
+      "\" status=\"ok\">"
+      "<updatecheck status=\"ok\">"
+      "<urls><url codebase=\"http://missing/field/test/\"/></urls>"
+      "<manifest version=\"10.2.3.4\">"
+      "<packages><package hash=\"not-used\" name=\"f\" "
+      "size=\"587\" hash_sha256=\"lkq34j5345\"/></packages>"
+      "<actions><action event=\"postinstall\" "
+      "Prompt=\"false\" "
+      "IsDeltaPayload=\"false\" "
+      "sha256=\"not-used\" "
+      "/></actions></manifest></updatecheck></app></response>";
+  LOG(INFO) << "Input Response = " << input_response;
+
+  OmahaResponse response;
+  ASSERT_TRUE(TestUpdateCheck(input_response,
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_TRUE(response.update_exists);
+  EXPECT_EQ("10.2.3.4", response.version);
+  EXPECT_EQ("http://missing/field/test/f",
+            response.packages[0].payload_urls[0]);
+  EXPECT_EQ("", response.more_info_url);
+  EXPECT_EQ("lkq34j5345", response.packages[0].hash);
+  EXPECT_EQ(587u, response.packages[0].size);
+  EXPECT_FALSE(response.prompt);
+  EXPECT_TRUE(response.deadline.empty());
+}
+
+namespace {
+class TerminateEarlyTestProcessorDelegate : public ActionProcessorDelegate {
+ public:
+  void ProcessingStopped(const ActionProcessor* processor) {
+    brillo::MessageLoop::current()->BreakLoop();
+  }
+};
+
+void TerminateTransferTestStarter(ActionProcessor* processor) {
+  processor->StartProcessing();
+  CHECK(processor->IsRunning());
+  processor->StopProcessing();
+}
+}  // namespace
+
+TEST_F(OmahaRequestActionTest, TerminateTransferTest) {
+  brillo::FakeMessageLoop loop(nullptr);
+  loop.SetAsCurrent();
+
+  string http_response("doesn't matter");
+  auto action = std::make_unique<OmahaRequestAction>(
+      &fake_system_state_,
+      nullptr,
+      std::make_unique<MockHttpFetcher>(
+          http_response.data(), http_response.size(), nullptr),
+      false);
+  TerminateEarlyTestProcessorDelegate delegate;
+  ActionProcessor processor;
+  processor.set_delegate(&delegate);
+  processor.EnqueueAction(std::move(action));
+
+  loop.PostTask(base::Bind(&TerminateTransferTestStarter, &processor));
+  loop.Run();
+  EXPECT_FALSE(loop.PendingTasks());
+}
+
+TEST_F(OmahaRequestActionTest, XmlEncodeTest) {
+  string output;
+  EXPECT_TRUE(XmlEncode("ab", &output));
+  EXPECT_EQ("ab", output);
+  EXPECT_TRUE(XmlEncode("a<b", &output));
+  EXPECT_EQ("a&lt;b", output);
+  EXPECT_TRUE(XmlEncode("<&>\"\'\\", &output));
+  EXPECT_EQ("&lt;&amp;&gt;&quot;&apos;\\", output);
+  EXPECT_TRUE(XmlEncode("&lt;&amp;&gt;", &output));
+  EXPECT_EQ("&amp;lt;&amp;amp;&amp;gt;", output);
+  // Check that unterminated UTF-8 strings are handled properly.
+  EXPECT_FALSE(XmlEncode("\xc2", &output));
+  // Fail with invalid ASCII-7 chars.
+  EXPECT_FALSE(XmlEncode("This is an 'n' with a tilde: \xc3\xb1", &output));
+}
+
+TEST_F(OmahaRequestActionTest, XmlEncodeWithDefaultTest) {
+  EXPECT_EQ("&lt;&amp;&gt;", XmlEncodeWithDefault("<&>", "something else"));
+  EXPECT_EQ("<not escaped>", XmlEncodeWithDefault("\xc2", "<not escaped>"));
+}
+
+TEST_F(OmahaRequestActionTest, XmlEncodeIsUsedForParams) {
+  brillo::Blob post_data;
+
+  // Make sure XML Encode is being called on the params
+  request_params_.set_os_sp("testtheservice_pack>");
+  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_hwid("<OEM MODEL>");
+  fake_prefs_.SetString(kPrefsOmahaCohort, "evil\nstring");
+  fake_prefs_.SetString(kPrefsOmahaCohortHint, "evil&string\\");
+  fake_prefs_.SetString(
+      kPrefsOmahaCohortName,
+      base::JoinString(vector<string>(100, "My spoon is too big."), " "));
+  OmahaResponse response;
+  ASSERT_FALSE(TestUpdateCheck("invalid xml>",
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kOmahaRequestXMLParseError,
+                               metrics::CheckResult::kParsingError,
+                               metrics::CheckReaction::kUnset,
+                               metrics::DownloadErrorCode::kUnset,
+                               &response,
+                               &post_data));
+  // convert post_data to string
+  string post_str(post_data.begin(), post_data.end());
+  EXPECT_NE(string::npos, post_str.find("testtheservice_pack&gt;"));
+  EXPECT_EQ(string::npos, post_str.find("testtheservice_pack>"));
+  EXPECT_NE(string::npos, post_str.find("x86 generic&lt;id"));
+  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("&lt;OEM MODEL&gt;"));
+  EXPECT_EQ(string::npos, post_str.find("<OEM MODEL>"));
+  EXPECT_NE(string::npos, post_str.find("cohort=\"evil\nstring\""));
+  EXPECT_EQ(string::npos, post_str.find("cohorthint=\"evil&string\\\""));
+  EXPECT_NE(string::npos, post_str.find("cohorthint=\"evil&amp;string\\\""));
+  // Values from Prefs that are too big are removed from the XML instead of
+  // encoded.
+  EXPECT_EQ(string::npos, post_str.find("cohortname="));
+}
+
+TEST_F(OmahaRequestActionTest, XmlDecodeTest) {
+  OmahaResponse response;
+  fake_update_response_.deadline = "&lt;20110101";
+  fake_update_response_.more_info_url = "testthe&lt;url";
+  fake_update_response_.codebase = "testthe&amp;codebase/";
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+
+  EXPECT_EQ("testthe<url", response.more_info_url);
+  EXPECT_EQ("testthe&codebase/file.signed",
+            response.packages[0].payload_urls[0]);
+  EXPECT_EQ("<20110101", response.deadline);
+}
+
+TEST_F(OmahaRequestActionTest, ParseIntTest) {
+  OmahaResponse response;
+  // overflows int32_t:
+  fake_update_response_.size = 123123123123123ull;
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+
+  EXPECT_EQ(fake_update_response_.size, response.packages[0].size);
+}
+
+TEST_F(OmahaRequestActionTest, FormatUpdateCheckOutputTest) {
+  brillo::Blob post_data;
+  NiceMock<MockPrefs> prefs;
+  fake_system_state_.set_prefs(&prefs);
+
+  EXPECT_CALL(prefs, GetString(kPrefsPreviousVersion, _))
+      .WillOnce(DoAll(SetArgPointee<1>(string("")), Return(true)));
+  // An existing but empty previous version means that we didn't reboot to a new
+  // update, therefore, no need to update the previous version.
+  EXPECT_CALL(prefs, SetString(kPrefsPreviousVersion, _)).Times(0);
+  ASSERT_FALSE(TestUpdateCheck("invalid xml>",
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kOmahaRequestXMLParseError,
+                               metrics::CheckResult::kParsingError,
+                               metrics::CheckReaction::kUnset,
+                               metrics::DownloadErrorCode::kUnset,
+                               nullptr,  // response
+                               &post_data));
+  // convert post_data to string
+  string post_str(post_data.begin(), post_data.end());
+  EXPECT_NE(
+      post_str.find("        <ping active=\"1\" a=\"-1\" r=\"-1\"></ping>\n"
+                    "        <updatecheck></updatecheck>\n"),
+      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);
+}
+
+TEST_F(OmahaRequestActionTest, FormatSuccessEventOutputTest) {
+  brillo::Blob post_data;
+  TestEvent(new OmahaEvent(OmahaEvent::kTypeUpdateDownloadStarted),
+            "invalid xml>",
+            &post_data);
+  // convert post_data to string
+  string post_str(post_data.begin(), post_data.end());
+  string expected_event = base::StringPrintf(
+      "        <event eventtype=\"%d\" eventresult=\"%d\"></event>\n",
+      OmahaEvent::kTypeUpdateDownloadStarted,
+      OmahaEvent::kResultSuccess);
+  EXPECT_NE(post_str.find(expected_event), string::npos);
+  EXPECT_EQ(post_str.find("ping"), string::npos);
+  EXPECT_EQ(post_str.find("updatecheck"), string::npos);
+}
+
+TEST_F(OmahaRequestActionTest, FormatErrorEventOutputTest) {
+  brillo::Blob post_data;
+  TestEvent(new OmahaEvent(OmahaEvent::kTypeDownloadComplete,
+                           OmahaEvent::kResultError,
+                           ErrorCode::kError),
+            "invalid xml>",
+            &post_data);
+  // convert post_data to string
+  string post_str(post_data.begin(), post_data.end());
+  string expected_event = base::StringPrintf(
+      "        <event eventtype=\"%d\" eventresult=\"%d\" "
+      "errorcode=\"%d\"></event>\n",
+      OmahaEvent::kTypeDownloadComplete,
+      OmahaEvent::kResultError,
+      static_cast<int>(ErrorCode::kError));
+  EXPECT_NE(post_str.find(expected_event), string::npos);
+  EXPECT_EQ(post_str.find("updatecheck"), string::npos);
+}
+
+TEST_F(OmahaRequestActionTest, IsEventTest) {
+  string http_response("doesn't matter");
+  OmahaRequestAction update_check_action(
+      &fake_system_state_,
+      nullptr,
+      std::make_unique<MockHttpFetcher>(
+          http_response.data(), http_response.size(), nullptr),
+      false);
+  EXPECT_FALSE(update_check_action.IsEvent());
+
+  OmahaRequestAction event_action(
+      &fake_system_state_,
+      new OmahaEvent(OmahaEvent::kTypeUpdateComplete),
+      std::make_unique<MockHttpFetcher>(
+          http_response.data(), http_response.size(), nullptr),
+      false);
+  EXPECT_TRUE(event_action.IsEvent());
+}
+
+TEST_F(OmahaRequestActionTest, FormatDeltaOkayOutputTest) {
+  for (int i = 0; i < 2; i++) {
+    bool delta_okay = i == 1;
+    const char* delta_okay_str = delta_okay ? "true" : "false";
+    brillo::Blob post_data;
+
+    request_params_.set_delta_okay(delta_okay);
+
+    ASSERT_FALSE(TestUpdateCheck("invalid xml>",
+                                 -1,
+                                 false,  // ping_only
+                                 ErrorCode::kOmahaRequestXMLParseError,
+                                 metrics::CheckResult::kParsingError,
+                                 metrics::CheckReaction::kUnset,
+                                 metrics::DownloadErrorCode::kUnset,
+                                 nullptr,
+                                 &post_data));
+    // convert post_data to string
+    string post_str(post_data.begin(), post_data.end());
+    EXPECT_NE(
+        post_str.find(base::StringPrintf(" delta_okay=\"%s\"", delta_okay_str)),
+        string::npos)
+        << "i = " << i;
+  }
+}
+
+TEST_F(OmahaRequestActionTest, FormatInteractiveOutputTest) {
+  for (int i = 0; i < 2; i++) {
+    bool interactive = i == 1;
+    const char* interactive_str = interactive ? "ondemandupdate" : "scheduler";
+    brillo::Blob post_data;
+    FakeSystemState fake_system_state;
+
+    request_params_.set_interactive(interactive);
+
+    ASSERT_FALSE(TestUpdateCheck("invalid xml>",
+                                 -1,
+                                 false,  // ping_only
+                                 ErrorCode::kOmahaRequestXMLParseError,
+                                 metrics::CheckResult::kParsingError,
+                                 metrics::CheckReaction::kUnset,
+                                 metrics::DownloadErrorCode::kUnset,
+                                 nullptr,
+                                 &post_data));
+    // convert post_data to string
+    string post_str(post_data.begin(), post_data.end());
+    EXPECT_NE(post_str.find(
+                  base::StringPrintf("installsource=\"%s\"", interactive_str)),
+              string::npos)
+        << "i = " << i;
+  }
+}
+
+TEST_F(OmahaRequestActionTest, FormatTargetVersionPrefixOutputTest) {
+  for (int i = 0; i < 2; i++) {
+    bool target_version_set = i == 1;
+    const char* target_version_prefix = target_version_set ? "10032." : "";
+    brillo::Blob post_data;
+    FakeSystemState fake_system_state;
+
+    request_params_.set_target_version_prefix(target_version_prefix);
+
+    ASSERT_FALSE(TestUpdateCheck("invalid xml>",
+                                 -1,
+                                 false,  // ping_only
+                                 ErrorCode::kOmahaRequestXMLParseError,
+                                 metrics::CheckResult::kParsingError,
+                                 metrics::CheckReaction::kUnset,
+                                 metrics::DownloadErrorCode::kUnset,
+                                 nullptr,
+                                 &post_data));
+    // convert post_data to string
+    string post_str(post_data.begin(), post_data.end());
+    if (target_version_set) {
+      EXPECT_NE(post_str.find("<updatecheck targetversionprefix=\"10032.\">"),
+                string::npos)
+          << "i = " << i;
+    } else {
+      EXPECT_EQ(post_str.find("targetversionprefix"), string::npos)
+          << "i = " << i;
+    }
+  }
+}
+
+TEST_F(OmahaRequestActionTest, FormatRollbackAllowedOutputTest) {
+  for (int i = 0; i < 4; i++) {
+    bool rollback_allowed = i / 2 == 0;
+    bool target_version_set = i % 2 == 0;
+    brillo::Blob post_data;
+    FakeSystemState fake_system_state;
+
+    request_params_.set_target_version_prefix(target_version_set ? "10032."
+                                                                 : "");
+    request_params_.set_rollback_allowed(rollback_allowed);
+
+    ASSERT_FALSE(TestUpdateCheck("invalid xml>",
+                                 -1,
+                                 false,  // ping_only
+                                 ErrorCode::kOmahaRequestXMLParseError,
+                                 metrics::CheckResult::kParsingError,
+                                 metrics::CheckReaction::kUnset,
+                                 metrics::DownloadErrorCode::kUnset,
+                                 nullptr,
+                                 &post_data));
+    // convert post_data to string
+    string post_str(post_data.begin(), post_data.end());
+    if (rollback_allowed && target_version_set) {
+      EXPECT_NE(post_str.find("rollback_allowed=\"true\""), string::npos)
+          << "i = " << i;
+    } else {
+      EXPECT_EQ(post_str.find("rollback_allowed"), string::npos) << "i = " << i;
+    }
+  }
+}
+
+TEST_F(OmahaRequestActionTest, OmahaEventTest) {
+  OmahaEvent default_event;
+  EXPECT_EQ(OmahaEvent::kTypeUnknown, default_event.type);
+  EXPECT_EQ(OmahaEvent::kResultError, default_event.result);
+  EXPECT_EQ(ErrorCode::kError, default_event.error_code);
+
+  OmahaEvent success_event(OmahaEvent::kTypeUpdateDownloadStarted);
+  EXPECT_EQ(OmahaEvent::kTypeUpdateDownloadStarted, success_event.type);
+  EXPECT_EQ(OmahaEvent::kResultSuccess, success_event.result);
+  EXPECT_EQ(ErrorCode::kSuccess, success_event.error_code);
+
+  OmahaEvent error_event(OmahaEvent::kTypeUpdateDownloadFinished,
+                         OmahaEvent::kResultError,
+                         ErrorCode::kError);
+  EXPECT_EQ(OmahaEvent::kTypeUpdateDownloadFinished, error_event.type);
+  EXPECT_EQ(OmahaEvent::kResultError, error_event.result);
+  EXPECT_EQ(ErrorCode::kError, error_event.error_code);
+}
+
+void OmahaRequestActionTest::PingTest(bool ping_only) {
+  NiceMock<MockPrefs> prefs;
+  fake_system_state_.set_prefs(&prefs);
+  EXPECT_CALL(prefs, GetInt64(kPrefsMetricsCheckLastReportingTime, _))
+      .Times(AnyNumber());
+  EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
+  // Add a few hours to the day difference to test no rounding, etc.
+  int64_t five_days_ago =
+      (Time::Now() - TimeDelta::FromHours(5 * 24 + 13)).ToInternalValue();
+  int64_t six_days_ago =
+      (Time::Now() - TimeDelta::FromHours(6 * 24 + 11)).ToInternalValue();
+  EXPECT_CALL(prefs, GetInt64(kPrefsInstallDateDays, _))
+      .WillOnce(DoAll(SetArgPointee<1>(0), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastActivePingDay, _))
+      .WillOnce(DoAll(SetArgPointee<1>(six_days_ago), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastRollCallPingDay, _))
+      .WillOnce(DoAll(SetArgPointee<1>(five_days_ago), Return(true)));
+  brillo::Blob post_data;
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetNoUpdateResponse(),
+                              -1,
+                              ping_only,
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kNoUpdateAvailable,
+                              metrics::CheckReaction::kUnset,
+                              metrics::DownloadErrorCode::kUnset,
+                              nullptr,
+                              &post_data));
+  string post_str(post_data.begin(), post_data.end());
+  EXPECT_NE(post_str.find("<ping active=\"1\" a=\"6\" r=\"5\"></ping>"),
+            string::npos);
+  if (ping_only) {
+    EXPECT_EQ(post_str.find("updatecheck"), string::npos);
+    EXPECT_EQ(post_str.find("previousversion"), string::npos);
+  } else {
+    EXPECT_NE(post_str.find("updatecheck"), string::npos);
+    EXPECT_NE(post_str.find("previousversion"), string::npos);
+  }
+}
+
+TEST_F(OmahaRequestActionTest, PingTestSendOnlyAPing) {
+  PingTest(true /* ping_only */);
+}
+
+TEST_F(OmahaRequestActionTest, PingTestSendAlsoAnUpdateCheck) {
+  PingTest(false /* ping_only */);
+}
+
+TEST_F(OmahaRequestActionTest, ActivePingTest) {
+  NiceMock<MockPrefs> prefs;
+  fake_system_state_.set_prefs(&prefs);
+  EXPECT_CALL(prefs, GetInt64(kPrefsMetricsCheckLastReportingTime, _))
+      .Times(AnyNumber());
+  EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
+  int64_t three_days_ago =
+      (Time::Now() - TimeDelta::FromHours(3 * 24 + 12)).ToInternalValue();
+  int64_t now = Time::Now().ToInternalValue();
+  EXPECT_CALL(prefs, GetInt64(kPrefsInstallDateDays, _))
+      .WillOnce(DoAll(SetArgPointee<1>(0), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastActivePingDay, _))
+      .WillOnce(DoAll(SetArgPointee<1>(three_days_ago), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastRollCallPingDay, _))
+      .WillOnce(DoAll(SetArgPointee<1>(now), Return(true)));
+  brillo::Blob post_data;
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetNoUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kNoUpdateAvailable,
+                              metrics::CheckReaction::kUnset,
+                              metrics::DownloadErrorCode::kUnset,
+                              nullptr,
+                              &post_data));
+  string post_str(post_data.begin(), post_data.end());
+  EXPECT_NE(post_str.find("<ping active=\"1\" a=\"3\"></ping>"), string::npos);
+}
+
+TEST_F(OmahaRequestActionTest, RollCallPingTest) {
+  NiceMock<MockPrefs> prefs;
+  fake_system_state_.set_prefs(&prefs);
+  EXPECT_CALL(prefs, GetInt64(kPrefsMetricsCheckLastReportingTime, _))
+      .Times(AnyNumber());
+  EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
+  int64_t four_days_ago =
+      (Time::Now() - TimeDelta::FromHours(4 * 24)).ToInternalValue();
+  int64_t now = Time::Now().ToInternalValue();
+  EXPECT_CALL(prefs, GetInt64(kPrefsInstallDateDays, _))
+      .WillOnce(DoAll(SetArgPointee<1>(0), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastActivePingDay, _))
+      .WillOnce(DoAll(SetArgPointee<1>(now), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastRollCallPingDay, _))
+      .WillOnce(DoAll(SetArgPointee<1>(four_days_ago), Return(true)));
+  brillo::Blob post_data;
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetNoUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kNoUpdateAvailable,
+                              metrics::CheckReaction::kUnset,
+                              metrics::DownloadErrorCode::kUnset,
+                              nullptr,
+                              &post_data));
+  string post_str(post_data.begin(), post_data.end());
+  EXPECT_NE(post_str.find("<ping active=\"1\" r=\"4\"></ping>\n"),
+            string::npos);
+}
+
+TEST_F(OmahaRequestActionTest, NoPingTest) {
+  NiceMock<MockPrefs> prefs;
+  fake_system_state_.set_prefs(&prefs);
+  EXPECT_CALL(prefs, GetInt64(kPrefsMetricsCheckLastReportingTime, _))
+      .Times(AnyNumber());
+  EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
+  int64_t one_hour_ago =
+      (Time::Now() - TimeDelta::FromHours(1)).ToInternalValue();
+  EXPECT_CALL(prefs, GetInt64(kPrefsInstallDateDays, _))
+      .WillOnce(DoAll(SetArgPointee<1>(0), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastActivePingDay, _))
+      .WillOnce(DoAll(SetArgPointee<1>(one_hour_ago), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastRollCallPingDay, _))
+      .WillOnce(DoAll(SetArgPointee<1>(one_hour_ago), Return(true)));
+  // LastActivePingDay and PrefsLastRollCallPingDay are set even if we didn't
+  // send a ping.
+  EXPECT_CALL(prefs, SetInt64(kPrefsLastActivePingDay, _))
+      .WillOnce(Return(true));
+  EXPECT_CALL(prefs, SetInt64(kPrefsLastRollCallPingDay, _))
+      .WillOnce(Return(true));
+  brillo::Blob post_data;
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetNoUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kNoUpdateAvailable,
+                              metrics::CheckReaction::kUnset,
+                              metrics::DownloadErrorCode::kUnset,
+                              nullptr,
+                              &post_data));
+  string post_str(post_data.begin(), post_data.end());
+  EXPECT_EQ(post_str.find("ping"), string::npos);
+}
+
+TEST_F(OmahaRequestActionTest, IgnoreEmptyPingTest) {
+  // This test ensures that we ignore empty ping only requests.
+  NiceMock<MockPrefs> prefs;
+  fake_system_state_.set_prefs(&prefs);
+  int64_t now = Time::Now().ToInternalValue();
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastActivePingDay, _))
+      .WillOnce(DoAll(SetArgPointee<1>(now), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastRollCallPingDay, _))
+      .WillOnce(DoAll(SetArgPointee<1>(now), Return(true)));
+  EXPECT_CALL(prefs, SetInt64(kPrefsLastActivePingDay, _)).Times(0);
+  EXPECT_CALL(prefs, SetInt64(kPrefsLastRollCallPingDay, _)).Times(0);
+  brillo::Blob post_data;
+  EXPECT_TRUE(TestUpdateCheck(fake_update_response_.GetNoUpdateResponse(),
+                              -1,
+                              true,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUnset,
+                              metrics::CheckReaction::kUnset,
+                              metrics::DownloadErrorCode::kUnset,
+                              nullptr,
+                              &post_data));
+  EXPECT_EQ(0U, post_data.size());
+}
+
+TEST_F(OmahaRequestActionTest, BackInTimePingTest) {
+  NiceMock<MockPrefs> prefs;
+  fake_system_state_.set_prefs(&prefs);
+  EXPECT_CALL(prefs, GetInt64(kPrefsMetricsCheckLastReportingTime, _))
+      .Times(AnyNumber());
+  EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
+  int64_t future =
+      (Time::Now() + TimeDelta::FromHours(3 * 24 + 4)).ToInternalValue();
+  EXPECT_CALL(prefs, GetInt64(kPrefsInstallDateDays, _))
+      .WillOnce(DoAll(SetArgPointee<1>(0), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastActivePingDay, _))
+      .WillOnce(DoAll(SetArgPointee<1>(future), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastRollCallPingDay, _))
+      .WillOnce(DoAll(SetArgPointee<1>(future), Return(true)));
+  EXPECT_CALL(prefs, SetInt64(kPrefsLastActivePingDay, _))
+      .WillOnce(Return(true));
+  EXPECT_CALL(prefs, SetInt64(kPrefsLastRollCallPingDay, _))
+      .WillOnce(Return(true));
+  brillo::Blob post_data;
+  ASSERT_TRUE(
+      TestUpdateCheck("<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
+                      "protocol=\"3.0\"><daystart elapsed_seconds=\"100\"/>"
+                      "<app appid=\"foo\" status=\"ok\"><ping status=\"ok\"/>"
+                      "<updatecheck status=\"noupdate\"/></app></response>",
+                      -1,
+                      false,  // ping_only
+                      ErrorCode::kSuccess,
+                      metrics::CheckResult::kNoUpdateAvailable,
+                      metrics::CheckReaction::kUnset,
+                      metrics::DownloadErrorCode::kUnset,
+                      nullptr,
+                      &post_data));
+  string post_str(post_data.begin(), post_data.end());
+  EXPECT_EQ(post_str.find("ping"), string::npos);
+}
+
+TEST_F(OmahaRequestActionTest, LastPingDayUpdateTest) {
+  // This test checks that the action updates the last ping day to now
+  // minus 200 seconds with a slack of 5 seconds. Therefore, the test
+  // may fail if it runs for longer than 5 seconds. It shouldn't run
+  // that long though.
+  int64_t midnight =
+      (Time::Now() - TimeDelta::FromSeconds(200)).ToInternalValue();
+  int64_t midnight_slack =
+      (Time::Now() - TimeDelta::FromSeconds(195)).ToInternalValue();
+  NiceMock<MockPrefs> prefs;
+  fake_system_state_.set_prefs(&prefs);
+  EXPECT_CALL(prefs, GetInt64(_, _)).Times(AnyNumber());
+  EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
+  EXPECT_CALL(prefs,
+              SetInt64(kPrefsLastActivePingDay,
+                       AllOf(Ge(midnight), Le(midnight_slack))))
+      .WillOnce(Return(true));
+  EXPECT_CALL(prefs,
+              SetInt64(kPrefsLastRollCallPingDay,
+                       AllOf(Ge(midnight), Le(midnight_slack))))
+      .WillOnce(Return(true));
+  ASSERT_TRUE(
+      TestUpdateCheck("<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
+                      "protocol=\"3.0\"><daystart elapsed_seconds=\"200\"/>"
+                      "<app appid=\"foo\" status=\"ok\"><ping status=\"ok\"/>"
+                      "<updatecheck status=\"noupdate\"/></app></response>",
+                      -1,
+                      false,  // ping_only
+                      ErrorCode::kSuccess,
+                      metrics::CheckResult::kNoUpdateAvailable,
+                      metrics::CheckReaction::kUnset,
+                      metrics::DownloadErrorCode::kUnset,
+                      nullptr,
+                      nullptr));
+}
+
+TEST_F(OmahaRequestActionTest, NoElapsedSecondsTest) {
+  NiceMock<MockPrefs> prefs;
+  fake_system_state_.set_prefs(&prefs);
+  EXPECT_CALL(prefs, GetInt64(_, _)).Times(AnyNumber());
+  EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
+  EXPECT_CALL(prefs, SetInt64(kPrefsLastActivePingDay, _)).Times(0);
+  EXPECT_CALL(prefs, SetInt64(kPrefsLastRollCallPingDay, _)).Times(0);
+  ASSERT_TRUE(
+      TestUpdateCheck("<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
+                      "protocol=\"3.0\"><daystart blah=\"200\"/>"
+                      "<app appid=\"foo\" status=\"ok\"><ping status=\"ok\"/>"
+                      "<updatecheck status=\"noupdate\"/></app></response>",
+                      -1,
+                      false,  // ping_only
+                      ErrorCode::kSuccess,
+                      metrics::CheckResult::kNoUpdateAvailable,
+                      metrics::CheckReaction::kUnset,
+                      metrics::DownloadErrorCode::kUnset,
+                      nullptr,
+                      nullptr));
+}
+
+TEST_F(OmahaRequestActionTest, BadElapsedSecondsTest) {
+  NiceMock<MockPrefs> prefs;
+  fake_system_state_.set_prefs(&prefs);
+  EXPECT_CALL(prefs, GetInt64(_, _)).Times(AnyNumber());
+  EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
+  EXPECT_CALL(prefs, SetInt64(kPrefsLastActivePingDay, _)).Times(0);
+  EXPECT_CALL(prefs, SetInt64(kPrefsLastRollCallPingDay, _)).Times(0);
+  ASSERT_TRUE(
+      TestUpdateCheck("<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
+                      "protocol=\"3.0\"><daystart elapsed_seconds=\"x\"/>"
+                      "<app appid=\"foo\" status=\"ok\"><ping status=\"ok\"/>"
+                      "<updatecheck status=\"noupdate\"/></app></response>",
+                      -1,
+                      false,  // ping_only
+                      ErrorCode::kSuccess,
+                      metrics::CheckResult::kNoUpdateAvailable,
+                      metrics::CheckReaction::kUnset,
+                      metrics::DownloadErrorCode::kUnset,
+                      nullptr,
+                      nullptr));
+}
+
+TEST_F(OmahaRequestActionTest, ParseUpdateCheckAttributesTest) {
+  // Test that the "eol" flags is only parsed from the "_eol" attribute and not
+  // the "eol" attribute.
+  ASSERT_TRUE(
+      TestUpdateCheck("<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
+                      "protocol=\"3.0\"><app appid=\"foo\" status=\"ok\">"
+                      "<ping status=\"ok\"/><updatecheck status=\"noupdate\" "
+                      "_eol=\"security-only\" eol=\"eol\" _foo=\"bar\"/>"
+                      "</app></response>",
+                      -1,
+                      false,  // ping_only
+                      ErrorCode::kSuccess,
+                      metrics::CheckResult::kNoUpdateAvailable,
+                      metrics::CheckReaction::kUnset,
+                      metrics::DownloadErrorCode::kUnset,
+                      nullptr,
+                      nullptr));
+  string eol_pref;
+  EXPECT_TRUE(
+      fake_system_state_.prefs()->GetString(kPrefsOmahaEolStatus, &eol_pref));
+  // Note that the eol="eol" attribute should be ignored and the _eol should be
+  // used instead.
+  EXPECT_EQ("security-only", eol_pref);
+}
+
+TEST_F(OmahaRequestActionTest, NoUniqueIDTest) {
+  brillo::Blob post_data;
+  ASSERT_FALSE(TestUpdateCheck("invalid xml>",
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kOmahaRequestXMLParseError,
+                               metrics::CheckResult::kParsingError,
+                               metrics::CheckReaction::kUnset,
+                               metrics::DownloadErrorCode::kUnset,
+                               nullptr,  // response
+                               &post_data));
+  // convert post_data to string
+  string post_str(post_data.begin(), post_data.end());
+  EXPECT_EQ(post_str.find("machineid="), string::npos);
+  EXPECT_EQ(post_str.find("userid="), string::npos);
+}
+
+TEST_F(OmahaRequestActionTest, NetworkFailureTest) {
+  OmahaResponse response;
+  const int http_error_code =
+      static_cast<int>(ErrorCode::kOmahaRequestHTTPResponseBase) + 501;
+  ASSERT_FALSE(TestUpdateCheck("",
+                               501,
+                               false,  // ping_only
+                               static_cast<ErrorCode>(http_error_code),
+                               metrics::CheckResult::kDownloadError,
+                               metrics::CheckReaction::kUnset,
+                               static_cast<metrics::DownloadErrorCode>(501),
+                               &response,
+                               nullptr));
+  EXPECT_FALSE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, NetworkFailureBadHTTPCodeTest) {
+  OmahaResponse response;
+  const int http_error_code =
+      static_cast<int>(ErrorCode::kOmahaRequestHTTPResponseBase) + 999;
+  ASSERT_FALSE(TestUpdateCheck("",
+                               1500,
+                               false,  // ping_only
+                               static_cast<ErrorCode>(http_error_code),
+                               metrics::CheckResult::kDownloadError,
+                               metrics::CheckReaction::kUnset,
+                               metrics::DownloadErrorCode::kHttpStatusOther,
+                               &response,
+                               nullptr));
+  EXPECT_FALSE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, TestUpdateFirstSeenAtGetsPersistedFirstTime) {
+  OmahaResponse response;
+  request_params_.set_wall_clock_based_wait_enabled(true);
+  request_params_.set_waiting_period(TimeDelta().FromDays(1));
+  request_params_.set_update_check_count_wait_enabled(false);
+
+  Time arbitrary_date;
+  ASSERT_TRUE(Time::FromString("6/4/1989", &arbitrary_date));
+  fake_system_state_.fake_clock()->SetWallclockTime(arbitrary_date);
+  ASSERT_FALSE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kOmahaUpdateDeferredPerPolicy,
+                               metrics::CheckResult::kUpdateAvailable,
+                               metrics::CheckReaction::kDeferring,
+                               metrics::DownloadErrorCode::kUnset,
+                               &response,
+                               nullptr));
+
+  int64_t timestamp = 0;
+  ASSERT_TRUE(fake_prefs_.GetInt64(kPrefsUpdateFirstSeenAt, &timestamp));
+  EXPECT_EQ(arbitrary_date.ToInternalValue(), timestamp);
+  EXPECT_FALSE(response.update_exists);
+
+  // Verify if we are interactive check we don't defer.
+  request_params_.set_interactive(true);
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_TRUE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, TestUpdateFirstSeenAtGetsUsedIfAlreadyPresent) {
+  OmahaResponse response;
+  request_params_.set_wall_clock_based_wait_enabled(true);
+  request_params_.set_waiting_period(TimeDelta().FromDays(1));
+  request_params_.set_update_check_count_wait_enabled(false);
+
+  Time t1, t2;
+  ASSERT_TRUE(Time::FromString("1/1/2012", &t1));
+  ASSERT_TRUE(Time::FromString("1/3/2012", &t2));
+  ASSERT_TRUE(
+      fake_prefs_.SetInt64(kPrefsUpdateFirstSeenAt, t1.ToInternalValue()));
+  fake_system_state_.fake_clock()->SetWallclockTime(t2);
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+
+  EXPECT_TRUE(response.update_exists);
+
+  // Make sure the timestamp t1 is unchanged showing that it was reused.
+  int64_t timestamp = 0;
+  ASSERT_TRUE(fake_prefs_.GetInt64(kPrefsUpdateFirstSeenAt, &timestamp));
+  ASSERT_TRUE(timestamp == t1.ToInternalValue());
+}
+
+TEST_F(OmahaRequestActionTest, TestChangingToMoreStableChannel) {
+  // Create a uniquely named test directory.
+  base::ScopedTempDir tempdir;
+  ASSERT_TRUE(tempdir.CreateUniqueTempDir());
+
+  brillo::Blob post_data;
+  request_params_.set_root(tempdir.GetPath().value());
+  request_params_.set_app_id("{22222222-2222-2222-2222-222222222222}");
+  request_params_.set_app_version("1.2.3.4");
+  request_params_.set_product_components("o.bundle=1");
+  request_params_.set_current_channel("canary-channel");
+  EXPECT_TRUE(
+      request_params_.SetTargetChannel("stable-channel", true, nullptr));
+  request_params_.UpdateDownloadChannel();
+  EXPECT_TRUE(request_params_.ShouldPowerwash());
+  ASSERT_FALSE(TestUpdateCheck("invalid xml>",
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kOmahaRequestXMLParseError,
+                               metrics::CheckResult::kParsingError,
+                               metrics::CheckReaction::kUnset,
+                               metrics::DownloadErrorCode::kUnset,
+                               nullptr,  // response
+                               &post_data));
+  // convert post_data to string
+  string post_str(post_data.begin(), post_data.end());
+  EXPECT_NE(
+      string::npos,
+      post_str.find("appid=\"{22222222-2222-2222-2222-222222222222}\" "
+                    "version=\"0.0.0.0\" from_version=\"1.2.3.4\" "
+                    "track=\"stable-channel\" from_track=\"canary-channel\" "));
+  EXPECT_EQ(string::npos, post_str.find("o.bundle"));
+}
+
+TEST_F(OmahaRequestActionTest, TestChangingToLessStableChannel) {
+  // Create a uniquely named test directory.
+  base::ScopedTempDir tempdir;
+  ASSERT_TRUE(tempdir.CreateUniqueTempDir());
+
+  brillo::Blob post_data;
+  request_params_.set_root(tempdir.GetPath().value());
+  request_params_.set_app_id("{11111111-1111-1111-1111-111111111111}");
+  request_params_.set_app_version("5.6.7.8");
+  request_params_.set_product_components("o.bundle=1");
+  request_params_.set_current_channel("stable-channel");
+  EXPECT_TRUE(
+      request_params_.SetTargetChannel("canary-channel", false, nullptr));
+  request_params_.UpdateDownloadChannel();
+  EXPECT_FALSE(request_params_.ShouldPowerwash());
+  ASSERT_FALSE(TestUpdateCheck("invalid xml>",
+                               -1,
+                               false,  // ping_only
+                               ErrorCode::kOmahaRequestXMLParseError,
+                               metrics::CheckResult::kParsingError,
+                               metrics::CheckReaction::kUnset,
+                               metrics::DownloadErrorCode::kUnset,
+                               nullptr,  // response
+                               &post_data));
+  // Convert post_data to string.
+  string post_str(post_data.begin(), post_data.end());
+  EXPECT_NE(
+      string::npos,
+      post_str.find("appid=\"{11111111-1111-1111-1111-111111111111}\" "
+                    "version=\"5.6.7.8\" "
+                    "track=\"canary-channel\" from_track=\"stable-channel\""));
+  EXPECT_EQ(string::npos, post_str.find("from_version"));
+  EXPECT_NE(string::npos, post_str.find("o.bundle.version=\"1\""));
+}
+
+// Checks that the initial ping with a=-1 r=-1 is not send when the device
+// was powerwashed.
+TEST_F(OmahaRequestActionTest, PingWhenPowerwashed) {
+  fake_prefs_.SetString(kPrefsPreviousVersion, "");
+
+  // Flag that the device was powerwashed in the past.
+  fake_system_state_.fake_hardware()->SetPowerwashCount(1);
+
+  brillo::Blob post_data;
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetNoUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kNoUpdateAvailable,
+                              metrics::CheckReaction::kUnset,
+                              metrics::DownloadErrorCode::kUnset,
+                              nullptr,
+                              &post_data));
+  // We shouldn't send a ping in this case since powerwash > 0.
+  string post_str(post_data.begin(), post_data.end());
+  EXPECT_EQ(string::npos, post_str.find("<ping"));
+}
+
+// Checks that the initial ping with a=-1 r=-1 is not send when the device
+// first_active_omaha_ping_sent is set.
+TEST_F(OmahaRequestActionTest, PingWhenFirstActiveOmahaPingIsSent) {
+  fake_prefs_.SetString(kPrefsPreviousVersion, "");
+
+  // Flag that the device was not powerwashed in the past.
+  fake_system_state_.fake_hardware()->SetPowerwashCount(0);
+
+  // Flag that the device has sent first active ping in the past.
+  fake_system_state_.fake_hardware()->SetFirstActiveOmahaPingSent();
+
+  brillo::Blob post_data;
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetNoUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kNoUpdateAvailable,
+                              metrics::CheckReaction::kUnset,
+                              metrics::DownloadErrorCode::kUnset,
+                              nullptr,
+                              &post_data));
+  // We shouldn't send a ping in this case since
+  // first_active_omaha_ping_sent=true
+  string post_str(post_data.begin(), post_data.end());
+  EXPECT_EQ(string::npos, post_str.find("<ping"));
+}
+
+// Checks that the event 54 is sent on a reboot to a new update.
+TEST_F(OmahaRequestActionTest, RebootAfterUpdateEvent) {
+  // Flag that the device was updated in a previous boot.
+  fake_prefs_.SetString(kPrefsPreviousVersion, "1.2.3.4");
+
+  brillo::Blob post_data;
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetNoUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kNoUpdateAvailable,
+                              metrics::CheckReaction::kUnset,
+                              metrics::DownloadErrorCode::kUnset,
+                              nullptr,
+                              &post_data));
+  string post_str(post_data.begin(), post_data.end());
+
+  // An event 54 is included and has the right version.
+  EXPECT_NE(
+      string::npos,
+      post_str.find(base::StringPrintf("<event eventtype=\"%d\"",
+                                       OmahaEvent::kTypeRebootedAfterUpdate)));
+  EXPECT_NE(string::npos,
+            post_str.find("previousversion=\"1.2.3.4\"></event>"));
+
+  // The previous version flag should have been removed.
+  EXPECT_TRUE(fake_prefs_.Exists(kPrefsPreviousVersion));
+  string prev_version;
+  EXPECT_TRUE(fake_prefs_.GetString(kPrefsPreviousVersion, &prev_version));
+  EXPECT_TRUE(prev_version.empty());
+}
+
+void OmahaRequestActionTest::P2PTest(bool initial_allow_p2p_for_downloading,
+                                     bool initial_allow_p2p_for_sharing,
+                                     bool omaha_disable_p2p_for_downloading,
+                                     bool omaha_disable_p2p_for_sharing,
+                                     bool payload_state_allow_p2p_attempt,
+                                     bool expect_p2p_client_lookup,
+                                     const string& p2p_client_result_url,
+                                     bool expected_allow_p2p_for_downloading,
+                                     bool expected_allow_p2p_for_sharing,
+                                     const string& expected_p2p_url) {
+  OmahaResponse response;
+  bool actual_allow_p2p_for_downloading = initial_allow_p2p_for_downloading;
+  bool actual_allow_p2p_for_sharing = initial_allow_p2p_for_sharing;
+  string actual_p2p_url;
+
+  MockPayloadState mock_payload_state;
+  fake_system_state_.set_payload_state(&mock_payload_state);
+  EXPECT_CALL(mock_payload_state, P2PAttemptAllowed())
+      .WillRepeatedly(Return(payload_state_allow_p2p_attempt));
+  EXPECT_CALL(mock_payload_state, GetUsingP2PForDownloading())
+      .WillRepeatedly(ReturnPointee(&actual_allow_p2p_for_downloading));
+  EXPECT_CALL(mock_payload_state, GetUsingP2PForSharing())
+      .WillRepeatedly(ReturnPointee(&actual_allow_p2p_for_sharing));
+  EXPECT_CALL(mock_payload_state, SetUsingP2PForDownloading(_))
+      .WillRepeatedly(SaveArg<0>(&actual_allow_p2p_for_downloading));
+  EXPECT_CALL(mock_payload_state, SetUsingP2PForSharing(_))
+      .WillRepeatedly(SaveArg<0>(&actual_allow_p2p_for_sharing));
+  EXPECT_CALL(mock_payload_state, SetP2PUrl(_))
+      .WillRepeatedly(SaveArg<0>(&actual_p2p_url));
+
+  MockP2PManager mock_p2p_manager;
+  fake_system_state_.set_p2p_manager(&mock_p2p_manager);
+  mock_p2p_manager.fake().SetLookupUrlForFileResult(p2p_client_result_url);
+
+  TimeDelta timeout = TimeDelta::FromSeconds(kMaxP2PNetworkWaitTimeSeconds);
+  EXPECT_CALL(mock_p2p_manager, LookupUrlForFile(_, _, timeout, _))
+      .Times(expect_p2p_client_lookup ? 1 : 0);
+
+  fake_update_response_.disable_p2p_for_downloading =
+      omaha_disable_p2p_for_downloading;
+  fake_update_response_.disable_p2p_for_sharing = omaha_disable_p2p_for_sharing;
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_TRUE(response.update_exists);
+
+  EXPECT_EQ(omaha_disable_p2p_for_downloading,
+            response.disable_p2p_for_downloading);
+  EXPECT_EQ(omaha_disable_p2p_for_sharing, response.disable_p2p_for_sharing);
+
+  EXPECT_EQ(expected_allow_p2p_for_downloading,
+            actual_allow_p2p_for_downloading);
+  EXPECT_EQ(expected_allow_p2p_for_sharing, actual_allow_p2p_for_sharing);
+  EXPECT_EQ(expected_p2p_url, actual_p2p_url);
+}
+
+TEST_F(OmahaRequestActionTest, P2PWithPeer) {
+  P2PTest(true,                   // initial_allow_p2p_for_downloading
+          true,                   // initial_allow_p2p_for_sharing
+          false,                  // omaha_disable_p2p_for_downloading
+          false,                  // omaha_disable_p2p_for_sharing
+          true,                   // payload_state_allow_p2p_attempt
+          true,                   // expect_p2p_client_lookup
+          "http://1.3.5.7/p2p",   // p2p_client_result_url
+          true,                   // expected_allow_p2p_for_downloading
+          true,                   // expected_allow_p2p_for_sharing
+          "http://1.3.5.7/p2p");  // expected_p2p_url
+}
+
+TEST_F(OmahaRequestActionTest, P2PWithoutPeer) {
+  P2PTest(true,   // initial_allow_p2p_for_downloading
+          true,   // initial_allow_p2p_for_sharing
+          false,  // omaha_disable_p2p_for_downloading
+          false,  // omaha_disable_p2p_for_sharing
+          true,   // payload_state_allow_p2p_attempt
+          true,   // expect_p2p_client_lookup
+          "",     // p2p_client_result_url
+          false,  // expected_allow_p2p_for_downloading
+          true,   // expected_allow_p2p_for_sharing
+          "");    // expected_p2p_url
+}
+
+TEST_F(OmahaRequestActionTest, P2PDownloadNotAllowed) {
+  P2PTest(false,    // initial_allow_p2p_for_downloading
+          true,     // initial_allow_p2p_for_sharing
+          false,    // omaha_disable_p2p_for_downloading
+          false,    // omaha_disable_p2p_for_sharing
+          true,     // payload_state_allow_p2p_attempt
+          false,    // expect_p2p_client_lookup
+          "unset",  // p2p_client_result_url
+          false,    // expected_allow_p2p_for_downloading
+          true,     // expected_allow_p2p_for_sharing
+          "");      // expected_p2p_url
+}
+
+TEST_F(OmahaRequestActionTest, P2PWithPeerDownloadDisabledByOmaha) {
+  P2PTest(true,     // initial_allow_p2p_for_downloading
+          true,     // initial_allow_p2p_for_sharing
+          true,     // omaha_disable_p2p_for_downloading
+          false,    // omaha_disable_p2p_for_sharing
+          true,     // payload_state_allow_p2p_attempt
+          false,    // expect_p2p_client_lookup
+          "unset",  // p2p_client_result_url
+          false,    // expected_allow_p2p_for_downloading
+          true,     // expected_allow_p2p_for_sharing
+          "");      // expected_p2p_url
+}
+
+TEST_F(OmahaRequestActionTest, P2PWithPeerSharingDisabledByOmaha) {
+  P2PTest(true,                   // initial_allow_p2p_for_downloading
+          true,                   // initial_allow_p2p_for_sharing
+          false,                  // omaha_disable_p2p_for_downloading
+          true,                   // omaha_disable_p2p_for_sharing
+          true,                   // payload_state_allow_p2p_attempt
+          true,                   // expect_p2p_client_lookup
+          "http://1.3.5.7/p2p",   // p2p_client_result_url
+          true,                   // expected_allow_p2p_for_downloading
+          false,                  // expected_allow_p2p_for_sharing
+          "http://1.3.5.7/p2p");  // expected_p2p_url
+}
+
+TEST_F(OmahaRequestActionTest, P2PWithPeerBothDisabledByOmaha) {
+  P2PTest(true,     // initial_allow_p2p_for_downloading
+          true,     // initial_allow_p2p_for_sharing
+          true,     // omaha_disable_p2p_for_downloading
+          true,     // omaha_disable_p2p_for_sharing
+          true,     // payload_state_allow_p2p_attempt
+          false,    // expect_p2p_client_lookup
+          "unset",  // p2p_client_result_url
+          false,    // expected_allow_p2p_for_downloading
+          false,    // expected_allow_p2p_for_sharing
+          "");      // expected_p2p_url
+}
+
+bool OmahaRequestActionTest::InstallDateParseHelper(const string& elapsed_days,
+                                                    OmahaResponse* response) {
+  fake_update_response_.elapsed_days = elapsed_days;
+  return TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                         -1,
+                         false,  // ping_only
+                         ErrorCode::kSuccess,
+                         metrics::CheckResult::kUpdateAvailable,
+                         metrics::CheckReaction::kUpdating,
+                         metrics::DownloadErrorCode::kUnset,
+                         response,
+                         nullptr);
+}
+
+TEST_F(OmahaRequestActionTest, ParseInstallDateFromResponse) {
+  OmahaResponse response;
+
+  // Simulate a successful update check that happens during OOBE.  The
+  // deadline in the response is needed to force the update attempt to
+  // occur; responses without a deadline seen during OOBE will normally
+  // return ErrorCode::kNonCriticalUpdateInOOBE.
+  fake_system_state_.fake_hardware()->UnsetIsOOBEComplete();
+  fake_update_response_.deadline = "20101020";
+
+  // Check that we parse elapsed_days in the Omaha Response correctly.
+  // and that the kPrefsInstallDateDays value is written to.
+  EXPECT_FALSE(fake_prefs_.Exists(kPrefsInstallDateDays));
+  EXPECT_TRUE(InstallDateParseHelper("42", &response));
+  EXPECT_TRUE(response.update_exists);
+  EXPECT_EQ(42, response.install_date_days);
+  EXPECT_TRUE(fake_prefs_.Exists(kPrefsInstallDateDays));
+  int64_t prefs_days;
+  EXPECT_TRUE(fake_prefs_.GetInt64(kPrefsInstallDateDays, &prefs_days));
+  EXPECT_EQ(prefs_days, 42);
+
+  // If there already is a value set, we shouldn't do anything.
+  EXPECT_TRUE(InstallDateParseHelper("7", &response));
+  EXPECT_TRUE(response.update_exists);
+  EXPECT_EQ(7, response.install_date_days);
+  EXPECT_TRUE(fake_prefs_.GetInt64(kPrefsInstallDateDays, &prefs_days));
+  EXPECT_EQ(prefs_days, 42);
+
+  // Note that elapsed_days is not necessarily divisible by 7 so check
+  // that we round down correctly when populating kPrefsInstallDateDays.
+  EXPECT_TRUE(fake_prefs_.Delete(kPrefsInstallDateDays));
+  EXPECT_TRUE(InstallDateParseHelper("23", &response));
+  EXPECT_TRUE(response.update_exists);
+  EXPECT_EQ(23, response.install_date_days);
+  EXPECT_TRUE(fake_prefs_.GetInt64(kPrefsInstallDateDays, &prefs_days));
+  EXPECT_EQ(prefs_days, 21);
+
+  // Check that we correctly handle elapsed_days not being included in
+  // the Omaha Response.
+  EXPECT_TRUE(InstallDateParseHelper("", &response));
+  EXPECT_TRUE(response.update_exists);
+  EXPECT_EQ(-1, response.install_date_days);
+}
+
+// If there is no prefs and OOBE is not complete, we should not
+// report anything to Omaha.
+TEST_F(OmahaRequestActionTest, GetInstallDateWhenNoPrefsNorOOBE) {
+  fake_system_state_.fake_hardware()->UnsetIsOOBEComplete();
+  EXPECT_EQ(OmahaRequestAction::GetInstallDate(&fake_system_state_), -1);
+  EXPECT_FALSE(fake_prefs_.Exists(kPrefsInstallDateDays));
+}
+
+// If OOBE is complete and happened on a valid date (e.g. after Jan
+// 1 2007 0:00 PST), that date should be used and written to
+// prefs. However, first try with an invalid date and check we do
+// nothing.
+TEST_F(OmahaRequestActionTest, GetInstallDateWhenOOBECompletedWithInvalidDate) {
+  Time oobe_date = Time::FromTimeT(42);  // Dec 31, 1969 16:00:42 PST.
+  fake_system_state_.fake_hardware()->SetIsOOBEComplete(oobe_date);
+  EXPECT_EQ(OmahaRequestAction::GetInstallDate(&fake_system_state_), -1);
+  EXPECT_FALSE(fake_prefs_.Exists(kPrefsInstallDateDays));
+}
+
+// Then check with a valid date. The date Jan 20, 2007 0:00 PST
+// should yield an InstallDate of 14.
+TEST_F(OmahaRequestActionTest, GetInstallDateWhenOOBECompletedWithValidDate) {
+  Time oobe_date = Time::FromTimeT(1169280000);  // Jan 20, 2007 0:00 PST.
+  fake_system_state_.fake_hardware()->SetIsOOBEComplete(oobe_date);
+  EXPECT_EQ(OmahaRequestAction::GetInstallDate(&fake_system_state_), 14);
+  EXPECT_TRUE(fake_prefs_.Exists(kPrefsInstallDateDays));
+
+  int64_t prefs_days;
+  EXPECT_TRUE(fake_prefs_.GetInt64(kPrefsInstallDateDays, &prefs_days));
+  EXPECT_EQ(prefs_days, 14);
+}
+
+// Now that we have a valid date in prefs, check that we keep using
+// that even if OOBE date reports something else. The date Jan 30,
+// 2007 0:00 PST should yield an InstallDate of 28... but since
+// there's a prefs file, we should still get 14.
+TEST_F(OmahaRequestActionTest, GetInstallDateWhenOOBECompletedDateChanges) {
+  // Set a valid date in the prefs first.
+  EXPECT_TRUE(fake_prefs_.SetInt64(kPrefsInstallDateDays, 14));
+
+  Time oobe_date = Time::FromTimeT(1170144000);  // Jan 30, 2007 0:00 PST.
+  fake_system_state_.fake_hardware()->SetIsOOBEComplete(oobe_date);
+  EXPECT_EQ(OmahaRequestAction::GetInstallDate(&fake_system_state_), 14);
+
+  int64_t prefs_days;
+  EXPECT_TRUE(fake_prefs_.GetInt64(kPrefsInstallDateDays, &prefs_days));
+  EXPECT_EQ(prefs_days, 14);
+
+  // If we delete the prefs file, we should get 28 days.
+  EXPECT_TRUE(fake_prefs_.Delete(kPrefsInstallDateDays));
+  EXPECT_EQ(OmahaRequestAction::GetInstallDate(&fake_system_state_), 28);
+  EXPECT_TRUE(fake_prefs_.GetInt64(kPrefsInstallDateDays, &prefs_days));
+  EXPECT_EQ(prefs_days, 28);
+}
+
+// Verifies that a device with no device policy, and is not a consumer
+// device sets the max kernel key version to the current version.
+// ie. the same behavior as if rollback is enabled.
+TEST_F(OmahaRequestActionTest, NoPolicyEnterpriseDevicesSetMaxRollback) {
+  FakeHardware* fake_hw = fake_system_state_.fake_hardware();
+
+  // Setup and verify some initial default values for the kernel TPM
+  // values that control verified boot and rollback.
+  const int min_kernel_version = 4;
+  fake_hw->SetMinKernelKeyVersion(min_kernel_version);
+  fake_hw->SetMaxKernelKeyRollforward(kRollforwardInfinity);
+  EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
+  EXPECT_EQ(kRollforwardInfinity, fake_hw->GetMaxKernelKeyRollforward());
+
+  EXPECT_CALL(
+      *fake_system_state_.mock_metrics_reporter(),
+      ReportKeyVersionMetrics(min_kernel_version, min_kernel_version, true))
+      .Times(1);
+
+  OmahaResponse response;
+  TestRollbackCheck(false /* is_consumer_device */,
+                    3 /* rollback_allowed_milestones */,
+                    false /* is_policy_loaded */,
+                    &response);
+
+  // Verify kernel_max_rollforward was set to the current minimum
+  // kernel key version. This has the effect of freezing roll
+  // forwards indefinitely. This will hold the rollback window
+  // open until a future change will be able to move this forward
+  // relative the configured window.
+  EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
+  EXPECT_EQ(min_kernel_version, fake_hw->GetMaxKernelKeyRollforward());
+}
+
+// Verifies that a conmsumer device with no device policy sets the
+// max kernel key version to the current version. ie. the same
+// behavior as if rollback is enabled.
+TEST_F(OmahaRequestActionTest, NoPolicyConsumerDevicesSetMaxRollback) {
+  FakeHardware* fake_hw = fake_system_state_.fake_hardware();
+
+  // Setup and verify some initial default values for the kernel TPM
+  // values that control verified boot and rollback.
+  const int min_kernel_version = 3;
+  fake_hw->SetMinKernelKeyVersion(min_kernel_version);
+  fake_hw->SetMaxKernelKeyRollforward(kRollforwardInfinity);
+  EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
+  EXPECT_EQ(kRollforwardInfinity, fake_hw->GetMaxKernelKeyRollforward());
+
+  EXPECT_CALL(
+      *fake_system_state_.mock_metrics_reporter(),
+      ReportKeyVersionMetrics(min_kernel_version, kRollforwardInfinity, true))
+      .Times(1);
+
+  OmahaResponse response;
+  TestRollbackCheck(true /* is_consumer_device */,
+                    3 /* rollback_allowed_milestones */,
+                    false /* is_policy_loaded */,
+                    &response);
+
+  // Verify that with rollback disabled that kernel_max_rollforward
+  // was set to logical infinity. This is the expected behavior for
+  // consumer devices and matches the existing behavior prior to the
+  // rollback features.
+  EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
+  EXPECT_EQ(kRollforwardInfinity, fake_hw->GetMaxKernelKeyRollforward());
+}
+
+// Verifies that a device with rollback enabled sets kernel_max_rollforward
+// in the TPM to prevent roll forward.
+TEST_F(OmahaRequestActionTest, RollbackEnabledDevicesSetMaxRollback) {
+  FakeHardware* fake_hw = fake_system_state_.fake_hardware();
+
+  // Setup and verify some initial default values for the kernel TPM
+  // values that control verified boot and rollback.
+  const int allowed_milestones = 4;
+  const int min_kernel_version = 3;
+  fake_hw->SetMinKernelKeyVersion(min_kernel_version);
+  fake_hw->SetMaxKernelKeyRollforward(kRollforwardInfinity);
+  EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
+  EXPECT_EQ(kRollforwardInfinity, fake_hw->GetMaxKernelKeyRollforward());
+
+  EXPECT_CALL(
+      *fake_system_state_.mock_metrics_reporter(),
+      ReportKeyVersionMetrics(min_kernel_version, min_kernel_version, true))
+      .Times(1);
+
+  OmahaResponse response;
+  TestRollbackCheck(false /* is_consumer_device */,
+                    allowed_milestones,
+                    true /* is_policy_loaded */,
+                    &response);
+
+  // Verify that with rollback enabled that kernel_max_rollforward
+  // was set to the current minimum kernel key version. This has
+  // the effect of freezing roll forwards indefinitely. This will
+  // hold the rollback window open until a future change will
+  // be able to move this forward relative the configured window.
+  EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
+  EXPECT_EQ(min_kernel_version, fake_hw->GetMaxKernelKeyRollforward());
+}
+
+// Verifies that a device with rollback disabled sets kernel_max_rollforward
+// in the TPM to logical infinity, to allow roll forward.
+TEST_F(OmahaRequestActionTest, RollbackDisabledDevicesSetMaxRollback) {
+  FakeHardware* fake_hw = fake_system_state_.fake_hardware();
+
+  // Setup and verify some initial default values for the kernel TPM
+  // values that control verified boot and rollback.
+  const int allowed_milestones = 0;
+  const int min_kernel_version = 3;
+  fake_hw->SetMinKernelKeyVersion(min_kernel_version);
+  fake_hw->SetMaxKernelKeyRollforward(kRollforwardInfinity);
+  EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
+  EXPECT_EQ(kRollforwardInfinity, fake_hw->GetMaxKernelKeyRollforward());
+
+  EXPECT_CALL(
+      *fake_system_state_.mock_metrics_reporter(),
+      ReportKeyVersionMetrics(min_kernel_version, kRollforwardInfinity, true))
+      .Times(1);
+
+  OmahaResponse response;
+  TestRollbackCheck(false /* is_consumer_device */,
+                    allowed_milestones,
+                    true /* is_policy_loaded */,
+                    &response);
+
+  // Verify that with rollback disabled that kernel_max_rollforward
+  // was set to logical infinity.
+  EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
+  EXPECT_EQ(kRollforwardInfinity, fake_hw->GetMaxKernelKeyRollforward());
+}
+
+TEST_F(OmahaRequestActionTest, RollbackResponseParsedNoEntries) {
+  OmahaResponse response;
+  fake_update_response_.rollback = true;
+  TestRollbackCheck(false /* is_consumer_device */,
+                    4 /* rollback_allowed_milestones */,
+                    true /* is_policy_loaded */,
+                    &response);
+  EXPECT_TRUE(response.is_rollback);
+}
+
+TEST_F(OmahaRequestActionTest, RollbackResponseValidVersionsParsed) {
+  OmahaResponse response;
+  fake_update_response_.rollback_firmware_version = "1.2";
+  fake_update_response_.rollback_kernel_version = "3.4";
+  fake_update_response_.rollback = true;
+  TestRollbackCheck(false /* is_consumer_device */,
+                    4 /* rollback_allowed_milestones */,
+                    true /* is_policy_loaded */,
+                    &response);
+  EXPECT_TRUE(response.is_rollback);
+  EXPECT_EQ(1, response.rollback_key_version.firmware_key);
+  EXPECT_EQ(2, response.rollback_key_version.firmware);
+  EXPECT_EQ(3, response.rollback_key_version.kernel_key);
+  EXPECT_EQ(4, response.rollback_key_version.kernel);
+}
+
+TEST_F(OmahaRequestActionTest,
+       TestUpdateFirstSeenAtPrefPersistedIfUpdateExists) {
+  FakeClock fake_clock;
+  Time now = Time::Now();
+  fake_clock.SetWallclockTime(now);
+  fake_system_state_.set_clock(&fake_clock);
+
+  OmahaResponse response;
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_TRUE(response.update_exists);
+  EXPECT_TRUE(fake_prefs_.Exists(kPrefsUpdateFirstSeenAt));
+
+  int64_t stored_first_seen_at_time;
+  EXPECT_TRUE(fake_prefs_.GetInt64(kPrefsUpdateFirstSeenAt,
+                                   &stored_first_seen_at_time));
+  EXPECT_EQ(now.ToInternalValue(), stored_first_seen_at_time);
+}
+
+TEST_F(OmahaRequestActionTest,
+       TestUpdateFirstSeenAtPrefNotPersistedIfUpdateFails) {
+  FakeClock fake_clock;
+  Time now = Time::Now();
+  fake_clock.SetWallclockTime(now);
+  fake_system_state_.set_clock(&fake_clock);
+
+  OmahaResponse response;
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetNoUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kNoUpdateAvailable,
+                              metrics::CheckReaction::kUnset,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_FALSE(response.update_exists);
+  EXPECT_FALSE(fake_prefs_.Exists(kPrefsUpdateFirstSeenAt));
+}
+
+TEST_F(OmahaRequestActionTest, InstallTest) {
+  OmahaResponse response;
+  request_params_.set_is_install(true);
+  request_params_.set_dlc_module_ids({"dlc_no_0", "dlc_no_1"});
+  brillo::Blob post_data;
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              true,   // is_consumer_device
+                              0,      // rollback_allowed_milestones
+                              false,  // is_policy_loaded
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              &post_data));
+  // Convert post_data to string.
+  string post_str(post_data.begin(), post_data.end());
+  for (const auto& dlc_module_id : request_params_.dlc_module_ids()) {
+    EXPECT_NE(string::npos,
+              post_str.find("appid=\"" + fake_update_response_.app_id + "_" +
+                            dlc_module_id + "\""));
+  }
+  EXPECT_NE(string::npos,
+            post_str.find("appid=\"" + fake_update_response_.app_id + "\""));
+
+  // Count number of updatecheck tag in response.
+  int updatecheck_count = 0;
+  size_t pos = 0;
+  while ((pos = post_str.find("<updatecheck", pos)) != string::npos) {
+    updatecheck_count++;
+    pos++;
+  }
+  EXPECT_EQ(request_params_.dlc_module_ids().size(), updatecheck_count);
+}
+
+TEST_F(OmahaRequestActionTest, InstallMissingPlatformVersionTest) {
+  fake_update_response_.multi_app_skip_updatecheck = true;
+  fake_update_response_.multi_app_no_update = false;
+  request_params_.set_is_install(true);
+  request_params_.set_dlc_module_ids({"dlc_no_0", "dlc_no_1"});
+  request_params_.set_app_id(fake_update_response_.app_id_skip_updatecheck);
+  OmahaResponse response;
+  ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+                              -1,
+                              false,  // ping_only
+                              ErrorCode::kSuccess,
+                              metrics::CheckResult::kUpdateAvailable,
+                              metrics::CheckReaction::kUpdating,
+                              metrics::DownloadErrorCode::kUnset,
+                              &response,
+                              nullptr));
+  EXPECT_TRUE(response.update_exists);
+  EXPECT_EQ(fake_update_response_.current_version, response.version);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/cros/omaha_request_params.cc b/omaha_request_params.cc
similarity index 69%
rename from cros/omaha_request_params.cc
rename to omaha_request_params.cc
index adcfc75..8c410f1 100644
--- a/cros/omaha_request_params.cc
+++ b/omaha_request_params.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/omaha_request_params.h"
+#include "update_engine/omaha_request_params.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -25,7 +25,6 @@
 #include <vector>
 
 #include <base/files/file_util.h>
-#include <base/stl_util.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
 #include <brillo/key_value_store.h>
@@ -35,14 +34,14 @@
 #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/update_manager/policy.h"
+#include "update_engine/system_state.h"
 
 #define CALL_MEMBER_FN(object, member) ((object).*(member))
 
-using chromeos_update_manager::UpdateCheckParams;
+using std::map;
 using std::string;
+using std::vector;
 
 namespace chromeos_update_engine {
 
@@ -61,14 +60,14 @@
     test::SetImagePropertiesRootPrefix(nullptr);
 }
 
-bool OmahaRequestParams::Init(const string& app_version,
-                              const string& update_url,
-                              const UpdateCheckParams& params) {
+bool OmahaRequestParams::Init(const string& in_app_version,
+                              const string& in_update_url,
+                              bool in_interactive) {
   LOG(INFO) << "Initializing parameters for this update attempt";
-  image_props_ = LoadImageProperties();
-  mutable_image_props_ = LoadMutableImageProperties();
+  image_props_ = LoadImageProperties(system_state_);
+  mutable_image_props_ = LoadMutableImageProperties(system_state_);
 
-  // Validation check the channel names.
+  // Sanity check the channel names.
   if (!IsValidChannel(image_props_.current_channel))
     image_props_.current_channel = "stable-channel";
   if (!IsValidChannel(mutable_image_props_.target_channel))
@@ -78,14 +77,24 @@
   LOG(INFO) << "Running from channel " << image_props_.current_channel;
 
   os_platform_ = constants::kOmahaPlatformName;
-  os_version_ = OmahaRequestParams::kOsVersion;
-  if (!app_version.empty())
-    image_props_.version = app_version;
+  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_sp_ = image_props_.version + "_" + GetMachineType();
   app_lang_ = "en-US";
-  hwid_ = SystemState::Get()->hardware()->GetHardwareClass();
-  device_requisition_ = SystemState::Get()->hardware()->GetDeviceRequisition();
+  hwid_ = system_state_->hardware()->GetHardwareClass();
+  if (CollectECFWVersions()) {
+    fw_version_ = system_state_->hardware()->GetFirmwareVersion();
+    ec_version_ = system_state_->hardware()->GetECVersion();
+  }
 
   if (image_props_.current_channel == mutable_image_props_.target_channel) {
     // deltas are only okay if the /.nodelta file does not exist.  if we don't
@@ -106,53 +115,17 @@
     delta_okay_ = false;
   }
 
-  if (update_url.empty())
+  if (in_update_url.empty())
     update_url_ = image_props_.omaha_url;
   else
-    update_url_ = update_url;
+    update_url_ = in_update_url;
 
   // Set the interactive flag accordingly.
-  interactive_ = params.interactive;
+  interactive_ = in_interactive;
 
-  dlc_apps_params_.clear();
+  dlc_module_ids_.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;
-
-  autoupdate_token_ = params.quick_fix_build_token;
-
-  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;
 }
 
@@ -161,6 +134,14 @@
           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) {
@@ -179,7 +160,7 @@
   new_props.target_channel = new_target_channel;
   new_props.is_powerwash_allowed = is_powerwash_allowed;
 
-  if (!StoreMutableImageProperties(new_props)) {
+  if (!StoreMutableImageProperties(system_state_, new_props)) {
     if (error_message)
       *error_message = "Error storing the new channel value.";
     return false;
@@ -236,7 +217,7 @@
 }
 
 int OmahaRequestParams::GetChannelIndex(const string& channel) const {
-  for (size_t t = 0; t < base::size(kChannelsByStability); ++t)
+  for (size_t t = 0; t < arraysize(kChannelsByStability); ++t)
     if (channel == kChannelsByStability[t])
       return t;
 
@@ -266,29 +247,4 @@
                                                : image_props_.product_id;
 }
 
-string OmahaRequestParams::GetDlcAppId(const std::string& dlc_id) const {
-  // Create APP ID according to |dlc_id| (sticking the current AppID to the
-  // DLC module ID with an underscode).
-  return GetAppId() + "_" + dlc_id;
-}
-
-bool OmahaRequestParams::IsDlcAppId(const std::string& app_id) const {
-  return dlc_apps_params().find(app_id) != dlc_apps_params().end();
-}
-
-bool OmahaRequestParams::GetDlcId(const string& app_id, string* dlc_id) const {
-  auto itr = dlc_apps_params_.find(app_id);
-  if (itr == dlc_apps_params_.end())
-    return false;
-  *dlc_id = itr->second.name;
-  return true;
-}
-
-void OmahaRequestParams::SetDlcNoUpdate(const string& app_id) {
-  auto itr = dlc_apps_params_.find(app_id);
-  if (itr == dlc_apps_params_.end())
-    return;
-  itr->second.updated = false;
-}
-
 }  // namespace chromeos_update_engine
diff --git a/cros/omaha_request_params.h b/omaha_request_params.h
similarity index 73%
rename from cros/omaha_request_params.h
rename to omaha_request_params.h
index fd4c2e2..18235c0 100644
--- a/cros/omaha_request_params.h
+++ b/omaha_request_params.h
@@ -14,12 +14,11 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_OMAHA_REQUEST_PARAMS_H_
-#define UPDATE_ENGINE_CROS_OMAHA_REQUEST_PARAMS_H_
+#ifndef UPDATE_ENGINE_OMAHA_REQUEST_PARAMS_H_
+#define UPDATE_ENGINE_OMAHA_REQUEST_PARAMS_H_
 
 #include <stdint.h>
 
-#include <map>
 #include <string>
 #include <vector>
 
@@ -27,16 +26,16 @@
 #include <base/time/time.h>
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
-#include "update_engine/common/constants.h"
 #include "update_engine/common/platform_constants.h"
-#include "update_engine/cros/image_properties.h"
-#include "update_engine/update_manager/policy.h"
+#include "update_engine/image_properties.h"
 
 // This gathers local system information and prepares info used by the
 // Omaha request action.
 
 namespace chromeos_update_engine {
 
+class SystemState;
+
 // This class encapsulates the data Omaha gets for the request, along with
 // essential state needed for the processing of the request/response.  The
 // strings in this struct should not be XML escaped.
@@ -45,13 +44,13 @@
 // reflect its lifetime more appropriately.
 class OmahaRequestParams {
  public:
-  OmahaRequestParams()
-      : os_platform_(constants::kOmahaPlatformName),
+  explicit OmahaRequestParams(SystemState* system_state)
+      : system_state_(system_state),
+        os_platform_(constants::kOmahaPlatformName),
         os_version_(kOsVersion),
         delta_okay_(true),
         interactive_(false),
         rollback_allowed_(false),
-        rollback_data_save_requested_(false),
         wall_clock_based_wait_enabled_(false),
         update_check_count_wait_enabled_(false),
         min_update_checks_needed_(kDefaultMinUpdateChecks),
@@ -60,24 +59,6 @@
 
   virtual ~OmahaRequestParams();
 
-  enum ActiveCountingType {
-    kDayBased = 0,
-    kDateBased,
-  };
-
-  struct AppParams {
-    ActiveCountingType active_counting_type;
-    // |name| is only used for DLCs to store the DLC ID.
-    std::string name;
-    int64_t ping_active;
-    int64_t ping_date_last_active;
-    int64_t ping_date_last_rollcall;
-    bool send_ping;
-    // |updated| is only used for DLCs to decide sending DBus message to
-    // dlcservice on an install/update completion.
-    bool updated = true;
-  };
-
   // Setters and getters for the various properties.
   inline std::string os_platform() const { return os_platform_; }
   inline std::string os_version() const { return os_version_; }
@@ -91,18 +72,26 @@
   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 device_requisition() const { return device_requisition_; }
+  inline std::string fw_version() const { return fw_version_; }
+  inline std::string ec_version() const { return ec_version_; }
 
   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;
   }
@@ -137,33 +126,12 @@
     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;
   }
 
   inline bool rollback_allowed() const { return rollback_allowed_; }
 
-  inline void set_rollback_data_save_requested(
-      bool rollback_data_save_requested) {
-    rollback_data_save_requested_ = rollback_data_save_requested;
-  }
-
-  inline bool rollback_data_save_requested() const {
-    return rollback_data_save_requested_;
-  }
-
-  inline void set_rollback_allowed_milestones(int rollback_allowed_milestones) {
-    rollback_allowed_milestones_ = rollback_allowed_milestones;
-  }
-
-  inline int rollback_allowed_milestones() const {
-    return rollback_allowed_milestones_;
-  }
-
   inline void set_wall_clock_based_wait_enabled(bool enabled) {
     wall_clock_based_wait_enabled_ = enabled;
   }
@@ -197,41 +165,20 @@
   inline int64_t max_update_checks_allowed() const {
     return max_update_checks_allowed_;
   }
-  inline void set_dlc_apps_params(
-      const std::map<std::string, AppParams>& dlc_apps_params) {
-    dlc_apps_params_ = dlc_apps_params;
+  inline void set_dlc_module_ids(
+      const std::vector<std::string>& dlc_module_ids) {
+    dlc_module_ids_ = dlc_module_ids;
   }
-  inline const std::map<std::string, AppParams>& dlc_apps_params() const {
-    return dlc_apps_params_;
+  inline std::vector<std::string> dlc_module_ids() const {
+    return dlc_module_ids_;
   }
   inline void set_is_install(bool is_install) { is_install_ = is_install; }
   inline bool is_install() const { return is_install_; }
 
-  inline void set_autoupdate_token(const std::string& token) {
-    autoupdate_token_ = token;
-  }
-  inline const std::string& autoupdate_token() const {
-    return autoupdate_token_;
-  }
-
-  // Returns the App ID corresponding to the current value of the
+  // Returns the app id corresponding to the current value of the
   // download channel.
   virtual std::string GetAppId() const;
 
-  // Returns the DLC app ID.
-  virtual std::string GetDlcAppId(const std::string& dlc_id) const;
-
-  // Returns true if the App ID is a DLC App ID that is currently part of the
-  // request parameters.
-  virtual bool IsDlcAppId(const std::string& app_id) const;
-
-  // Returns the DLC App ID if the given App ID is a DLC that is currently part
-  // of the request parameters.
-  virtual bool GetDlcId(const std::string& app_id, std::string* dlc_id) const;
-
-  // If the App ID is a DLC App ID will set to no update.
-  void SetDlcNoUpdate(const std::string& app_id);
-
   // Suggested defaults
   static const char kOsVersion[];
   static const int64_t kDefaultMinUpdateChecks = 0;
@@ -242,7 +189,7 @@
   // of the parameter. Returns true on success, false otherwise.
   bool Init(const std::string& in_app_version,
             const std::string& in_update_url,
-            const chromeos_update_manager::UpdateCheckParams& params);
+            bool in_interactive);
 
   // Permanently changes the release channel to |channel|. Performs a
   // powerwash, if required and allowed.
@@ -263,10 +210,8 @@
   // or Init is called again.
   virtual void UpdateDownloadChannel();
 
-  // Returns whether we should powerwash for this update. Note that this is
-  // just an indication, the final decision to powerwash or not is made in the
-  // response handler.
-  bool ShouldPowerwash() const;
+  // Returns whether we should powerwash for this update.
+  virtual bool ShouldPowerwash() const;
 
   // Check if the provided update URL is official, meaning either the default
   // autoupdate server or the autoupdate autotest server.
@@ -286,19 +231,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);
@@ -321,9 +266,16 @@
   // 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 whitelisted prefixes.
+  bool CollectECFWVersions() const;
+
   // Gets the machine type (e.g. "i686").
   std::string GetMachineType() const;
 
+  // Global system context.
+  SystemState* system_state_;
+
   // The system image properties.
   ImageProperties image_props_;
   MutableImageProperties mutable_image_props_;
@@ -354,15 +306,11 @@
   //   changed and cancel the current download attempt.
   std::string download_channel_;
 
-  // 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
+  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.
+  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_;
@@ -374,12 +322,6 @@
   // Whether the client is accepting rollback images too.
   bool rollback_allowed_;
 
-  // Whether rollbacks should preserve some system state during powerwash.
-  bool rollback_data_save_requested_;
-
-  // How many milestones the client can rollback to.
-  int rollback_allowed_milestones_;
-
   // True if scattering or staging are enabled, in which case waiting_period_
   // specifies the amount of absolute time that we've to wait for before sending
   // a request to Omaha.
@@ -397,22 +339,17 @@
   // When reading files, prepend root_ to the paths. Useful for testing.
   std::string root_;
 
-  // A list of DLC modules to install. A mapping from DLC App ID to |AppParams|.
-  std::map<std::string, AppParams> dlc_apps_params_;
+  // A list of DLC module IDs to install.
+  std::vector<std::string> dlc_module_ids_;
 
   // This variable defines whether the payload is being installed in the current
   // partition. At the moment, this is used for installing DLC modules on the
   // current active partition instead of the inactive partition.
   bool is_install_;
 
-  // Token used when making an update request for a specific build.
-  // For example: Token for a Quick Fix Build:
-  // https://cloud.google.com/docs/chrome-enterprise/policies/?policy=DeviceQuickFixBuildToken
-  std::string autoupdate_token_;
-
   DISALLOW_COPY_AND_ASSIGN(OmahaRequestParams);
 };
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_OMAHA_REQUEST_PARAMS_H_
+#endif  // UPDATE_ENGINE_OMAHA_REQUEST_PARAMS_H_
diff --git a/cros/omaha_request_params_unittest.cc b/omaha_request_params_unittest.cc
similarity index 76%
rename from cros/omaha_request_params_unittest.cc
rename to omaha_request_params_unittest.cc
index 2d67ec0..7332431 100644
--- a/cros/omaha_request_params_unittest.cc
+++ b/omaha_request_params_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/omaha_request_params.h"
+#include "update_engine/omaha_request_params.h"
 
 #include <stdio.h>
 
@@ -25,10 +25,11 @@
 #include <gtest/gtest.h>
 
 #include "update_engine/common/constants.h"
+#include "update_engine/common/fake_prefs.h"
 #include "update_engine/common/platform_constants.h"
 #include "update_engine/common/test_utils.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/cros/fake_system_state.h"
+#include "update_engine/fake_system_state.h"
 
 using chromeos_update_engine::test_utils::WriteFileString;
 using std::string;
@@ -37,23 +38,26 @@
 
 class OmahaRequestParamsTest : public ::testing::Test {
  public:
-  OmahaRequestParamsTest() : params_() {}
+  OmahaRequestParamsTest() : params_(&fake_system_state_) {}
 
  protected:
   void SetUp() override {
     // Create a uniquely named test directory.
     ASSERT_TRUE(tempdir_.CreateUniqueTempDir());
     params_.set_root(tempdir_.GetPath().value());
-    FakeSystemState::CreateInstance();
     SetLockDown(false);
+    fake_system_state_.set_prefs(&fake_prefs_);
   }
 
   void SetLockDown(bool locked_down) {
-    FakeSystemState::Get()->fake_hardware()->SetIsOfficialBuild(locked_down);
-    FakeSystemState::Get()->fake_hardware()->SetIsNormalBootMode(locked_down);
+    fake_system_state_.fake_hardware()->SetIsOfficialBuild(locked_down);
+    fake_system_state_.fake_hardware()->SetIsNormalBootMode(locked_down);
   }
 
-  OmahaRequestParams params_;
+  FakeSystemState fake_system_state_;
+  OmahaRequestParams params_{&fake_system_state_};
+  FakePrefs fake_prefs_;
+
   base::ScopedTempDir tempdir_;
 };
 
@@ -71,73 +75,73 @@
 }  // namespace
 
 TEST_F(OmahaRequestParamsTest, MissingChannelTest) {
-  EXPECT_TRUE(params_.Init("", "", {}));
+  EXPECT_TRUE(params_.Init("", "", false));
   // 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", "", {}));
+  EXPECT_TRUE(params_.Init("ForcedVersion", "", false));
   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", {}));
+  EXPECT_TRUE(params_.Init("", "http://forced.google.com", false));
   EXPECT_EQ("http://forced.google.com", params_.update_url());
 }
 
 TEST_F(OmahaRequestParamsTest, MissingURLTest) {
-  EXPECT_TRUE(params_.Init("", "", {}));
+  EXPECT_TRUE(params_.Init("", "", false));
   EXPECT_EQ(constants::kOmahaDefaultProductionURL, params_.update_url());
 }
 
 TEST_F(OmahaRequestParamsTest, DeltaOKTest) {
-  EXPECT_TRUE(params_.Init("", "", {}));
+  EXPECT_TRUE(params_.Init("", "", false));
   EXPECT_TRUE(params_.delta_okay());
 }
 
 TEST_F(OmahaRequestParamsTest, NoDeltasTest) {
   ASSERT_TRUE(
       WriteFileString(tempdir_.GetPath().Append(".nodelta").value(), ""));
-  EXPECT_TRUE(params_.Init("", "", {}));
+  EXPECT_TRUE(params_.Init("", "", false));
   EXPECT_FALSE(params_.delta_okay());
 }
 
 TEST_F(OmahaRequestParamsTest, SetTargetChannelTest) {
   {
-    OmahaRequestParams params;
+    OmahaRequestParams params(&fake_system_state_);
     params.set_root(tempdir_.GetPath().value());
-    EXPECT_TRUE(params.Init("", "", {}));
+    EXPECT_TRUE(params.Init("", "", false));
     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("", "", {}));
+  EXPECT_TRUE(params_.Init("", "", false));
   EXPECT_EQ("canary-channel", params_.target_channel());
   EXPECT_FALSE(params_.mutable_image_props_.is_powerwash_allowed);
 }
 
 TEST_F(OmahaRequestParamsTest, SetIsPowerwashAllowedTest) {
   {
-    OmahaRequestParams params;
+    OmahaRequestParams params(&fake_system_state_);
     params.set_root(tempdir_.GetPath().value());
-    EXPECT_TRUE(params.Init("", "", {}));
+    EXPECT_TRUE(params.Init("", "", false));
     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("", "", {}));
+  EXPECT_TRUE(params_.Init("", "", false));
   EXPECT_EQ("canary-channel", params_.target_channel());
   EXPECT_TRUE(params_.mutable_image_props_.is_powerwash_allowed);
 }
 
 TEST_F(OmahaRequestParamsTest, SetTargetChannelInvalidTest) {
   {
-    OmahaRequestParams params;
+    OmahaRequestParams params(&fake_system_state_);
     params.set_root(tempdir_.GetPath().value());
     SetLockDown(true);
-    EXPECT_TRUE(params.Init("", "", {}));
+    EXPECT_TRUE(params.Init("", "", false));
     params.image_props_.allow_arbitrary_channels = false;
     string error_message;
     EXPECT_FALSE(
@@ -147,7 +151,7 @@
     EXPECT_FALSE(params.mutable_image_props_.is_powerwash_allowed);
   }
   params_.set_root(tempdir_.GetPath().value());
-  EXPECT_TRUE(params_.Init("", "", {}));
+  EXPECT_TRUE(params_.Init("", "", false));
   EXPECT_EQ("stable-channel", params_.target_channel());
   EXPECT_FALSE(params_.mutable_image_props_.is_powerwash_allowed);
 }
@@ -193,7 +197,7 @@
 
   // When set to a valid value while a change is already pending, it should
   // succeed.
-  params_.Init("", "", {});
+  params_.Init("", "", false);
   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 ...
@@ -232,13 +236,6 @@
   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());
@@ -253,42 +250,12 @@
   EXPECT_TRUE(params_.ShouldPowerwash());
 }
 
-TEST_F(OmahaRequestParamsTest, RequisitionIsSetTest) {
-  EXPECT_TRUE(params_.Init("", "", {}));
-  EXPECT_EQ("fake_requisition", params_.device_requisition());
-}
+TEST_F(OmahaRequestParamsTest, CollectECFWVersionsTest) {
+  params_.hwid_ = string("STUMPY ALEX 12345");
+  EXPECT_FALSE(params_.CollectECFWVersions());
 
-TEST_F(OmahaRequestParamsTest, GetMissingDlcId) {
-  EXPECT_TRUE(params_.Init("", "", {}));
-
-  string dlc_id;
-  EXPECT_FALSE(params_.GetDlcId("some-dlc-app-id", &dlc_id));
-}
-
-TEST_F(OmahaRequestParamsTest, GetDlcId) {
-  EXPECT_TRUE(params_.Init("", "", {}));
-  const string kExpectedDlcId = "test-dlc";
-  const string dlc_app_id = params_.GetDlcAppId(kExpectedDlcId);
-  params_.set_dlc_apps_params({{dlc_app_id, {.name = kExpectedDlcId}}});
-
-  string dlc_id;
-  EXPECT_TRUE(params_.GetDlcId(dlc_app_id, &dlc_id));
-  EXPECT_EQ(kExpectedDlcId, dlc_id);
-}
-
-TEST_F(OmahaRequestParamsTest, GetDlcAppId) {
-  EXPECT_TRUE(params_.Init("", "", {}));
-  const string kAppId = "test-app-id";
-  params_.set_app_id(kAppId);
-  const string kDlcId = "test-dlc";
-  const string expected_dlc_app_id = kAppId + "_" + kDlcId;
-
-  EXPECT_EQ(expected_dlc_app_id, params_.GetDlcAppId(kDlcId));
-}
-
-TEST_F(OmahaRequestParamsTest, AutoUpdateTokenTest) {
-  EXPECT_TRUE(params_.Init("", "", {.quick_fix_build_token = "foo-token"}));
-  EXPECT_EQ("foo-token", params_.autoupdate_token());
+  params_.hwid_ = string("SNOW 12345");
+  EXPECT_TRUE(params_.CollectECFWVersions());
 }
 
 }  // namespace chromeos_update_engine
diff --git a/cros/omaha_response.h b/omaha_response.h
similarity index 81%
rename from cros/omaha_response.h
rename to omaha_response.h
index 3b07745..0ac09df 100644
--- a/cros/omaha_response.h
+++ b/omaha_response.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_OMAHA_RESPONSE_H_
-#define UPDATE_ENGINE_CROS_OMAHA_RESPONSE_H_
+#ifndef UPDATE_ENGINE_OMAHA_RESPONSE_H_
+#define UPDATE_ENGINE_OMAHA_RESPONSE_H_
 
 #include <fcntl.h>
 #include <sys/stat.h>
@@ -38,6 +38,7 @@
 
   // 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
@@ -50,13 +51,6 @@
     // True if the payload described in this response is a delta payload.
     // False if it's a full payload.
     bool is_delta = false;
-    // 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;
-    // The unique fingerprint value associated with the package.
-    std::string fp;
   };
   std::vector<Package> packages;
 
@@ -108,16 +102,9 @@
   // Key versions of the returned rollback image. Values are 0xffff if the
   // image not a rollback, or the fields were not present.
   RollbackKeyVersion rollback_key_version;
-
-  // Key versions of the N - rollback_allowed_milestones release. For example,
-  // if the current version is 70 and rollback_allowed_milestones is 4, this
-  // will contain the key versions of version 66. This is used to ensure that
-  // the kernel and firmware keys are at most those of v66 so that v66 can be
-  // rolled back to.
-  RollbackKeyVersion past_rollback_key_version;
 };
 static_assert(sizeof(off_t) == 8, "off_t not 64 bit");
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_OMAHA_RESPONSE_H_
+#endif  // UPDATE_ENGINE_OMAHA_RESPONSE_H_
diff --git a/omaha_response_handler_action.cc b/omaha_response_handler_action.cc
new file mode 100644
index 0000000..ab41b84
--- /dev/null
+++ b/omaha_response_handler_action.cc
@@ -0,0 +1,247 @@
+//
+// Copyright (C) 2011 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/omaha_response_handler_action.h"
+
+#include <limits>
+#include <string>
+
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <policy/device_policy.h>
+
+#include "update_engine/common/constants.h"
+#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/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"
+
+using chromeos_update_manager::Policy;
+using chromeos_update_manager::UpdateManager;
+using std::numeric_limits;
+using std::string;
+
+namespace chromeos_update_engine {
+
+OmahaResponseHandlerAction::OmahaResponseHandlerAction(
+    SystemState* system_state)
+    : system_state_(system_state),
+      deadline_file_(constants::kOmahaResponseDeadlineFile) {}
+
+void OmahaResponseHandlerAction::PerformAction() {
+  CHECK(HasInputObject());
+  ScopedActionCompleter completer(processor_, this);
+  const OmahaResponse& response = GetInputObject();
+  if (!response.update_exists) {
+    LOG(INFO) << "There are no updates. Aborting.";
+    completer.set_code(ErrorCode::kNoUpdate);
+    return;
+  }
+
+  // All decisions as to which URL should be used have already been done. So,
+  // make the current URL as the download URL.
+  string current_url = system_state_->payload_state()->GetCurrentUrl();
+  if (current_url.empty()) {
+    // This shouldn't happen as we should always supply the HTTPS backup URL.
+    // Handling this anyway, just in case.
+    LOG(ERROR) << "There are no suitable URLs in the response to use.";
+    completer.set_code(ErrorCode::kOmahaResponseInvalid);
+    return;
+  }
+
+  // This is the url to the first package, not all packages.
+  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();
+
+  // If we're using p2p to download and there is a local peer, use it.
+  if (payload_state->GetUsingP2PForDownloading() &&
+      !payload_state->GetP2PUrl().empty()) {
+    LOG(INFO) << "Replacing URL " << install_plan_.download_url
+              << " with local URL " << payload_state->GetP2PUrl()
+              << " since p2p is enabled.";
+    install_plan_.download_url = payload_state->GetP2PUrl();
+    payload_state->SetUsingP2PForDownloading(true);
+  }
+
+  // Fill up the other properties based on the response.
+  string update_check_response_hash;
+  for (const auto& package : response.packages) {
+    brillo::Blob raw_hash;
+    if (!base::HexStringToBytes(package.hash, &raw_hash)) {
+      LOG(ERROR) << "Failed to convert payload hash from hex string to bytes: "
+                 << package.hash;
+      completer.set_code(ErrorCode::kOmahaResponseInvalid);
+      return;
+    }
+    install_plan_.payloads.push_back(
+        {.size = package.size,
+         .metadata_size = package.metadata_size,
+         .metadata_signature = package.metadata_signature,
+         .hash = raw_hash,
+         .type = package.is_delta ? InstallPayloadType::kDelta
+                                  : InstallPayloadType::kFull});
+    update_check_response_hash += package.hash + ":";
+  }
+  install_plan_.public_key_rsa = response.public_key_rsa;
+  install_plan_.hash_checks_mandatory = AreHashChecksMandatory(response);
+  install_plan_.is_resume = DeltaPerformer::CanResumeUpdate(
+      system_state_->prefs(), update_check_response_hash);
+  if (install_plan_.is_resume) {
+    payload_state->UpdateResumed();
+  } else {
+    payload_state->UpdateRestarted();
+    LOG_IF(WARNING,
+           !DeltaPerformer::ResetUpdateProgress(system_state_->prefs(), false))
+        << "Unable to reset the update progress.";
+    LOG_IF(WARNING,
+           !system_state_->prefs()->SetString(kPrefsUpdateCheckResponseHash,
+                                              update_check_response_hash))
+        << "Unable to save the update check response hash.";
+  }
+
+  if (params->is_install()) {
+    install_plan_.target_slot = system_state_->boot_control()->GetCurrentSlot();
+    install_plan_.source_slot = BootControlInterface::kInvalidSlot;
+  } else {
+    install_plan_.source_slot = system_state_->boot_control()->GetCurrentSlot();
+    install_plan_.target_slot = install_plan_.source_slot == 0 ? 1 : 0;
+  }
+
+  // The Omaha response doesn't include the channel name for this image, so we
+  // use the download_channel we used during the request to tag the target slot.
+  // This will be used in the next boot to know the channel the image was
+  // downloaded from.
+  string current_channel_key =
+      kPrefsChannelOnSlotPrefix + std::to_string(install_plan_.target_slot);
+  system_state_->prefs()->SetString(current_channel_key,
+                                    params->download_channel());
+
+  // Checking whether device is able to boot up the returned rollback image.
+  if (response.is_rollback) {
+    if (!params->rollback_allowed()) {
+      LOG(ERROR) << "Received rollback image but rollback is not allowed.";
+      completer.set_code(ErrorCode::kOmahaResponseInvalid);
+      return;
+    }
+    auto min_kernel_key_version = static_cast<uint32_t>(
+        system_state_->hardware()->GetMinKernelKeyVersion());
+    auto min_firmware_key_version = static_cast<uint32_t>(
+        system_state_->hardware()->GetMinFirmwareKeyVersion());
+    uint32_t kernel_key_version =
+        static_cast<uint32_t>(response.rollback_key_version.kernel_key) << 16 |
+        static_cast<uint32_t>(response.rollback_key_version.kernel);
+    uint32_t firmware_key_version =
+        static_cast<uint32_t>(response.rollback_key_version.firmware_key)
+            << 16 |
+        static_cast<uint32_t>(response.rollback_key_version.firmware);
+
+    // Don't attempt a rollback if the versions are incompatible or the
+    // target image does not specify the version information.
+    if (kernel_key_version == numeric_limits<uint32_t>::max() ||
+        firmware_key_version == numeric_limits<uint32_t>::max() ||
+        kernel_key_version < min_kernel_key_version ||
+        firmware_key_version < min_firmware_key_version) {
+      LOG(ERROR) << "Device won't be able to boot up the rollback image.";
+      completer.set_code(ErrorCode::kRollbackNotPossible);
+      return;
+    }
+    install_plan_.is_rollback = true;
+  }
+
+  if (response.powerwash_required || params->ShouldPowerwash())
+    install_plan_.powerwash_required = true;
+
+  TEST_AND_RETURN(HasOutputPipe());
+  if (HasOutputPipe())
+    SetOutputObject(install_plan_);
+  LOG(INFO) << "Using this install plan:";
+  install_plan_.Dump();
+
+  // Send the deadline data (if any) to Chrome through a file. This is a pretty
+  // hacky solution but should be OK for now.
+  //
+  // TODO(petkov): Re-architect this to avoid communication through a
+  // file. Ideally, we would include this information in D-Bus's GetStatus
+  // method and UpdateStatus signal. A potential issue is that update_engine may
+  // be unresponsive during an update download.
+  if (!deadline_file_.empty()) {
+    if (payload_state->GetRollbackHappened()) {
+      // Don't do forced update if rollback has happened since the last update
+      // check where policy was present.
+      LOG(INFO) << "Not forcing update because a rollback happened.";
+      utils::WriteFile(deadline_file_.c_str(), nullptr, 0);
+    } else {
+      utils::WriteFile(deadline_file_.c_str(),
+                       response.deadline.data(),
+                       response.deadline.size());
+    }
+    chmod(deadline_file_.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+  }
+
+  // Check the generated install-plan with the Policy to confirm that
+  // it can be applied at this time (or at all).
+  UpdateManager* const update_manager = system_state_->update_manager();
+  CHECK(update_manager);
+  auto ec = ErrorCode::kSuccess;
+  update_manager->PolicyRequest(
+      &Policy::UpdateCanBeApplied, &ec, &install_plan_);
+  completer.set_code(ec);
+}
+
+bool OmahaResponseHandlerAction::AreHashChecksMandatory(
+    const OmahaResponse& response) {
+  // We sometimes need to waive the hash checks in order to download from
+  // sources that don't provide hashes, such as dev server.
+  // At this point UpdateAttempter::IsAnyUpdateSourceAllowed() has already been
+  // checked, so an unofficial update URL won't get this far unless it's OK to
+  // use without a hash. Additionally, we want to always waive hash checks on
+  // unofficial builds (i.e. dev/test images).
+  // The end result is this:
+  //  * Base image:
+  //    - Official URLs require a hash.
+  //    - Unofficial URLs only get this far if the IsAnyUpdateSourceAllowed()
+  //      devmode/debugd checks pass, in which case the hash is waived.
+  //  * Dev/test image:
+  //    - Any URL is allowed through with no hash checking.
+  if (!system_state_->request_params()->IsUpdateUrlOfficial() ||
+      !system_state_->hardware()->IsOfficialBuild()) {
+    // Still do a hash check if a public key is included.
+    if (!response.public_key_rsa.empty()) {
+      // The autoupdate_CatchBadSignatures test checks for this string
+      // in log-files. Keep in sync.
+      LOG(INFO) << "Mandating payload hash checks since Omaha Response "
+                << "for unofficial build includes public RSA key.";
+      return true;
+    } else {
+      LOG(INFO) << "Waiving payload hash checks for unofficial update URL.";
+      return false;
+    }
+  }
+
+  LOG(INFO) << "Mandating hash checks for official URL on official build.";
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/cros/omaha_response_handler_action.h b/omaha_response_handler_action.h
similarity index 88%
rename from cros/omaha_response_handler_action.h
rename to omaha_response_handler_action.h
index 9842c94..d2e6db8 100644
--- a/cros/omaha_response_handler_action.h
+++ b/omaha_response_handler_action.h
@@ -14,16 +14,17 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_OMAHA_RESPONSE_HANDLER_ACTION_H_
-#define UPDATE_ENGINE_CROS_OMAHA_RESPONSE_HANDLER_ACTION_H_
+#ifndef UPDATE_ENGINE_OMAHA_RESPONSE_HANDLER_ACTION_H_
+#define UPDATE_ENGINE_OMAHA_RESPONSE_HANDLER_ACTION_H_
 
 #include <string>
 
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
 #include "update_engine/common/action.h"
-#include "update_engine/cros/omaha_request_action.h"
+#include "update_engine/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.
@@ -41,7 +42,7 @@
 
 class OmahaResponseHandlerAction : public Action<OmahaResponseHandlerAction> {
  public:
-  OmahaResponseHandlerAction();
+  explicit OmahaResponseHandlerAction(SystemState* system_state);
 
   typedef ActionTraits<OmahaResponseHandlerAction>::InputObjectType
       InputObjectType;
@@ -64,6 +65,9 @@
   // of the system and the contents of the Omaha response. False otherwise.
   bool AreHashChecksMandatory(const OmahaResponse& response);
 
+  // Global system context.
+  SystemState* system_state_;
+
   // The install plan, if we have an update.
   InstallPlan install_plan_;
 
@@ -86,4 +90,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_OMAHA_RESPONSE_HANDLER_ACTION_H_
+#endif  // UPDATE_ENGINE_OMAHA_RESPONSE_HANDLER_ACTION_H_
diff --git a/omaha_response_handler_action_unittest.cc b/omaha_response_handler_action_unittest.cc
new file mode 100644
index 0000000..b47040b
--- /dev/null
+++ b/omaha_response_handler_action_unittest.cc
@@ -0,0 +1,688 @@
+//
+// Copyright (C) 2011 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/omaha_response_handler_action.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <brillo/message_loops/fake_message_loop.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/constants.h"
+#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/payload_consumer/payload_constants.h"
+#include "update_engine/update_manager/mock_policy.h"
+
+using chromeos_update_engine::test_utils::System;
+using chromeos_update_engine::test_utils::WriteFileString;
+using chromeos_update_manager::EvalStatus;
+using chromeos_update_manager::FakeUpdateManager;
+using chromeos_update_manager::MockPolicy;
+using std::string;
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::SetArgPointee;
+
+namespace chromeos_update_engine {
+
+class OmahaResponseHandlerActionProcessorDelegate
+    : public ActionProcessorDelegate {
+ public:
+  OmahaResponseHandlerActionProcessorDelegate()
+      : code_(ErrorCode::kError), code_set_(false) {}
+  void ActionCompleted(ActionProcessor* processor,
+                       AbstractAction* action,
+                       ErrorCode code) {
+    if (action->Type() == OmahaResponseHandlerAction::StaticType()) {
+      auto response_handler_action =
+          static_cast<OmahaResponseHandlerAction*>(action);
+      code_ = code;
+      code_set_ = true;
+      response_handler_action_install_plan_.reset(
+          new InstallPlan(response_handler_action->install_plan_));
+    } else if (action->Type() ==
+               ObjectCollectorAction<InstallPlan>::StaticType()) {
+      auto collector_action =
+          static_cast<ObjectCollectorAction<InstallPlan>*>(action);
+      collector_action_install_plan_.reset(
+          new InstallPlan(collector_action->object()));
+    }
+  }
+  ErrorCode code_;
+  bool code_set_;
+  std::unique_ptr<InstallPlan> collector_action_install_plan_;
+  std::unique_ptr<InstallPlan> response_handler_action_install_plan_;
+};
+
+class OmahaResponseHandlerActionTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    FakeBootControl* fake_boot_control = fake_system_state_.fake_boot_control();
+    fake_boot_control->SetPartitionDevice(kPartitionNameKernel, 0, "/dev/sdz2");
+    fake_boot_control->SetPartitionDevice(kPartitionNameRoot, 0, "/dev/sdz3");
+    fake_boot_control->SetPartitionDevice(kPartitionNameKernel, 1, "/dev/sdz4");
+    fake_boot_control->SetPartitionDevice(kPartitionNameRoot, 1, "/dev/sdz5");
+  }
+
+  // Return true iff the OmahaResponseHandlerAction succeeded.
+  // If out is non-null, it's set w/ the response from the action.
+  bool DoTest(const OmahaResponse& in,
+              const string& deadline_file,
+              InstallPlan* out);
+
+  // Delegate passed to the ActionProcessor.
+  OmahaResponseHandlerActionProcessorDelegate delegate_;
+
+  // Captures the action's result code, for tests that need to directly verify
+  // it in non-success cases.
+  ErrorCode action_result_code_;
+
+  FakeSystemState fake_system_state_;
+  // "Hash+"
+  const brillo::Blob expected_hash_ = {0x48, 0x61, 0x73, 0x68, 0x2b};
+};
+
+namespace {
+const char* const kLongName =
+    "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
+    "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
+    "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
+    "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
+    "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
+    "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
+    "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
+    "-the_update_a.b.c.d_DELTA_.tgz";
+const char* const kBadVersion = "don't update me";
+const char* const kPayloadHashHex = "486173682b";
+}  // namespace
+
+bool OmahaResponseHandlerActionTest::DoTest(const OmahaResponse& in,
+                                            const string& test_deadline_file,
+                                            InstallPlan* out) {
+  brillo::FakeMessageLoop loop(nullptr);
+  loop.SetAsCurrent();
+  ActionProcessor processor;
+  processor.set_delegate(&delegate_);
+
+  auto feeder_action = std::make_unique<ObjectFeederAction<OmahaResponse>>();
+  feeder_action->set_obj(in);
+  if (in.update_exists && in.version != kBadVersion) {
+    string expected_hash;
+    for (const auto& package : in.packages)
+      expected_hash += package.hash + ":";
+    EXPECT_CALL(*(fake_system_state_.mock_prefs()),
+                SetString(kPrefsUpdateCheckResponseHash, expected_hash))
+        .WillOnce(Return(true));
+
+    int slot =
+        fake_system_state_.request_params()->is_install()
+            ? fake_system_state_.fake_boot_control()->GetCurrentSlot()
+            : 1 - fake_system_state_.fake_boot_control()->GetCurrentSlot();
+    string key = kPrefsChannelOnSlotPrefix + std::to_string(slot);
+    EXPECT_CALL(*(fake_system_state_.mock_prefs()), SetString(key, testing::_))
+        .WillOnce(Return(true));
+  }
+
+  string current_url = in.packages.size() ? in.packages[0].payload_urls[0] : "";
+  EXPECT_CALL(*(fake_system_state_.mock_payload_state()), GetCurrentUrl())
+      .WillRepeatedly(Return(current_url));
+
+  auto response_handler_action =
+      std::make_unique<OmahaResponseHandlerAction>(&fake_system_state_);
+  if (!test_deadline_file.empty())
+    response_handler_action->deadline_file_ = test_deadline_file;
+
+  auto collector_action =
+      std::make_unique<ObjectCollectorAction<InstallPlan>>();
+
+  BondActions(feeder_action.get(), response_handler_action.get());
+  BondActions(response_handler_action.get(), collector_action.get());
+  processor.EnqueueAction(std::move(feeder_action));
+  processor.EnqueueAction(std::move(response_handler_action));
+  processor.EnqueueAction(std::move(collector_action));
+  processor.StartProcessing();
+  EXPECT_TRUE(!processor.IsRunning())
+      << "Update test to handle non-async actions";
+
+  if (out && delegate_.collector_action_install_plan_)
+    *out = *delegate_.collector_action_install_plan_;
+
+  EXPECT_TRUE(delegate_.code_set_);
+  action_result_code_ = delegate_.code_;
+  return delegate_.code_ == ErrorCode::kSuccess;
+}
+
+TEST_F(OmahaResponseHandlerActionTest, SimpleTest) {
+  test_utils::ScopedTempFile test_deadline_file(
+      "omaha_response_handler_action_unittest-XXXXXX");
+  {
+    OmahaResponse in;
+    in.update_exists = true;
+    in.version = "a.b.c.d";
+    in.packages.push_back(
+        {.payload_urls = {"http://foo/the_update_a.b.c.d.tgz"},
+         .size = 12,
+         .hash = kPayloadHashHex});
+    in.more_info_url = "http://more/info";
+    in.prompt = false;
+    in.deadline = "20101020";
+    InstallPlan install_plan;
+    EXPECT_TRUE(DoTest(in, test_deadline_file.path(), &install_plan));
+    EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+    EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+    EXPECT_EQ(1U, install_plan.target_slot);
+    string deadline;
+    EXPECT_TRUE(utils::ReadFile(test_deadline_file.path(), &deadline));
+    EXPECT_EQ("20101020", deadline);
+    struct stat deadline_stat;
+    EXPECT_EQ(0, stat(test_deadline_file.path().c_str(), &deadline_stat));
+    EXPECT_EQ(
+        static_cast<mode_t>(S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH),
+        deadline_stat.st_mode);
+    EXPECT_EQ(in.version, install_plan.version);
+  }
+  {
+    OmahaResponse in;
+    in.update_exists = true;
+    in.version = "a.b.c.d";
+    in.packages.push_back(
+        {.payload_urls = {"http://foo/the_update_a.b.c.d.tgz"},
+         .size = 12,
+         .hash = kPayloadHashHex});
+    in.more_info_url = "http://more/info";
+    in.prompt = true;
+    InstallPlan install_plan;
+    // Set the other slot as current.
+    fake_system_state_.fake_boot_control()->SetCurrentSlot(1);
+    EXPECT_TRUE(DoTest(in, test_deadline_file.path(), &install_plan));
+    EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+    EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+    EXPECT_EQ(0U, install_plan.target_slot);
+    string deadline;
+    EXPECT_TRUE(utils::ReadFile(test_deadline_file.path(), &deadline) &&
+                deadline.empty());
+    EXPECT_EQ(in.version, install_plan.version);
+  }
+  {
+    OmahaResponse in;
+    in.update_exists = true;
+    in.version = "a.b.c.d";
+    in.packages.push_back(
+        {.payload_urls = {kLongName}, .size = 12, .hash = kPayloadHashHex});
+    in.more_info_url = "http://more/info";
+    in.prompt = true;
+    in.deadline = "some-deadline";
+    InstallPlan install_plan;
+    fake_system_state_.fake_boot_control()->SetCurrentSlot(0);
+    // Because rollback happened, the deadline shouldn't be written into the
+    // file.
+    EXPECT_CALL(*(fake_system_state_.mock_payload_state()),
+                GetRollbackHappened())
+        .WillOnce(Return(true));
+    EXPECT_TRUE(DoTest(in, test_deadline_file.path(), &install_plan));
+    EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+    EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+    EXPECT_EQ(1U, install_plan.target_slot);
+    string deadline;
+    EXPECT_TRUE(utils::ReadFile(test_deadline_file.path(), &deadline));
+    EXPECT_TRUE(deadline.empty());
+    EXPECT_EQ(in.version, install_plan.version);
+  }
+  {
+    OmahaResponse in;
+    in.update_exists = true;
+    in.version = "a.b.c.d";
+    in.packages.push_back(
+        {.payload_urls = {kLongName}, .size = 12, .hash = kPayloadHashHex});
+    in.more_info_url = "http://more/info";
+    in.prompt = true;
+    in.deadline = "some-deadline";
+    InstallPlan install_plan;
+    fake_system_state_.fake_boot_control()->SetCurrentSlot(0);
+    EXPECT_CALL(*(fake_system_state_.mock_payload_state()),
+                GetRollbackHappened())
+        .WillOnce(Return(false));
+    EXPECT_TRUE(DoTest(in, test_deadline_file.path(), &install_plan));
+    EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+    EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+    EXPECT_EQ(1U, install_plan.target_slot);
+    string deadline;
+    EXPECT_TRUE(utils::ReadFile(test_deadline_file.path(), &deadline));
+    EXPECT_EQ("some-deadline", deadline);
+    EXPECT_EQ(in.version, install_plan.version);
+  }
+}
+
+TEST_F(OmahaResponseHandlerActionTest, NoUpdatesTest) {
+  OmahaResponse in;
+  in.update_exists = false;
+  InstallPlan install_plan;
+  EXPECT_FALSE(DoTest(in, "", &install_plan));
+  EXPECT_TRUE(install_plan.partitions.empty());
+}
+
+TEST_F(OmahaResponseHandlerActionTest, InstallTest) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.version = "a.b.c.d";
+  in.packages.push_back(
+      {.payload_urls = {kLongName}, .size = 1, .hash = kPayloadHashHex});
+  in.packages.push_back(
+      {.payload_urls = {kLongName}, .size = 2, .hash = kPayloadHashHex});
+  in.more_info_url = "http://more/info";
+
+  OmahaRequestParams params(&fake_system_state_);
+  params.set_is_install(true);
+
+  fake_system_state_.set_request_params(&params);
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_EQ(install_plan.source_slot, UINT_MAX);
+}
+
+TEST_F(OmahaResponseHandlerActionTest, MultiPackageTest) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.version = "a.b.c.d";
+  in.packages.push_back({.payload_urls = {"http://package/1"},
+                         .size = 1,
+                         .hash = kPayloadHashHex});
+  in.packages.push_back({.payload_urls = {"http://package/2"},
+                         .size = 2,
+                         .hash = kPayloadHashHex});
+  in.more_info_url = "http://more/info";
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+  EXPECT_EQ(2u, install_plan.payloads.size());
+  EXPECT_EQ(in.packages[0].size, install_plan.payloads[0].size);
+  EXPECT_EQ(in.packages[1].size, install_plan.payloads[1].size);
+  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);
+}
+
+TEST_F(OmahaResponseHandlerActionTest, HashChecksForHttpTest) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.version = "a.b.c.d";
+  in.packages.push_back(
+      {.payload_urls = {"http://test.should/need/hash.checks.signed"},
+       .size = 12,
+       .hash = kPayloadHashHex});
+  in.more_info_url = "http://more/info";
+  // Hash checks are always skipped for non-official update URLs.
+  EXPECT_CALL(*(fake_system_state_.mock_request_params()),
+              IsUpdateUrlOfficial())
+      .WillRepeatedly(Return(true));
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+  EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+  EXPECT_TRUE(install_plan.hash_checks_mandatory);
+  EXPECT_EQ(in.version, install_plan.version);
+}
+
+TEST_F(OmahaResponseHandlerActionTest, HashChecksForUnofficialUpdateUrl) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.version = "a.b.c.d";
+  in.packages.push_back(
+      {.payload_urls = {"http://url.normally/needs/hash.checks.signed"},
+       .size = 12,
+       .hash = kPayloadHashHex});
+  in.more_info_url = "http://more/info";
+  EXPECT_CALL(*(fake_system_state_.mock_request_params()),
+              IsUpdateUrlOfficial())
+      .WillRepeatedly(Return(false));
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+  EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+  EXPECT_FALSE(install_plan.hash_checks_mandatory);
+  EXPECT_EQ(in.version, install_plan.version);
+}
+
+TEST_F(OmahaResponseHandlerActionTest,
+       HashChecksForOfficialUrlUnofficialBuildTest) {
+  // Official URLs for unofficial builds (dev/test images) don't require hash.
+  OmahaResponse in;
+  in.update_exists = true;
+  in.version = "a.b.c.d";
+  in.packages.push_back(
+      {.payload_urls = {"http://url.normally/needs/hash.checks.signed"},
+       .size = 12,
+       .hash = kPayloadHashHex});
+  in.more_info_url = "http://more/info";
+  EXPECT_CALL(*(fake_system_state_.mock_request_params()),
+              IsUpdateUrlOfficial())
+      .WillRepeatedly(Return(true));
+  fake_system_state_.fake_hardware()->SetIsOfficialBuild(false);
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+  EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+  EXPECT_FALSE(install_plan.hash_checks_mandatory);
+  EXPECT_EQ(in.version, install_plan.version);
+}
+
+TEST_F(OmahaResponseHandlerActionTest, HashChecksForHttpsTest) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.version = "a.b.c.d";
+  in.packages.push_back(
+      {.payload_urls = {"https://test.should/need/hash.checks.signed"},
+       .size = 12,
+       .hash = kPayloadHashHex});
+  in.more_info_url = "http://more/info";
+  EXPECT_CALL(*(fake_system_state_.mock_request_params()),
+              IsUpdateUrlOfficial())
+      .WillRepeatedly(Return(true));
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+  EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+  EXPECT_TRUE(install_plan.hash_checks_mandatory);
+  EXPECT_EQ(in.version, install_plan.version);
+}
+
+TEST_F(OmahaResponseHandlerActionTest, HashChecksForBothHttpAndHttpsTest) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.version = "a.b.c.d";
+  in.packages.push_back(
+      {.payload_urls = {"http://test.should.still/need/hash.checks",
+                        "https://test.should.still/need/hash.checks"},
+       .size = 12,
+       .hash = kPayloadHashHex});
+  in.more_info_url = "http://more/info";
+  EXPECT_CALL(*(fake_system_state_.mock_request_params()),
+              IsUpdateUrlOfficial())
+      .WillRepeatedly(Return(true));
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+  EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+  EXPECT_TRUE(install_plan.hash_checks_mandatory);
+  EXPECT_EQ(in.version, install_plan.version);
+}
+
+TEST_F(OmahaResponseHandlerActionTest, ChangeToMoreStableChannelTest) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.version = "a.b.c.d";
+  in.packages.push_back({.payload_urls = {"https://MoreStableChannelTest"},
+                         .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("canary-channel");
+  // The ImageProperties in Android uses prefs to store MutableImageProperties.
+#ifdef __ANDROID__
+  EXPECT_CALL(*fake_system_state_.mock_prefs(), SetBoolean(_, true))
+      .WillOnce(Return(true));
+#endif  // __ANDROID__
+  EXPECT_TRUE(params.SetTargetChannel("stable-channel", true, nullptr));
+  params.UpdateDownloadChannel();
+  EXPECT_TRUE(params.ShouldPowerwash());
+
+  fake_system_state_.set_request_params(&params);
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_TRUE(install_plan.powerwash_required);
+}
+
+TEST_F(OmahaResponseHandlerActionTest, ChangeToLessStableChannelTest) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.version = "a.b.c.d";
+  in.packages.push_back({.payload_urls = {"https://LessStableChannelTest"},
+                         .size = 15,
+                         .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("stable-channel");
+  // The ImageProperties in Android uses prefs to store MutableImageProperties.
+#ifdef __ANDROID__
+  EXPECT_CALL(*fake_system_state_.mock_prefs(), SetBoolean(_, false))
+      .WillOnce(Return(true));
+#endif  // __ANDROID__
+  EXPECT_TRUE(params.SetTargetChannel("canary-channel", false, nullptr));
+  params.UpdateDownloadChannel();
+  EXPECT_FALSE(params.ShouldPowerwash());
+
+  fake_system_state_.set_request_params(&params);
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_FALSE(install_plan.powerwash_required);
+}
+
+TEST_F(OmahaResponseHandlerActionTest, P2PUrlIsUsedAndHashChecksMandatory) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.version = "a.b.c.d";
+  in.packages.push_back(
+      {.payload_urls = {"https://would.not/cause/hash/checks"},
+       .size = 12,
+       .hash = kPayloadHashHex});
+  in.more_info_url = "http://more/info";
+
+  OmahaRequestParams params(&fake_system_state_);
+  // We're using a real OmahaRequestParams object here so we can't mock
+  // IsUpdateUrlOfficial(), but setting the update URL to the AutoUpdate test
+  // server will cause IsUpdateUrlOfficial() to return true.
+  params.set_update_url(constants::kOmahaDefaultAUTestURL);
+  fake_system_state_.set_request_params(&params);
+
+  EXPECT_CALL(*fake_system_state_.mock_payload_state(),
+              SetUsingP2PForDownloading(true));
+
+  string p2p_url = "http://9.8.7.6/p2p";
+  EXPECT_CALL(*fake_system_state_.mock_payload_state(), GetP2PUrl())
+      .WillRepeatedly(Return(p2p_url));
+  EXPECT_CALL(*fake_system_state_.mock_payload_state(),
+              GetUsingP2PForDownloading())
+      .WillRepeatedly(Return(true));
+
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+  EXPECT_EQ(p2p_url, install_plan.download_url);
+  EXPECT_TRUE(install_plan.hash_checks_mandatory);
+}
+
+TEST_F(OmahaResponseHandlerActionTest, RollbackTest) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.packages.push_back({.payload_urls = {"https://RollbackTest"},
+                         .size = 1,
+                         .hash = kPayloadHashHex});
+  in.is_rollback = true;
+  in.rollback_key_version.kernel = 1;
+  in.rollback_key_version.kernel = 2;
+  in.rollback_key_version.firmware_key = 3;
+  in.rollback_key_version.firmware = 4;
+
+  fake_system_state_.fake_hardware()->SetMinKernelKeyVersion(0x00010002);
+  fake_system_state_.fake_hardware()->SetMinFirmwareKeyVersion(0x00030004);
+
+  OmahaRequestParams params(&fake_system_state_);
+  params.set_rollback_allowed(true);
+
+  fake_system_state_.set_request_params(&params);
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_TRUE(install_plan.is_rollback);
+}
+
+TEST_F(OmahaResponseHandlerActionTest, RollbackKernelVersionErrorTest) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.packages.push_back({.payload_urls = {"https://RollbackTest"},
+                         .size = 1,
+                         .hash = kPayloadHashHex});
+  in.is_rollback = true;
+  in.rollback_key_version.kernel_key = 1;
+  in.rollback_key_version.kernel = 1;  // This is lower than the minimum.
+  in.rollback_key_version.firmware_key = 3;
+  in.rollback_key_version.firmware = 4;
+
+  fake_system_state_.fake_hardware()->SetMinKernelKeyVersion(0x00010002);
+  fake_system_state_.fake_hardware()->SetMinFirmwareKeyVersion(0x00030004);
+
+  OmahaRequestParams params(&fake_system_state_);
+  params.set_rollback_allowed(true);
+
+  fake_system_state_.set_request_params(&params);
+  InstallPlan install_plan;
+  EXPECT_FALSE(DoTest(in, "", &install_plan));
+}
+
+TEST_F(OmahaResponseHandlerActionTest, RollbackFirmwareVersionErrorTest) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.packages.push_back({.payload_urls = {"https://RollbackTest"},
+                         .size = 1,
+                         .hash = kPayloadHashHex});
+  in.is_rollback = true;
+  in.rollback_key_version.kernel_key = 1;
+  in.rollback_key_version.kernel = 2;
+  in.rollback_key_version.firmware_key = 3;
+  in.rollback_key_version.firmware = 3;  // This is lower than the minimum.
+
+  fake_system_state_.fake_hardware()->SetMinKernelKeyVersion(0x00010002);
+  fake_system_state_.fake_hardware()->SetMinFirmwareKeyVersion(0x00030004);
+
+  OmahaRequestParams params(&fake_system_state_);
+  params.set_rollback_allowed(true);
+
+  fake_system_state_.set_request_params(&params);
+  InstallPlan install_plan;
+  EXPECT_FALSE(DoTest(in, "", &install_plan));
+}
+
+TEST_F(OmahaResponseHandlerActionTest, RollbackNotRollbackTest) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.packages.push_back({.payload_urls = {"https://RollbackTest"},
+                         .size = 1,
+                         .hash = kPayloadHashHex});
+  in.is_rollback = false;
+
+  OmahaRequestParams params(&fake_system_state_);
+  params.set_rollback_allowed(true);
+
+  fake_system_state_.set_request_params(&params);
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_FALSE(install_plan.is_rollback);
+}
+
+TEST_F(OmahaResponseHandlerActionTest, RollbackNotAllowedTest) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.packages.push_back({.payload_urls = {"https://RollbackTest"},
+                         .size = 1,
+                         .hash = kPayloadHashHex});
+  in.is_rollback = true;
+
+  OmahaRequestParams params(&fake_system_state_);
+  params.set_rollback_allowed(false);
+
+  fake_system_state_.set_request_params(&params);
+  InstallPlan install_plan;
+  EXPECT_FALSE(DoTest(in, "", &install_plan));
+}
+
+TEST_F(OmahaResponseHandlerActionTest, SystemVersionTest) {
+  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});
+  in.packages.push_back({.payload_urls = {"http://package/2"},
+                         .size = 2,
+                         .hash = kPayloadHashHex});
+  in.more_info_url = "http://more/info";
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+  EXPECT_EQ(2u, install_plan.payloads.size());
+  EXPECT_EQ(in.packages[0].size, install_plan.payloads[0].size);
+  EXPECT_EQ(in.packages[1].size, install_plan.payloads[1].size);
+  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) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.version = "a.b.c.d";
+  in.packages.push_back({.payload_urls = {"http://foo/the_update_a.b.c.d.tgz"},
+                         .size = 12,
+                         .hash = kPayloadHashHex});
+  // Setup the UpdateManager to disallow the update.
+  FakeClock fake_clock;
+  MockPolicy* mock_policy = new MockPolicy(&fake_clock);
+  FakeUpdateManager* fake_update_manager =
+      fake_system_state_.fake_update_manager();
+  fake_update_manager->set_policy(mock_policy);
+  EXPECT_CALL(*mock_policy, UpdateCanBeApplied(_, _, _, _, _))
+      .WillOnce(
+          DoAll(SetArgPointee<3>(ErrorCode::kOmahaUpdateDeferredPerPolicy),
+                Return(EvalStatus::kSucceeded)));
+  // Perform the Action. It should "fail" with kOmahaUpdateDeferredPerPolicy.
+  InstallPlan install_plan;
+  EXPECT_FALSE(DoTest(in, "", &install_plan));
+  EXPECT_EQ(ErrorCode::kOmahaUpdateDeferredPerPolicy, action_result_code_);
+  // Verify that DoTest() didn't set the output install plan.
+  EXPECT_EQ("", install_plan.version);
+  // Now verify the InstallPlan that was generated.
+  install_plan = *delegate_.response_handler_action_install_plan_;
+  EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+  EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+  EXPECT_EQ(1U, install_plan.target_slot);
+  EXPECT_EQ(in.version, install_plan.version);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/omaha_utils.cc b/omaha_utils.cc
new file mode 100644
index 0000000..6bd7525
--- /dev/null
+++ b/omaha_utils.cc
@@ -0,0 +1,57 @@
+//
+// 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/omaha_utils.h"
+
+#include <base/logging.h>
+
+namespace chromeos_update_engine {
+
+namespace {
+
+// The possible string values for the end-of-life status.
+const char kEolStatusSupported[] = "supported";
+const char kEolStatusSecurityOnly[] = "security-only";
+const char kEolStatusEol[] = "eol";
+
+}  // namespace
+
+const char* EolStatusToString(EolStatus eol_status) {
+  switch (eol_status) {
+    case EolStatus::kSupported:
+      return kEolStatusSupported;
+    case EolStatus::kSecurityOnly:
+      return kEolStatusSecurityOnly;
+    case EolStatus::kEol:
+      return kEolStatusEol;
+  }
+  // Only reached if an invalid number is casted to |EolStatus|.
+  LOG(WARNING) << "Invalid EolStatus value: " << static_cast<int>(eol_status);
+  return kEolStatusSupported;
+}
+
+EolStatus StringToEolStatus(const std::string& eol_status) {
+  if (eol_status == kEolStatusSupported || eol_status.empty())
+    return EolStatus::kSupported;
+  if (eol_status == kEolStatusSecurityOnly)
+    return EolStatus::kSecurityOnly;
+  if (eol_status == kEolStatusEol)
+    return EolStatus::kEol;
+  LOG(WARNING) << "Invalid end-of-life attribute: " << eol_status;
+  return EolStatus::kSupported;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/omaha_utils.h b/omaha_utils.h
new file mode 100644
index 0000000..8614540
--- /dev/null
+++ b/omaha_utils.h
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_OMAHA_UTILS_H_
+#define UPDATE_ENGINE_OMAHA_UTILS_H_
+
+#include <string>
+
+namespace chromeos_update_engine {
+
+// The end-of-life status of the device.
+enum class EolStatus {
+  kSupported = 0,
+  kSecurityOnly,
+  kEol,
+};
+
+// Returns the string representation of the |eol_status|.
+const char* EolStatusToString(EolStatus eol_status);
+
+// Converts the end-of-life status string to an EolStatus numeric value. In case
+// of an invalid string, the default "supported" value will be used instead.
+EolStatus StringToEolStatus(const std::string& eol_status);
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_OMAHA_UTILS_H_
diff --git a/cros/omaha_utils_unittest.cc b/omaha_utils_unittest.cc
similarity index 61%
rename from cros/omaha_utils_unittest.cc
rename to omaha_utils_unittest.cc
index f89f690..8ceb76b 100644
--- a/cros/omaha_utils_unittest.cc
+++ b/omaha_utils_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/omaha_utils.h"
+#include "update_engine/omaha_utils.h"
 
 #include <gtest/gtest.h>
 #include <vector>
@@ -23,17 +23,20 @@
 
 class OmahaUtilsTest : public ::testing::Test {};
 
-TEST(OmahaUtilsTest, EolDateTest) {
+TEST(OmahaUtilsTest, EolStatusTest) {
+  EXPECT_EQ(EolStatus::kEol, StringToEolStatus("eol"));
+
   // Supported values are converted back and forth properly.
-  const std::vector<EolDate> tests = {kEolDateInvalid, -1, 0, 1};
-  for (EolDate eol_date : tests) {
-    EXPECT_EQ(eol_date, StringToEolDate(EolDateToString(eol_date)))
-        << "The StringToEolDate() was " << EolDateToString(eol_date);
+  const std::vector<EolStatus> tests = {
+      EolStatus::kSupported, EolStatus::kSecurityOnly, EolStatus::kEol};
+  for (EolStatus eol_status : tests) {
+    EXPECT_EQ(eol_status, StringToEolStatus(EolStatusToString(eol_status)))
+        << "The StringToEolStatus() was " << EolStatusToString(eol_status);
   }
 
   // Invalid values are assumed as "supported".
-  EXPECT_EQ(kEolDateInvalid, StringToEolDate(""));
-  EXPECT_EQ(kEolDateInvalid, StringToEolDate("hello, world!"));
+  EXPECT_EQ(EolStatus::kSupported, StringToEolStatus(""));
+  EXPECT_EQ(EolStatus::kSupported, StringToEolStatus("hello, world!"));
 }
 
 }  // namespace chromeos_update_engine
diff --git a/cros/p2p_manager.cc b/p2p_manager.cc
similarity index 96%
rename from cros/p2p_manager.cc
rename to p2p_manager.cc
index 19e2600..6720908 100644
--- a/cros/p2p_manager.cc
+++ b/p2p_manager.cc
@@ -23,7 +23,7 @@
 #define _BSD_SOURCE
 #endif
 
-#include "update_engine/cros/p2p_manager.h"
+#include "update_engine/p2p_manager.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -51,7 +51,6 @@
 #include <base/strings/stringprintf.h>
 
 #include "update_engine/common/subprocess.h"
-#include "update_engine/common/system_state.h"
 #include "update_engine/common/utils.h"
 #include "update_engine/update_manager/policy.h"
 #include "update_engine/update_manager/update_manager.h"
@@ -66,6 +65,7 @@
 using chromeos_update_manager::EvalStatus;
 using chromeos_update_manager::Policy;
 using chromeos_update_manager::UpdateManager;
+using std::map;
 using std::pair;
 using std::string;
 using std::unique_ptr;
@@ -116,6 +116,7 @@
 class P2PManagerImpl : public P2PManager {
  public:
   P2PManagerImpl(Configuration* configuration,
+                 ClockInterface* clock,
                  UpdateManager* update_manager,
                  const string& file_extension,
                  const int num_files_to_keep,
@@ -170,6 +171,9 @@
   // Configuration object.
   unique_ptr<Configuration> configuration_;
 
+  // Object for telling the time.
+  ClockInterface* clock_;
+
   // A pointer to the global Update Manager.
   UpdateManager* update_manager_;
 
@@ -208,11 +212,13 @@
 const char P2PManagerImpl::kTmpExtension[] = ".tmp";
 
 P2PManagerImpl::P2PManagerImpl(Configuration* configuration,
+                               ClockInterface* clock,
                                UpdateManager* update_manager,
                                const string& file_extension,
                                const int num_files_to_keep,
                                const TimeDelta& max_file_age)
-    : update_manager_(update_manager),
+    : clock_(clock),
+      update_manager_(update_manager),
       file_extension_(file_extension),
       num_files_to_keep_(num_files_to_keep),
       max_file_age_(max_file_age) {
@@ -243,12 +249,12 @@
 
 bool P2PManagerImpl::EnsureP2P(bool should_be_running) {
   int return_code = 0;
-  string stderr;
+  string output;
 
   may_be_running_ = true;  // Unless successful, we must be conservative.
 
   vector<string> args = configuration_->GetInitctlArgs(should_be_running);
-  if (!Subprocess::SynchronousExec(args, &return_code, nullptr, &stderr)) {
+  if (!Subprocess::SynchronousExec(args, &return_code, &output)) {
     LOG(ERROR) << "Error spawning " << utils::StringVectorToString(args);
     return false;
   }
@@ -262,7 +268,7 @@
     const char* expected_error_message =
         should_be_running ? "initctl: Job is already running: p2p\n"
                           : "initctl: Unknown instance \n";
-    if (stderr != expected_error_message)
+    if (output != expected_error_message)
       return false;
   }
 
@@ -339,9 +345,8 @@
     // If instructed to keep only files younger than a given age
     // (|max_file_age_| != 0), delete files satisfying this criteria
     // right now. Otherwise add it to a list we'll consider for later.
-    if (max_file_age_ != TimeDelta() &&
-        SystemState::Get()->clock()->GetWallclockTime() - time >
-            max_file_age_) {
+    if (clock_ != nullptr && max_file_age_ != TimeDelta() &&
+        clock_->GetWallclockTime() - time > max_file_age_) {
       if (!DeleteP2PFile(name, "file too old"))
         deletion_failed = true;
     } else {
@@ -718,11 +723,13 @@
 }
 
 P2PManager* P2PManager::Construct(Configuration* configuration,
+                                  ClockInterface* clock,
                                   UpdateManager* update_manager,
                                   const string& file_extension,
                                   const int num_files_to_keep,
                                   const TimeDelta& max_file_age) {
   return new P2PManagerImpl(configuration,
+                            clock,
                             update_manager,
                             file_extension,
                             num_files_to_keep,
diff --git a/cros/p2p_manager.h b/p2p_manager.h
similarity index 97%
rename from cros/p2p_manager.h
rename to p2p_manager.h
index bef7806..ef62f0d 100644
--- a/cros/p2p_manager.h
+++ b/p2p_manager.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_P2P_MANAGER_H_
-#define UPDATE_ENGINE_CROS_P2P_MANAGER_H_
+#ifndef UPDATE_ENGINE_P2P_MANAGER_H_
+#define UPDATE_ENGINE_P2P_MANAGER_H_
 
 #include <string>
 #include <vector>
@@ -27,6 +27,7 @@
 #include <policy/device_policy.h>
 #include <policy/libpolicy.h>
 
+#include "update_engine/common/clock_interface.h"
 #include "update_engine/common/prefs_interface.h"
 #include "update_engine/update_manager/update_manager.h"
 
@@ -173,6 +174,7 @@
   // performing housekeeping (pass zero to allow files of any age).
   static P2PManager* Construct(
       Configuration* configuration,
+      ClockInterface* clock,
       chromeos_update_manager::UpdateManager* update_manager,
       const std::string& file_extension,
       const int num_files_to_keep,
@@ -181,4 +183,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_P2P_MANAGER_H_
+#endif  // UPDATE_ENGINE_P2P_MANAGER_H_
diff --git a/cros/p2p_manager_unittest.cc b/p2p_manager_unittest.cc
similarity index 94%
rename from cros/p2p_manager_unittest.cc
rename to p2p_manager_unittest.cc
index 17c4886..5771ec1 100644
--- a/cros/p2p_manager_unittest.cc
+++ b/p2p_manager_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/p2p_manager.h"
+#include "update_engine/p2p_manager.h"
 
 #include <dirent.h>
 #include <fcntl.h>
@@ -30,13 +30,8 @@
 #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,12 +41,12 @@
 #include <policy/libpolicy.h>
 #include <policy/mock_device_policy.h>
 
+#include "update_engine/common/fake_clock.h"
 #include "update_engine/common/prefs.h"
 #include "update_engine/common/subprocess.h"
 #include "update_engine/common/test_utils.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/cros/fake_p2p_manager_configuration.h"
-#include "update_engine/cros/fake_system_state.h"
+#include "update_engine/fake_p2p_manager_configuration.h"
 #include "update_engine/update_manager/fake_update_manager.h"
 #include "update_engine/update_manager/mock_policy.h"
 
@@ -72,13 +67,12 @@
 // done.
 class P2PManagerTest : public testing::Test {
  protected:
-  P2PManagerTest() = default;
-  ~P2PManagerTest() override = default;
+  P2PManagerTest() : fake_um_(&fake_clock_) {}
+  ~P2PManagerTest() override {}
 
   // Derived from testing::Test.
   void SetUp() override {
     loop_.SetAsCurrent();
-    FakeSystemState::CreateInstance();
     async_signal_handler_.Init();
     subprocess_.Init(&async_signal_handler_);
     test_conf_ = new FakeP2PManagerConfiguration();
@@ -86,30 +80,27 @@
     // Allocate and install a mock policy implementation in the fake Update
     // Manager.  Note that the FakeUpdateManager takes ownership of the policy
     // object.
-    mock_policy_ = new chromeos_update_manager::MockPolicy();
+    mock_policy_ = new chromeos_update_manager::MockPolicy(&fake_clock_);
     fake_um_.set_policy(mock_policy_);
 
     // Construct the P2P manager under test.
     manager_.reset(P2PManager::Construct(test_conf_,
+                                         &fake_clock_,
                                          &fake_um_,
                                          "cros_au",
                                          3,
                                          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_;
 
   // The P2PManager::Configuration instance used for testing.
   FakeP2PManagerConfiguration* test_conf_;
 
+  FakeClock fake_clock_;
   chromeos_update_manager::MockPolicy* mock_policy_ = nullptr;
   chromeos_update_manager::FakeUpdateManager fake_um_;
 
@@ -148,6 +139,7 @@
   // will be freed.
   test_conf_ = new FakeP2PManagerConfiguration();
   manager_.reset(P2PManager::Construct(test_conf_,
+                                       &fake_clock_,
                                        &fake_um_,
                                        "cros_au",
                                        3,
@@ -205,14 +197,14 @@
 
   // Set the clock just so files with a timestamp before |cutoff_time|
   // will be deleted at housekeeping.
-  FakeSystemState::Get()->fake_clock()->SetWallclockTime(cutoff_time +
-                                                         age_limit);
+  fake_clock_.SetWallclockTime(cutoff_time + age_limit);
 
   // Specifically pass 0 for |num_files_to_keep| to allow any number of files.
   // Note that we need to reallocate the test_conf_ member, whose currently
   // aliased object will be freed.
   test_conf_ = new FakeP2PManagerConfiguration();
   manager_.reset(P2PManager::Construct(test_conf_,
+                                       &fake_clock_,
                                        &fake_um_,
                                        "cros_au",
                                        0 /* num_files_to_keep */,
@@ -358,7 +350,7 @@
 
 // Check that sharing a *new* file works.
 TEST_F(P2PManagerTest, ShareFile) {
-  const int kP2PTestFileSize = 1000 * 8;  // 8 KB
+  const int kP2PTestFileSize = 1000 * 1000;  // 1 MB
 
   EXPECT_TRUE(manager_->FileShare("foo", kP2PTestFileSize));
   EXPECT_EQ(manager_->FileGetPath("foo"),
@@ -377,7 +369,7 @@
 
 // Check that making a shared file visible, does what is expected.
 TEST_F(P2PManagerTest, MakeFileVisible) {
-  const int kP2PTestFileSize = 1000 * 8;  // 8 KB
+  const int kP2PTestFileSize = 1000 * 1000;  // 1 MB
 
   // First, check that it's not visible.
   manager_->FileShare("foo", kP2PTestFileSize);
diff --git a/parcelable_update_engine_status.cc b/parcelable_update_engine_status.cc
new file mode 100644
index 0000000..8a2dbeb
--- /dev/null
+++ b/parcelable_update_engine_status.cc
@@ -0,0 +1,122 @@
+//
+// 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/parcelable_update_engine_status.h"
+#include "update_engine/update_status_utils.h"
+
+#include <binder/Parcel.h>
+
+using update_engine::UpdateEngineStatus;
+
+namespace android {
+namespace brillo {
+
+ParcelableUpdateEngineStatus::ParcelableUpdateEngineStatus(
+    const UpdateEngineStatus& status)
+    : last_checked_time_(status.last_checked_time),
+      current_operation_(
+          chromeos_update_engine::UpdateStatusToString(status.status)),
+      progress_(status.progress),
+      current_version_(String16{status.current_version.c_str()}),
+      current_system_version_(String16{status.current_system_version.c_str()}),
+      new_size_(status.new_size_bytes),
+      new_version_(String16{status.new_version.c_str()}),
+      new_system_version_(String16{status.new_system_version.c_str()}) {}
+
+status_t ParcelableUpdateEngineStatus::writeToParcel(Parcel* parcel) const {
+  status_t status;
+
+  status = parcel->writeInt64(last_checked_time_);
+  if (status != OK) {
+    return status;
+  }
+
+  status = parcel->writeString16(current_operation_);
+  if (status != OK) {
+    return status;
+  }
+
+  status = parcel->writeDouble(progress_);
+  if (status != OK) {
+    return status;
+  }
+
+  status = parcel->writeString16(current_version_);
+  if (status != OK) {
+    return status;
+  }
+
+  status = parcel->writeString16(current_system_version_);
+  if (status != OK) {
+    return status;
+  }
+
+  status = parcel->writeInt64(new_size_);
+  if (status != OK) {
+    return status;
+  }
+
+  status = parcel->writeString16(new_version_);
+  if (status != OK) {
+    return status;
+  }
+
+  return parcel->writeString16(new_system_version_);
+}
+
+status_t ParcelableUpdateEngineStatus::readFromParcel(const Parcel* parcel) {
+  status_t status;
+
+  status = parcel->readInt64(&last_checked_time_);
+  if (status != OK) {
+    return status;
+  }
+
+  status = parcel->readString16(&current_operation_);
+  if (status != OK) {
+    return status;
+  }
+
+  status = parcel->readDouble(&progress_);
+  if (status != OK) {
+    return status;
+  }
+
+  status = parcel->readString16(&current_version_);
+  if (status != OK) {
+    return status;
+  }
+
+  status = parcel->readString16(&current_system_version_);
+  if (status != OK) {
+    return status;
+  }
+
+  status = parcel->readInt64(&new_size_);
+  if (status != OK) {
+    return status;
+  }
+
+  status = parcel->readString16(&new_version_);
+  if (status != OK) {
+    return status;
+  }
+
+  return parcel->readString16(&new_system_version_);
+}
+
+}  // namespace brillo
+}  // namespace android
diff --git a/parcelable_update_engine_status.h b/parcelable_update_engine_status.h
new file mode 100644
index 0000000..3feac76
--- /dev/null
+++ b/parcelable_update_engine_status.h
@@ -0,0 +1,63 @@
+//
+// 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_PARCELABLE_UPDATE_ENGINE_STATUS_H_
+#define UPDATE_ENGINE_PARCELABLE_UPDATE_ENGINE_STATUS_H_
+
+#include <binder/Parcelable.h>
+#include <utils/String16.h>
+
+#include "update_engine/client_library/include/update_engine/update_status.h"
+
+namespace android {
+namespace brillo {
+
+// Parcelable object containing the current status of update engine, to be sent
+// over binder to clients from the server.
+class ParcelableUpdateEngineStatus : public Parcelable {
+ public:
+  ParcelableUpdateEngineStatus() = default;
+  explicit ParcelableUpdateEngineStatus(
+      const update_engine::UpdateEngineStatus& status);
+  virtual ~ParcelableUpdateEngineStatus() = default;
+
+  status_t writeToParcel(Parcel* parcel) const override;
+  status_t readFromParcel(const Parcel* parcel) override;
+
+  // This list is kept in the Parcelable serialization order.
+
+  // When the update_engine last checked for updates (seconds since unix Epoch)
+  int64_t last_checked_time_;
+  // The current status/operation of the update_engine.
+  android::String16 current_operation_;
+  // The current progress (0.0f-1.0f).
+  double progress_;
+  // The current product version.
+  android::String16 current_version_;
+  // The current system version.
+  android::String16 current_system_version_;
+  // The size of the update (bytes).  This is int64_t for java compatibility.
+  int64_t new_size_;
+  // The new product version.
+  android::String16 new_version_;
+  // The new system version, if there is one (empty, otherwise).
+  android::String16 new_system_version_;
+};
+
+}  // namespace brillo
+}  // namespace android
+
+#endif  // UPDATE_ENGINE_PARCELABLE_UPDATE_ENGINE_STATUS_H_
diff --git a/parcelable_update_engine_status_unittest.cc b/parcelable_update_engine_status_unittest.cc
new file mode 100644
index 0000000..20decb6
--- /dev/null
+++ b/parcelable_update_engine_status_unittest.cc
@@ -0,0 +1,92 @@
+//
+// 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/parcelable_update_engine_status.h"
+#include "update_engine/update_status_utils.h"
+
+#include <binder/Parcel.h>
+#include <gtest/gtest.h>
+
+using android::Parcel;
+using android::status_t;
+using android::String16;
+using android::brillo::ParcelableUpdateEngineStatus;
+using update_engine::UpdateEngineStatus;
+using update_engine::UpdateStatus;
+
+TEST(ParcelableUpdateEngineStatusTest, TestCreationFromUpdateEngineStatus) {
+  // This test creates an object and verifies that all the UpdateEngineStatus
+  // values are properly reflected in the Parcelable version of the class.
+
+  UpdateEngineStatus ue_status = {123456789,
+                                  UpdateStatus::DOWNLOADING,
+                                  "0.1.2.3",
+                                  "1.2.3.4",
+                                  0.5f,
+                                  34567,
+                                  "2.3.4.5",
+                                  "3.4.5.6"};
+  ParcelableUpdateEngineStatus parcelable_status(ue_status);
+  EXPECT_EQ(ue_status.last_checked_time, parcelable_status.last_checked_time_);
+  EXPECT_EQ(
+      String16{chromeos_update_engine::UpdateStatusToString(ue_status.status)},
+      parcelable_status.current_operation_);
+  EXPECT_EQ(String16{ue_status.current_version.c_str()},
+            parcelable_status.current_version_);
+  EXPECT_EQ(String16{ue_status.current_system_version.c_str()},
+            parcelable_status.current_system_version_);
+  EXPECT_EQ(ue_status.progress, parcelable_status.progress_);
+  EXPECT_EQ(static_cast<int64_t>(ue_status.new_size_bytes),
+            parcelable_status.new_size_);
+  EXPECT_EQ(String16{ue_status.new_version.c_str()},
+            parcelable_status.new_version_);
+  EXPECT_EQ(String16{ue_status.new_system_version.c_str()},
+            parcelable_status.new_system_version_);
+}
+
+TEST(ParcelableUpdateEngineStatusTest, TestParceling) {
+  // This tests the writeToParcel and readFromParcel methods for being correctly
+  // matched.
+  UpdateEngineStatus ue_status = {123456789,
+                                  UpdateStatus::DOWNLOADING,
+                                  "0.1.2.3",
+                                  "1.2.3.4",
+                                  0.5f,
+                                  34567,
+                                  "2.3.4.5",
+                                  "3.4.5.6"};
+  ParcelableUpdateEngineStatus source_status(ue_status);
+  Parcel parcel_source, parcel_target;
+  status_t status = source_status.writeToParcel(&parcel_source);
+  EXPECT_EQ(::android::OK, status);
+  size_t parcel_len = parcel_source.dataSize();
+  status = parcel_target.setData(parcel_source.data(), parcel_len);
+  EXPECT_EQ(::android::OK, status);
+  ParcelableUpdateEngineStatus target_status;
+  status = target_status.readFromParcel(&parcel_target);
+  EXPECT_EQ(::android::OK, status);
+
+  EXPECT_EQ(source_status.last_checked_time_, target_status.last_checked_time_);
+  EXPECT_EQ(source_status.current_operation_, target_status.current_operation_);
+  EXPECT_EQ(source_status.current_version_, target_status.current_version_);
+  EXPECT_EQ(source_status.current_system_version_,
+            target_status.current_system_version_);
+  EXPECT_EQ(source_status.progress_, target_status.progress_);
+  EXPECT_EQ(source_status.new_size_, target_status.new_size_);
+  EXPECT_EQ(source_status.new_version_, target_status.new_version_);
+  EXPECT_EQ(source_status.new_system_version_,
+            target_status.new_system_version_);
+}
diff --git a/payload_consumer/bzip_extent_writer.cc b/payload_consumer/bzip_extent_writer.cc
index 26fdc5f..0c25c71 100644
--- a/payload_consumer/bzip_extent_writer.cc
+++ b/payload_consumer/bzip_extent_writer.cc
@@ -29,7 +29,8 @@
   TEST_AND_RETURN(input_buffer_.empty());
 }
 
-bool BzipExtentWriter::Init(const RepeatedPtrField<Extent>& extents,
+bool BzipExtentWriter::Init(FileDescriptorPtr fd,
+                            const RepeatedPtrField<Extent>& extents,
                             uint32_t block_size) {
   // Init bzip2 stream
   int rc = BZ2_bzDecompressInit(&stream_,
@@ -38,7 +39,7 @@
 
   TEST_AND_RETURN_FALSE(rc == BZ_OK);
 
-  return next_->Init(extents, block_size);
+  return next_->Init(fd, extents, block_size);
 }
 
 bool BzipExtentWriter::Write(const void* bytes, size_t count) {
diff --git a/payload_consumer/bzip_extent_writer.h b/payload_consumer/bzip_extent_writer.h
index 38c041a..ec181a7 100644
--- a/payload_consumer/bzip_extent_writer.h
+++ b/payload_consumer/bzip_extent_writer.h
@@ -40,7 +40,8 @@
   }
   ~BzipExtentWriter() override;
 
-  bool Init(const google::protobuf::RepeatedPtrField<Extent>& extents,
+  bool Init(FileDescriptorPtr fd,
+            const google::protobuf::RepeatedPtrField<Extent>& extents,
             uint32_t block_size) override;
   bool Write(const void* bytes, size_t count) override;
 
diff --git a/payload_consumer/bzip_extent_writer_unittest.cc b/payload_consumer/bzip_extent_writer_unittest.cc
index c93545a..125e1e5 100644
--- a/payload_consumer/bzip_extent_writer_unittest.cc
+++ b/payload_consumer/bzip_extent_writer_unittest.cc
@@ -29,6 +29,7 @@
 #include "update_engine/common/utils.h"
 #include "update_engine/payload_generator/extent_ranges.h"
 
+using google::protobuf::RepeatedPtrField;
 using std::min;
 using std::string;
 using std::vector;
@@ -48,7 +49,7 @@
   void TearDown() override { fd_->Close(); }
 
   FileDescriptorPtr fd_;
-  ScopedTempFile temp_file_{"BzipExtentWriterTest-file.XXXXXX"};
+  test_utils::ScopedTempFile temp_file_{"BzipExtentWriterTest-file.XXXXXX"};
 };
 
 TEST_F(BzipExtentWriterTest, SimpleTest) {
@@ -63,8 +64,9 @@
       0x22, 0x9c, 0x28, 0x48, 0x66, 0x61, 0xb8, 0xea, 0x00,
   };
 
-  BzipExtentWriter bzip_writer(std::make_unique<DirectExtentWriter>(fd_));
-  EXPECT_TRUE(bzip_writer.Init({extents.begin(), extents.end()}, kBlockSize));
+  BzipExtentWriter bzip_writer(std::make_unique<DirectExtentWriter>());
+  EXPECT_TRUE(
+      bzip_writer.Init(fd_, {extents.begin(), extents.end()}, kBlockSize));
   EXPECT_TRUE(bzip_writer.Write(test, sizeof(test)));
 
   brillo::Blob buf;
@@ -95,8 +97,9 @@
 
   vector<Extent> extents = {ExtentForBytes(kBlockSize, 0, kDecompressedLength)};
 
-  BzipExtentWriter bzip_writer(std::make_unique<DirectExtentWriter>(fd_));
-  EXPECT_TRUE(bzip_writer.Init({extents.begin(), extents.end()}, kBlockSize));
+  BzipExtentWriter bzip_writer(std::make_unique<DirectExtentWriter>());
+  EXPECT_TRUE(
+      bzip_writer.Init(fd_, {extents.begin(), extents.end()}, kBlockSize));
 
   brillo::Blob original_compressed_data = compressed_data;
   for (brillo::Blob::size_type i = 0; i < compressed_data.size();
diff --git a/payload_consumer/cached_file_descriptor_unittest.cc b/payload_consumer/cached_file_descriptor_unittest.cc
index b64420a..d2965fc 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};
-  ScopedTempFile temp_file_{"CachedFileDescriptor-file.XXXXXX"};
+  test_utils::ScopedTempFile temp_file_{"CachedFileDescriptor-file.XXXXXX"};
   int value_{1};
   FileDescriptorPtr cfd_;
 };
diff --git a/payload_consumer/certificate_parser_stub.cc b/payload_consumer/certificate_parser_stub.cc
index a365ab8..95fd6e8 100644
--- a/payload_consumer/certificate_parser_stub.cc
+++ b/payload_consumer/certificate_parser_stub.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/payload_consumer/certificate_parser_stub.h"
+#include <payload_consumer/certificate_parser_stub.h>
 
 namespace chromeos_update_engine {
 bool CertificateParserStub::ReadPublicKeysFromCertificates(
diff --git a/payload_consumer/certificate_parser_stub.h b/payload_consumer/certificate_parser_stub.h
index a51c2c6..f4f8825 100644
--- a/payload_consumer/certificate_parser_stub.h
+++ b/payload_consumer/certificate_parser_stub.h
@@ -23,7 +23,7 @@
 
 #include <base/macros.h>
 
-#include "update_engine/payload_consumer/certificate_parser_interface.h"
+#include "payload_consumer/certificate_parser_interface.h"
 
 namespace chromeos_update_engine {
 class CertificateParserStub : public CertificateParserInterface {
diff --git a/payload_consumer/cow_writer_file_descriptor.cc b/payload_consumer/cow_writer_file_descriptor.cc
deleted file mode 100644
index 2de6664..0000000
--- a/payload_consumer/cow_writer_file_descriptor.cc
+++ /dev/null
@@ -1,151 +0,0 @@
-//
-// Copyright (C) 2021 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/cow_writer_file_descriptor.h"
-
-#include <memory>
-#include <utility>
-
-#include <base/logging.h>
-
-#include "update_engine/common/utils.h"
-#include "update_engine/payload_consumer/file_descriptor.h"
-
-namespace chromeos_update_engine {
-CowWriterFileDescriptor::CowWriterFileDescriptor(
-    std::unique_ptr<android::snapshot::ISnapshotWriter> cow_writer)
-    : cow_writer_(std::move(cow_writer)),
-      cow_reader_(cow_writer_->OpenReader()) {
-  CHECK_NE(cow_writer_, nullptr);
-  CHECK_NE(cow_reader_, nullptr);
-}
-
-bool CowWriterFileDescriptor::Open(const char* path, int flags, mode_t mode) {
-  LOG(ERROR) << "CowWriterFileDescriptor doesn't support Open()";
-  return false;
-}
-bool CowWriterFileDescriptor::Open(const char* path, int flags) {
-  LOG(ERROR) << "CowWriterFileDescriptor doesn't support Open()";
-  return false;
-}
-
-ssize_t CowWriterFileDescriptor::Read(void* buf, size_t count) {
-  if (dirty_) {
-    // OK, CowReader provides a snapshot view of what the cow contains. Which
-    // means any writes happened after opening a CowReader isn't visible to
-    // that CowReader. Therefore, we re-open CowReader whenever we attempt a
-    // read after write. This does incur an overhead everytime you read after
-    // write.
-    // The usage of |dirty_| flag to coordinate re-open is a very coarse grained
-    // checked. This implementation has suboptimal performance. For better
-    // performance, keep track of blocks which are overwritten, and only re-open
-    // if reading a dirty block.
-    // TODO(b/173432386) Implement finer grained dirty checks
-    const auto offset = cow_reader_->Seek(0, SEEK_CUR);
-    cow_reader_.reset();
-    if (!cow_writer_->Finalize()) {
-      LOG(ERROR) << "Failed to Finalize() cow writer";
-      return -1;
-    }
-    cow_reader_ = cow_writer_->OpenReader();
-    if (cow_reader_ == nullptr) {
-      LOG(ERROR) << "Failed to re-open cow reader after writing to COW";
-      return -1;
-    }
-    const auto pos = cow_reader_->Seek(offset, SEEK_SET);
-    if (pos != offset) {
-      LOG(ERROR) << "Failed to seek to previous position after re-opening cow "
-                    "reader, expected "
-                 << offset << " actual: " << pos;
-      return -1;
-    }
-    dirty_ = false;
-  }
-  return cow_reader_->Read(buf, count);
-}
-
-ssize_t CowWriterFileDescriptor::Write(const void* buf, size_t count) {
-  auto offset = cow_reader_->Seek(0, SEEK_CUR);
-  CHECK_EQ(offset % cow_writer_->options().block_size, 0);
-  auto success = cow_writer_->AddRawBlocks(
-      offset / cow_writer_->options().block_size, buf, count);
-  if (success) {
-    if (cow_reader_->Seek(count, SEEK_CUR) < 0) {
-      return -1;
-    }
-    dirty_ = true;
-    return count;
-  }
-  return -1;
-}
-
-off64_t CowWriterFileDescriptor::Seek(const off64_t offset, int whence) {
-  return cow_reader_->Seek(offset, whence);
-}
-
-uint64_t CowWriterFileDescriptor::BlockDevSize() {
-  LOG(ERROR) << "CowWriterFileDescriptor doesn't support BlockDevSize()";
-  return 0;
-}
-
-bool CowWriterFileDescriptor::BlkIoctl(int request,
-                                       uint64_t start,
-                                       uint64_t length,
-                                       int* result) {
-  LOG(ERROR) << "CowWriterFileDescriptor doesn't support BlkIoctl()";
-  return false;
-}
-
-bool CowWriterFileDescriptor::Flush() {
-  // CowWriter already automatilly flushes, no need to do anything.
-  return true;
-}
-
-bool CowWriterFileDescriptor::Close() {
-  if (cow_writer_) {
-    // b/186196758
-    // When calling
-    // InitializeAppend(kEndOfInstall), the SnapshotWriter only reads up to the
-    // given label. But OpenReader() completely disregards the resume label and
-    // reads all ops. Therefore, update_engine sees the verity data. However,
-    // when calling SnapshotWriter::Finalize(), data after resume label are
-    // discarded, therefore verity data is gone. To prevent phantom reads, don't
-    // call Finalize() unless we actually write something.
-    if (dirty_) {
-      TEST_AND_RETURN_FALSE(cow_writer_->Finalize());
-    }
-    cow_writer_ = nullptr;
-  }
-  if (cow_reader_) {
-    TEST_AND_RETURN_FALSE(cow_reader_->Close());
-    cow_reader_ = nullptr;
-  }
-  return true;
-}
-
-bool CowWriterFileDescriptor::IsSettingErrno() {
-  return false;
-}
-
-bool CowWriterFileDescriptor::IsOpen() {
-  return cow_writer_ != nullptr && cow_reader_ != nullptr;
-}
-
-CowWriterFileDescriptor::~CowWriterFileDescriptor() {
-  Close();
-}
-
-}  // namespace chromeos_update_engine
diff --git a/payload_consumer/cow_writer_file_descriptor.h b/payload_consumer/cow_writer_file_descriptor.h
deleted file mode 100644
index 5d9ffc6..0000000
--- a/payload_consumer/cow_writer_file_descriptor.h
+++ /dev/null
@@ -1,66 +0,0 @@
-//
-// Copyright (C) 2021 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 <memory>
-
-#include <libsnapshot/snapshot_writer.h>
-
-#include "update_engine/payload_consumer/file_descriptor.h"
-
-namespace chromeos_update_engine {
-
-// A Readable/Writable FileDescriptor class. This is a simple wrapper around
-// CowWriter. Only intended to be used by FileSystemVerifierAction for writing
-// FEC. Writes must be block aligned(4096) or write will fail.
-class CowWriterFileDescriptor final : public FileDescriptor {
- public:
-  explicit CowWriterFileDescriptor(
-      std::unique_ptr<android::snapshot::ISnapshotWriter> cow_writer);
-  ~CowWriterFileDescriptor();
-
-  bool Open(const char* path, int flags, mode_t mode) override;
-  bool Open(const char* path, int flags) override;
-
-  ssize_t Read(void* buf, size_t count) override;
-
-  // |count| must be block aligned, current offset of this fd must also be block
-  // aligned.
-  ssize_t Write(const void* buf, size_t count) override;
-
-  off64_t Seek(off64_t offset, int whence) override;
-
-  uint64_t BlockDevSize() override;
-
-  bool BlkIoctl(int request,
-                uint64_t start,
-                uint64_t length,
-                int* result) override;
-
-  bool Flush() override;
-
-  bool Close() override;
-
-  bool IsSettingErrno() override;
-
-  bool IsOpen() override;
-
- private:
-  std::unique_ptr<android::snapshot::ISnapshotWriter> cow_writer_;
-  FileDescriptorPtr cow_reader_;
-  bool dirty_ = false;
-};
-}  // namespace chromeos_update_engine
diff --git a/payload_consumer/cow_writer_file_descriptor_unittest.cc b/payload_consumer/cow_writer_file_descriptor_unittest.cc
deleted file mode 100644
index c596e3b..0000000
--- a/payload_consumer/cow_writer_file_descriptor_unittest.cc
+++ /dev/null
@@ -1,120 +0,0 @@
-//
-// Copyright (C) 2021 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/cow_writer_file_descriptor.h"
-
-#include <cstring>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include <android-base/unique_fd.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <libsnapshot/snapshot_writer.h>
-
-#include "update_engine/common/utils.h"
-
-namespace chromeos_update_engine {
-constexpr size_t BLOCK_SIZE = 4096;
-constexpr size_t PARTITION_SIZE = BLOCK_SIZE * 10;
-
-using android::base::unique_fd;
-using android::snapshot::CompressedSnapshotWriter;
-using android::snapshot::CowOptions;
-using android::snapshot::ISnapshotWriter;
-
-class CowWriterFileDescriptorUnittest : public ::testing::Test {
- public:
-  void SetUp() override {
-    ASSERT_EQ(ftruncate64(cow_device_file_.fd(), PARTITION_SIZE), 0)
-        << "Failed to truncate cow_device file to " << PARTITION_SIZE
-        << strerror(errno);
-    ASSERT_EQ(ftruncate64(cow_source_file_.fd(), PARTITION_SIZE), 0)
-        << "Failed to truncate cow_source file to " << PARTITION_SIZE
-        << strerror(errno);
-  }
-
-  std::unique_ptr<CompressedSnapshotWriter> GetCowWriter() {
-    const CowOptions options{.block_size = BLOCK_SIZE, .compression = "gz"};
-    auto snapshot_writer = std::make_unique<CompressedSnapshotWriter>(options);
-    int fd = open(cow_device_file_.path().c_str(), O_RDWR);
-    EXPECT_NE(fd, -1);
-    EXPECT_TRUE(snapshot_writer->SetCowDevice(unique_fd{fd}));
-    snapshot_writer->SetSourceDevice(cow_source_file_.path());
-    return snapshot_writer;
-  }
-  CowWriterFileDescriptor GetCowFd() {
-    auto cow_writer = GetCowWriter();
-    return CowWriterFileDescriptor{std::move(cow_writer)};
-  }
-
-  ScopedTempFile cow_source_file_{"cow_source.XXXXXX", true};
-  ScopedTempFile cow_device_file_{"cow_device.XXXXXX", true};
-};
-
-TEST_F(CowWriterFileDescriptorUnittest, ReadAfterWrite) {
-  std::vector<unsigned char> buffer;
-  buffer.resize(BLOCK_SIZE);
-  std::fill(buffer.begin(), buffer.end(), 234);
-
-  std::vector<unsigned char> verity_data;
-  verity_data.resize(BLOCK_SIZE);
-  std::fill(verity_data.begin(), verity_data.end(), 0xAA);
-
-  auto cow_writer = GetCowWriter();
-  cow_writer->Initialize();
-
-  // Simulate Writing InstallOp data
-  ASSERT_TRUE(cow_writer->AddRawBlocks(0, buffer.data(), buffer.size()));
-  ASSERT_TRUE(cow_writer->AddZeroBlocks(1, 2));
-  ASSERT_TRUE(cow_writer->AddCopy(3, 1));
-  // Fake label to simulate "end of install"
-  ASSERT_TRUE(cow_writer->AddLabel(23));
-  ASSERT_TRUE(
-      cow_writer->AddRawBlocks(4, verity_data.data(), verity_data.size()));
-  ASSERT_TRUE(cow_writer->Finalize());
-
-  cow_writer = GetCowWriter();
-  ASSERT_NE(nullptr, cow_writer);
-  ASSERT_TRUE(cow_writer->InitializeAppend(23));
-  auto cow_fd =
-      std::make_unique<CowWriterFileDescriptor>(std::move(cow_writer));
-
-  ASSERT_EQ((ssize_t)BLOCK_SIZE * 4, cow_fd->Seek(BLOCK_SIZE * 4, SEEK_SET));
-  std::vector<unsigned char> read_back(4096);
-  ASSERT_EQ((ssize_t)read_back.size(),
-            cow_fd->Read(read_back.data(), read_back.size()));
-  ASSERT_EQ(verity_data, read_back);
-
-  // Since we didn't write anything to this instance of cow_fd, destructor
-  // should not call Finalize(). As finalize will drop ops after resume label,
-  // causing subsequent reads to fail.
-  cow_writer = GetCowWriter();
-  ASSERT_NE(nullptr, cow_writer);
-  ASSERT_TRUE(cow_writer->InitializeAppend(23));
-  cow_fd = std::make_unique<CowWriterFileDescriptor>(std::move(cow_writer));
-
-  ASSERT_EQ((ssize_t)BLOCK_SIZE * 4, cow_fd->Seek(BLOCK_SIZE * 4, SEEK_SET));
-  ASSERT_EQ((ssize_t)read_back.size(),
-            cow_fd->Read(read_back.data(), read_back.size()));
-  ASSERT_EQ(verity_data, read_back)
-      << "Could not read verity data afeter InitializeAppend() => Read() => "
-         "InitializeAppend() sequence. If no writes happened while CowWriterFd "
-         "is open, Finalize() should not be called.";
-}
-
-}  // namespace chromeos_update_engine
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index a57169b..4c4ff04 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -23,7 +23,6 @@
 #include <cstring>
 #include <map>
 #include <memory>
-#include <set>
 #include <string>
 #include <utility>
 #include <vector>
@@ -41,26 +40,24 @@
 #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"
-#include "update_engine/payload_consumer/partition_writer.h"
 #if USE_FEC
 #include "update_engine/payload_consumer/fec_file_descriptor.h"
 #endif  // USE_FEC
 #include "update_engine/payload_consumer/file_descriptor_utils.h"
 #include "update_engine/payload_consumer/mount_history.h"
+#if USE_MTD
+#include "update_engine/payload_consumer/mtd_file_descriptor.h"
+#endif  // USE_MTD
 #include "update_engine/payload_consumer/payload_constants.h"
 #include "update_engine/payload_consumer/payload_verifier.h"
 #include "update_engine/payload_consumer/xz_extent_writer.h"
@@ -80,6 +77,103 @@
 namespace {
 const int kUpdateStateOperationInvalid = -1;
 const int kMaxResumedUpdateFailures = 10;
+#if USE_MTD
+const int kUbiVolumeAttachTimeout = 5 * 60;
+#endif
+
+const uint64_t kCacheSize = 1024 * 1024;  // 1MB
+
+FileDescriptorPtr CreateFileDescriptor(const char* path) {
+  FileDescriptorPtr ret;
+#if USE_MTD
+  if (strstr(path, "/dev/ubi") == path) {
+    if (!UbiFileDescriptor::IsUbi(path)) {
+      // The volume might not have been attached at boot time.
+      int volume_no;
+      if (utils::SplitPartitionName(path, nullptr, &volume_no)) {
+        utils::TryAttachingUbiVolume(volume_no, kUbiVolumeAttachTimeout);
+      }
+    }
+    if (UbiFileDescriptor::IsUbi(path)) {
+      LOG(INFO) << path << " is a UBI device.";
+      ret.reset(new UbiFileDescriptor);
+    }
+  } else if (MtdFileDescriptor::IsMtd(path)) {
+    LOG(INFO) << path << " is an MTD device.";
+    ret.reset(new MtdFileDescriptor);
+  } else {
+    LOG(INFO) << path << " is not an MTD nor a UBI device.";
+#endif
+    ret.reset(new EintrSafeFileDescriptor);
+#if USE_MTD
+  }
+#endif
+  return ret;
+}
+
+// Opens path for read/write. On success returns an open FileDescriptor
+// and sets *err to 0. On failure, sets *err to errno and returns nullptr.
+FileDescriptorPtr OpenFile(const char* path,
+                           int mode,
+                           bool cache_writes,
+                           int* err) {
+  // Try to mark the block device read-only based on the mode. Ignore any
+  // failure since this won't work when passing regular files.
+  bool read_only = (mode & O_ACCMODE) == O_RDONLY;
+  utils::SetBlockDeviceReadOnly(path, read_only);
+
+  FileDescriptorPtr fd = CreateFileDescriptor(path);
+  if (cache_writes && !read_only) {
+    fd = FileDescriptorPtr(new CachedFileDescriptor(fd, kCacheSize));
+    LOG(INFO) << "Caching writes.";
+  }
+#if USE_MTD
+  // On NAND devices, we can either read, or write, but not both. So here we
+  // use O_WRONLY.
+  if (UbiFileDescriptor::IsUbi(path) || MtdFileDescriptor::IsMtd(path)) {
+    mode = O_WRONLY;
+  }
+#endif
+  if (!fd->Open(path, mode, 000)) {
+    *err = errno;
+    PLOG(ERROR) << "Unable to open file " << path;
+    return nullptr;
+  }
+  *err = 0;
+  return fd;
+}
+
+// Discard the tail of the block device referenced by |fd|, from the offset
+// |data_size| until the end of the block device. Returns whether the data was
+// discarded.
+bool DiscardPartitionTail(const FileDescriptorPtr& fd, uint64_t data_size) {
+  uint64_t part_size = fd->BlockDevSize();
+  if (!part_size || part_size <= data_size)
+    return false;
+
+  struct blkioctl_request {
+    int number;
+    const char* name;
+  };
+  const vector<blkioctl_request> blkioctl_requests = {
+      {BLKDISCARD, "BLKDISCARD"},
+      {BLKSECDISCARD, "BLKSECDISCARD"},
+#ifdef BLKZEROOUT
+      {BLKZEROOUT, "BLKZEROOUT"},
+#endif
+  };
+  for (const auto& req : blkioctl_requests) {
+    int error = 0;
+    if (fd->BlkIoctl(req.number, data_size, part_size - data_size, &error) &&
+        error == 0) {
+      return true;
+    }
+    LOG(WARNING) << "Error discarding the last "
+                 << (part_size - data_size) / 1024 << " KiB using ioctl("
+                 << req.name << ")";
+  }
+  return false;
+}
 
 }  // namespace
 
@@ -198,9 +292,12 @@
   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 "
-             << GetPartitionOperationNum() << " in partition \""
+             << next_operation_num_ - partition_first_op_num
+             << " in partition \""
              << partitions_[current_partition_].partition_name() << "\"";
   if (*error == ErrorCode::kSuccess)
     *error = ErrorCode::kDownloadOperationExecutionError;
@@ -222,12 +319,33 @@
 }
 
 int DeltaPerformer::CloseCurrentPartition() {
-  if (!partition_writer_) {
-    return 0;
+  int err = 0;
+  if (source_fd_ && !source_fd_->Close()) {
+    err = errno;
+    PLOG(ERROR) << "Error closing source partition";
+    if (!err)
+      err = 1;
   }
-  int err = partition_writer_->Close();
-  partition_writer_ = nullptr;
-  return err;
+  source_fd_.reset();
+  if (source_ecc_fd_ && !source_ecc_fd_->Close()) {
+    err = errno;
+    PLOG(ERROR) << "Error closing ECC source partition";
+    if (!err)
+      err = 1;
+  }
+  source_ecc_fd_.reset();
+  source_ecc_open_failure_ = false;
+  source_path_.clear();
+
+  if (target_fd_ && !target_fd_->Close()) {
+    err = errno;
+    PLOG(ERROR) << "Error closing target partition";
+    if (!err)
+      err = 1;
+  }
+  target_fd_.reset();
+  target_path_.clear();
+  return -err;
 }
 
 bool DeltaPerformer::OpenCurrentPartition() {
@@ -239,29 +357,91 @@
       install_plan_->partitions.size() - partitions_.size();
   const InstallPlan::Partition& install_part =
       install_plan_->partitions[num_previous_partitions + current_partition_];
-  auto dynamic_control = boot_control_->GetDynamicPartitionControl();
-  partition_writer_ = CreatePartitionWriter(
-      partition,
-      install_part,
-      dynamic_control,
-      block_size_,
-      interactive_,
-      IsDynamicPartition(install_part.name, install_plan_->target_slot));
-  // 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;
-  const size_t partition_operation_num = GetPartitionOperationNum();
+  // Open source fds if we have a delta payload with minor version >= 2.
+  if (payload_->type == InstallPayloadType::kDelta &&
+      GetMinorVersion() != kInPlaceMinorPayloadVersion &&
+      // With dynamic partitions we could create a new partition in a
+      // delta payload, and we shouldn't open source partition in that case.
+      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(install_plan_->source_slot)
+                 << ", file " << source_path_;
+      return false;
+    }
+  }
 
-  TEST_AND_RETURN_FALSE(partition_writer_->Init(
-      install_plan_, source_may_exist, partition_operation_num));
-  CheckpointUpdateProgress(true);
+  target_path_ = install_part.target_path;
+  int err;
+
+  int flags = O_RDWR;
+  if (!interactive_)
+    flags |= O_DSYNC;
+
+  LOG(INFO) << "Opening " << target_path_ << " partition with"
+            << (interactive_ ? "out" : "") << " O_DSYNC";
+
+  target_fd_ = OpenFile(target_path_.c_str(), flags, true, &err);
+  if (!target_fd_) {
+    LOG(ERROR) << "Unable to open target partition "
+               << partition.partition_name() << " on slot "
+               << BootControlInterface::SlotName(install_plan_->target_slot)
+               << ", file " << target_path_;
+    return false;
+  }
+
+  LOG(INFO) << "Applying " << partition.operations().size()
+            << " operations to partition \"" << partition.partition_name()
+            << "\"";
+
+  // Discard the end of the partition, but ignore failures.
+  DiscardPartitionTail(target_fd_, install_part.target_size);
+
   return true;
 }
 
-size_t DeltaPerformer::GetPartitionOperationNum() {
-  return next_operation_num_ -
-         (current_partition_ ? acc_num_operations_[current_partition_ - 1] : 0);
+bool DeltaPerformer::OpenCurrentECCPartition() {
+  if (source_ecc_fd_)
+    return true;
+
+  if (source_ecc_open_failure_)
+    return false;
+
+  if (current_partition_ >= partitions_.size())
+    return false;
+
+  // No support for ECC in minor version 1 or full payloads.
+  if (payload_->type == InstallPayloadType::kFull ||
+      GetMinorVersion() == kInPlaceMinorPayloadVersion)
+    return false;
+
+#if USE_FEC
+  const PartitionUpdate& partition = partitions_[current_partition_];
+  size_t num_previous_partitions =
+      install_plan_->partitions.size() - partitions_.size();
+  const InstallPlan::Partition& install_part =
+      install_plan_->partitions[num_previous_partitions + current_partition_];
+  string path = install_part.source_path;
+  FileDescriptorPtr fd(new FecFileDescriptor());
+  if (!fd->Open(path.c_str(), O_RDONLY, 0)) {
+    PLOG(ERROR) << "Unable to open ECC source partition "
+                << partition.partition_name() << " on slot "
+                << BootControlInterface::SlotName(install_plan_->source_slot)
+                << ", file " << path;
+    source_ecc_open_failure_ = true;
+    return false;
+  }
+  source_ecc_fd_ = fd;
+#else
+  // No support for ECC compiled.
+  source_ecc_open_failure_ = true;
+#endif  // USE_FEC
+
+  return !source_ecc_open_failure_;
 }
 
 namespace {
@@ -285,6 +465,15 @@
 
 }  // 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;
 }
@@ -316,21 +505,6 @@
         return MetadataParseResult::kError;
       }
     }
-
-    // Check that the |metadata signature size_| and |metadata_size_| are not
-    // very big numbers. This is necessary since |update_engine| needs to write
-    // these values into the buffer before being able to use them, and if an
-    // attacker sets these values to a very big number, the buffer will overflow
-    // and |update_engine| will crash. A simple way of solving this is to check
-    // that the size of both values is smaller than the payload itself.
-    if (metadata_size_ + metadata_signature_size_ > payload_->size) {
-      LOG(ERROR) << "The size of the metadata_size(" << metadata_size_ << ")"
-                 << " or metadata signature(" << metadata_signature_size_ << ")"
-                 << " is greater than the size of the payload"
-                 << "(" << payload_->size << ")";
-      *error = ErrorCode::kDownloadInvalidMetadataSize;
-      return MetadataParseResult::kError;
-    }
   }
 
   // Now that we have validated the metadata size, we should wait for the full
@@ -353,7 +527,6 @@
                  << "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.";
@@ -394,7 +567,7 @@
 #define OP_DURATION_HISTOGRAM(_op_name, _start_time)                         \
   LOCAL_HISTOGRAM_CUSTOM_TIMES(                                              \
       "UpdateEngine.DownloadAction.InstallOperation::" _op_name ".Duration", \
-      (base::TimeTicks::Now() - _start_time),                                \
+      base::TimeTicks::Now() - _start_time,                                  \
       base::TimeDelta::FromMilliseconds(10),                                 \
       base::TimeDelta::FromMinutes(5),                                       \
       20);
@@ -435,10 +608,6 @@
     if ((*error = ValidateManifest()) != ErrorCode::kSuccess)
       return false;
     manifest_valid_ = true;
-    if (!install_plan_->is_resume) {
-      auto begin = reinterpret_cast<const char*>(buffer_.data());
-      prefs_->SetString(kPrefsManifestBytes, {begin, buffer_.size()});
-    }
 
     // Clear the download buffer.
     DiscardBuffer(false, metadata_size_);
@@ -498,9 +667,6 @@
     // 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_]) {
@@ -511,9 +677,12 @@
         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(GetPartitionOperationNum());
+        partitions_[current_partition_].operations(partition_operation_num);
 
     CopyDataToBuffer(&c_bytes, &count, op.data_length());
 
@@ -521,24 +690,27 @@
     if (!CanPerformInstallOperation(op))
       return true;
 
-    // Validate the operation unconditionally. This helps prevent the
-    // exploitation of vulnerabilities in the patching libraries, e.g. bspatch.
-    // The hash of the patch data for a given operation is embedded in the
-    // payload metadata; and thus has been verified against the public key on
-    // device.
-    // Note: Validate must be called only if CanPerformInstallOperation is
-    // called. Otherwise, we might be failing operations before even if there
-    // isn't sufficient data to compute the proper hash.
-    *error = ValidateOperationHash(op);
-    if (*error != ErrorCode::kSuccess) {
-      if (install_plan_->hash_checks_mandatory) {
-        LOG(ERROR) << "Mandatory operation hash check failed";
-        return false;
-      }
+    // Validate the operation only if the metadata signature is present.
+    // Otherwise, keep the old behavior. This serves as a knob to disable
+    // the validation logic in case we find some regression after rollout.
+    // NOTE: If hash checks are mandatory and if metadata_signature is empty,
+    // we would have already failed in ParsePayloadMetadata method and thus not
+    // even be here. So no need to handle that case again here.
+    if (!payload_->metadata_signature.empty()) {
+      // Note: Validate must be called only if CanPerformInstallOperation is
+      // called. Otherwise, we might be failing operations before even if there
+      // isn't sufficient data to compute the proper hash.
+      *error = ValidateOperationHash(op);
+      if (*error != ErrorCode::kSuccess) {
+        if (install_plan_->hash_checks_mandatory) {
+          LOG(ERROR) << "Mandatory operation hash check failed";
+          return false;
+        }
 
-      // For non-mandatory cases, just send a UMA stat.
-      LOG(WARNING) << "Ignoring operation validation errors";
-      *error = ErrorCode::kSuccess;
+        // For non-mandatory cases, just send a UMA stat.
+        LOG(WARNING) << "Ignoring operation validation errors";
+        *error = ErrorCode::kSuccess;
+      }
     }
 
     // Makes sure we unblock exit when this operation completes.
@@ -560,6 +732,14 @@
         op_result = PerformZeroOrDiscardOperation(op);
         OP_DURATION_HISTOGRAM("ZERO_OR_DISCARD", op_start_time);
         break;
+      case InstallOperation::MOVE:
+        op_result = PerformMoveOperation(op);
+        OP_DURATION_HISTOGRAM("MOVE", op_start_time);
+        break;
+      case InstallOperation::BSDIFF:
+        op_result = PerformBsdiffOperation(op);
+        OP_DURATION_HISTOGRAM("BSDIFF", op_start_time);
+        break;
       case InstallOperation::SOURCE_COPY:
         op_result = PerformSourceCopyOperation(op, error);
         OP_DURATION_HISTOGRAM("SOURCE_COPY", op_start_time);
@@ -579,19 +759,19 @@
     if (!HandleOpResult(op_result, InstallOperationTypeName(op.type()), error))
       return false;
 
+    if (!target_fd_->Flush()) {
+      return false;
+    }
+
     next_operation_num_++;
     UpdateOverallProgress(false, "Completed ");
     CheckpointUpdateProgress(false);
   }
 
-  if (partition_writer_) {
-    TEST_AND_RETURN_FALSE(partition_writer_->FinishedInstallOps());
-  }
-  CloseCurrentPartition();
-
-  // In major version 2, we don't add unused operation to the payload.
+  // In major version 2, we don't add dummy operation to the payload.
   // If we already extracted the signature we should skip this step.
-  if (manifest_.has_signatures_offset() && manifest_.has_signatures_size() &&
+  if (major_payload_version_ == kBrilloMajorPayloadVersion &&
+      manifest_.has_signatures_offset() && manifest_.has_signatures_size() &&
       signatures_message_data_.empty()) {
     if (manifest_.signatures_offset() != buffer_offset_) {
       LOG(ERROR) << "Payload signatures offset points to blob offset "
@@ -626,65 +806,49 @@
 }
 
 bool DeltaPerformer::ParseManifestPartitions(ErrorCode* error) {
-  partitions_.clear();
-  for (const PartitionUpdate& partition : manifest_.partitions()) {
-    partitions_.push_back(partition);
-  }
-
-  // For VAB and partial updates, the partition preparation will copy the
-  // dynamic partitions metadata to the target metadata slot, and rename the
-  // slot suffix of the partitions in the metadata.
-  if (install_plan_->target_slot != BootControlInterface::kInvalidSlot) {
-    uint64_t required_size = 0;
-    if (!PreparePartitionsForUpdate(&required_size)) {
-      if (required_size > 0) {
-        *error = ErrorCode::kNotEnoughSpace;
-      } else {
-        *error = ErrorCode::kInstallDeviceOpenError;
-      }
-      return false;
+  if (major_payload_version_ == kBrilloMajorPayloadVersion) {
+    partitions_.clear();
+    for (const PartitionUpdate& partition : manifest_.partitions()) {
+      partitions_.push_back(partition);
     }
-  }
-
-  // Partitions in manifest are no longer needed after preparing partitions.
-  manifest_.clear_partitions();
-  // TODO(xunchang) TBD: allow partial update only on devices with dynamic
-  // partition.
-  if (manifest_.partial_update()) {
-    std::set<std::string> touched_partitions;
-    for (const auto& partition_update : partitions_) {
-      touched_partitions.insert(partition_update.partition_name());
+  } else if (major_payload_version_ == kChromeOSMajorPayloadVersion) {
+    LOG(INFO) << "Converting update information from old format.";
+    PartitionUpdate root_part;
+    root_part.set_partition_name(kPartitionNameRoot);
+#ifdef __ANDROID__
+    LOG(WARNING) << "Legacy payload major version provided to an Android "
+                    "build. Assuming no post-install. Please use major version "
+                    "2 or newer.";
+    root_part.set_run_postinstall(false);
+#else
+    root_part.set_run_postinstall(true);
+#endif  // __ANDROID__
+    if (manifest_.has_old_rootfs_info()) {
+      *root_part.mutable_old_partition_info() = manifest_.old_rootfs_info();
+      manifest_.clear_old_rootfs_info();
     }
-
-    auto generator = partition_update_generator::Create(boot_control_,
-                                                        manifest_.block_size());
-    std::vector<PartitionUpdate> untouched_static_partitions;
-    TEST_AND_RETURN_FALSE(
-        generator->GenerateOperationsForPartitionsNotInPayload(
-            install_plan_->source_slot,
-            install_plan_->target_slot,
-            touched_partitions,
-            &untouched_static_partitions));
-    partitions_.insert(partitions_.end(),
-                       untouched_static_partitions.begin(),
-                       untouched_static_partitions.end());
-
-    // Save the untouched dynamic partitions in install plan.
-    std::vector<std::string> dynamic_partitions;
-    if (!boot_control_->GetDynamicPartitionControl()
-             ->ListDynamicPartitionsForSlot(install_plan_->source_slot,
-                                            boot_control_->GetCurrentSlot(),
-                                            &dynamic_partitions)) {
-      LOG(ERROR) << "Failed to load dynamic partitions from slot "
-                 << install_plan_->source_slot;
-      return false;
+    if (manifest_.has_new_rootfs_info()) {
+      *root_part.mutable_new_partition_info() = manifest_.new_rootfs_info();
+      manifest_.clear_new_rootfs_info();
     }
-    install_plan_->untouched_dynamic_partitions.clear();
-    for (const auto& name : dynamic_partitions) {
-      if (touched_partitions.find(name) == touched_partitions.end()) {
-        install_plan_->untouched_dynamic_partitions.push_back(name);
-      }
+    *root_part.mutable_operations() = manifest_.install_operations();
+    manifest_.clear_install_operations();
+    partitions_.push_back(std::move(root_part));
+
+    PartitionUpdate kern_part;
+    kern_part.set_partition_name(kPartitionNameKernel);
+    kern_part.set_run_postinstall(false);
+    if (manifest_.has_old_kernel_info()) {
+      *kern_part.mutable_old_partition_info() = manifest_.old_kernel_info();
+      manifest_.clear_old_kernel_info();
     }
+    if (manifest_.has_new_kernel_info()) {
+      *kern_part.mutable_new_partition_info() = manifest_.new_kernel_info();
+      manifest_.clear_new_kernel_info();
+    }
+    *kern_part.mutable_operations() = manifest_.kernel_install_operations();
+    manifest_.clear_kernel_install_operations();
+    partitions_.push_back(std::move(kern_part));
   }
 
   // Fill in the InstallPlan::partitions based on the partitions from the
@@ -760,9 +924,22 @@
     install_plan_->partitions.push_back(install_part);
   }
 
-  // TODO(xunchang) only need to load the partitions for those in payload.
-  // Because we have already loaded the other once when generating SOURCE_COPY
-  // operations.
+  if (install_plan_->target_slot != BootControlInterface::kInvalidSlot) {
+    uint64_t required_size = 0;
+    if (!PreparePartitionsForUpdate(&required_size)) {
+      if (required_size > 0) {
+        *error = ErrorCode::kNotEnoughSpace;
+      } else {
+        *error = ErrorCode::kInstallDeviceOpenError;
+      }
+      return false;
+    }
+  }
+
+  if (major_payload_version_ == kBrilloMajorPayloadVersion) {
+    manifest_.clear_partitions();
+  }
+
   if (!install_plan_->LoadPartitionsFromSlots(boot_control_)) {
     LOG(ERROR) << "Unable to determine all the partition devices.";
     *error = ErrorCode::kInstallDeviceOpenError;
@@ -807,7 +984,6 @@
   } else {
     LOG(INFO) << "Preparing partitions for new update. last hash = "
               << last_hash << ", new hash = " << update_check_response_hash;
-    ResetUpdateProgress(prefs, false);
   }
 
   if (!boot_control->GetDynamicPartitionControl()->PreparePartitionsForUpdate(
@@ -852,10 +1028,30 @@
 
   // Since we delete data off the beginning of the buffer as we use it,
   // the data we need should be exactly at the beginning of the buffer.
+  TEST_AND_RETURN_FALSE(buffer_offset_ == operation.data_offset());
   TEST_AND_RETURN_FALSE(buffer_.size() >= operation.data_length());
 
-  TEST_AND_RETURN_FALSE(partition_writer_->PerformReplaceOperation(
-      operation, buffer_.data(), buffer_.size()));
+  // Extract the signature message if it's in this operation.
+  if (ExtractSignatureMessageFromOperation(operation)) {
+    // If this is dummy replace operation, we ignore it after extracting the
+    // signature.
+    DiscardBuffer(true, 0);
+    return true;
+  }
+
+  // Setup the ExtentWriter stack based on the operation type.
+  std::unique_ptr<ExtentWriter> writer = std::make_unique<DirectExtentWriter>();
+
+  if (operation.type() == InstallOperation::REPLACE_BZ) {
+    writer.reset(new BzipExtentWriter(std::move(writer)));
+  } else if (operation.type() == InstallOperation::REPLACE_XZ) {
+    writer.reset(new XzExtentWriter(std::move(writer)));
+  }
+
+  TEST_AND_RETURN_FALSE(
+      writer->Init(target_fd_, operation.dst_extents(), block_size_));
+  TEST_AND_RETURN_FALSE(writer->Write(buffer_.data(), operation.data_length()));
+
   // Update buffer
   DiscardBuffer(true, buffer_.size());
   return true;
@@ -870,13 +1066,92 @@
   TEST_AND_RETURN_FALSE(!operation.has_data_offset());
   TEST_AND_RETURN_FALSE(!operation.has_data_length());
 
-  return partition_writer_->PerformZeroOrDiscardOperation(operation);
+#ifdef BLKZEROOUT
+  bool attempt_ioctl = true;
+  int request =
+      (operation.type() == InstallOperation::ZERO ? BLKZEROOUT : BLKDISCARD);
+#else   // !defined(BLKZEROOUT)
+  bool attempt_ioctl = false;
+  int request = 0;
+#endif  // !defined(BLKZEROOUT)
+
+  brillo::Blob zeros;
+  for (const Extent& extent : operation.dst_extents()) {
+    const uint64_t start = extent.start_block() * block_size_;
+    const uint64_t length = extent.num_blocks() * block_size_;
+    if (attempt_ioctl) {
+      int result = 0;
+      if (target_fd_->BlkIoctl(request, start, length, &result) && result == 0)
+        continue;
+      attempt_ioctl = false;
+    }
+    // In case of failure, we fall back to writing 0 to the selected region.
+    zeros.resize(16 * block_size_);
+    for (uint64_t offset = 0; offset < length; offset += zeros.size()) {
+      uint64_t chunk_length =
+          min(length - offset, static_cast<uint64_t>(zeros.size()));
+      TEST_AND_RETURN_FALSE(utils::PWriteAll(
+          target_fd_, zeros.data(), chunk_length, start + offset));
+    }
+  }
+  return true;
 }
 
-bool PartitionWriter::ValidateSourceHash(const brillo::Blob& calculated_hash,
-                                         const InstallOperation& operation,
-                                         const FileDescriptorPtr source_fd,
-                                         ErrorCode* error) {
+bool DeltaPerformer::PerformMoveOperation(const InstallOperation& operation) {
+  // Calculate buffer size. Note, this function doesn't do a sliding
+  // window to copy in case the source and destination blocks overlap.
+  // If we wanted to do a sliding window, we could program the server
+  // to generate deltas that effectively did a sliding window.
+
+  uint64_t blocks_to_read = 0;
+  for (int i = 0; i < operation.src_extents_size(); i++)
+    blocks_to_read += operation.src_extents(i).num_blocks();
+
+  uint64_t blocks_to_write = 0;
+  for (int i = 0; i < operation.dst_extents_size(); i++)
+    blocks_to_write += operation.dst_extents(i).num_blocks();
+
+  DCHECK_EQ(blocks_to_write, blocks_to_read);
+  brillo::Blob buf(blocks_to_write * block_size_);
+
+  // Read in bytes.
+  ssize_t bytes_read = 0;
+  for (int i = 0; i < operation.src_extents_size(); i++) {
+    ssize_t bytes_read_this_iteration = 0;
+    const Extent& extent = operation.src_extents(i);
+    const size_t bytes = extent.num_blocks() * block_size_;
+    TEST_AND_RETURN_FALSE(extent.start_block() != kSparseHole);
+    TEST_AND_RETURN_FALSE(utils::PReadAll(target_fd_,
+                                          &buf[bytes_read],
+                                          bytes,
+                                          extent.start_block() * block_size_,
+                                          &bytes_read_this_iteration));
+    TEST_AND_RETURN_FALSE(bytes_read_this_iteration ==
+                          static_cast<ssize_t>(bytes));
+    bytes_read += bytes_read_this_iteration;
+  }
+
+  // Write bytes out.
+  ssize_t bytes_written = 0;
+  for (int i = 0; i < operation.dst_extents_size(); i++) {
+    const Extent& extent = operation.dst_extents(i);
+    const size_t bytes = extent.num_blocks() * block_size_;
+    TEST_AND_RETURN_FALSE(extent.start_block() != kSparseHole);
+    TEST_AND_RETURN_FALSE(utils::PWriteAll(target_fd_,
+                                           &buf[bytes_written],
+                                           bytes,
+                                           extent.start_block() * block_size_));
+    bytes_written += bytes;
+  }
+  DCHECK_EQ(bytes_written, bytes_read);
+  DCHECK_EQ(bytes_written, static_cast<ssize_t>(buf.size()));
+  return true;
+}
+
+bool DeltaPerformer::ValidateSourceHash(const brillo::Blob& calculated_hash,
+                                        const InstallOperation& operation,
+                                        const FileDescriptorPtr source_fd,
+                                        ErrorCode* error) {
   brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
                                     operation.src_sha256_hash().end());
   if (calculated_hash != expected_source_hash) {
@@ -917,7 +1192,169 @@
     TEST_AND_RETURN_FALSE(operation.src_length() % block_size_ == 0);
   if (operation.has_dst_length())
     TEST_AND_RETURN_FALSE(operation.dst_length() % block_size_ == 0);
-  return partition_writer_->PerformSourceCopyOperation(operation, error);
+
+  TEST_AND_RETURN_FALSE(source_fd_ != nullptr);
+
+  // The device may optimize the SOURCE_COPY operation.
+  // Being this a device-specific optimization let DynamicPartitionController
+  // decide it the operation should be skipped.
+  const PartitionUpdate& partition = partitions_[current_partition_];
+  const auto& partition_control = boot_control_->GetDynamicPartitionControl();
+
+  InstallOperation buf;
+  bool should_optimize = partition_control->OptimizeOperation(
+      partition.partition_name(), operation, &buf);
+  const InstallOperation& optimized = should_optimize ? buf : operation;
+
+  if (operation.has_src_sha256_hash()) {
+    bool read_ok;
+    brillo::Blob source_hash;
+    brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
+                                      operation.src_sha256_hash().end());
+
+    // We fall back to use the error corrected device if the hash of the raw
+    // device doesn't match or there was an error reading the source partition.
+    // Note that this code will also fall back if writing the target partition
+    // fails.
+    if (should_optimize) {
+      // Hash operation.src_extents(), then copy optimized.src_extents to
+      // optimized.dst_extents.
+      read_ok =
+          fd_utils::ReadAndHashExtents(
+              source_fd_, operation.src_extents(), block_size_, &source_hash) &&
+          fd_utils::CopyAndHashExtents(source_fd_,
+                                       optimized.src_extents(),
+                                       target_fd_,
+                                       optimized.dst_extents(),
+                                       block_size_,
+                                       nullptr /* skip hashing */);
+    } else {
+      read_ok = fd_utils::CopyAndHashExtents(source_fd_,
+                                             operation.src_extents(),
+                                             target_fd_,
+                                             operation.dst_extents(),
+                                             block_size_,
+                                             &source_hash);
+    }
+    if (read_ok && expected_source_hash == source_hash)
+      return true;
+
+    if (!OpenCurrentECCPartition()) {
+      // The following function call will return false since the source hash
+      // mismatches, but we still want to call it so it prints the appropriate
+      // log message.
+      return ValidateSourceHash(source_hash, operation, source_fd_, error);
+    }
+
+    LOG(WARNING) << "Source hash from RAW device mismatched: found "
+                 << base::HexEncode(source_hash.data(), source_hash.size())
+                 << ", expected "
+                 << base::HexEncode(expected_source_hash.data(),
+                                    expected_source_hash.size());
+
+    if (should_optimize) {
+      TEST_AND_RETURN_FALSE(fd_utils::ReadAndHashExtents(
+          source_ecc_fd_, operation.src_extents(), block_size_, &source_hash));
+      TEST_AND_RETURN_FALSE(
+          fd_utils::CopyAndHashExtents(source_ecc_fd_,
+                                       optimized.src_extents(),
+                                       target_fd_,
+                                       optimized.dst_extents(),
+                                       block_size_,
+                                       nullptr /* skip hashing */));
+    } else {
+      TEST_AND_RETURN_FALSE(
+          fd_utils::CopyAndHashExtents(source_ecc_fd_,
+                                       operation.src_extents(),
+                                       target_fd_,
+                                       operation.dst_extents(),
+                                       block_size_,
+                                       &source_hash));
+    }
+    TEST_AND_RETURN_FALSE(
+        ValidateSourceHash(source_hash, operation, source_ecc_fd_, error));
+    // At this point reading from the the error corrected device worked, but
+    // reading from the raw device failed, so this is considered a recovered
+    // failure.
+    source_ecc_recovered_failures_++;
+  } else {
+    // When the operation doesn't include a source hash, we attempt the error
+    // corrected device first since we can't verify the block in the raw device
+    // at this point, but we fall back to the raw device since the error
+    // corrected device can be shorter or not available.
+
+    if (OpenCurrentECCPartition() &&
+        fd_utils::CopyAndHashExtents(source_ecc_fd_,
+                                     optimized.src_extents(),
+                                     target_fd_,
+                                     optimized.dst_extents(),
+                                     block_size_,
+                                     nullptr)) {
+      return true;
+    }
+    TEST_AND_RETURN_FALSE(fd_utils::CopyAndHashExtents(source_fd_,
+                                                       optimized.src_extents(),
+                                                       target_fd_,
+                                                       optimized.dst_extents(),
+                                                       block_size_,
+                                                       nullptr));
+  }
+  return true;
+}
+
+FileDescriptorPtr DeltaPerformer::ChooseSourceFD(
+    const InstallOperation& operation, ErrorCode* error) {
+  if (source_fd_ == nullptr) {
+    LOG(ERROR) << "ChooseSourceFD fail: source_fd_ == nullptr";
+    return nullptr;
+  }
+
+  if (!operation.has_src_sha256_hash()) {
+    // When the operation doesn't include a source hash, we attempt the error
+    // corrected device first since we can't verify the block in the raw device
+    // at this point, but we first need to make sure all extents are readable
+    // since the error corrected device can be shorter or not available.
+    if (OpenCurrentECCPartition() &&
+        fd_utils::ReadAndHashExtents(
+            source_ecc_fd_, operation.src_extents(), block_size_, nullptr)) {
+      return source_ecc_fd_;
+    }
+    return source_fd_;
+  }
+
+  brillo::Blob source_hash;
+  brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
+                                    operation.src_sha256_hash().end());
+  if (fd_utils::ReadAndHashExtents(
+          source_fd_, operation.src_extents(), block_size_, &source_hash) &&
+      source_hash == expected_source_hash) {
+    return source_fd_;
+  }
+  // We fall back to use the error corrected device if the hash of the raw
+  // device doesn't match or there was an error reading the source partition.
+  if (!OpenCurrentECCPartition()) {
+    // The following function call will return false since the source hash
+    // mismatches, but we still want to call it so it prints the appropriate
+    // log message.
+    ValidateSourceHash(source_hash, operation, source_fd_, error);
+    return nullptr;
+  }
+  LOG(WARNING) << "Source hash from RAW device mismatched: found "
+               << base::HexEncode(source_hash.data(), source_hash.size())
+               << ", expected "
+               << base::HexEncode(expected_source_hash.data(),
+                                  expected_source_hash.size());
+
+  if (fd_utils::ReadAndHashExtents(
+          source_ecc_fd_, operation.src_extents(), block_size_, &source_hash) &&
+      ValidateSourceHash(source_hash, operation, source_ecc_fd_, error)) {
+    // At this point reading from the the error corrected device worked, but
+    // reading from the raw device failed, so this is considered a recovered
+    // failure.
+    source_ecc_recovered_failures_++;
+    return source_ecc_fd_;
+  }
+  return nullptr;
 }
 
 bool DeltaPerformer::ExtentsToBsdiffPositionsString(
@@ -942,6 +1379,110 @@
   return true;
 }
 
+bool DeltaPerformer::PerformBsdiffOperation(const InstallOperation& operation) {
+  // Since we delete data off the beginning of the buffer as we use it,
+  // the data we need should be exactly at the beginning of the buffer.
+  TEST_AND_RETURN_FALSE(buffer_offset_ == operation.data_offset());
+  TEST_AND_RETURN_FALSE(buffer_.size() >= operation.data_length());
+
+  string input_positions;
+  TEST_AND_RETURN_FALSE(ExtentsToBsdiffPositionsString(operation.src_extents(),
+                                                       block_size_,
+                                                       operation.src_length(),
+                                                       &input_positions));
+  string output_positions;
+  TEST_AND_RETURN_FALSE(ExtentsToBsdiffPositionsString(operation.dst_extents(),
+                                                       block_size_,
+                                                       operation.dst_length(),
+                                                       &output_positions));
+
+  TEST_AND_RETURN_FALSE(bsdiff::bspatch(target_path_.c_str(),
+                                        target_path_.c_str(),
+                                        buffer_.data(),
+                                        buffer_.size(),
+                                        input_positions.c_str(),
+                                        output_positions.c_str()) == 0);
+  DiscardBuffer(true, buffer_.size());
+
+  if (operation.dst_length() % block_size_) {
+    // Zero out rest of final block.
+    // TODO(adlr): build this into bspatch; it's more efficient that way.
+    const Extent& last_extent =
+        operation.dst_extents(operation.dst_extents_size() - 1);
+    const uint64_t end_byte =
+        (last_extent.start_block() + last_extent.num_blocks()) * block_size_;
+    const uint64_t begin_byte =
+        end_byte - (block_size_ - operation.dst_length() % block_size_);
+    brillo::Blob zeros(end_byte - begin_byte);
+    TEST_AND_RETURN_FALSE(utils::PWriteAll(
+        target_fd_, zeros.data(), end_byte - begin_byte, begin_byte));
+  }
+  return true;
+}
+
+namespace {
+
+class BsdiffExtentFile : public bsdiff::FileInterface {
+ public:
+  BsdiffExtentFile(std::unique_ptr<ExtentReader> reader, size_t size)
+      : BsdiffExtentFile(std::move(reader), nullptr, size) {}
+  BsdiffExtentFile(std::unique_ptr<ExtentWriter> writer, size_t size)
+      : BsdiffExtentFile(nullptr, std::move(writer), size) {}
+
+  ~BsdiffExtentFile() override = default;
+
+  bool Read(void* buf, size_t count, size_t* bytes_read) override {
+    TEST_AND_RETURN_FALSE(reader_->Read(buf, count));
+    *bytes_read = count;
+    offset_ += count;
+    return true;
+  }
+
+  bool Write(const void* buf, size_t count, size_t* bytes_written) override {
+    TEST_AND_RETURN_FALSE(writer_->Write(buf, count));
+    *bytes_written = count;
+    offset_ += count;
+    return true;
+  }
+
+  bool Seek(off_t pos) override {
+    if (reader_ != nullptr) {
+      TEST_AND_RETURN_FALSE(reader_->Seek(pos));
+      offset_ = pos;
+    } else {
+      // For writes technically there should be no change of position, or it
+      // should be equivalent of current offset.
+      TEST_AND_RETURN_FALSE(offset_ == static_cast<uint64_t>(pos));
+    }
+    return true;
+  }
+
+  bool Close() override { return true; }
+
+  bool GetSize(uint64_t* size) override {
+    *size = size_;
+    return true;
+  }
+
+ private:
+  BsdiffExtentFile(std::unique_ptr<ExtentReader> reader,
+                   std::unique_ptr<ExtentWriter> writer,
+                   size_t size)
+      : reader_(std::move(reader)),
+        writer_(std::move(writer)),
+        size_(size),
+        offset_(0) {}
+
+  std::unique_ptr<ExtentReader> reader_;
+  std::unique_ptr<ExtentWriter> writer_;
+  uint64_t size_;
+  uint64_t offset_;
+
+  DISALLOW_COPY_AND_ASSIGN(BsdiffExtentFile);
+};
+
+}  // namespace
+
 bool DeltaPerformer::PerformSourceBsdiffOperation(
     const InstallOperation& operation, ErrorCode* error) {
   // Since we delete data off the beginning of the buffer as we use it,
@@ -953,24 +1494,153 @@
   if (operation.has_dst_length())
     TEST_AND_RETURN_FALSE(operation.dst_length() % block_size_ == 0);
 
-  TEST_AND_RETURN_FALSE(partition_writer_->PerformSourceBsdiffOperation(
-      operation, error, buffer_.data(), buffer_.size()));
+  FileDescriptorPtr source_fd = ChooseSourceFD(operation, error);
+  TEST_AND_RETURN_FALSE(source_fd != nullptr);
+
+  auto reader = std::make_unique<DirectExtentReader>();
+  TEST_AND_RETURN_FALSE(
+      reader->Init(source_fd, operation.src_extents(), block_size_));
+  auto src_file = std::make_unique<BsdiffExtentFile>(
+      std::move(reader),
+      utils::BlocksInExtents(operation.src_extents()) * block_size_);
+
+  auto writer = std::make_unique<DirectExtentWriter>();
+  TEST_AND_RETURN_FALSE(
+      writer->Init(target_fd_, operation.dst_extents(), block_size_));
+  auto dst_file = std::make_unique<BsdiffExtentFile>(
+      std::move(writer),
+      utils::BlocksInExtents(operation.dst_extents()) * block_size_);
+
+  TEST_AND_RETURN_FALSE(bsdiff::bspatch(std::move(src_file),
+                                        std::move(dst_file),
+                                        buffer_.data(),
+                                        buffer_.size()) == 0);
   DiscardBuffer(true, buffer_.size());
   return true;
 }
 
+namespace {
+
+// A class to be passed to |puffpatch| for reading from |source_fd_| and writing
+// into |target_fd_|.
+class PuffinExtentStream : public puffin::StreamInterface {
+ public:
+  // Constructor for creating a stream for reading from an |ExtentReader|.
+  PuffinExtentStream(std::unique_ptr<ExtentReader> reader, uint64_t size)
+      : PuffinExtentStream(std::move(reader), nullptr, size) {}
+
+  // Constructor for creating a stream for writing to an |ExtentWriter|.
+  PuffinExtentStream(std::unique_ptr<ExtentWriter> writer, uint64_t size)
+      : PuffinExtentStream(nullptr, std::move(writer), size) {}
+
+  ~PuffinExtentStream() override = default;
+
+  bool GetSize(uint64_t* size) const override {
+    *size = size_;
+    return true;
+  }
+
+  bool GetOffset(uint64_t* offset) const override {
+    *offset = offset_;
+    return true;
+  }
+
+  bool Seek(uint64_t offset) override {
+    if (is_read_) {
+      TEST_AND_RETURN_FALSE(reader_->Seek(offset));
+      offset_ = offset;
+    } else {
+      // For writes technically there should be no change of position, or it
+      // should equivalent of current offset.
+      TEST_AND_RETURN_FALSE(offset_ == offset);
+    }
+    return true;
+  }
+
+  bool Read(void* buffer, size_t count) override {
+    TEST_AND_RETURN_FALSE(is_read_);
+    TEST_AND_RETURN_FALSE(reader_->Read(buffer, count));
+    offset_ += count;
+    return true;
+  }
+
+  bool Write(const void* buffer, size_t count) override {
+    TEST_AND_RETURN_FALSE(!is_read_);
+    TEST_AND_RETURN_FALSE(writer_->Write(buffer, count));
+    offset_ += count;
+    return true;
+  }
+
+  bool Close() override { return true; }
+
+ private:
+  PuffinExtentStream(std::unique_ptr<ExtentReader> reader,
+                     std::unique_ptr<ExtentWriter> writer,
+                     uint64_t size)
+      : reader_(std::move(reader)),
+        writer_(std::move(writer)),
+        size_(size),
+        offset_(0),
+        is_read_(reader_ ? true : false) {}
+
+  std::unique_ptr<ExtentReader> reader_;
+  std::unique_ptr<ExtentWriter> writer_;
+  uint64_t size_;
+  uint64_t offset_;
+  bool is_read_;
+
+  DISALLOW_COPY_AND_ASSIGN(PuffinExtentStream);
+};
+
+}  // namespace
+
 bool DeltaPerformer::PerformPuffDiffOperation(const InstallOperation& operation,
                                               ErrorCode* error) {
   // Since we delete data off the beginning of the buffer as we use it,
   // the data we need should be exactly at the beginning of the buffer.
   TEST_AND_RETURN_FALSE(buffer_offset_ == operation.data_offset());
   TEST_AND_RETURN_FALSE(buffer_.size() >= operation.data_length());
-  TEST_AND_RETURN_FALSE(partition_writer_->PerformPuffDiffOperation(
-      operation, error, buffer_.data(), buffer_.size()));
+
+  FileDescriptorPtr source_fd = ChooseSourceFD(operation, error);
+  TEST_AND_RETURN_FALSE(source_fd != nullptr);
+
+  auto reader = std::make_unique<DirectExtentReader>();
+  TEST_AND_RETURN_FALSE(
+      reader->Init(source_fd, operation.src_extents(), block_size_));
+  puffin::UniqueStreamPtr src_stream(new PuffinExtentStream(
+      std::move(reader),
+      utils::BlocksInExtents(operation.src_extents()) * block_size_));
+
+  auto writer = std::make_unique<DirectExtentWriter>();
+  TEST_AND_RETURN_FALSE(
+      writer->Init(target_fd_, operation.dst_extents(), block_size_));
+  puffin::UniqueStreamPtr dst_stream(new PuffinExtentStream(
+      std::move(writer),
+      utils::BlocksInExtents(operation.dst_extents()) * block_size_));
+
+  const size_t kMaxCacheSize = 5 * 1024 * 1024;  // Total 5MB cache.
+  TEST_AND_RETURN_FALSE(puffin::PuffPatch(std::move(src_stream),
+                                          std::move(dst_stream),
+                                          buffer_.data(),
+                                          buffer_.size(),
+                                          kMaxCacheSize));
   DiscardBuffer(true, buffer_.size());
   return true;
 }
 
+bool DeltaPerformer::ExtractSignatureMessageFromOperation(
+    const InstallOperation& operation) {
+  if (operation.type() != InstallOperation::REPLACE ||
+      !manifest_.has_signatures_offset() ||
+      manifest_.signatures_offset() != operation.data_offset()) {
+    return false;
+  }
+  TEST_AND_RETURN_FALSE(manifest_.has_signatures_size() &&
+                        manifest_.signatures_size() == operation.data_length());
+  TEST_AND_RETURN_FALSE(ExtractSignatureMessage());
+  return true;
+}
+
 bool DeltaPerformer::ExtractSignatureMessage() {
   TEST_AND_RETURN_FALSE(signatures_message_data_.empty());
   TEST_AND_RETURN_FALSE(buffer_offset_ == manifest_.signatures_offset());
@@ -978,6 +1648,20 @@
   signatures_message_data_.assign(
       buffer_.begin(), buffer_.begin() + manifest_.signatures_size());
 
+  // Save the signature blob because if the update is interrupted after the
+  // download phase we don't go through this path anymore. Some alternatives to
+  // consider:
+  //
+  // 1. On resume, re-download the signature blob from the server and re-verify
+  // it.
+  //
+  // 2. Verify the signature as soon as it's received and don't checkpoint the
+  // blob and the signed sha-256 context.
+  LOG_IF(WARNING,
+         !prefs_->SetString(kPrefsUpdateStateSignatureBlob,
+                            signatures_message_data_))
+      << "Unable to store the signature blob.";
+
   LOG(INFO) << "Extracted signature data of size "
             << manifest_.signatures_size() << " at "
             << manifest_.signatures_offset();
@@ -992,8 +1676,8 @@
     return utils::ReadFile(public_key_path_, out_public_key);
   }
 
-  // If this is an official build then we are not allowed to use public key
-  // from Omaha response.
+  // If this is an official build then we are not allowed to use public key from
+  // Omaha response.
   if (!hardware_->IsOfficialBuild() && !install_plan_->public_key_rsa.empty()) {
     LOG(INFO) << "Verifying using public key from Omaha response.";
     return brillo::data_encoding::Base64Decode(install_plan_->public_key_rsa,
@@ -1026,21 +1710,19 @@
 }
 
 ErrorCode DeltaPerformer::ValidateManifest() {
-  // Perform assorted checks to validation check the manifest, make sure it
+  // Perform assorted checks to sanity check the manifest, make sure it
   // matches data from other sources, and that it is a supported version.
-  bool has_old_fields = std::any_of(manifest_.partitions().begin(),
-                                    manifest_.partitions().end(),
-                                    [](const PartitionUpdate& partition) {
-                                      return partition.has_old_partition_info();
-                                    });
+
+  bool has_old_fields =
+      (manifest_.has_old_kernel_info() || manifest_.has_old_rootfs_info());
+  for (const PartitionUpdate& partition : manifest_.partitions()) {
+    has_old_fields = has_old_fields || partition.has_old_partition_info();
+  }
 
   // The presence of an old partition hash is the sole indicator for a delta
-  // update. Also, always treat the partial update as delta so that we can
-  // perform the minor version check correctly.
+  // update.
   InstallPayloadType actual_payload_type =
-      (has_old_fields || manifest_.partial_update())
-          ? InstallPayloadType::kDelta
-          : InstallPayloadType::kFull;
+      has_old_fields ? InstallPayloadType::kDelta : InstallPayloadType::kFull;
 
   if (payload_->type == InstallPayloadType::kUnknown) {
     LOG(INFO) << "Detected a '"
@@ -1055,8 +1737,8 @@
                << "' payload.";
     return ErrorCode::kPayloadMismatchedType;
   }
+
   // Check that the minor version is compatible.
-  // TODO(xunchang) increment minor version & add check for partial update
   if (actual_payload_type == InstallPayloadType::kFull) {
     if (manifest_.minor_version() != kFullPayloadMinorVersion) {
       LOG(ERROR) << "Manifest contains minor version "
@@ -1077,114 +1759,43 @@
     }
   }
 
-  ErrorCode error_code = CheckTimestampError();
-  if (error_code != ErrorCode::kSuccess) {
-    if (error_code == ErrorCode::kPayloadTimestampError) {
-      if (!hardware_->AllowDowngrade()) {
-        return ErrorCode::kPayloadTimestampError;
-      }
-      LOG(INFO) << "The current OS build allows downgrade, continuing to apply"
-                   " the payload with an older timestamp.";
-    } else {
-      LOG(ERROR) << "Timestamp check returned "
-                 << utils::ErrorCodeToString(error_code);
-      return error_code;
+  if (major_payload_version_ != kChromeOSMajorPayloadVersion) {
+    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 field only supported in "
+                 << "major payload version 1, but the payload major version is "
+                 << major_payload_version_;
+      return ErrorCode::kPayloadMismatchedType;
     }
   }
 
-  // TODO(crbug.com/37661) we should be adding more and more manifest checks,
-  // such as partition boundaries, etc.
-
-  return ErrorCode::kSuccess;
-}
-
-ErrorCode DeltaPerformer::CheckTimestampError() const {
-  bool is_partial_update =
-      manifest_.has_partial_update() && manifest_.partial_update();
-  const auto& partitions = manifest_.partitions();
-
-  // Check version field for a given PartitionUpdate object. If an error
-  // is encountered, set |error_code| accordingly. If downgrade is detected,
-  // |downgrade_detected| is set. Return true if the program should continue
-  // to check the next partition or not, or false if it should exit early due
-  // to errors.
-  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_name
-          << " doesn't have a version field. Not allowed in partial updates.";
-      return ErrorCode::kDownloadManifestParseError;
-    }
-
-    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_name
-                     << " has an older version than partition on device.";
-        break;
-      default:
-        LOG(ERROR) << "IsPartitionUpdateValid(" << partition_name
-                   << ") returned" << utils::ErrorCodeToString(error_code);
-        break;
-    }
-    return error_code;
-  };
-
-  bool downgrade_detected = false;
-
-  if (is_partial_update) {
-    // for partial updates, all partition MUST have valid timestamps
-    // But max_timestamp can be empty
-    for (const auto& partition : partitions) {
-      auto error_code = timestamp_valid(
-          partition, false /* allow_empty_version */, &downgrade_detected);
-      if (error_code != ErrorCode::kSuccess &&
-          error_code != ErrorCode::kPayloadTimestampError) {
-        return error_code;
-      }
-    }
-    if (downgrade_detected) {
-      return ErrorCode::kPayloadTimestampError;
-    }
-    return ErrorCode::kSuccess;
-  }
-
-  // For non-partial updates, check max_timestamp first.
   if (manifest_.max_timestamp() < hardware_->GetBuildTimestamp()) {
     LOG(ERROR) << "The current OS build timestamp ("
                << hardware_->GetBuildTimestamp()
                << ") is newer than the maximum timestamp in the manifest ("
                << manifest_.max_timestamp() << ")";
-    return ErrorCode::kPayloadTimestampError;
+    if (!hardware_->AllowDowngrade()) {
+      return ErrorCode::kPayloadTimestampError;
+    }
+    LOG(INFO) << "The current OS build allows downgrade, continuing to apply"
+                 " the payload with an older timestamp.";
   }
-  // Otherwise... partitions can have empty timestamps.
-  for (const auto& partition : partitions) {
-    auto error_code = timestamp_valid(
-        partition, true /* allow_empty_version */, &downgrade_detected);
-    if (error_code != ErrorCode::kSuccess &&
-        error_code != ErrorCode::kPayloadTimestampError) {
-      return error_code;
+
+  if (major_payload_version_ == kChromeOSMajorPayloadVersion) {
+    if (manifest_.has_dynamic_partition_metadata()) {
+      LOG(ERROR)
+          << "Should not contain dynamic_partition_metadata for major version "
+          << kChromeOSMajorPayloadVersion
+          << ". Please use major version 2 or above.";
+      return ErrorCode::kPayloadMismatchedType;
     }
   }
-  if (downgrade_detected) {
-    return ErrorCode::kPayloadTimestampError;
-  }
+
+  // TODO(garnold) we should be adding more and more manifest checks, such as
+  // partition boundaries etc (see chromium-os:37661).
+
   return ErrorCode::kSuccess;
 }
 
@@ -1192,11 +1803,10 @@
     const InstallOperation& operation) {
   if (!operation.data_sha256_hash().size()) {
     if (!operation.data_length()) {
-      // Operations that do not have any data blob won't have any operation
-      // hash either. So, these operations are always considered validated
-      // since the metadata that contains all the non-data-blob portions of
-      // the operation has already been validated. This is true for both HTTP
-      // and HTTPS cases.
+      // Operations that do not have any data blob won't have any operation hash
+      // either. So, these operations are always considered validated since the
+      // metadata that contains all the non-data-blob portions of the operation
+      // has already been validated. This is true for both HTTP and HTTPS cases.
       return ErrorCode::kSuccess;
     }
 
@@ -1205,7 +1815,7 @@
     // corresponding update should have been produced with the operation
     // hashes. So if it happens it means either we've turned operation hash
     // generation off in DeltaDiffGenerator or it's a regression of some sort.
-    // One caveat though: The last operation is a unused signature operation
+    // One caveat though: The last operation is a dummy signature operation
     // that doesn't have a hash at the time the manifest is created. So we
     // should not complaint about that operation. This operation can be
     // recognized by the fact that it's offset is mentioned in the manifest.
@@ -1280,7 +1890,12 @@
       ErrorCode::kPayloadHashMismatchError,
       payload_hash_calculator_.raw_hash() == update_check_response_hash);
 
-  // NOLINTNEXTLINE(whitespace/braces)
+  TEST_AND_RETURN_VAL(ErrorCode::kSignedDeltaPayloadExpectedError,
+                      !signatures_message_data_.empty());
+  brillo::Blob hash_data = signed_hash_calculator_.raw_hash();
+  TEST_AND_RETURN_VAL(ErrorCode::kDownloadPayloadPubKeyVerificationError,
+                      hash_data.size() == kSHA256Size);
+
   auto [payload_verifier, perform_verification] = CreatePayloadVerifier();
   if (!perform_verification) {
     LOG(WARNING) << "Not verifying signed delta payload -- missing public key.";
@@ -1290,13 +1905,6 @@
     LOG(ERROR) << "Failed to create the payload verifier.";
     return ErrorCode::kDownloadPayloadPubKeyVerificationError;
   }
-
-  TEST_AND_RETURN_VAL(ErrorCode::kSignedDeltaPayloadExpectedError,
-                      !signatures_message_data_.empty());
-  brillo::Blob hash_data = signed_hash_calculator_.raw_hash();
-  TEST_AND_RETURN_VAL(ErrorCode::kDownloadPayloadPubKeyVerificationError,
-                      hash_data.size() == kSHA256Size);
-
   if (!payload_verifier->VerifySignature(signatures_message_data_, hash_data)) {
     // The autoupdate_CatchBadSignatures test checks for this string
     // in log-files. Keep in sync.
@@ -1336,13 +1944,13 @@
     return false;
 
   int64_t resumed_update_failures;
-  // Note that storing this value is optional, but if it is there it should
-  // not be more than the limit.
+  // Note that storing this value is optional, but if it is there it should not
+  // be more than the limit.
   if (prefs->GetInt64(kPrefsResumedUpdateFailures, &resumed_update_failures) &&
       resumed_update_failures > kMaxResumedUpdateFailures)
     return false;
 
-  // Validation check the rest.
+  // Sanity check the rest.
   int64_t next_data_offset = -1;
   if (!(prefs->GetInt64(kPrefsUpdateStateNextDataOffset, &next_data_offset) &&
         next_data_offset >= 0))
@@ -1393,38 +2001,18 @@
   return true;
 }
 
-bool DeltaPerformer::ShouldCheckpoint() {
-  base::TimeTicks curr_time = base::TimeTicks::Now();
-  if (curr_time > update_checkpoint_time_) {
-    update_checkpoint_time_ = curr_time + update_checkpoint_wait_;
-    return true;
-  }
-  return false;
-}
-
 bool DeltaPerformer::CheckpointUpdateProgress(bool force) {
-  if (!force && !ShouldCheckpoint()) {
+  base::TimeTicks curr_time = base::TimeTicks::Now();
+  if (force || curr_time > update_checkpoint_time_) {
+    update_checkpoint_time_ = curr_time + update_checkpoint_wait_;
+  } else {
     return false;
   }
+
   Terminator::set_exit_blocked(true);
-  if (last_updated_operation_num_ != next_operation_num_ || force) {
+  if (last_updated_buffer_offset_ != buffer_offset_) {
     // Resets the progress in case we die in the middle of the state update.
     ResetUpdateProgress(prefs_, true);
-    if (!signatures_message_data_.empty()) {
-      // Save the signature blob because if the update is interrupted after the
-      // download phase we don't go through this path anymore. Some alternatives
-      // to consider:
-      //
-      // 1. On resume, re-download the signature blob from the server and
-      // re-verify it.
-      //
-      // 2. Verify the signature as soon as it's received and don't checkpoint
-      // the blob and the signed sha-256 context.
-      LOG_IF(WARNING,
-             !prefs_->SetString(kPrefsUpdateStateSignatureBlob,
-                                signatures_message_data_))
-          << "Unable to store the signature blob.";
-    }
     TEST_AND_RETURN_FALSE(prefs_->SetString(
         kPrefsUpdateStateSHA256Context, payload_hash_calculator_.GetContext()));
     TEST_AND_RETURN_FALSE(
@@ -1432,13 +2020,12 @@
                           signed_hash_calculator_.GetContext()));
     TEST_AND_RETURN_FALSE(
         prefs_->SetInt64(kPrefsUpdateStateNextDataOffset, buffer_offset_));
-    last_updated_operation_num_ = next_operation_num_;
+    last_updated_buffer_offset_ = buffer_offset_;
 
     if (next_operation_num_ < num_total_operations_) {
       size_t partition_index = current_partition_;
-      while (next_operation_num_ >= acc_num_operations_[partition_index]) {
+      while (next_operation_num_ >= acc_num_operations_[partition_index])
         partition_index++;
-      }
       const size_t partition_operation_num =
           next_operation_num_ -
           (partition_index ? acc_num_operations_[partition_index - 1] : 0);
@@ -1450,14 +2037,6 @@
       TEST_AND_RETURN_FALSE(
           prefs_->SetInt64(kPrefsUpdateStateNextDataLength, 0));
     }
-    if (partition_writer_) {
-      partition_writer_->CheckpointUpdateProgress(GetPartitionOperationNum());
-    } else {
-      CHECK_EQ(next_operation_num_, num_total_operations_)
-          << "Partition writer is null, we are expected to finish all "
-             "operations: "
-          << next_operation_num_ << "/" << num_total_operations_;
-    }
   }
   TEST_AND_RETURN_FALSE(
       prefs_->SetInt64(kPrefsUpdateStateNextOperation, next_operation_num_));
@@ -1525,26 +2104,4 @@
   return true;
 }
 
-bool DeltaPerformer::IsDynamicPartition(const std::string& part_name,
-                                        uint32_t slot) {
-  return boot_control_->GetDynamicPartitionControl()->IsDynamicPartition(
-      part_name, slot);
-}
-
-std::unique_ptr<PartitionWriter> DeltaPerformer::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 partition_writer::CreatePartitionWriter(
-      partition_update,
-      install_part,
-      dynamic_control,
-      block_size_,
-      interactive_,
-      IsDynamicPartition(install_part.name, install_plan_->target_slot));
-}
-
 }  // namespace chromeos_update_engine
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index c54316b..01fcc5c 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -35,7 +35,6 @@
 #include "update_engine/payload_consumer/file_descriptor.h"
 #include "update_engine/payload_consumer/file_writer.h"
 #include "update_engine/payload_consumer/install_plan.h"
-#include "update_engine/payload_consumer/partition_writer.h"
 #include "update_engine/payload_consumer/payload_metadata.h"
 #include "update_engine/payload_consumer/payload_verifier.h"
 #include "update_engine/update_metadata.pb.h"
@@ -49,6 +48,7 @@
 
 // This class performs the actions in a delta update synchronously. The delta
 // update itself should be passed in in chunks as it is received.
+
 class DeltaPerformer : public FileWriter {
  public:
   // Defines the granularity of progress logging in terms of how many "completed
@@ -78,9 +78,7 @@
         download_delegate_(download_delegate),
         install_plan_(install_plan),
         payload_(payload),
-        interactive_(interactive) {
-    CHECK(install_plan_);
-  }
+        interactive_(interactive) {}
 
   // FileWriter's Write implementation where caller doesn't care about
   // error codes.
@@ -102,6 +100,10 @@
   // work. Returns whether the required file descriptors were successfully open.
   bool OpenCurrentPartition();
 
+  // Attempt to open the error-corrected device for the current partition.
+  // Returns whether the operation succeeded.
+  bool OpenCurrentECCPartition();
+
   // Closes the current partition file descriptors if open. Returns 0 on success
   // or -errno on error.
   int CloseCurrentPartition();
@@ -170,11 +172,9 @@
   // Return true if header parsing is finished and no errors occurred.
   bool IsHeaderParsed() const;
 
-  // Checkpoints the update progress into persistent storage to allow this
-  // update attempt to be resumed after reboot.
-  // If |force| is false, checkpoint may be throttled.
-  // Exposed for testing purposes.
-  bool CheckpointUpdateProgress(bool force);
+  // 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.
@@ -202,33 +202,14 @@
       const std::string& update_check_response_hash,
       uint64_t* required_size);
 
- protected:
-  // Exposed as virtual for testing purposes.
-  virtual 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 true if it has been long enough and a checkpoint should be saved.
-  // Exposed for unittest purposes.
-  virtual bool ShouldCheckpoint();
-
  private:
   friend class DeltaPerformerTest;
   friend class DeltaPerformerIntegrationTest;
   FRIEND_TEST(DeltaPerformerTest, BrilloMetadataSignatureSizeTest);
   FRIEND_TEST(DeltaPerformerTest, BrilloParsePayloadMetadataTest);
+  FRIEND_TEST(DeltaPerformerTest, ChooseSourceFDTest);
   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.
@@ -273,6 +254,8 @@
   // set even if it fails.
   bool PerformReplaceOperation(const InstallOperation& operation);
   bool PerformZeroOrDiscardOperation(const InstallOperation& operation);
+  bool PerformMoveOperation(const InstallOperation& operation);
+  bool PerformBsdiffOperation(const InstallOperation& operation);
   bool PerformSourceCopyOperation(const InstallOperation& operation,
                                   ErrorCode* error);
   bool PerformSourceBsdiffOperation(const InstallOperation& operation,
@@ -280,6 +263,18 @@
   bool PerformPuffDiffOperation(const InstallOperation& operation,
                                 ErrorCode* error);
 
+  // For a given operation, choose the source fd to be used (raw device or error
+  // correction device) based on the source operation hash.
+  // Returns nullptr if the source hash mismatch cannot be corrected, and set
+  // the |error| accordingly.
+  FileDescriptorPtr ChooseSourceFD(const InstallOperation& operation,
+                                   ErrorCode* error);
+
+  // Extracts the payload signature message from the blob on the |operation| if
+  // the offset matches the one specified by the manifest. Returns whether the
+  // signature was extracted.
+  bool ExtractSignatureMessageFromOperation(const InstallOperation& operation);
+
   // Extracts the payload signature message from the current |buffer_| if the
   // offset matches the one specified by the manifest. Returns whether the
   // signature was extracted.
@@ -292,6 +287,11 @@
   // accordingly.
   void DiscardBuffer(bool do_advance_offset, size_t signed_hash_buffer_size);
 
+  // Checkpoints the update progress into persistent storage to allow this
+  // update attempt to be resumed after reboot.
+  // If |force| is false, checkpoint may be throttled.
+  bool CheckpointUpdateProgress(bool force);
+
   // Primes the required update state. Returns true if the update state was
   // successfully initialized to a saved resume state or if the update is a new
   // update. Returns false otherwise.
@@ -315,18 +315,6 @@
   // Also see comment for the static PreparePartitionsForUpdate().
   bool PreparePartitionsForUpdate(uint64_t* required_size);
 
-  // Check if current manifest contains timestamp errors.
-  // Return:
-  // - kSuccess if update is valid.
-  // - kPayloadTimestampError if downgrade is detected
-  // - kDownloadManifestParseError if |new_version| has an incorrect format
-  // - Other error values if the source of error is known, or kError for
-  //   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, uint32_t slot);
-
   // Update Engine preference store.
   PrefsInterface* prefs_;
 
@@ -344,6 +332,34 @@
   // Pointer to the current payload in install_plan_.payloads.
   InstallPlan::Payload* payload_{nullptr};
 
+  // File descriptor of the source partition. Only set while updating a
+  // partition when using a delta payload.
+  FileDescriptorPtr source_fd_{nullptr};
+
+  // File descriptor of the error corrected source partition. Only set while
+  // updating partition using a delta payload for a partition where error
+  // correction is available. The size of the error corrected device is smaller
+  // than the underlying raw device, since it doesn't include the error
+  // correction blocks.
+  FileDescriptorPtr source_ecc_fd_{nullptr};
+
+  // The total number of operations that failed source hash verification but
+  // passed after falling back to the error-corrected |source_ecc_fd_| device.
+  uint64_t source_ecc_recovered_failures_{0};
+
+  // Whether opening the current partition as an error-corrected device failed.
+  // Used to avoid re-opening the same source partition if it is not actually
+  // error corrected.
+  bool source_ecc_open_failure_{false};
+
+  // File descriptor of the target partition. Only set while performing the
+  // operations of a given partition.
+  FileDescriptorPtr target_fd_{nullptr};
+
+  // Paths the |source_fd_| and |target_fd_| refer to.
+  std::string source_path_;
+  std::string target_path_;
+
   PayloadMetadata payload_metadata_;
 
   // Parsed manifest. Set after enough bytes to parse the manifest were
@@ -364,28 +380,28 @@
   // 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};
 
-  // Last |next_operation_num_| value updated as part of the progress update.
-  uint64_t last_updated_operation_num_{std::numeric_limits<uint64_t>::max()};
+  // Last |buffer_offset_| value updated as part of the progress update.
+  uint64_t last_updated_buffer_offset_{std::numeric_limits<uint64_t>::max()};
 
   // The block size (parsed from the manifest).
   uint32_t block_size_{0};
@@ -421,9 +437,8 @@
   // 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_;
@@ -434,8 +449,6 @@
       base::TimeDelta::FromSeconds(kCheckpointFrequencySeconds)};
   base::TimeTicks update_checkpoint_time_;
 
-  std::unique_ptr<PartitionWriter> partition_writer_;
-
   DISALLOW_COPY_AND_ASSIGN(DeltaPerformer);
 };
 
diff --git a/payload_consumer/delta_performer_fuzzer.cc b/payload_consumer/delta_performer_fuzzer.cc
deleted file mode 100644
index 0ce5081..0000000
--- a/payload_consumer/delta_performer_fuzzer.cc
+++ /dev/null
@@ -1,105 +0,0 @@
-//
-// Copyright 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <string>
-
-#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/install_plan.h"
-
-namespace chromeos_update_engine {
-
-class FakeDownloadActionDelegate : public DownloadActionDelegate {
- public:
-  FakeDownloadActionDelegate() = default;
-  ~FakeDownloadActionDelegate() = default;
-
-  // DownloadActionDelegate overrides;
-  void BytesReceived(uint64_t bytes_progressed,
-                     uint64_t bytes_received,
-                     uint64_t total) override{};
-
-  bool ShouldCancel(ErrorCode* cancel_reason) override { return false; };
-
-  void DownloadComplete() override{};
-
-  DISALLOW_COPY_AND_ASSIGN(FakeDownloadActionDelegate);
-};
-
-void FuzzDeltaPerformer(const uint8_t* data, size_t size) {
-  MemoryPrefs prefs;
-  FakeBootControl boot_control;
-  FakeHardware hardware;
-  FakeDownloadActionDelegate download_action_delegate;
-
-  FuzzedDataProvider data_provider(data, size);
-
-  InstallPlan install_plan{
-      .target_slot = 1,
-      .partitions = {InstallPlan::Partition{
-          .source_path = "/dev/zero",
-          .source_size = 4096,
-          .target_path = "/dev/null",
-          .target_size = 4096,
-      }},
-      .hash_checks_mandatory = true,
-  };
-
-  InstallPlan::Payload payload{
-      .size = data_provider.ConsumeIntegralInRange<uint64_t>(0, 10000),
-      .metadata_size = data_provider.ConsumeIntegralInRange<uint64_t>(0, 1000),
-      .hash = data_provider.ConsumeBytes<uint8_t>(32),
-      .type = static_cast<InstallPayloadType>(
-          data_provider.ConsumeIntegralInRange(0, 3)),
-      .already_applied = data_provider.ConsumeBool(),
-  };
-
-  DeltaPerformer performer(&prefs,
-                           &boot_control,
-                           &hardware,
-                           &download_action_delegate,
-                           &install_plan,
-                           &payload,
-                           data_provider.ConsumeBool());
-  do {
-    auto chunk_size = data_provider.ConsumeIntegralInRange<size_t>(0, 100);
-    auto data = data_provider.ConsumeBytes<uint8_t>(chunk_size);
-    if (!performer.Write(data.data(), data.size()))
-      break;
-  } while (data_provider.remaining_bytes() > 0);
-}
-
-}  // namespace chromeos_update_engine
-
-class Environment {
- public:
-  Environment() { logging::SetMinLogLevel(logging::LOG_FATAL); }
-};
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  if (size > 1000000) {
-    return 0;
-  }
-
-  static Environment env;
-  chromeos_update_engine::FuzzDeltaPerformer(data, size);
-  return 0;
-}
diff --git a/payload_consumer/delta_performer_integration_test.cc b/payload_consumer/delta_performer_integration_test.cc
index 4fab975..4797137 100644
--- a/payload_consumer/delta_performer_integration_test.cc
+++ b/payload_consumer/delta_performer_integration_test.cc
@@ -25,10 +25,8 @@
 
 #include <base/files/file_path.h>
 #include <base/files/file_util.h>
-#include <base/stl_util.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
-#include <gmock/gmock-matchers.h>
 #include <google/protobuf/repeated_field.h>
 #include <gtest/gtest.h>
 #include <openssl/pem.h>
@@ -36,13 +34,10 @@
 #include "update_engine/common/constants.h"
 #include "update_engine/common/fake_boot_control.h"
 #include "update_engine/common/fake_hardware.h"
-#include "update_engine/common/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/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,18 +47,13 @@
 
 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;
 using test_utils::ScopedLoopMounter;
 using test_utils::System;
 using testing::_;
-using testing::IsEmpty;
-using testing::NiceMock;
-using testing::Not;
 using testing::Return;
 
 extern const char* kUnittestPrivateKeyPath;
@@ -81,24 +71,21 @@
 
 namespace {
 struct DeltaState {
-  unique_ptr<ScopedTempFile> a_img;
-  unique_ptr<ScopedTempFile> b_img;
-  unique_ptr<ScopedTempFile> result_img;
+  string a_img;
+  string b_img;
+  string result_img;
   size_t image_size;
 
-  unique_ptr<ScopedTempFile> delta_file;
-  // The in-memory copy of delta file.
-  brillo::Blob delta;
+  string delta_path;
   uint64_t metadata_size;
-  uint32_t metadata_signature_size;
 
-  unique_ptr<ScopedTempFile> old_kernel;
+  string old_kernel;
   brillo::Blob old_kernel_data;
 
-  unique_ptr<ScopedTempFile> new_kernel;
+  string new_kernel;
   brillo::Blob new_kernel_data;
 
-  unique_ptr<ScopedTempFile> result_kernel;
+  string result_kernel;
   brillo::Blob result_kernel_data;
   size_t kernel_size;
 
@@ -106,6 +93,9 @@
   // 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_;
@@ -132,41 +122,7 @@
 
 }  // namespace
 
-class DeltaPerformerIntegrationTest : public ::testing::Test {
- public:
-  void RunManifestValidation(const DeltaArchiveManifest& manifest,
-                             uint64_t major_version,
-                             ErrorCode expected) {
-    FakePrefs prefs;
-    InstallPlan::Payload payload;
-    InstallPlan install_plan;
-    DeltaPerformer performer{&prefs,
-                             nullptr,
-                             &fake_hardware_,
-                             nullptr,
-                             &install_plan,
-                             &payload,
-                             false /* interactive*/};
-    // Delta performer will treat manifest as kDelta payload
-    // if it's a partial update.
-    payload.type = manifest.partial_update() ? InstallPayloadType::kDelta
-                                             : InstallPayloadType::kFull;
-
-    // The Manifest we are validating.
-    performer.manifest_.CopyFrom(manifest);
-    performer.major_payload_version_ = major_version;
-
-    EXPECT_EQ(expected, performer.ValidateManifest());
-  }
-  void AddPartition(DeltaArchiveManifest* manifest,
-                    string name,
-                    int timestamp) {
-    auto& partition = *manifest->add_partitions();
-    partition.set_version(std::to_string(timestamp));
-    partition.set_partition_name(name);
-  }
-  FakeHardware fake_hardware_;
-};
+class DeltaPerformerIntegrationTest : public ::testing::Test {};
 
 static void CompareFilesByBlock(const string& a_file,
                                 const string& b_file,
@@ -231,18 +187,15 @@
   size_t signature_size;
   ASSERT_TRUE(PayloadSigner::GetMaximumSignatureSize(private_key_path,
                                                      &signature_size));
-  brillo::Blob metadata_hash, payload_hash;
+  brillo::Blob hash;
   ASSERT_TRUE(PayloadSigner::HashPayloadForSigning(
-      payload_path, {signature_size}, &payload_hash, &metadata_hash));
-  brillo::Blob metadata_signature, payload_signature;
-  ASSERT_TRUE(PayloadSigner::SignHash(
-      payload_hash, private_key_path, &payload_signature));
-  ASSERT_TRUE(PayloadSigner::SignHash(
-      metadata_hash, private_key_path, &metadata_signature));
+      payload_path, {signature_size}, &hash, nullptr));
+  brillo::Blob signature;
+  ASSERT_TRUE(PayloadSigner::SignHash(hash, private_key_path, &signature));
   ASSERT_TRUE(PayloadSigner::AddSignatureToPayload(payload_path,
                                                    {signature_size},
-                                                   {payload_signature},
-                                                   {metadata_signature},
+                                                   {signature},
+                                                   {},
                                                    payload_path,
                                                    out_metadata_size));
   EXPECT_TRUE(PayloadSigner::VerifySignedPayload(
@@ -263,56 +216,39 @@
   }
   string signature_size_string = base::JoinString(signature_size_strings, ":");
 
-  ScopedTempFile hash_file("hash.XXXXXX"), metadata_hash_file("hash.XXXXXX");
+  test_utils::ScopedTempFile hash_file("hash.XXXXXX");
   string delta_generator_path = GetBuildArtifactsPath("delta_generator");
   ASSERT_EQ(0,
             System(base::StringPrintf(
-                "%s -in_file=%s -signature_size=%s -out_hash_file=%s "
-                "-out_metadata_hash_file=%s",
+                "%s -in_file=%s -signature_size=%s -out_hash_file=%s",
                 delta_generator_path.c_str(),
                 payload_path.c_str(),
                 signature_size_string.c_str(),
-                hash_file.path().c_str(),
-                metadata_hash_file.path().c_str())));
+                hash_file.path().c_str())));
 
   // Sign the hash with all private keys.
-  list<ScopedTempFile> sig_files, metadata_sig_files;
-  vector<string> sig_file_paths, metadata_sig_file_paths;
+  vector<test_utils::ScopedTempFile> sig_files;
+  vector<string> 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));
 
-    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));
-
-    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_files.back().path());
+    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));
   }
   string sig_files_string = base::JoinString(sig_file_paths, ":");
-  string metadata_sig_files_string =
-      base::JoinString(metadata_sig_file_paths, ":");
 
   // Add the signature to the payload.
   ASSERT_EQ(0,
             System(base::StringPrintf("%s --signature_size=%s -in_file=%s "
-                                      "-payload_signature_file=%s "
-                                      "-metadata_signature_file=%s "
-                                      "-out_file=%s",
+                                      "-payload_signature_file=%s -out_file=%s",
                                       delta_generator_path.c_str(),
                                       signature_size_string.c_str(),
                                       payload_path.c_str(),
                                       sig_files_string.c_str(),
-                                      metadata_sig_files_string.c_str(),
                                       payload_path.c_str())));
 
   int verify_result = System(base::StringPrintf("%s -in_file=%s -public_key=%s",
@@ -378,7 +314,7 @@
         GetBuildArtifactsPath(kUnittestPrivateKey2Path));
   }
 
-  string public_key;
+  std::string public_key;
   if (signature_test == kSignatureGeneratedShellRotateCl2) {
     public_key = GetBuildArtifactsPath(kUnittestPublicKey2Path);
   } else if (signature_test == kSignatureGeneratedShellECKey) {
@@ -394,27 +330,49 @@
 
 static void GenerateDeltaFile(bool full_kernel,
                               bool full_rootfs,
+                              bool noop,
                               ssize_t chunk_size,
                               SignatureTest signature_test,
                               DeltaState* state,
                               uint32_t minor_version) {
-  state->a_img.reset(new ScopedTempFile("a_img.XXXXXX"));
-  state->b_img.reset(new ScopedTempFile("b_img.XXXXXX"));
+  EXPECT_TRUE(utils::MakeTempFile("a_img.XXXXXX", &state->a_img, nullptr));
+  EXPECT_TRUE(utils::MakeTempFile("b_img.XXXXXX", &state->b_img, nullptr));
 
   // 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.
-  state->result_img.reset(new ScopedTempFile("result_img.XXXXXX"));
+  EXPECT_TRUE(
+      utils::MakeTempFile("result_img.XXXXXX", &state->result_img, nullptr));
 
   EXPECT_TRUE(
       base::CopyFile(GetBuildArtifactsPath().Append("gen/disk_ext2_4k.img"),
-                     base::FilePath(state->a_img->path())));
+                     base::FilePath(state->a_img)));
 
-  state->image_size = utils::FileSize(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");
 
   // Make some changes to the A image.
   {
     string a_mnt;
-    ScopedLoopMounter b_mounter(state->a_img->path(), &a_mnt, 0);
+    ScopedLoopMounter b_mounter(state->a_img, &a_mnt, 0);
 
     brillo::Blob hardtocompress;
     while (hardtocompress.size() < 3 * kBlockSize) {
@@ -449,29 +407,32 @@
                          ones.size()));
   }
 
-  // Create a result image with image_size bytes of garbage.
-  brillo::Blob ones(state->image_size, 0xff);
-  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()));
+  if (noop) {
+    EXPECT_TRUE(base::CopyFile(base::FilePath(state->a_img),
+                               base::FilePath(state->b_img)));
+    old_image_info = new_image_info;
+  } else {
+    if (minor_version == kSourceMinorPayloadVersion) {
+      // 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(
-      base::CopyFile(GetBuildArtifactsPath().Append("gen/disk_ext2_4k.img"),
-                     base::FilePath(state->b_img->path())));
-  {
+    EXPECT_TRUE(
+        base::CopyFile(GetBuildArtifactsPath().Append("gen/disk_ext2_4k.img"),
+                       base::FilePath(state->b_img)));
+
     // Make some changes to the B image.
     string b_mnt;
-    ScopedLoopMounter b_mounter(state->b_img->path(), &b_mnt, 0);
+    ScopedLoopMounter b_mounter(state->b_img, &b_mnt, 0);
     base::FilePath mnt_path(b_mnt);
 
     EXPECT_TRUE(base::CopyFile(mnt_path.Append("regular-small"),
                                mnt_path.Append("regular-small2")));
-#if BASE_VER < 800000
     EXPECT_TRUE(base::DeleteFile(mnt_path.Append("regular-small"), false));
-#else
-    EXPECT_TRUE(base::DeleteFile(mnt_path.Append("regular-small")));
-#endif
     EXPECT_TRUE(base::Move(mnt_path.Append("regular-small2"),
                            mnt_path.Append("regular-small")));
     EXPECT_TRUE(
@@ -498,11 +459,7 @@
     EXPECT_TRUE(base::Move(mnt_path.Append("tmp"),
                            mnt_path.Append("link-hard-regular-16k")));
 
-#if BASE_VER < 800000
     EXPECT_TRUE(base::DeleteFile(mnt_path.Append("link-short_symlink"), false));
-#else
-    EXPECT_TRUE(base::DeleteFile(mnt_path.Append("link-short_symlink")));
-#endif
     EXPECT_TRUE(test_utils::WriteFileString(
         mnt_path.Append("link-short_symlink").value(), "foobar"));
 
@@ -518,9 +475,18 @@
         hardtocompress.size()));
   }
 
-  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"));
+  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->kernel_size = kDefaultKernelSize;
   state->old_kernel_data.resize(kDefaultKernelSize);
   state->new_kernel_data.resize(state->old_kernel_data.size());
@@ -533,18 +499,23 @@
   std::copy(
       std::begin(kNewData), std::end(kNewData), state->new_kernel_data.begin());
 
+  if (noop) {
+    state->old_kernel_data = state->new_kernel_data;
+  }
+
   // Write kernels to disk
-  EXPECT_TRUE(utils::WriteFile(state->old_kernel->path().c_str(),
+  EXPECT_TRUE(utils::WriteFile(state->old_kernel.c_str(),
                                state->old_kernel_data.data(),
                                state->old_kernel_data.size()));
-  EXPECT_TRUE(utils::WriteFile(state->new_kernel->path().c_str(),
+  EXPECT_TRUE(utils::WriteFile(state->new_kernel.c_str(),
                                state->new_kernel_data.data(),
                                state->new_kernel_data.size()));
-  EXPECT_TRUE(utils::WriteFile(state->result_kernel->path().c_str(),
+  EXPECT_TRUE(utils::WriteFile(state->result_kernel.c_str(),
                                state->result_kernel_data.data(),
                                state->result_kernel_data.size()));
 
-  state->delta_file.reset(new ScopedTempFile("delta.XXXXXX"));
+  EXPECT_TRUE(utils::MakeTempFile("delta.XXXXXX", &state->delta_path, nullptr));
+  LOG(INFO) << "delta path: " << state->delta_path;
   {
     const string private_key =
         signature_test == kSignatureGenerator
@@ -555,15 +526,15 @@
     payload_config.is_delta = !full_rootfs;
     payload_config.hard_chunk_size = chunk_size;
     payload_config.rootfs_partition_size = kRootFSPartitionSize;
-    payload_config.version.major = kBrilloMajorPayloadVersion;
+    payload_config.version.major = kChromeOSMajorPayloadVersion;
     payload_config.version.minor = minor_version;
     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->path();
+      payload_config.source.partitions.front().path = state->a_img;
       if (!full_kernel)
-        payload_config.source.partitions.back().path =
-            state->old_kernel->path();
+        payload_config.source.partitions.back().path = state->old_kernel;
+      payload_config.source.image_info = old_image_info;
       EXPECT_TRUE(payload_config.source.LoadImageSize());
       for (PartitionConfig& part : payload_config.source.partitions)
         EXPECT_TRUE(part.OpenFilesystem());
@@ -573,30 +544,29 @@
         payload_config.hard_chunk_size = 1024 * 1024;
     }
     payload_config.target.partitions.emplace_back(kPartitionNameRoot);
-    payload_config.target.partitions.back().path = state->b_img->path();
+    payload_config.target.partitions.back().path = state->b_img;
     payload_config.target.partitions.emplace_back(kPartitionNameKernel);
-    payload_config.target.partitions.back().path = state->new_kernel->path();
+    payload_config.target.partitions.back().path = state->new_kernel;
+    payload_config.target.image_info = new_image_info;
     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_file->path(),
-                                          private_key,
-                                          &state->metadata_size));
+    EXPECT_TRUE(GenerateUpdatePayloadFile(
+        payload_config, state->delta_path, private_key, &state->metadata_size));
   }
   // Extend the "partitions" holding the file system a bit.
   EXPECT_EQ(0,
-            HANDLE_EINTR(truncate(state->a_img->path().c_str(),
+            HANDLE_EINTR(truncate(state->a_img.c_str(),
                                   state->image_size + 1024 * 1024)));
   EXPECT_EQ(static_cast<off_t>(state->image_size + 1024 * 1024),
-            utils::FileSize(state->a_img->path()));
+            utils::FileSize(state->a_img));
   EXPECT_EQ(0,
-            HANDLE_EINTR(truncate(state->b_img->path().c_str(),
+            HANDLE_EINTR(truncate(state->b_img.c_str(),
                                   state->image_size + 1024 * 1024)));
   EXPECT_EQ(static_cast<off_t>(state->image_size + 1024 * 1024),
-            utils::FileSize(state->b_img->path()));
+            utils::FileSize(state->b_img));
 
   if (signature_test == kSignatureGeneratedPlaceholder ||
       signature_test == kSignatureGeneratedPlaceholderMismatch) {
@@ -605,13 +575,13 @@
         GetBuildArtifactsPath(kUnittestPrivateKeyPath), &signature_size));
     LOG(INFO) << "Inserting placeholder signature.";
     ASSERT_TRUE(InsertSignaturePlaceholder(
-        signature_size, state->delta_file->path(), &state->metadata_size));
+        signature_size, state->delta_path, &state->metadata_size));
 
     if (signature_test == kSignatureGeneratedPlaceholderMismatch) {
       signature_size -= 1;
       LOG(INFO) << "Inserting mismatched placeholder signature.";
       ASSERT_FALSE(InsertSignaturePlaceholder(
-          signature_size, state->delta_file->path(), &state->metadata_size));
+          signature_size, state->delta_path, &state->metadata_size));
       return;
     }
   }
@@ -623,18 +593,19 @@
     // reflect the new size after adding the signature operation to the
     // manifest.
     LOG(INFO) << "Signing payload.";
-    SignGeneratedPayload(state->delta_file->path(), &state->metadata_size);
+    SignGeneratedPayload(state->delta_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_file->path());
+    SignGeneratedShellPayload(signature_test, state->delta_path);
   }
 }
 
 static void ApplyDeltaFile(bool full_kernel,
                            bool full_rootfs,
+                           bool noop,
                            SignatureTest signature_test,
                            DeltaState* state,
                            bool hash_checks_mandatory,
@@ -643,14 +614,11 @@
                            uint32_t minor_version) {
   // Check the metadata.
   {
-    EXPECT_TRUE(utils::ReadFile(state->delta_file->path(), &state->delta));
+    EXPECT_TRUE(utils::ReadFile(state->delta_path, &state->delta));
     PayloadMetadata payload_metadata;
     EXPECT_TRUE(payload_metadata.ParsePayloadHeader(state->delta));
     state->metadata_size = payload_metadata.GetMetadataSize();
     LOG(INFO) << "Metadata size: " << state->metadata_size;
-    state->metadata_signature_size =
-        payload_metadata.GetMetadataSignatureSize();
-    LOG(INFO) << "Metadata signature size: " << state->metadata_signature_size;
 
     DeltaArchiveManifest manifest;
     EXPECT_TRUE(payload_metadata.GetManifest(state->delta, &manifest));
@@ -662,8 +630,7 @@
       EXPECT_TRUE(manifest.has_signatures_size());
       Signatures sigs_message;
       EXPECT_TRUE(sigs_message.ParseFromArray(
-          &state->delta[state->metadata_size + state->metadata_signature_size +
-                        manifest.signatures_offset()],
+          &state->delta[state->metadata_size + manifest.signatures_offset()],
           manifest.signatures_size()));
       if (signature_test == kSignatureGeneratedShellRotateCl1 ||
           signature_test == kSignatureGeneratedShellRotateCl2)
@@ -686,63 +653,68 @@
       EXPECT_FALSE(signature.data().empty());
     }
 
-    // TODO(ahassani): Make |DeltaState| into a partition list kind of struct
-    // instead of hardcoded kernel/rootfs so its cleaner and we can make the
-    // following code into a helper function instead.
-    const auto& kernel_part = *std::find_if(
-        manifest.partitions().begin(),
-        manifest.partitions().end(),
-        [](const PartitionUpdate& partition) {
-          return partition.partition_name() == kPartitionNameKernel;
-        });
+    if (noop) {
+      EXPECT_EQ(0, manifest.install_operations_size());
+      EXPECT_EQ(1, manifest.kernel_install_operations_size());
+    }
+
     if (full_kernel) {
-      EXPECT_FALSE(kernel_part.has_old_partition_info());
+      EXPECT_FALSE(manifest.has_old_kernel_info());
     } else {
       EXPECT_EQ(state->old_kernel_data.size(),
-                kernel_part.old_partition_info().size());
-      EXPECT_FALSE(kernel_part.old_partition_info().hash().empty());
+                manifest.old_kernel_info().size());
+      EXPECT_FALSE(manifest.old_kernel_info().hash().empty());
     }
-    EXPECT_EQ(state->new_kernel_data.size(),
-              kernel_part.new_partition_info().size());
-    EXPECT_FALSE(kernel_part.new_partition_info().hash().empty());
 
-    const auto& rootfs_part =
-        *std::find_if(manifest.partitions().begin(),
-                      manifest.partitions().end(),
-                      [](const PartitionUpdate& partition) {
-                        return partition.partition_name() == kPartitionNameRoot;
-                      });
-    if (full_rootfs) {
-      EXPECT_FALSE(rootfs_part.has_old_partition_info());
-    } else {
-      EXPECT_FALSE(rootfs_part.old_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) {
+      if (noop) {
+        EXPECT_EQ(manifest.old_image_info().channel(), "test-channel");
+        EXPECT_EQ(manifest.old_image_info().board(), "test-board");
+        EXPECT_EQ(manifest.old_image_info().version(), "test-version");
+        EXPECT_EQ(manifest.old_image_info().key(), "test-key");
+        EXPECT_EQ(manifest.old_image_info().build_channel(),
+                  "test-build-channel");
+        EXPECT_EQ(manifest.old_image_info().build_version(),
+                  "test-build-version");
+      } else {
+        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");
+      }
     }
-    EXPECT_FALSE(rootfs_part.new_partition_info().hash().empty());
+
+    if (full_rootfs) {
+      EXPECT_FALSE(manifest.has_old_rootfs_info());
+      EXPECT_FALSE(manifest.has_old_image_info());
+      EXPECT_TRUE(manifest.has_new_image_info());
+    } else {
+      EXPECT_EQ(state->image_size, manifest.old_rootfs_info().size());
+      EXPECT_FALSE(manifest.old_rootfs_info().hash().empty());
+    }
+
+    EXPECT_EQ(state->new_kernel_data.size(), manifest.new_kernel_info().size());
+    EXPECT_EQ(state->image_size, manifest.new_rootfs_info().size());
+
+    EXPECT_FALSE(manifest.new_kernel_info().hash().empty());
+    EXPECT_FALSE(manifest.new_rootfs_info().hash().empty());
   }
 
-  NiceMock<MockPrefs> prefs;
-  ON_CALL(prefs, SetInt64(kPrefsManifestMetadataSize, -1))
-      .WillByDefault(Return(true));
-  ON_CALL(prefs, SetInt64(kPrefsUpdateCheckResponseHash, -1))
-      .WillByDefault(Return(true));
-  ON_CALL(prefs, GetString(kPrefsUpdateCheckResponseHash, _))
-      .WillByDefault(Return(true));
-  ON_CALL(prefs, GetString(kPrefsDynamicPartitionMetadataUpdated, _))
-      .WillByDefault(Return(true));
-
-  // Set default expectation to ignore uninteresting calls to
-  // SetString/SetInt64. When starting an update delta_performer might reset
-  // update checkpoints, which results in a lot of calls with empty string or
-  // integer -1. Ignore these.
-  EXPECT_CALL(prefs, SetString(_, IsEmpty())).WillRepeatedly(Return(true));
-  EXPECT_CALL(prefs, SetInt64(_, -1)).WillRepeatedly(Return(true));
-  EXPECT_CALL(prefs, SetInt64(_, 0)).WillRepeatedly(Return(true));
-
+  MockPrefs prefs;
   EXPECT_CALL(prefs, SetInt64(kPrefsManifestMetadataSize, state->metadata_size))
       .WillOnce(Return(true));
-  EXPECT_CALL(
-      prefs,
-      SetInt64(kPrefsManifestSignatureSize, state->metadata_signature_size))
+  EXPECT_CALL(prefs, SetInt64(kPrefsManifestSignatureSize, 0))
       .WillOnce(Return(true));
   EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextOperation, _))
       .WillRepeatedly(Return(true));
@@ -758,15 +730,9 @@
       .WillRepeatedly(Return(true));
   EXPECT_CALL(prefs, SetString(kPrefsDynamicPartitionMetadataUpdated, _))
       .WillRepeatedly(Return(true));
-  EXPECT_CALL(prefs,
-              SetString(kPrefsManifestBytes,
-                        testing::SizeIs(state->metadata_signature_size +
-                                        state->metadata_size)))
-      .WillRepeatedly(Return(true));
   if (op_hash_test == kValidOperationData && signature_test != kSignatureNone) {
-    EXPECT_CALL(prefs,
-                SetString(kPrefsUpdateStateSignatureBlob, Not(IsEmpty())))
-        .WillRepeatedly(Return(true));
+    EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignatureBlob, _))
+        .WillOnce(Return(true));
   }
 
   EXPECT_CALL(state->mock_delegate_, ShouldCancel(_))
@@ -775,8 +741,7 @@
   // Update the A image in place.
   InstallPlan* install_plan = &state->install_plan;
   install_plan->hash_checks_mandatory = hash_checks_mandatory;
-  install_plan->payloads = {{.size = state->delta.size(),
-                             .metadata_size = state->metadata_size,
+  install_plan->payloads = {{.metadata_size = state->metadata_size,
                              .type = (full_kernel && full_rootfs)
                                          ? InstallPayloadType::kFull
                                          : InstallPayloadType::kDelta}};
@@ -814,26 +779,34 @@
   (*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->path(), state->image_size, &root_part.source_hash));
+  EXPECT_EQ(static_cast<off_t>(state->image_size),
+            HashCalculator::RawHashOfFile(
+                state->a_img, state->image_size, &root_part.source_hash));
   EXPECT_TRUE(HashCalculator::RawHashOfData(state->old_kernel_data,
                                             &kernel_part.source_hash));
 
   // The partitions should be empty before DeltaPerformer.
   install_plan->partitions.clear();
 
+  // With minor version 2, we want the target to be the new image, result_img,
+  // but with version 1, we want to update A in place.
+  string target_root, target_kernel;
+  if (minor_version == kSourceMinorPayloadVersion) {
+    target_root = state->result_img;
+    target_kernel = state->result_kernel;
+  } else {
+    target_root = state->a_img;
+    target_kernel = state->old_kernel;
+  }
+
   state->fake_boot_control_.SetPartitionDevice(
-      kPartitionNameRoot, install_plan->source_slot, state->a_img->path());
-  state->fake_boot_control_.SetPartitionDevice(kPartitionNameKernel,
-                                               install_plan->source_slot,
-                                               state->old_kernel->path());
+      kPartitionNameRoot, install_plan->source_slot, state->a_img);
   state->fake_boot_control_.SetPartitionDevice(
-      kPartitionNameRoot, install_plan->target_slot, state->result_img->path());
-  state->fake_boot_control_.SetPartitionDevice(kPartitionNameKernel,
-                                               install_plan->target_slot,
-                                               state->result_kernel->path());
+      kPartitionNameKernel, install_plan->source_slot, state->old_kernel);
+  state->fake_boot_control_.SetPartitionDevice(
+      kPartitionNameRoot, install_plan->target_slot, target_root);
+  state->fake_boot_control_.SetPartitionDevice(
+      kPartitionNameKernel, install_plan->target_slot, target_kernel);
 
   ErrorCode expected_error, actual_error;
   bool continue_writing;
@@ -912,16 +885,21 @@
     return;
   }
 
-  CompareFilesByBlock(state->result_kernel->path(),
-                      state->new_kernel->path(),
-                      state->kernel_size);
-  CompareFilesByBlock(
-      state->result_img->path(), state->b_img->path(), state->image_size);
-
   brillo::Blob updated_kernel_partition;
-  EXPECT_TRUE(
-      utils::ReadFile(state->result_kernel->path(), &updated_kernel_partition));
-  ASSERT_GE(updated_kernel_partition.size(), base::size(kNewData));
+  if (minor_version == kSourceMinorPayloadVersion) {
+    CompareFilesByBlock(
+        state->result_kernel, state->new_kernel, state->kernel_size);
+    CompareFilesByBlock(state->result_img, state->b_img, state->image_size);
+    EXPECT_TRUE(
+        utils::ReadFile(state->result_kernel, &updated_kernel_partition));
+  } else {
+    CompareFilesByBlock(
+        state->old_kernel, state->new_kernel, state->kernel_size);
+    CompareFilesByBlock(state->a_img, state->b_img, state->image_size);
+    EXPECT_TRUE(utils::ReadFile(state->old_kernel, &updated_kernel_partition));
+  }
+
+  ASSERT_GE(updated_kernel_partition.size(), arraysize(kNewData));
   EXPECT_TRUE(std::equal(std::begin(kNewData),
                          std::end(kNewData),
                          updated_kernel_partition.begin()));
@@ -939,10 +917,9 @@
 
   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->path(), state->image_size, &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(expected_new_rootfs_hash, partitions[0].target_hash);
 }
 
@@ -967,6 +944,7 @@
 
 void DoSmallImageTest(bool full_kernel,
                       bool full_rootfs,
+                      bool noop,
                       ssize_t chunk_size,
                       SignatureTest signature_test,
                       bool hash_checks_mandatory,
@@ -975,13 +953,22 @@
   DeltaPerformer* performer = nullptr;
   GenerateDeltaFile(full_kernel,
                     full_rootfs,
+                    noop,
                     chunk_size,
                     signature_test,
                     &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,
+                 noop,
                  signature_test,
                  &state,
                  hash_checks_mandatory,
@@ -996,10 +983,17 @@
                                  bool hash_checks_mandatory) {
   DeltaState state;
   uint64_t minor_version = kFullPayloadMinorVersion;
-  GenerateDeltaFile(true, true, -1, kSignatureGenerated, &state, minor_version);
+  GenerateDeltaFile(
+      true, true, false, -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,
+                 false,
                  kSignatureGenerated,
                  &state,
                  hash_checks_mandatory,
@@ -1009,242 +1003,166 @@
   delete performer;
 }
 
-TEST_F(DeltaPerformerIntegrationTest, RunAsRootSmallImageTest) {
-  DoSmallImageTest(
-      false, false, -1, kSignatureGenerator, false, kSourceMinorPayloadVersion);
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageTest) {
+  DoSmallImageTest(false,
+                   false,
+                   false,
+                   -1,
+                   kSignatureGenerator,
+                   false,
+                   kInPlaceMinorPayloadVersion);
 }
 
-TEST_F(DeltaPerformerIntegrationTest,
-       RunAsRootSmallImageSignaturePlaceholderTest) {
+TEST(DeltaPerformerIntegrationTest,
+     RunAsRootSmallImageSignaturePlaceholderTest) {
   DoSmallImageTest(false,
                    false,
+                   false,
                    -1,
                    kSignatureGeneratedPlaceholder,
                    false,
-                   kSourceMinorPayloadVersion);
+                   kInPlaceMinorPayloadVersion);
 }
 
-TEST_F(DeltaPerformerIntegrationTest,
-       RunAsRootSmallImageSignaturePlaceholderMismatchTest) {
+TEST(DeltaPerformerIntegrationTest,
+     RunAsRootSmallImageSignaturePlaceholderMismatchTest) {
   DeltaState state;
   GenerateDeltaFile(false,
                     false,
+                    false,
                     -1,
                     kSignatureGeneratedPlaceholderMismatch,
                     &state,
-                    kSourceMinorPayloadVersion);
+                    kInPlaceMinorPayloadVersion);
 }
 
-TEST_F(DeltaPerformerIntegrationTest, RunAsRootSmallImageChunksTest) {
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageChunksTest) {
   DoSmallImageTest(false,
                    false,
+                   false,
                    kBlockSize,
                    kSignatureGenerator,
                    false,
+                   kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootFullKernelSmallImageTest) {
+  DoSmallImageTest(true,
+                   false,
+                   false,
+                   -1,
+                   kSignatureGenerator,
+                   false,
+                   kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootFullSmallImageTest) {
+  DoSmallImageTest(true,
+                   true,
+                   false,
+                   -1,
+                   kSignatureGenerator,
+                   true,
+                   kFullPayloadMinorVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootNoopSmallImageTest) {
+  DoSmallImageTest(false,
+                   false,
+                   true,
+                   -1,
+                   kSignatureGenerator,
+                   false,
+                   kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignNoneTest) {
+  DoSmallImageTest(false,
+                   false,
+                   false,
+                   -1,
+                   kSignatureNone,
+                   false,
+                   kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedTest) {
+  DoSmallImageTest(false,
+                   false,
+                   false,
+                   -1,
+                   kSignatureGenerated,
+                   true,
+                   kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedShellTest) {
+  DoSmallImageTest(false,
+                   false,
+                   false,
+                   -1,
+                   kSignatureGeneratedShell,
+                   false,
+                   kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest,
+     RunAsRootSmallImageSignGeneratedShellECKeyTest) {
+  DoSmallImageTest(false,
+                   false,
+                   false,
+                   -1,
+                   kSignatureGeneratedShellECKey,
+                   false,
+                   kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest,
+     RunAsRootSmallImageSignGeneratedShellBadKeyTest) {
+  DoSmallImageTest(false,
+                   false,
+                   false,
+                   -1,
+                   kSignatureGeneratedShellBadKey,
+                   false,
+                   kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest,
+     RunAsRootSmallImageSignGeneratedShellRotateCl1Test) {
+  DoSmallImageTest(false,
+                   false,
+                   false,
+                   -1,
+                   kSignatureGeneratedShellRotateCl1,
+                   false,
+                   kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest,
+     RunAsRootSmallImageSignGeneratedShellRotateCl2Test) {
+  DoSmallImageTest(false,
+                   false,
+                   false,
+                   -1,
+                   kSignatureGeneratedShellRotateCl2,
+                   false,
+                   kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSourceOpsTest) {
+  DoSmallImageTest(false,
+                   false,
+                   false,
+                   -1,
+                   kSignatureGenerator,
+                   false,
                    kSourceMinorPayloadVersion);
 }
 
-TEST_F(DeltaPerformerIntegrationTest, RunAsRootFullKernelSmallImageTest) {
-  DoSmallImageTest(
-      true, false, -1, kSignatureGenerator, false, kSourceMinorPayloadVersion);
-}
-
-TEST_F(DeltaPerformerIntegrationTest, RunAsRootFullSmallImageTest) {
-  DoSmallImageTest(
-      true, true, -1, kSignatureGenerator, true, kFullPayloadMinorVersion);
-}
-
-TEST_F(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignNoneTest) {
-  DoSmallImageTest(
-      false, false, -1, kSignatureNone, false, kSourceMinorPayloadVersion);
-}
-
-TEST_F(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedTest) {
-  DoSmallImageTest(
-      false, false, -1, kSignatureGenerated, true, kSourceMinorPayloadVersion);
-}
-
-TEST_F(DeltaPerformerIntegrationTest,
-       RunAsRootSmallImageSignGeneratedShellTest) {
-  DoSmallImageTest(false,
-                   false,
-                   -1,
-                   kSignatureGeneratedShell,
-                   false,
-                   kSourceMinorPayloadVersion);
-}
-
-TEST_F(DeltaPerformerIntegrationTest,
-       RunAsRootSmallImageSignGeneratedShellECKeyTest) {
-  DoSmallImageTest(false,
-                   false,
-                   -1,
-                   kSignatureGeneratedShellECKey,
-                   false,
-                   kSourceMinorPayloadVersion);
-}
-
-TEST_F(DeltaPerformerIntegrationTest,
-       RunAsRootSmallImageSignGeneratedShellBadKeyTest) {
-  DoSmallImageTest(false,
-                   false,
-                   -1,
-                   kSignatureGeneratedShellBadKey,
-                   false,
-                   kSourceMinorPayloadVersion);
-}
-
-TEST_F(DeltaPerformerIntegrationTest,
-       RunAsRootSmallImageSignGeneratedShellRotateCl1Test) {
-  DoSmallImageTest(false,
-                   false,
-                   -1,
-                   kSignatureGeneratedShellRotateCl1,
-                   false,
-                   kSourceMinorPayloadVersion);
-}
-
-TEST_F(DeltaPerformerIntegrationTest,
-       RunAsRootSmallImageSignGeneratedShellRotateCl2Test) {
-  DoSmallImageTest(false,
-                   false,
-                   -1,
-                   kSignatureGeneratedShellRotateCl2,
-                   false,
-                   kSourceMinorPayloadVersion);
-}
-
-TEST_F(DeltaPerformerIntegrationTest, RunAsRootSmallImageSourceOpsTest) {
-  DoSmallImageTest(
-      false, false, -1, kSignatureGenerator, false, kSourceMinorPayloadVersion);
-}
-
-TEST_F(DeltaPerformerIntegrationTest,
-       RunAsRootMandatoryOperationHashMismatchTest) {
+TEST(DeltaPerformerIntegrationTest,
+     RunAsRootMandatoryOperationHashMismatchTest) {
   DoOperationHashMismatchTest(kInvalidOperationData, true);
 }
 
-TEST_F(DeltaPerformerIntegrationTest, ValidatePerPartitionTimestampSuccess) {
-  // The Manifest we are validating.
-  DeltaArchiveManifest manifest;
-
-  fake_hardware_.SetVersion("system", "5");
-  fake_hardware_.SetVersion("product", "99");
-  fake_hardware_.SetBuildTimestamp(1);
-
-  manifest.set_minor_version(kFullPayloadMinorVersion);
-  manifest.set_max_timestamp(2);
-  AddPartition(&manifest, "system", 10);
-  AddPartition(&manifest, "product", 100);
-
-  RunManifestValidation(
-      manifest, kMaxSupportedMajorPayloadVersion, ErrorCode::kSuccess);
-}
-
-TEST_F(DeltaPerformerIntegrationTest, ValidatePerPartitionTimestampFailure) {
-  // The Manifest we are validating.
-  DeltaArchiveManifest manifest;
-
-  fake_hardware_.SetVersion("system", "5");
-  fake_hardware_.SetVersion("product", "99");
-  fake_hardware_.SetBuildTimestamp(1);
-
-  manifest.set_minor_version(kFullPayloadMinorVersion);
-  manifest.set_max_timestamp(2);
-  AddPartition(&manifest, "system", 10);
-  AddPartition(&manifest, "product", 98);
-
-  RunManifestValidation(manifest,
-                        kMaxSupportedMajorPayloadVersion,
-                        ErrorCode::kPayloadTimestampError);
-}
-
-TEST_F(DeltaPerformerIntegrationTest,
-       ValidatePerPartitionTimestampMissingTimestamp) {
-  // The Manifest we are validating.
-  DeltaArchiveManifest manifest;
-
-  fake_hardware_.SetVersion("system", "5");
-  fake_hardware_.SetVersion("product", "99");
-  fake_hardware_.SetBuildTimestamp(1);
-
-  manifest.set_minor_version(kFullPayloadMinorVersion);
-  manifest.set_max_timestamp(2);
-  AddPartition(&manifest, "system", 10);
-  {
-    auto& partition = *manifest.add_partitions();
-    // For complete updates, missing timestamp should not trigger
-    // timestamp error.
-    partition.set_partition_name("product");
-  }
-
-  RunManifestValidation(
-      manifest, kMaxSupportedMajorPayloadVersion, ErrorCode::kSuccess);
-}
-
-TEST_F(DeltaPerformerIntegrationTest,
-       ValidatePerPartitionTimestampPartialUpdatePass) {
-  fake_hardware_.SetVersion("system", "5");
-  fake_hardware_.SetVersion("product", "99");
-
-  DeltaArchiveManifest manifest;
-  manifest.set_minor_version(kPartialUpdateMinorPayloadVersion);
-  manifest.set_partial_update(true);
-  AddPartition(&manifest, "product", 100);
-  RunManifestValidation(
-      manifest, kMaxSupportedMajorPayloadVersion, ErrorCode::kSuccess);
-}
-
-TEST_F(DeltaPerformerIntegrationTest,
-       ValidatePerPartitionTimestampPartialUpdateDowngrade) {
-  fake_hardware_.SetVersion("system", "5");
-  fake_hardware_.SetVersion("product", "99");
-
-  DeltaArchiveManifest manifest;
-  manifest.set_minor_version(kPartialUpdateMinorPayloadVersion);
-  manifest.set_partial_update(true);
-  AddPartition(&manifest, "product", 98);
-  RunManifestValidation(manifest,
-                        kMaxSupportedMajorPayloadVersion,
-                        ErrorCode::kPayloadTimestampError);
-}
-
-TEST_F(DeltaPerformerIntegrationTest,
-       ValidatePerPartitionTimestampPartialUpdateMissingVersion) {
-  fake_hardware_.SetVersion("system", "5");
-  fake_hardware_.SetVersion("product", "99");
-
-  DeltaArchiveManifest manifest;
-  manifest.set_minor_version(kPartialUpdateMinorPayloadVersion);
-  manifest.set_partial_update(true);
-  {
-    auto& partition = *manifest.add_partitions();
-    // For partial updates, missing timestamp should trigger an error
-    partition.set_partition_name("product");
-    // has_version() == false.
-  }
-  RunManifestValidation(manifest,
-                        kMaxSupportedMajorPayloadVersion,
-                        ErrorCode::kDownloadManifestParseError);
-}
-
-TEST_F(DeltaPerformerIntegrationTest,
-       ValidatePerPartitionTimestampPartialUpdateEmptyVersion) {
-  fake_hardware_.SetVersion("system", "5");
-  fake_hardware_.SetVersion("product", "99");
-
-  DeltaArchiveManifest manifest;
-  manifest.set_minor_version(kPartialUpdateMinorPayloadVersion);
-  manifest.set_partial_update(true);
-  {
-    auto& partition = *manifest.add_partitions();
-    // For partial updates, invalid timestamp should trigger an error
-    partition.set_partition_name("product");
-    partition.set_version("something");
-  }
-  RunManifestValidation(manifest,
-                        kMaxSupportedMajorPayloadVersion,
-                        ErrorCode::kDownloadManifestParseError);
-}
-
 }  // namespace chromeos_update_engine
diff --git a/payload_consumer/delta_performer_unittest.cc b/payload_consumer/delta_performer_unittest.cc
index 840ecf6..e9022ba 100644
--- a/payload_consumer/delta_performer_unittest.cc
+++ b/payload_consumer/delta_performer_unittest.cc
@@ -20,8 +20,6 @@
 #include <inttypes.h>
 #include <time.h>
 
-#include <algorithm>
-#include <map>
 #include <memory>
 #include <string>
 #include <vector>
@@ -29,29 +27,22 @@
 #include <base/files/file_path.h>
 #include <base/files/file_util.h>
 #include <base/files/scoped_temp_dir.h>
-#include <base/stl_util.h>
 #include <base/strings/string_number_conversions.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
-#include <brillo/secure_blob.h>
 #include <gmock/gmock.h>
 #include <google/protobuf/repeated_field.h>
 #include <gtest/gtest.h>
 
 #include "update_engine/common/constants.h"
-#include "update_engine/common/error_code.h"
 #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/hash_calculator.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_partition_writer.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"
 #include "update_engine/payload_generator/extent_ranges.h"
 #include "update_engine/payload_generator/payload_file.h"
@@ -64,9 +55,8 @@
 using std::vector;
 using test_utils::GetBuildArtifactsPath;
 using test_utils::kRandomString;
+using test_utils::System;
 using testing::_;
-using testing::Return;
-using ::testing::Sequence;
 
 extern const char* kUnittestPrivateKeyPath;
 extern const char* kUnittestPublicKeyPath;
@@ -208,7 +198,7 @@
                                uint64_t major_version,
                                uint32_t minor_version,
                                PartitionConfig* old_part = nullptr) {
-    ScopedTempFile blob_file("Blob-XXXXXX");
+    test_utils::ScopedTempFile blob_file("Blob-XXXXXX");
     EXPECT_TRUE(test_utils::WriteFileVector(blob_file.path(), blob_data));
 
     PayloadGenerationConfig config;
@@ -234,15 +224,15 @@
     new_part.path = "/dev/zero";
     new_part.size = 1234;
 
-    payload.AddPartition(*old_part, new_part, aops, {}, 0);
+    payload.AddPartition(*old_part, new_part, aops);
 
     // 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, {}, {}, 0);
+    payload.AddPartition(*old_part, new_part, {});
 
-    ScopedTempFile payload_file("Payload-XXXXXX");
+    test_utils::ScopedTempFile payload_file("Payload-XXXXXX");
     string private_key =
         sign_payload ? GetBuildArtifactsPath(kUnittestPrivateKeyPath) : "";
     EXPECT_TRUE(payload.WritePayload(payload_file.path(),
@@ -281,14 +271,7 @@
                             const string& source_path,
                             bool expect_success) {
     return ApplyPayloadToData(
-        &performer_, payload_data, source_path, brillo::Blob(), expect_success);
-  }
-  brillo::Blob ApplyPayloadToData(const brillo::Blob& payload_data,
-                                  const string& source_path,
-                                  const brillo::Blob& target_data,
-                                  bool expect_success) {
-    return ApplyPayloadToData(
-        &performer_, payload_data, source_path, target_data, expect_success);
+        payload_data, source_path, brillo::Blob(), expect_success);
   }
 
   // Apply the payload provided in |payload_data| reading from the |source_path|
@@ -296,15 +279,13 @@
   // new target file are set to |target_data| before applying the payload.
   // Expect result of performer_.Write() to be |expect_success|.
   // Returns the result of the payload application.
-  brillo::Blob ApplyPayloadToData(DeltaPerformer* delta_performer,
-                                  const brillo::Blob& payload_data,
+  brillo::Blob ApplyPayloadToData(const brillo::Blob& payload_data,
                                   const string& source_path,
                                   const brillo::Blob& target_data,
                                   bool expect_success) {
-    ScopedTempFile new_part("Partition-XXXXXX");
+    test_utils::ScopedTempFile new_part("Partition-XXXXXX");
     EXPECT_TRUE(test_utils::WriteFileVector(new_part.path(), target_data));
 
-    payload_.size = payload_data.size();
     // We installed the operations only in the rootfs partition, but the
     // delta performer needs to access all the partitions.
     fake_boot_control_.SetPartitionDevice(
@@ -317,7 +298,7 @@
         kPartitionNameKernel, install_plan_.source_slot, "/dev/null");
 
     EXPECT_EQ(expect_success,
-              delta_performer->Write(payload_data.data(), payload_data.size()));
+              performer_.Write(payload_data.data(), payload_data.size()));
     EXPECT_EQ(0, performer_.Close());
 
     brillo::Blob partition_data;
@@ -337,17 +318,14 @@
 
     // Set a valid magic string and version number 1.
     EXPECT_TRUE(performer_.Write("CrAU", 4));
-    uint64_t version = htobe64(kBrilloMajorPayloadVersion);
+    uint64_t version = htobe64(kChromeOSMajorPayloadVersion);
     EXPECT_TRUE(performer_.Write(&version, 8));
 
     payload_.metadata_size = expected_metadata_size;
-    payload_.size = actual_metadata_size + 1;
     ErrorCode error_code;
-    // When filling in size in manifest, exclude the size of the 24-byte header.
-    uint64_t size_in_manifest = htobe64(actual_metadata_size - 24);
-    performer_.Write(&size_in_manifest, 8, &error_code);
-    auto signature_size = htobe64(10);
-    bool result = performer_.Write(&signature_size, 4, &error_code);
+    // When filling in size in manifest, exclude the size of the 20-byte header.
+    uint64_t size_in_manifest = htobe64(actual_metadata_size - 20);
+    bool result = performer_.Write(&size_in_manifest, 8, &error_code);
     if (expected_metadata_size == actual_metadata_size ||
         !hash_checks_mandatory) {
       EXPECT_TRUE(result);
@@ -359,7 +337,7 @@
     EXPECT_LT(performer_.Close(), 0);
   }
 
-  // Generates a valid delta file but tests the delta performer by supplying
+  // Generates a valid delta file but tests the delta performer by suppling
   // different metadata signatures as per metadata_signature_test flag and
   // sees if the result of the parsing are as per hash_checks_mandatory flag.
   void DoMetadataSignatureTest(MetadataSignatureTest metadata_signature_test,
@@ -369,10 +347,9 @@
     brillo::Blob payload = GeneratePayload(brillo::Blob(),
                                            vector<AnnotatedOperation>(),
                                            sign_payload,
-                                           kBrilloMajorPayloadVersion,
+                                           kChromeOSMajorPayloadVersion,
                                            kFullPayloadMinorVersion);
 
-    payload_.size = payload.size();
     LOG(INFO) << "Payload size: " << payload.size();
 
     install_plan_.hash_checks_mandatory = hash_checks_mandatory;
@@ -384,9 +361,6 @@
     switch (metadata_signature_test) {
       case kEmptyMetadataSignature:
         payload_.metadata_signature.clear();
-        // We need to set the signature size in a signed payload to zero.
-        std::fill(
-            std::next(payload.begin(), 20), std::next(payload.begin(), 24), 0);
         expected_result = MetadataParseResult::kError;
         expected_error = ErrorCode::kDownloadMetadataSignatureMissingError;
         break;
@@ -432,6 +406,23 @@
     EXPECT_EQ(payload_.metadata_size, performer_.metadata_size_);
   }
 
+  // Helper function to pretend that the ECC file descriptor was already opened.
+  // Returns a pointer to the created file descriptor.
+  FakeFileDescriptor* SetFakeECCFile(size_t size) {
+    EXPECT_FALSE(performer_.source_ecc_fd_) << "source_ecc_fd_ already open.";
+    FakeFileDescriptor* ret = new FakeFileDescriptor();
+    fake_ecc_fd_.reset(ret);
+    // Call open to simulate it was already opened.
+    ret->Open("", 0);
+    ret->SetFileSize(size);
+    performer_.source_ecc_fd_ = fake_ecc_fd_;
+    return ret;
+  }
+
+  uint64_t GetSourceEccRecoveredFailures() const {
+    return performer_.source_ecc_recovered_failures_;
+  }
+
   FakePrefs prefs_;
   InstallPlan install_plan_;
   InstallPlan::Payload payload_;
@@ -464,7 +455,7 @@
   brillo::Blob payload_data = GeneratePayload(expected_data,
                                               aops,
                                               false,
-                                              kBrilloMajorPayloadVersion,
+                                              kChromeOSMajorPayloadVersion,
                                               kFullPayloadMinorVersion);
 
   EXPECT_EQ(expected_data, ApplyPayload(payload_data, "/dev/null", true));
@@ -486,7 +477,7 @@
   brillo::Blob payload_data = GeneratePayload(expected_data,
                                               aops,
                                               false,
-                                              kBrilloMajorPayloadVersion,
+                                              kChromeOSMajorPayloadVersion,
                                               kFullPayloadMinorVersion);
 
   testing::Mock::VerifyAndClearExpectations(&mock_delegate_);
@@ -588,7 +579,7 @@
   EXPECT_TRUE(HashCalculator::RawHashOfData(expected_data, &src_hash));
   aop.op.set_src_sha256_hash(src_hash.data(), src_hash.size());
 
-  ScopedTempFile source("Source-XXXXXX");
+  test_utils::ScopedTempFile source("Source-XXXXXX");
   EXPECT_TRUE(test_utils::WriteFileVector(source.path(), expected_data));
 
   PartitionConfig old_part(kPartitionNameRoot);
@@ -616,7 +607,7 @@
   EXPECT_TRUE(HashCalculator::RawHashOfData(src, &src_hash));
   aop.op.set_src_sha256_hash(src_hash.data(), src_hash.size());
 
-  ScopedTempFile source("Source-XXXXXX");
+  test_utils::ScopedTempFile source("Source-XXXXXX");
   EXPECT_TRUE(test_utils::WriteFileVector(source.path(), src));
 
   PartitionConfig old_part(kPartitionNameRoot);
@@ -644,7 +635,7 @@
   EXPECT_TRUE(HashCalculator::RawHashOfData(expected_data, &src_hash));
   aop.op.set_src_sha256_hash(src_hash.data(), src_hash.size());
 
-  ScopedTempFile source("Source-XXXXXX");
+  test_utils::ScopedTempFile source("Source-XXXXXX");
   EXPECT_TRUE(test_utils::WriteFileVector(source.path(), actual_data));
 
   PartitionConfig old_part(kPartitionNameRoot);
@@ -657,14 +648,103 @@
   EXPECT_EQ(actual_data, ApplyPayload(payload_data, source.path(), false));
 }
 
+// Test that the error-corrected file descriptor is used to read the partition
+// since the source partition doesn't match the operation hash.
+TEST_F(DeltaPerformerTest, ErrorCorrectionSourceCopyFallbackTest) {
+  constexpr size_t kCopyOperationSize = 4 * 4096;
+  test_utils::ScopedTempFile source("Source-XXXXXX");
+  // Write invalid data to the source image, which doesn't match the expected
+  // hash.
+  brillo::Blob invalid_data(kCopyOperationSize, 0x55);
+  EXPECT_TRUE(test_utils::WriteFileVector(source.path(), invalid_data));
+
+  // Setup the fec file descriptor as the fake stream, which matches
+  // |expected_data|.
+  FakeFileDescriptor* fake_fec = SetFakeECCFile(kCopyOperationSize);
+  brillo::Blob expected_data = FakeFileDescriptorData(kCopyOperationSize);
+
+  PartitionConfig old_part(kPartitionNameRoot);
+  old_part.path = source.path();
+  old_part.size = invalid_data.size();
+
+  brillo::Blob payload_data =
+      GenerateSourceCopyPayload(expected_data, true, &old_part);
+  EXPECT_EQ(expected_data, ApplyPayload(payload_data, source.path(), true));
+  // Verify that the fake_fec was actually used.
+  EXPECT_EQ(1U, fake_fec->GetReadOps().size());
+  EXPECT_EQ(1U, GetSourceEccRecoveredFailures());
+}
+
+// Test that the error-corrected file descriptor is used to read a partition
+// when no hash is available for SOURCE_COPY but it falls back to the normal
+// file descriptor when the size of the error corrected one is too small.
+TEST_F(DeltaPerformerTest, ErrorCorrectionSourceCopyWhenNoHashFallbackTest) {
+  constexpr size_t kCopyOperationSize = 4 * 4096;
+  test_utils::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));
+
+  // Setup the fec file descriptor as the fake stream, with smaller data than
+  // the expected.
+  FakeFileDescriptor* fake_fec = SetFakeECCFile(kCopyOperationSize / 2);
+
+  PartitionConfig old_part(kPartitionNameRoot);
+  old_part.path = source.path();
+  old_part.size = expected_data.size();
+
+  // The payload operation doesn't include an operation hash.
+  brillo::Blob payload_data =
+      GenerateSourceCopyPayload(expected_data, false, &old_part);
+  EXPECT_EQ(expected_data, ApplyPayload(payload_data, source.path(), true));
+  // Verify that the fake_fec was attempted to be used. Since the file
+  // descriptor is shorter it can actually do more than one read to realize it
+  // reached the EOF.
+  EXPECT_LE(1U, fake_fec->GetReadOps().size());
+  // This fallback doesn't count as an error-corrected operation since the
+  // operation hash was not available.
+  EXPECT_EQ(0U, GetSourceEccRecoveredFailures());
+}
+
+TEST_F(DeltaPerformerTest, ChooseSourceFDTest) {
+  constexpr size_t kSourceSize = 4 * 4096;
+  test_utils::ScopedTempFile source("Source-XXXXXX");
+  // Write invalid data to the source image, which doesn't match the expected
+  // hash.
+  brillo::Blob invalid_data(kSourceSize, 0x55);
+  EXPECT_TRUE(test_utils::WriteFileVector(source.path(), invalid_data));
+
+  performer_.source_fd_ = std::make_shared<EintrSafeFileDescriptor>();
+  performer_.source_fd_->Open(source.path().c_str(), O_RDONLY);
+  performer_.block_size_ = 4096;
+
+  // Setup the fec file descriptor as the fake stream, which matches
+  // |expected_data|.
+  FakeFileDescriptor* fake_fec = SetFakeECCFile(kSourceSize);
+  brillo::Blob expected_data = FakeFileDescriptorData(kSourceSize);
+
+  InstallOperation op;
+  *(op.add_src_extents()) = ExtentForRange(0, kSourceSize / 4096);
+  brillo::Blob src_hash;
+  EXPECT_TRUE(HashCalculator::RawHashOfData(expected_data, &src_hash));
+  op.set_src_sha256_hash(src_hash.data(), src_hash.size());
+
+  ErrorCode error = ErrorCode::kSuccess;
+  EXPECT_EQ(performer_.source_ecc_fd_, performer_.ChooseSourceFD(op, &error));
+  EXPECT_EQ(ErrorCode::kSuccess, error);
+  // Verify that the fake_fec was actually used.
+  EXPECT_EQ(1U, fake_fec->GetReadOps().size());
+  EXPECT_EQ(1U, GetSourceEccRecoveredFailures());
+}
+
 TEST_F(DeltaPerformerTest, ExtentsToByteStringTest) {
   uint64_t test[] = {1, 1, 4, 2, 0, 1};
-  static_assert(base::size(test) % 2 == 0, "Array size uneven");
+  static_assert(arraysize(test) % 2 == 0, "Array size uneven");
   const uint64_t block_size = 4096;
   const uint64_t file_length = 4 * block_size - 13;
 
   google::protobuf::RepeatedPtrField<Extent> extents;
-  for (size_t i = 0; i < base::size(test); i += 2) {
+  for (size_t i = 0; i < arraysize(test); i += 2) {
     *(extents.Add()) = ExtentForRange(test[i], test[i + 1]);
   }
 
@@ -678,32 +758,27 @@
 TEST_F(DeltaPerformerTest, ValidateManifestFullGoodTest) {
   // The Manifest we are validating.
   DeltaArchiveManifest manifest;
-  for (const auto& part_name : {"kernel", "rootfs"}) {
-    auto part = manifest.add_partitions();
-    part->set_partition_name(part_name);
-    part->mutable_new_partition_info();
-  }
+  manifest.mutable_new_kernel_info();
+  manifest.mutable_new_rootfs_info();
   manifest.set_minor_version(kFullPayloadMinorVersion);
 
   RunManifestValidation(manifest,
-                        kBrilloMajorPayloadVersion,
+                        kChromeOSMajorPayloadVersion,
                         InstallPayloadType::kFull,
                         ErrorCode::kSuccess);
 }
 
-TEST_F(DeltaPerformerTest, ValidateManifestDeltaMaxGoodTest) {
+TEST_F(DeltaPerformerTest, ValidateManifestDeltaGoodTest) {
   // The Manifest we are validating.
   DeltaArchiveManifest manifest;
-  for (const auto& part_name : {"kernel", "rootfs"}) {
-    auto part = manifest.add_partitions();
-    part->set_partition_name(part_name);
-    part->mutable_old_partition_info();
-    part->mutable_new_partition_info();
-  }
+  manifest.mutable_old_kernel_info();
+  manifest.mutable_old_rootfs_info();
+  manifest.mutable_new_kernel_info();
+  manifest.mutable_new_rootfs_info();
   manifest.set_minor_version(kMaxSupportedMinorPayloadVersion);
 
   RunManifestValidation(manifest,
-                        kBrilloMajorPayloadVersion,
+                        kChromeOSMajorPayloadVersion,
                         InstallPayloadType::kDelta,
                         ErrorCode::kSuccess);
 }
@@ -711,16 +786,14 @@
 TEST_F(DeltaPerformerTest, ValidateManifestDeltaMinGoodTest) {
   // The Manifest we are validating.
   DeltaArchiveManifest manifest;
-  for (const auto& part_name : {"kernel", "rootfs"}) {
-    auto part = manifest.add_partitions();
-    part->set_partition_name(part_name);
-    part->mutable_old_partition_info();
-    part->mutable_new_partition_info();
-  }
+  manifest.mutable_old_kernel_info();
+  manifest.mutable_old_rootfs_info();
+  manifest.mutable_new_kernel_info();
+  manifest.mutable_new_rootfs_info();
   manifest.set_minor_version(kMinSupportedMinorPayloadVersion);
 
   RunManifestValidation(manifest,
-                        kBrilloMajorPayloadVersion,
+                        kChromeOSMajorPayloadVersion,
                         InstallPayloadType::kDelta,
                         ErrorCode::kSuccess);
 }
@@ -738,11 +811,9 @@
 TEST_F(DeltaPerformerTest, ValidateManifestDeltaUnsetMinorVersion) {
   // The Manifest we are validating.
   DeltaArchiveManifest manifest;
-  // Add an empty rootfs partition info to trick the DeltaPerformer into think
-  // that this is a delta payload manifest with a missing minor version.
-  auto rootfs = manifest.add_partitions();
-  rootfs->set_partition_name("rootfs");
-  rootfs->mutable_old_partition_info();
+  // Add an empty old_rootfs_info() to trick the DeltaPerformer into think that
+  // this is a delta payload manifest with a missing minor version.
+  manifest.mutable_old_rootfs_info();
 
   RunManifestValidation(manifest,
                         kMaxSupportedMajorPayloadVersion,
@@ -753,15 +824,27 @@
 TEST_F(DeltaPerformerTest, ValidateManifestFullOldKernelTest) {
   // The Manifest we are validating.
   DeltaArchiveManifest manifest;
-  for (const auto& part_name : {"kernel", "rootfs"}) {
-    auto part = manifest.add_partitions();
-    part->set_partition_name(part_name);
-    part->mutable_old_partition_info();
-    part->mutable_new_partition_info();
-  }
-  manifest.mutable_partitions(0)->clear_old_partition_info();
+  manifest.mutable_old_kernel_info();
+  manifest.mutable_new_kernel_info();
+  manifest.mutable_new_rootfs_info();
+  manifest.set_minor_version(kMaxSupportedMinorPayloadVersion);
+
   RunManifestValidation(manifest,
-                        kBrilloMajorPayloadVersion,
+                        kChromeOSMajorPayloadVersion,
+                        InstallPayloadType::kFull,
+                        ErrorCode::kPayloadMismatchedType);
+}
+
+TEST_F(DeltaPerformerTest, ValidateManifestFullOldRootfsTest) {
+  // The Manifest we are validating.
+  DeltaArchiveManifest manifest;
+  manifest.mutable_old_rootfs_info();
+  manifest.mutable_new_kernel_info();
+  manifest.mutable_new_rootfs_info();
+  manifest.set_minor_version(kMaxSupportedMinorPayloadVersion);
+
+  RunManifestValidation(manifest,
+                        kChromeOSMajorPayloadVersion,
                         InstallPayloadType::kFull,
                         ErrorCode::kPayloadMismatchedType);
 }
@@ -786,8 +869,8 @@
 
   // Generate a bad version number.
   manifest.set_minor_version(kMaxSupportedMinorPayloadVersion + 10000);
-  // Mark the manifest as a delta payload by setting |old_partition_info|.
-  manifest.add_partitions()->mutable_old_partition_info();
+  // Mark the manifest as a delta payload by setting old_rootfs_info.
+  manifest.mutable_old_rootfs_info();
 
   RunManifestValidation(manifest,
                         kMaxSupportedMajorPayloadVersion,
@@ -809,50 +892,20 @@
                         ErrorCode::kPayloadTimestampError);
 }
 
-TEST_F(DeltaPerformerTest, ValidatePerPartitionTimestampSuccess) {
-  // The Manifest we are validating.
-  DeltaArchiveManifest manifest;
-
-  manifest.set_minor_version(kFullPayloadMinorVersion);
-  manifest.set_max_timestamp(2);
-  fake_hardware_.SetBuildTimestamp(1);
-  auto& partition = *manifest.add_partitions();
-  partition.set_version("10");
-  partition.set_partition_name("system");
-  fake_hardware_.SetVersion("system", "5");
-
-  RunManifestValidation(manifest,
-                        kMaxSupportedMajorPayloadVersion,
-                        InstallPayloadType::kFull,
-                        ErrorCode::kSuccess);
-}
-
 TEST_F(DeltaPerformerTest, BrilloMetadataSignatureSizeTest) {
   unsigned int seed = time(nullptr);
   EXPECT_TRUE(performer_.Write(kDeltaMagic, sizeof(kDeltaMagic)));
 
   uint64_t major_version = htobe64(kBrilloMajorPayloadVersion);
-  EXPECT_TRUE(
-      performer_.Write(&major_version, PayloadMetadata::kDeltaVersionSize));
+  EXPECT_TRUE(performer_.Write(&major_version, 8));
 
   uint64_t manifest_size = rand_r(&seed) % 256;
-  uint32_t metadata_signature_size = rand_r(&seed) % 256;
-
-  // The payload size has to be bigger than the |metadata_size| and
-  // |metadata_signature_size|
-  payload_.size = PayloadMetadata::kDeltaManifestSizeOffset +
-                  PayloadMetadata::kDeltaManifestSizeSize +
-                  PayloadMetadata::kDeltaMetadataSignatureSizeSize +
-                  manifest_size + metadata_signature_size + 1;
-
   uint64_t manifest_size_be = htobe64(manifest_size);
-  EXPECT_TRUE(performer_.Write(&manifest_size_be,
-                               PayloadMetadata::kDeltaManifestSizeSize));
+  EXPECT_TRUE(performer_.Write(&manifest_size_be, 8));
 
+  uint32_t metadata_signature_size = rand_r(&seed) % 256;
   uint32_t metadata_signature_size_be = htobe32(metadata_signature_size);
-  EXPECT_TRUE(
-      performer_.Write(&metadata_signature_size_be,
-                       PayloadMetadata::kDeltaMetadataSignatureSizeSize));
+  EXPECT_TRUE(performer_.Write(&metadata_signature_size_be, 4));
 
   EXPECT_LT(performer_.Close(), 0);
 
@@ -862,74 +915,10 @@
   EXPECT_EQ(metadata_signature_size, performer_.metadata_signature_size_);
 }
 
-TEST_F(DeltaPerformerTest, BrilloMetadataSizeNOKTest) {
-  unsigned int seed = time(nullptr);
-  EXPECT_TRUE(performer_.Write(kDeltaMagic, sizeof(kDeltaMagic)));
-
-  uint64_t major_version = htobe64(kBrilloMajorPayloadVersion);
-  EXPECT_TRUE(
-      performer_.Write(&major_version, PayloadMetadata::kDeltaVersionSize));
-
-  uint64_t manifest_size = UINT64_MAX - 600;  // Subtract to avoid wrap around.
-  uint64_t manifest_offset = PayloadMetadata::kDeltaManifestSizeOffset +
-                             PayloadMetadata::kDeltaManifestSizeSize +
-                             PayloadMetadata::kDeltaMetadataSignatureSizeSize;
-  payload_.metadata_size = manifest_offset + manifest_size;
-  uint32_t metadata_signature_size = rand_r(&seed) % 256;
-
-  // The payload size is greater than the payload header but smaller than
-  // |metadata_signature_size| + |metadata_size|
-  payload_.size = manifest_offset + metadata_signature_size + 1;
-
-  uint64_t manifest_size_be = htobe64(manifest_size);
-  EXPECT_TRUE(performer_.Write(&manifest_size_be,
-                               PayloadMetadata::kDeltaManifestSizeSize));
-  uint32_t metadata_signature_size_be = htobe32(metadata_signature_size);
-
-  ErrorCode error;
-  EXPECT_FALSE(
-      performer_.Write(&metadata_signature_size_be,
-                       PayloadMetadata::kDeltaMetadataSignatureSizeSize + 1,
-                       &error));
-
-  EXPECT_EQ(ErrorCode::kDownloadInvalidMetadataSize, error);
-}
-
-TEST_F(DeltaPerformerTest, BrilloMetadataSignatureSizeNOKTest) {
-  unsigned int seed = time(nullptr);
-  EXPECT_TRUE(performer_.Write(kDeltaMagic, sizeof(kDeltaMagic)));
-
-  uint64_t major_version = htobe64(kBrilloMajorPayloadVersion);
-  EXPECT_TRUE(
-      performer_.Write(&major_version, PayloadMetadata::kDeltaVersionSize));
-
-  uint64_t manifest_size = rand_r(&seed) % 256;
-  // Subtract from UINT32_MAX to avoid wrap around.
-  uint32_t metadata_signature_size = UINT32_MAX - 600;
-
-  // The payload size is greater than |manifest_size| but smaller than
-  // |metadata_signature_size|
-  payload_.size = manifest_size + 1;
-
-  uint64_t manifest_size_be = htobe64(manifest_size);
-  EXPECT_TRUE(performer_.Write(&manifest_size_be,
-                               PayloadMetadata::kDeltaManifestSizeSize));
-
-  uint32_t metadata_signature_size_be = htobe32(metadata_signature_size);
-  ErrorCode error;
-  EXPECT_FALSE(
-      performer_.Write(&metadata_signature_size_be,
-                       PayloadMetadata::kDeltaMetadataSignatureSizeSize + 1,
-                       &error));
-
-  EXPECT_EQ(ErrorCode::kDownloadInvalidMetadataSize, error);
-}
-
 TEST_F(DeltaPerformerTest, BrilloParsePayloadMetadataTest) {
   brillo::Blob payload_data = GeneratePayload(
       {}, {}, true, kBrilloMajorPayloadVersion, kSourceMinorPayloadVersion);
   install_plan_.hash_checks_mandatory = true;
-  payload_.size = payload_data.size();
   ErrorCode error;
   EXPECT_EQ(MetadataParseResult::kSuccess,
             performer_.ParsePayloadMetadata(payload_data, &error));
@@ -1084,117 +1073,4 @@
   EXPECT_EQ(kMaxSupportedMajorPayloadVersion, major_version);
 }
 
-TEST_F(DeltaPerformerTest, FullPayloadCanResumeTest) {
-  payload_.type = InstallPayloadType::kFull;
-  brillo::Blob expected_data =
-      brillo::Blob(std::begin(kRandomString), std::end(kRandomString));
-  expected_data.resize(4096);  // block size
-  vector<AnnotatedOperation> aops;
-  AnnotatedOperation aop;
-  *(aop.op.add_dst_extents()) = ExtentForRange(0, 1);
-  aop.op.set_data_offset(0);
-  aop.op.set_data_length(expected_data.size());
-  aop.op.set_type(InstallOperation::REPLACE);
-  aops.push_back(aop);
-
-  brillo::Blob payload_data = GeneratePayload(expected_data,
-                                              aops,
-                                              false,
-                                              kBrilloMajorPayloadVersion,
-                                              kFullPayloadMinorVersion);
-
-  ASSERT_EQ(expected_data, ApplyPayload(payload_data, "/dev/null", true));
-  performer_.CheckpointUpdateProgress(true);
-  const std::string payload_id = "12345";
-  prefs_.SetString(kPrefsUpdateCheckResponseHash, payload_id);
-  ASSERT_TRUE(DeltaPerformer::CanResumeUpdate(&prefs_, payload_id));
-}
-
-class TestDeltaPerformer : public DeltaPerformer {
- public:
-  using DeltaPerformer::DeltaPerformer;
-
-  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) {
-    LOG(INFO) << __FUNCTION__ << ": " << install_part.name;
-    auto node = partition_writers_.extract(install_part.name);
-    return std::move(node.mapped());
-  }
-
-  bool ShouldCheckpoint() override { return true; }
-
-  std::map<std::string, std::unique_ptr<MockPartitionWriter>>
-      partition_writers_;
-};
-
-namespace {
-AnnotatedOperation GetSourceCopyOp(uint32_t src_block,
-                                   uint32_t dst_block,
-                                   const void* data,
-                                   size_t length) {
-  AnnotatedOperation aop;
-  *(aop.op.add_src_extents()) = ExtentForRange(0, 1);
-  *(aop.op.add_dst_extents()) = ExtentForRange(0, 1);
-  aop.op.set_type(InstallOperation::SOURCE_COPY);
-  brillo::Blob src_hash;
-  HashCalculator::RawHashOfBytes(data, length, &src_hash);
-  aop.op.set_src_sha256_hash(src_hash.data(), src_hash.size());
-  return aop;
-}
-}  // namespace
-
-TEST_F(DeltaPerformerTest, SetNextOpIndex) {
-  TestDeltaPerformer delta_performer{&prefs_,
-                                     &fake_boot_control_,
-                                     &fake_hardware_,
-                                     &mock_delegate_,
-                                     &install_plan_,
-                                     &payload_,
-                                     false};
-  brillo::Blob expected_data(std::begin(kRandomString),
-                             std::end(kRandomString));
-  expected_data.resize(4096 * 2);  // block size
-  AnnotatedOperation aop;
-
-  ScopedTempFile source("Source-XXXXXX");
-  EXPECT_TRUE(test_utils::WriteFileVector(source.path(), expected_data));
-
-  PartitionConfig old_part(kPartitionNameRoot);
-  old_part.path = source.path();
-  old_part.size = expected_data.size();
-
-  delta_performer.partition_writers_[kPartitionNameRoot] =
-      std::make_unique<MockPartitionWriter>();
-  auto& writer1 = *delta_performer.partition_writers_[kPartitionNameRoot];
-
-  Sequence seq;
-  std::vector<size_t> indices;
-  EXPECT_CALL(writer1, CheckpointUpdateProgress(_))
-      .WillRepeatedly(
-          [&indices](size_t index) mutable { indices.emplace_back(index); });
-  EXPECT_CALL(writer1, Init(_, true, _)).Times(1).WillOnce(Return(true));
-  EXPECT_CALL(writer1, PerformSourceCopyOperation(_, _))
-      .Times(2)
-      .WillRepeatedly(Return(true));
-
-  brillo::Blob payload_data = GeneratePayload(
-      brillo::Blob(),
-      {GetSourceCopyOp(0, 0, expected_data.data(), 4096),
-       GetSourceCopyOp(1, 1, expected_data.data() + 4096, 4096)},
-      false,
-      &old_part);
-
-  ApplyPayloadToData(&delta_performer, payload_data, source.path(), {}, true);
-  ASSERT_TRUE(std::is_sorted(indices.begin(), indices.end()));
-  ASSERT_GT(indices.size(), 0UL);
-
-  // Should be equal to number of operations
-  ASSERT_EQ(indices[indices.size() - 1], 2UL);
-}
-
 }  // namespace chromeos_update_engine
diff --git a/cros/download_action_chromeos.cc b/payload_consumer/download_action.cc
similarity index 74%
rename from cros/download_action_chromeos.cc
rename to payload_consumer/download_action.cc
index ee9c9a7..09afc42 100644
--- a/cros/download_action_chromeos.cc
+++ b/payload_consumer/download_action.cc
@@ -1,5 +1,5 @@
 //
-// Copyright (C) 2020 The Android Open Source Project
+// Copyright (C) 2011 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,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/download_action_chromeos.h"
+#include "update_engine/payload_consumer/download_action.h"
 
 #include <errno.h>
 
@@ -29,37 +29,41 @@
 #include "update_engine/common/boot_control_interface.h"
 #include "update_engine/common/error_code_utils.h"
 #include "update_engine/common/multi_range_http_fetcher.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/cros/p2p_manager.h"
-#include "update_engine/cros/payload_state_interface.h"
+#include "update_engine/omaha_request_params.h"
+#include "update_engine/p2p_manager.h"
+#include "update_engine/payload_state_interface.h"
 
 using base::FilePath;
 using std::string;
 
 namespace chromeos_update_engine {
 
-DownloadActionChromeos::DownloadActionChromeos(
-    PrefsInterface* prefs,
-    BootControlInterface* boot_control,
-    HardwareInterface* hardware,
-    HttpFetcher* http_fetcher,
-    bool interactive)
+DownloadAction::DownloadAction(PrefsInterface* prefs,
+                               BootControlInterface* boot_control,
+                               HardwareInterface* hardware,
+                               SystemState* system_state,
+                               HttpFetcher* http_fetcher,
+                               bool interactive)
     : prefs_(prefs),
       boot_control_(boot_control),
       hardware_(hardware),
+      system_state_(system_state),
       http_fetcher_(new MultiRangeHttpFetcher(http_fetcher)),
       interactive_(interactive),
       writer_(nullptr),
       code_(ErrorCode::kSuccess),
       delegate_(nullptr),
       p2p_sharing_fd_(-1),
-      p2p_visible_(true) {}
+      p2p_visible_(true) {
+#if BASE_VER < 576279
+  base::StatisticsRecorder::Initialize();
+#endif
+}
 
-DownloadActionChromeos::~DownloadActionChromeos() {}
+DownloadAction::~DownloadAction() {}
 
-void DownloadActionChromeos::CloseP2PSharingFd(bool delete_p2p_file) {
+void DownloadAction::CloseP2PSharingFd(bool delete_p2p_file) {
   if (p2p_sharing_fd_ != -1) {
     if (close(p2p_sharing_fd_) != 0) {
       PLOG(ERROR) << "Error closing p2p sharing fd";
@@ -68,8 +72,7 @@
   }
 
   if (delete_p2p_file) {
-    FilePath path =
-        SystemState::Get()->p2p_manager()->FileGetPath(p2p_file_id_);
+    FilePath path = system_state_->p2p_manager()->FileGetPath(p2p_file_id_);
     if (unlink(path.value().c_str()) != 0) {
       PLOG(ERROR) << "Error deleting p2p file " << path.value();
     } else {
@@ -81,8 +84,8 @@
   p2p_file_id_.clear();
 }
 
-bool DownloadActionChromeos::SetupP2PSharingFd() {
-  P2PManager* p2p_manager = SystemState::Get()->p2p_manager();
+bool DownloadAction::SetupP2PSharingFd() {
+  P2PManager* p2p_manager = system_state_->p2p_manager();
 
   if (!p2p_manager->FileShare(p2p_file_id_, payload_->size)) {
     LOG(ERROR) << "Unable to share file via p2p";
@@ -117,9 +120,9 @@
   return true;
 }
 
-void DownloadActionChromeos::WriteToP2PFile(const void* data,
-                                            size_t length,
-                                            off_t file_offset) {
+void DownloadAction::WriteToP2PFile(const void* data,
+                                    size_t length,
+                                    off_t file_offset) {
   if (p2p_sharing_fd_ == -1) {
     if (!SetupP2PSharingFd())
       return;
@@ -164,7 +167,7 @@
   }
 }
 
-void DownloadActionChromeos::PerformAction() {
+void DownloadAction::PerformAction() {
   http_fetcher_->set_delegate(this);
 
   // Get the InstallPlan and read it
@@ -203,76 +206,18 @@
   StartDownloading();
 }
 
-bool DownloadActionChromeos::LoadCachedManifest(int64_t manifest_size) {
-  std::string cached_manifest_bytes;
-  if (!prefs_->GetString(kPrefsManifestBytes, &cached_manifest_bytes) ||
-      cached_manifest_bytes.size() <= 0) {
-    LOG(INFO) << "Cached Manifest data not found";
-    return false;
-  }
-  if (static_cast<int64_t>(cached_manifest_bytes.size()) != manifest_size) {
-    LOG(WARNING) << "Cached metadata has unexpected size: "
-                 << cached_manifest_bytes.size() << " vs. " << manifest_size;
-    return false;
-  }
-
-  ErrorCode error;
-  const bool success =
-      delta_performer_->Write(
-          cached_manifest_bytes.data(), cached_manifest_bytes.size(), &error) &&
-      delta_performer_->IsManifestValid();
-  if (success) {
-    LOG(INFO) << "Successfully parsed cached manifest";
-  } else {
-    // If parsing of cached data failed, fall back to fetch them using HTTP
-    LOG(WARNING) << "Cached manifest data fails to load, error code:"
-                 << static_cast<int>(error) << "," << error;
-  }
-  return success;
-}
-
-void DownloadActionChromeos::StartDownloading() {
+void DownloadAction::StartDownloading() {
   download_active_ = true;
   http_fetcher_->ClearRanges();
-
-  if (writer_ && writer_ != delta_performer_.get()) {
-    LOG(INFO) << "Using writer for test.";
-  } else {
-    delta_performer_.reset(new DeltaPerformer(prefs_,
-                                              boot_control_,
-                                              hardware_,
-                                              delegate_,
-                                              &install_plan_,
-                                              payload_,
-                                              interactive_));
-    writer_ = delta_performer_.get();
-  }
-
   if (install_plan_.is_resume &&
       payload_ == &install_plan_.payloads[resume_payload_index_]) {
-    // Resuming an update so parse the cached manifest first
+    // Resuming an update so fetch the update manifest metadata first.
     int64_t manifest_metadata_size = 0;
     int64_t manifest_signature_size = 0;
     prefs_->GetInt64(kPrefsManifestMetadataSize, &manifest_metadata_size);
     prefs_->GetInt64(kPrefsManifestSignatureSize, &manifest_signature_size);
-
-    // TODO(zhangkelvin) Add unittest for success and fallback route
-    if (!LoadCachedManifest(manifest_metadata_size + manifest_signature_size)) {
-      if (delta_performer_) {
-        // Create a new DeltaPerformer to reset all its state
-        delta_performer_ = std::make_unique<DeltaPerformer>(prefs_,
-                                                            boot_control_,
-                                                            hardware_,
-                                                            delegate_,
-                                                            &install_plan_,
-                                                            payload_,
-                                                            interactive_);
-        writer_ = delta_performer_.get();
-      }
-      http_fetcher_->AddRange(base_offset_,
-                              manifest_metadata_size + manifest_signature_size);
-    }
-
+    http_fetcher_->AddRange(base_offset_,
+                            manifest_metadata_size + manifest_signature_size);
     // If there're remaining unprocessed data blobs, fetch them. Be careful not
     // to request data beyond the end of the payload to avoid 416 HTTP response
     // error codes.
@@ -296,9 +241,20 @@
     }
   }
 
-  if (SystemState::Get() != nullptr) {
-    const PayloadStateInterface* payload_state =
-        SystemState::Get()->payload_state();
+  if (writer_ && writer_ != delta_performer_.get()) {
+    LOG(INFO) << "Using writer for test.";
+  } else {
+    delta_performer_.reset(new DeltaPerformer(prefs_,
+                                              boot_control_,
+                                              hardware_,
+                                              delegate_,
+                                              &install_plan_,
+                                              payload_,
+                                              interactive_));
+    writer_ = delta_performer_.get();
+  }
+  if (system_state_ != nullptr) {
+    const PayloadStateInterface* payload_state = system_state_->payload_state();
     string file_id = utils::CalculateP2PFileId(payload_->hash, payload_->size);
     if (payload_state->GetUsingP2PForSharing()) {
       // If we're sharing the update, store the file_id to convey
@@ -311,7 +267,7 @@
       // hash. If this is the case, we NEED to clean it up otherwise
       // we're essentially timing out other peers downloading from us
       // (since we're never going to complete the file).
-      FilePath path = SystemState::Get()->p2p_manager()->FileGetPath(file_id);
+      FilePath path = system_state_->p2p_manager()->FileGetPath(file_id);
       if (!path.empty()) {
         if (unlink(path.value().c_str()) != 0) {
           PLOG(ERROR) << "Error deleting p2p file " << path.value();
@@ -337,15 +293,15 @@
   http_fetcher_->BeginTransfer(install_plan_.download_url);
 }
 
-void DownloadActionChromeos::SuspendAction() {
+void DownloadAction::SuspendAction() {
   http_fetcher_->Pause();
 }
 
-void DownloadActionChromeos::ResumeAction() {
+void DownloadAction::ResumeAction() {
   http_fetcher_->Unpause();
 }
 
-void DownloadActionChromeos::TerminateProcessing() {
+void DownloadAction::TerminateProcessing() {
   if (writer_) {
     writer_->Close();
     writer_ = nullptr;
@@ -357,13 +313,13 @@
   http_fetcher_->TerminateTransfer();
 }
 
-void DownloadActionChromeos::SeekToOffset(off_t offset) {
+void DownloadAction::SeekToOffset(off_t offset) {
   bytes_received_ = offset;
 }
 
-bool DownloadActionChromeos::ReceivedBytes(HttpFetcher* fetcher,
-                                           const void* bytes,
-                                           size_t length) {
+bool DownloadAction::ReceivedBytes(HttpFetcher* fetcher,
+                                   const void* bytes,
+                                   size_t length) {
   // Note that bytes_received_ is the current offset.
   if (!p2p_file_id_.empty()) {
     WriteToP2PFile(bytes, length, bytes_received_);
@@ -393,17 +349,16 @@
 
   // Call p2p_manager_->FileMakeVisible() when we've successfully
   // verified the manifest!
-  if (!p2p_visible_ && SystemState::Get() && delta_performer_.get() &&
+  if (!p2p_visible_ && system_state_ && delta_performer_.get() &&
       delta_performer_->IsManifestValid()) {
     LOG(INFO) << "Manifest has been validated. Making p2p file visible.";
-    SystemState::Get()->p2p_manager()->FileMakeVisible(p2p_file_id_);
+    system_state_->p2p_manager()->FileMakeVisible(p2p_file_id_);
     p2p_visible_ = true;
   }
   return true;
 }
 
-void DownloadActionChromeos::TransferComplete(HttpFetcher* fetcher,
-                                              bool successful) {
+void DownloadAction::TransferComplete(HttpFetcher* fetcher, bool successful) {
   if (writer_) {
     LOG_IF(WARNING, writer_->Close() != 0) << "Error closing the writer.";
     if (delta_performer_.get() == writer_) {
@@ -419,7 +374,7 @@
       code = delta_performer_->VerifyPayload(payload_->hash, payload_->size);
     if (code == ErrorCode::kSuccess) {
       if (payload_ < &install_plan_.payloads.back() &&
-          SystemState::Get()->payload_state()->NextPayload()) {
+          system_state_->payload_state()->NextPayload()) {
         LOG(INFO) << "Incrementing to next payload";
         // No need to reset if this payload was already applied.
         if (delta_performer_ && !payload_->already_applied)
@@ -428,7 +383,7 @@
         bytes_received_previous_payloads_ += payload_->size;
         payload_++;
         install_plan_.download_url =
-            SystemState::Get()->payload_state()->GetCurrentUrl();
+            system_state_->payload_state()->GetCurrentUrl();
         StartDownloading();
         return;
       }
@@ -437,9 +392,11 @@
       if (delegate_)
         delegate_->DownloadComplete();
 
+      // Log UpdateEngine.DownloadAction.* histograms to help diagnose
+      // long-blocking operations.
       std::string histogram_output;
-      base::StatisticsRecorder::WriteGraph(
-          "UpdateEngine.DownloadActionChromeos.", &histogram_output);
+      base::StatisticsRecorder::WriteGraph("UpdateEngine.DownloadAction.",
+                                           &histogram_output);
       LOG(INFO) << histogram_output;
     } else {
       LOG(ERROR) << "Download of " << install_plan_.download_url
@@ -456,7 +413,7 @@
   processor_->ActionComplete(this, code);
 }
 
-void DownloadActionChromeos::TransferTerminated(HttpFetcher* fetcher) {
+void DownloadAction::TransferTerminated(HttpFetcher* fetcher) {
   if (code_ != ErrorCode::kSuccess) {
     processor_->ActionComplete(this, code_);
   } else if (payload_->already_applied) {
diff --git a/common/download_action.h b/payload_consumer/download_action.h
similarity index 72%
rename from common/download_action.h
rename to payload_consumer/download_action.h
index 7b496b1..1777e22 100644
--- a/common/download_action.h
+++ b/payload_consumer/download_action.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_COMMON_DOWNLOAD_ACTION_H_
-#define UPDATE_ENGINE_COMMON_DOWNLOAD_ACTION_H_
+#ifndef UPDATE_ENGINE_PAYLOAD_CONSUMER_DOWNLOAD_ACTION_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_DOWNLOAD_ACTION_H_
 
 #include <fcntl.h>
 #include <sys/stat.h>
@@ -23,7 +23,6 @@
 
 #include <memory>
 #include <string>
-#include <utility>
 
 #include "update_engine/common/action.h"
 #include "update_engine/common/boot_control_interface.h"
@@ -31,6 +30,7 @@
 #include "update_engine/common/multi_range_http_fetcher.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
@@ -71,11 +71,12 @@
 
   // Takes ownership of the passed in HttpFetcher. Useful for testing.
   // A good calling pattern is:
-  // DownloadAction(prefs, boot_contol, hardware,
+  // DownloadAction(prefs, boot_contol, hardware, system_state,
   //                new WhateverHttpFetcher, false);
   DownloadAction(PrefsInterface* prefs,
                  BootControlInterface* boot_control,
                  HardwareInterface* hardware,
+                 SystemState* system_state,
                  HttpFetcher* http_fetcher,
                  bool interactive);
   ~DownloadAction() override;
@@ -88,9 +89,7 @@
   std::string Type() const override { return StaticType(); }
 
   // Testing
-  void SetTestFileWriter(std::unique_ptr<DeltaPerformer> writer) {
-    delta_performer_ = std::move(writer);
-  }
+  void SetTestFileWriter(FileWriter* writer) { writer_ = writer; }
 
   int GetHTTPResponseCode() { return http_fetcher_->http_response_code(); }
 
@@ -109,22 +108,46 @@
 
   HttpFetcher* http_fetcher() { return http_fetcher_.get(); }
 
+  // Returns the p2p file id for the file being written or the empty
+  // string if we're not writing to a p2p file.
+  std::string p2p_file_id() { return p2p_file_id_; }
+
  private:
-  // Attempt to load cached manifest data from prefs
-  // return true on success, false otherwise.
-  bool LoadCachedManifest(int64_t manifest_size);
+  // Closes the file descriptor for the p2p file being written and
+  // clears |p2p_file_id_| to indicate that we're no longer sharing
+  // the file. If |delete_p2p_file| is True, also deletes the file.
+  // If there is no p2p file descriptor, this method does nothing.
+  void CloseP2PSharingFd(bool delete_p2p_file);
+
+  // Starts sharing the p2p file. Must be called before
+  // WriteToP2PFile(). Returns True if this worked.
+  bool SetupP2PSharingFd();
+
+  // Writes |length| bytes of payload from |data| into |file_offset|
+  // of the p2p file. Also does sanity checks; for example ensures we
+  // don't end up with a file with holes in it.
+  //
+  // This method does nothing if SetupP2PSharingFd() hasn't been
+  // called or if CloseP2PSharingFd() has been called.
+  void WriteToP2PFile(const void* data, size_t length, off_t file_offset);
 
   // Start downloading the current payload using delta_performer.
   void StartDownloading();
 
+  // The InstallPlan passed in
+  InstallPlan install_plan_;
+
   // Pointer to the current payload in install_plan_.payloads.
   InstallPlan::Payload* payload_{nullptr};
 
-  // Required pointers.
+  // SystemState required pointers.
   PrefsInterface* prefs_;
   BootControlInterface* boot_control_;
   HardwareInterface* hardware_;
 
+  // Global context for the system.
+  SystemState* system_state_;
+
   // Pointer to the MultiRangeHttpFetcher that does the http work.
   std::unique_ptr<MultiRangeHttpFetcher> http_fetcher_;
 
@@ -133,6 +156,10 @@
   // update.
   bool interactive_;
 
+  // The FileWriter that downloaded data should be written to. It will
+  // either point to *decompressing_file_writer_ or *delta_performer_.
+  FileWriter* writer_;
+
   std::unique_ptr<DeltaPerformer> delta_performer_;
 
   // Used by TransferTerminated to figure if this action terminated itself or
@@ -146,6 +173,17 @@
   uint64_t bytes_total_{0};
   bool download_active_{false};
 
+  // The file-id for the file we're sharing or the empty string
+  // if we're not using p2p to share.
+  std::string p2p_file_id_;
+
+  // The file descriptor for the p2p file used for caching the payload or -1
+  // if we're not using p2p to share.
+  int p2p_sharing_fd_;
+
+  // Set to |false| if p2p file is not visible.
+  bool p2p_visible_;
+
   // Loaded from prefs before downloading any payload.
   size_t resume_payload_index_{0};
 
@@ -161,4 +199,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_COMMON_DOWNLOAD_ACTION_H_
+#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_DOWNLOAD_ACTION_H_
diff --git a/cros/download_action_chromeos_unittest.cc b/payload_consumer/download_action_unittest.cc
similarity index 87%
rename from cros/download_action_chromeos_unittest.cc
rename to payload_consumer/download_action_unittest.cc
index 93c39ff..e6ca219 100644
--- a/cros/download_action_chromeos_unittest.cc
+++ b/payload_consumer/download_action_unittest.cc
@@ -14,6 +14,8 @@
 // limitations under the License.
 //
 
+#include "update_engine/payload_consumer/download_action.h"
+
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
@@ -32,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/cros/download_action_chromeos.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/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/update_manager/fake_update_manager.h"
 
 namespace chromeos_update_engine {
@@ -49,15 +51,14 @@
 using base::WriteFile;
 using std::string;
 using std::unique_ptr;
+using test_utils::ScopedTempFile;
 using testing::_;
 using testing::AtLeast;
 using testing::InSequence;
 using testing::Return;
 using testing::SetArgPointee;
 
-class DownloadActionChromeosTest : public ::testing::Test {
-  void SetUp() { FakeSystemState::CreateInstance(); }
-};
+class DownloadActionTest : public ::testing::Test {};
 
 namespace {
 
@@ -86,10 +87,9 @@
                        AbstractAction* action,
                        ErrorCode code) override {
     const string type = action->Type();
-    if (type == DownloadActionChromeos::StaticType()) {
+    if (type == DownloadAction::StaticType()) {
       EXPECT_EQ(expected_code_, code);
-      p2p_file_id_ =
-          static_cast<DownloadActionChromeos*>(action)->p2p_file_id();
+      p2p_file_id_ = static_cast<DownloadAction*>(action)->p2p_file_id();
     } else {
       EXPECT_EQ(ErrorCode::kSuccess, code);
     }
@@ -129,10 +129,11 @@
 void TestWithData(const brillo::Blob& data,
                   int fail_write,
                   bool use_download_delegate) {
-  FakeSystemState::CreateInstance();
   brillo::FakeMessageLoop loop(nullptr);
   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(
@@ -150,9 +151,9 @@
   install_plan.target_slot = 1;
   // We mark both slots as bootable. Only the target slot should be unbootable
   // after the download starts.
-  FakeSystemState::Get()->fake_boot_control()->SetSlotBootable(
+  fake_system_state.fake_boot_control()->SetSlotBootable(
       install_plan.source_slot, true);
-  FakeSystemState::Get()->fake_boot_control()->SetSlotBootable(
+  fake_system_state.fake_boot_control()->SetSlotBootable(
       install_plan.target_slot, true);
   auto feeder_action = std::make_unique<ObjectFeederAction<InstallPlan>>();
   feeder_action->set_obj(install_plan);
@@ -160,12 +161,13 @@
   MockHttpFetcher* http_fetcher =
       new MockHttpFetcher(data.data(), data.size(), nullptr);
   // takes ownership of passed in HttpFetcher
-  auto download_action = std::make_unique<DownloadActionChromeos>(
-      &prefs,
-      FakeSystemState::Get()->boot_control(),
-      FakeSystemState::Get()->hardware(),
-      http_fetcher,
-      false /* interactive */);
+  auto download_action =
+      std::make_unique<DownloadAction>(&prefs,
+                                       fake_system_state.boot_control(),
+                                       fake_system_state.hardware(),
+                                       &fake_system_state,
+                                       http_fetcher,
+                                       false /* interactive */);
   download_action->SetTestFileWriter(&writer);
   BondActions(feeder_action.get(), download_action.get());
   MockDownloadActionDelegate download_delegate;
@@ -194,9 +196,9 @@
   loop.Run();
   EXPECT_FALSE(loop.PendingTasks());
 
-  EXPECT_TRUE(FakeSystemState::Get()->fake_boot_control()->IsSlotBootable(
+  EXPECT_TRUE(fake_system_state.fake_boot_control()->IsSlotBootable(
       install_plan.source_slot));
-  EXPECT_FALSE(FakeSystemState::Get()->fake_boot_control()->IsSlotBootable(
+  EXPECT_FALSE(fake_system_state.fake_boot_control()->IsSlotBootable(
       install_plan.target_slot));
 }
 }  // namespace
@@ -251,7 +253,8 @@
   payload_datas.emplace_back(2 * kMockHttpFetcherChunkSize);
   brillo::FakeMessageLoop loop(nullptr);
   loop.SetAsCurrent();
-  EXPECT_CALL(*FakeSystemState::Get()->mock_payload_state(), NextPayload())
+  FakeSystemState fake_system_state;
+  EXPECT_CALL(*fake_system_state.mock_payload_state(), NextPayload())
       .WillOnce(Return(true));
 
   MockFileWriter mock_file_writer;
@@ -274,12 +277,13 @@
   MockHttpFetcher* http_fetcher = new MockHttpFetcher(
       payload_datas[0].data(), payload_datas[0].size(), nullptr);
   // takes ownership of passed in HttpFetcher
-  auto download_action = std::make_unique<DownloadActionChromeos>(
-      &prefs,
-      FakeSystemState::Get()->boot_control(),
-      FakeSystemState::Get()->hardware(),
-      http_fetcher,
-      false /* interactive */);
+  auto download_action =
+      std::make_unique<DownloadAction>(&prefs,
+                                       fake_system_state.boot_control(),
+                                       fake_system_state.hardware(),
+                                       &fake_system_state,
+                                       http_fetcher,
+                                       false /* interactive */);
   download_action->SetTestFileWriter(&mock_file_writer);
   BondActions(feeder_action.get(), download_action.get());
   MockDownloadActionDelegate download_delegate;
@@ -344,7 +348,6 @@
 }
 
 void TestTerminateEarly(bool use_download_delegate) {
-  FakeSystemState::CreateInstance();
   brillo::FakeMessageLoop loop(nullptr);
   loop.SetAsCurrent();
 
@@ -361,12 +364,13 @@
     InstallPlan install_plan;
     install_plan.payloads.resize(1);
     feeder_action->set_obj(install_plan);
-
+    FakeSystemState fake_system_state_;
     MockPrefs prefs;
     auto download_action = std::make_unique<DownloadAction>(
         &prefs,
-        FakeSystemState::Get()->boot_control(),
-        FakeSystemState::Get()->hardware(),
+        fake_system_state_.boot_control(),
+        fake_system_state_.hardware(),
+        &fake_system_state_,
         new MockHttpFetcher(data.data(), data.size(), nullptr),
         false /* interactive */);
     download_action->SetTestFileWriter(&writer);
@@ -459,7 +463,6 @@
 }  // namespace
 
 TEST(DownloadActionTest, PassObjectOutTest) {
-  FakeSystemState::CreateInstance();
   brillo::FakeMessageLoop loop(nullptr);
   loop.SetAsCurrent();
 
@@ -474,10 +477,12 @@
   auto feeder_action = std::make_unique<ObjectFeederAction<InstallPlan>>();
   feeder_action->set_obj(install_plan);
   MockPrefs prefs;
+  FakeSystemState fake_system_state_;
   auto download_action =
       std::make_unique<DownloadAction>(&prefs,
-                                       FakeSystemState::Get()->boot_control(),
-                                       FakeSystemState::Get()->hardware(),
+                                       fake_system_state_.boot_control(),
+                                       fake_system_state_.hardware(),
+                                       &fake_system_state_,
                                        new MockHttpFetcher("x", 1, nullptr),
                                        false /* interactive */);
   download_action->SetTestFileWriter(&writer);
@@ -508,15 +513,13 @@
 // Test fixture for P2P tests.
 class P2PDownloadActionTest : public testing::Test {
  protected:
-  P2PDownloadActionTest() : start_at_offset_(0) {}
+  P2PDownloadActionTest()
+      : start_at_offset_(0), fake_um_(fake_system_state_.fake_clock()) {}
 
   ~P2PDownloadActionTest() override {}
 
   // Derived from testing::Test.
-  void SetUp() override {
-    loop_.SetAsCurrent();
-    FakeSystemState::CreateInstance();
-  }
+  void SetUp() override { loop_.SetAsCurrent(); }
 
   // Derived from testing::Test.
   void TearDown() override { EXPECT_FALSE(loop_.PendingTasks()); }
@@ -532,15 +535,20 @@
 
     // Setup p2p.
     FakeP2PManagerConfiguration* test_conf = new FakeP2PManagerConfiguration();
-    p2p_manager_.reset(P2PManager::Construct(
-        test_conf, &fake_um_, "cros_au", 3, base::TimeDelta::FromDays(5)));
+    p2p_manager_.reset(P2PManager::Construct(test_conf,
+                                             nullptr,
+                                             &fake_um_,
+                                             "cros_au",
+                                             3,
+                                             base::TimeDelta::FromDays(5)));
+    fake_system_state_.set_p2p_manager(p2p_manager_.get());
   }
 
   // To be called by tests to perform the download. The
   // |use_p2p_to_share| parameter is used to indicate whether the
   // payload should be shared via p2p.
   void StartDownload(bool use_p2p_to_share) {
-    EXPECT_CALL(*FakeSystemState::Get()->mock_payload_state(),
+    EXPECT_CALL(*fake_system_state_.mock_payload_state(),
                 GetUsingP2PForSharing())
         .WillRepeatedly(Return(use_p2p_to_share));
 
@@ -558,8 +566,9 @@
     // Note that DownloadAction takes ownership of the passed in HttpFetcher.
     auto download_action = std::make_unique<DownloadAction>(
         &prefs,
-        FakeSystemState::Get()->boot_control(),
-        FakeSystemState::Get()->hardware(),
+        fake_system_state_.boot_control(),
+        fake_system_state_.hardware(),
+        &fake_system_state_,
         new MockHttpFetcher(data_.c_str(), data_.length(), nullptr),
         false /* interactive */);
     auto http_fetcher = download_action->http_fetcher();
@@ -596,6 +605,9 @@
   // The ActionProcessor used for running the actions.
   ActionProcessor processor_;
 
+  // A fake system state.
+  FakeSystemState fake_system_state_;
+
   // The data being downloaded.
   string data_;
 
diff --git a/payload_consumer/extent_reader.cc b/payload_consumer/extent_reader.cc
index 3c7329d..ad983ae 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::ReadAll(
+    TEST_AND_RETURN_FALSE(utils::PReadAll(
         fd_,
         bytes + bytes_read,
         bytes_to_read,
diff --git a/payload_consumer/extent_reader_unittest.cc b/payload_consumer/extent_reader_unittest.cc
index 686f14d..b7059bc 100644
--- a/payload_consumer/extent_reader_unittest.cc
+++ b/payload_consumer/extent_reader_unittest.cc
@@ -72,7 +72,7 @@
   }
 
   FileDescriptorPtr fd_;
-  ScopedTempFile temp_file_{"ExtentReaderTest-file.XXXXXX"};
+  test_utils::ScopedTempFile temp_file_{"ExtentReaderTest-file.XXXXXX"};
   brillo::Blob sample_;
 };
 
diff --git a/payload_consumer/extent_writer.h b/payload_consumer/extent_writer.h
index 8b1b532..9e53561 100644
--- a/payload_consumer/extent_writer.h
+++ b/payload_consumer/extent_writer.h
@@ -38,7 +38,8 @@
   virtual ~ExtentWriter() = default;
 
   // Returns true on success.
-  virtual bool Init(const google::protobuf::RepeatedPtrField<Extent>& extents,
+  virtual bool Init(FileDescriptorPtr fd,
+                    const google::protobuf::RepeatedPtrField<Extent>& extents,
                     uint32_t block_size) = 0;
 
   // Returns true on success.
@@ -50,11 +51,13 @@
 
 class DirectExtentWriter : public ExtentWriter {
  public:
-  explicit DirectExtentWriter(FileDescriptorPtr fd) : fd_(fd) {}
+  DirectExtentWriter() = default;
   ~DirectExtentWriter() override = default;
 
-  bool Init(const google::protobuf::RepeatedPtrField<Extent>& extents,
+  bool Init(FileDescriptorPtr fd,
+            const google::protobuf::RepeatedPtrField<Extent>& extents,
             uint32_t block_size) override {
+    fd_ = fd;
     block_size_ = block_size;
     extents_ = extents;
     cur_extent_ = extents_.begin();
diff --git a/payload_consumer/extent_writer_unittest.cc b/payload_consumer/extent_writer_unittest.cc
index 5c67d3e..aef856b 100644
--- a/payload_consumer/extent_writer_unittest.cc
+++ b/payload_consumer/extent_writer_unittest.cc
@@ -59,14 +59,15 @@
   void WriteAlignedExtents(size_t chunk_size, size_t first_chunk_size);
 
   FileDescriptorPtr fd_;
-  ScopedTempFile temp_file_{"ExtentWriterTest-file.XXXXXX"};
+  test_utils::ScopedTempFile temp_file_{"ExtentWriterTest-file.XXXXXX"};
 };
 
 TEST_F(ExtentWriterTest, SimpleTest) {
   vector<Extent> extents = {ExtentForRange(1, 1)};
   const string bytes = "1234";
-  DirectExtentWriter direct_writer{fd_};
-  EXPECT_TRUE(direct_writer.Init({extents.begin(), extents.end()}, kBlockSize));
+  DirectExtentWriter direct_writer;
+  EXPECT_TRUE(
+      direct_writer.Init(fd_, {extents.begin(), extents.end()}, kBlockSize));
   EXPECT_TRUE(direct_writer.Write(bytes.data(), bytes.size()));
 
   EXPECT_EQ(static_cast<off_t>(kBlockSize + bytes.size()),
@@ -83,8 +84,9 @@
 
 TEST_F(ExtentWriterTest, ZeroLengthTest) {
   vector<Extent> extents = {ExtentForRange(1, 1)};
-  DirectExtentWriter direct_writer{fd_};
-  EXPECT_TRUE(direct_writer.Init({extents.begin(), extents.end()}, kBlockSize));
+  DirectExtentWriter direct_writer;
+  EXPECT_TRUE(
+      direct_writer.Init(fd_, {extents.begin(), extents.end()}, kBlockSize));
   EXPECT_TRUE(direct_writer.Write(nullptr, 0));
 }
 
@@ -107,8 +109,9 @@
   brillo::Blob data(kBlockSize * 3);
   test_utils::FillWithData(&data);
 
-  DirectExtentWriter direct_writer{fd_};
-  EXPECT_TRUE(direct_writer.Init({extents.begin(), extents.end()}, kBlockSize));
+  DirectExtentWriter direct_writer;
+  EXPECT_TRUE(
+      direct_writer.Init(fd_, {extents.begin(), extents.end()}, kBlockSize));
 
   size_t bytes_written = 0;
   while (bytes_written < data.size()) {
@@ -147,8 +150,9 @@
   brillo::Blob data(17);
   test_utils::FillWithData(&data);
 
-  DirectExtentWriter direct_writer{fd_};
-  EXPECT_TRUE(direct_writer.Init({extents.begin(), extents.end()}, kBlockSize));
+  DirectExtentWriter direct_writer;
+  EXPECT_TRUE(
+      direct_writer.Init(fd_, {extents.begin(), extents.end()}, kBlockSize));
 
   size_t bytes_written = 0;
   while (bytes_written < (block_count * kBlockSize)) {
diff --git a/payload_consumer/fake_extent_writer.h b/payload_consumer/fake_extent_writer.h
index 680b1b3..7b2b7ac 100644
--- a/payload_consumer/fake_extent_writer.h
+++ b/payload_consumer/fake_extent_writer.h
@@ -33,7 +33,8 @@
   ~FakeExtentWriter() override = default;
 
   // ExtentWriter overrides.
-  bool Init(const google::protobuf::RepeatedPtrField<Extent>& /* extents */,
+  bool Init(FileDescriptorPtr /* fd */,
+            const google::protobuf::RepeatedPtrField<Extent>& /* extents */,
             uint32_t /* block_size */) override {
     init_called_ = true;
     return true;
diff --git a/payload_consumer/fec_file_descriptor.cc b/payload_consumer/fec_file_descriptor.cc
index 3fee196..de22cf3 100644
--- a/payload_consumer/fec_file_descriptor.cc
+++ b/payload_consumer/fec_file_descriptor.cc
@@ -16,8 +16,6 @@
 
 #include "update_engine/payload_consumer/fec_file_descriptor.h"
 
-#include <base/logging.h>
-
 namespace chromeos_update_engine {
 
 bool FecFileDescriptor::Open(const char* path, int flags) {
diff --git a/payload_consumer/file_descriptor.cc b/payload_consumer/file_descriptor.cc
index da76327..1de615c 100644
--- a/payload_consumer/file_descriptor.cc
+++ b/payload_consumer/file_descriptor.cc
@@ -21,7 +21,6 @@
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <unistd.h>
 
 #include <base/posix/eintr_wrapper.h>
 
@@ -29,12 +28,6 @@
 
 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);
@@ -132,19 +125,11 @@
 
 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() {
-  if (fd_ < 0) {
-    return false;
-  }
-  // 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_);
+  CHECK_GE(fd_, 0);
   if (IGNORE_EINTR(close(fd_)))
     return false;
   fd_ = -1;
diff --git a/payload_consumer/file_descriptor.h b/payload_consumer/file_descriptor.h
index faebcc1..55f76c6 100644
--- a/payload_consumer/file_descriptor.h
+++ b/payload_consumer/file_descriptor.h
@@ -21,7 +21,7 @@
 #include <sys/types.h>
 #include <memory>
 
-#include <base/macros.h>
+#include <base/logging.h>
 
 // Abstraction for managing opening, reading, writing and closing of file
 // descriptors. This includes an abstract class and one standard implementation
@@ -111,7 +111,6 @@
 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.cc b/payload_consumer/file_descriptor_utils.cc
index 9a6a601..846cbd7 100644
--- a/payload_consumer/file_descriptor_utils.cc
+++ b/payload_consumer/file_descriptor_utils.cc
@@ -82,8 +82,8 @@
                         const RepeatedPtrField<Extent>& tgt_extents,
                         uint64_t block_size,
                         brillo::Blob* hash_out) {
-  DirectExtentWriter writer{target};
-  TEST_AND_RETURN_FALSE(writer.Init(tgt_extents, block_size));
+  DirectExtentWriter writer;
+  TEST_AND_RETURN_FALSE(writer.Init(target, tgt_extents, block_size));
   TEST_AND_RETURN_FALSE(utils::BlocksInExtents(src_extents) ==
                         utils::BlocksInExtents(tgt_extents));
   TEST_AND_RETURN_FALSE(
diff --git a/payload_consumer/file_descriptor_utils_unittest.cc b/payload_consumer/file_descriptor_utils_unittest.cc
index 478893d..48e610f 100644
--- a/payload_consumer/file_descriptor_utils_unittest.cc
+++ b/payload_consumer/file_descriptor_utils_unittest.cc
@@ -52,13 +52,14 @@
 class FileDescriptorUtilsTest : public ::testing::Test {
  protected:
   void SetUp() override {
-    EXPECT_TRUE(target_->Open(tgt_file_.path().c_str(), O_RDWR));
+    EXPECT_TRUE(utils::MakeTempFile("fd_tgt.XXXXXX", &tgt_path_, nullptr));
+    EXPECT_TRUE(target_->Open(tgt_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_file_.path(), &target_contents));
+    EXPECT_TRUE(utils::ReadFile(tgt_path_, &target_contents));
     EXPECT_EQ(expected_contents.size(), target_contents.size());
     if (target_contents != expected_contents) {
       ADD_FAILURE() << "Contents don't match.";
@@ -69,7 +70,8 @@
     }
   }
 
-  ScopedTempFile tgt_file_{"fd_tgt.XXXXXX"};
+  // Path to the target temporary file.
+  std::string tgt_path_;
 
   // 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 3b959f3..59cfe2b 100644
--- a/payload_consumer/file_writer_unittest.cc
+++ b/payload_consumer/file_writer_unittest.cc
@@ -35,7 +35,8 @@
 class FileWriterTest : public ::testing::Test {};
 
 TEST(FileWriterTest, SimpleTest) {
-  ScopedTempFile file("FileWriterTest-XXXXXX");
+  // Create a uniquely named file for testing.
+  test_utils::ScopedTempFile file("FileWriterTest-XXXXXX");
   DirectFileWriter file_writer;
   EXPECT_EQ(0,
             file_writer.Open(file.path().c_str(),
@@ -59,7 +60,7 @@
 
 TEST(FileWriterTest, WriteErrorTest) {
   // Create a uniquely named file for testing.
-  ScopedTempFile file("FileWriterTest-XXXXXX");
+  test_utils::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 8b39f6d..36e5a35 100644
--- a/payload_consumer/filesystem_verifier_action.cc
+++ b/payload_consumer/filesystem_verifier_action.cc
@@ -20,65 +20,24 @@
 #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 <base/strings/string_util.h>
 #include <brillo/data_encoding.h>
-#include <brillo/message_loops/message_loop.h>
-#include <brillo/secure_blob.h>
 #include <brillo/streams/file_stream.h>
 
-#include "common/error_code.h"
-#include "payload_generator/delta_diff_generator.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/payload_consumer/file_descriptor.h"
 
 using brillo::data_encoding::Base64Encode;
 using std::string;
 
-// On a partition with verity enabled, we expect to see the following format:
-// ===================================================
-//              Normal Filesystem Data
-// (this should take most of the space, like over 90%)
-// ===================================================
-//                  Hash tree
-//         ~0.8% (e.g. 16M for 2GB image)
-// ===================================================
-//                  FEC data
-//                    ~0.8%
-// ===================================================
-//                   Footer
-//                     4K
-// ===================================================
-
-// For OTA that doesn't do on device verity computation, hash tree and fec data
-// are written during DownloadAction as a regular InstallOp, so no special
-// handling needed, we can just read the entire partition in 1 go.
-
-// Verity enabled case: Only Normal FS data is written during download action.
-// When hasing the entire partition, we will need to build the hash tree, write
-// it to disk, then build FEC, and write it to disk. Therefore, it is important
-// that we finish writing hash tree before we attempt to read & hash it. The
-// same principal applies to FEC data.
-
-// |verity_writer_| handles building and
-// writing of FEC/HashTree, we just need to be careful when reading.
-// Specifically, we must stop at beginning of Hash tree, let |verity_writer_|
-// write both hash tree and FEC, then continue reading the remaining part of
-// partition.
-
 namespace chromeos_update_engine {
 
 namespace {
 const off_t kReadFileBufferSize = 128 * 1024;
-constexpr float kVerityProgressPercent = 0.6;
 }  // namespace
 
 void FilesystemVerifierAction::PerformAction() {
@@ -98,7 +57,7 @@
     abort_action_completer.set_code(ErrorCode::kSuccess);
     return;
   }
-  install_plan_.Dump();
+
   StartPartitionHashing();
   abort_action_completer.set_should_complete(false);
 }
@@ -109,320 +68,162 @@
 }
 
 void FilesystemVerifierAction::Cleanup(ErrorCode code) {
-  partition_fd_.reset();
+  src_stream_.reset();
   // This memory is not used anymore.
   buffer_.clear();
 
-  // If we didn't write verity, partitions were maped. Releaase resource now.
-  if (!install_plan_.write_verity &&
-      dynamic_control_->UpdateUsesSnapshotCompression()) {
-    LOG(INFO) << "Not writing verity and VABC is enabled, unmapping all "
-                 "partitions";
-    dynamic_control_->UnmapAllPartitions();
-  }
-
   if (cancelled_)
     return;
   if (code == ErrorCode::kSuccess && HasOutputPipe())
     SetOutputObject(install_plan_);
-  UpdateProgress(1.0);
   processor_->ActionComplete(this, code);
 }
 
-void FilesystemVerifierAction::UpdateProgress(double progress) {
-  if (delegate_ != nullptr) {
-    delegate_->OnVerifyProgressUpdate(progress);
-  }
-}
-
-void FilesystemVerifierAction::UpdatePartitionProgress(double progress) {
-  // We don't consider sizes of each partition. Every partition
-  // has the same length on progress bar.
-  // TODO(b/186087589): Take sizes of each partition into account.
-  UpdateProgress((progress + partition_index_) /
-                 install_plan_.partitions.size());
-}
-
-bool FilesystemVerifierAction::InitializeFdVABC(bool should_write_verity) {
-  const InstallPlan::Partition& partition =
-      install_plan_.partitions[partition_index_];
-
-  if (!should_write_verity) {
-    // In VABC, we cannot map/unmap partitions w/o first closing ALL fds first.
-    // Since this function might be called inside a ScheduledTask, the closure
-    // might have a copy of partition_fd_ when executing this function. Which
-    // means even if we do |partition_fd_.reset()| here, there's a chance that
-    // underlying fd isn't closed until we return. This is unacceptable, we need
-    // to close |partition_fd| right away.
-    if (partition_fd_) {
-      partition_fd_->Close();
-      partition_fd_.reset();
-    }
-    // In VABC, if we are not writing verity, just map all partitions,
-    // and read using regular fd on |postinstall_mount_device| .
-    // All read will go through snapuserd, which provides a consistent
-    // view: device will use snapuserd to read partition during boot.
-    // b/186196758
-    // Call UnmapAllPartitions() first, because if we wrote verity before, these
-    // writes won't be visible to previously opened snapuserd daemon. To ensure
-    // that we will see the most up to date data from partitions, call Unmap()
-    // then Map() to re-spin daemon.
-    dynamic_control_->UnmapAllPartitions();
-    dynamic_control_->MapAllPartitions();
-    return InitializeFd(partition.readonly_target_path);
-  }
-  partition_fd_ =
-      dynamic_control_->OpenCowFd(partition.name, partition.source_path, true);
-  if (!partition_fd_) {
-    LOG(ERROR) << "OpenCowReader(" << partition.name << ", "
-               << partition.source_path << ") failed.";
-    return false;
-  }
-  partition_size_ = partition.target_size;
-  return true;
-}
-
-bool FilesystemVerifierAction::InitializeFd(const std::string& part_path) {
-  partition_fd_ = FileDescriptorPtr(new EintrSafeFileDescriptor());
-  const bool write_verity = ShouldWriteVerity();
-  int flags = write_verity ? O_RDWR : O_RDONLY;
-  if (!utils::SetBlockDeviceReadOnly(part_path, !write_verity)) {
-    LOG(WARNING) << "Failed to set block device " << part_path << " as "
-                 << (write_verity ? "writable" : "readonly");
-  }
-  if (!partition_fd_->Open(part_path.c_str(), flags)) {
-    LOG(ERROR) << "Unable to open " << part_path << " for reading.";
-    return false;
-  }
-  return true;
-}
-
-void FilesystemVerifierAction::WriteVerityAndHashPartition(
-    FileDescriptorPtr fd,
-    const off64_t start_offset,
-    const off64_t end_offset,
-    void* buffer,
-    const size_t buffer_size) {
-  if (start_offset >= end_offset) {
-    LOG_IF(WARNING, start_offset > end_offset)
-        << "start_offset is greater than end_offset : " << start_offset << " > "
-        << end_offset;
-    if (!verity_writer_->Finalize(fd, fd)) {
-      LOG(ERROR) << "Failed to write verity data";
-      Cleanup(ErrorCode::kVerityCalculationError);
-      return;
-    }
-    if (dynamic_control_->UpdateUsesSnapshotCompression()) {
-      // Spin up snapuserd to read fs.
-      if (!InitializeFdVABC(false)) {
-        LOG(ERROR) << "Failed to map all partitions";
-        Cleanup(ErrorCode::kFilesystemVerifierError);
-        return;
-      }
-    }
-    HashPartition(partition_fd_, 0, partition_size_, buffer, buffer_size);
-    return;
-  }
-  const auto cur_offset = fd->Seek(start_offset, SEEK_SET);
-  if (cur_offset != start_offset) {
-    PLOG(ERROR) << "Failed to seek to offset: " << start_offset;
-    Cleanup(ErrorCode::kVerityCalculationError);
-    return;
-  }
-  const auto read_size =
-      std::min<size_t>(buffer_size, end_offset - start_offset);
-  const auto bytes_read = fd->Read(buffer, read_size);
-  if (bytes_read < 0 || static_cast<size_t>(bytes_read) != read_size) {
-    PLOG(ERROR) << "Failed to read offset " << start_offset << " expected "
-                << read_size << " bytes, actual: " << bytes_read;
-    Cleanup(ErrorCode::kVerityCalculationError);
-    return;
-  }
-  if (!verity_writer_->Update(
-          start_offset, static_cast<const uint8_t*>(buffer), read_size)) {
-    LOG(ERROR) << "VerityWriter::Update() failed";
-    Cleanup(ErrorCode::kVerityCalculationError);
-    return;
-  }
-  UpdatePartitionProgress((start_offset + bytes_read) * 1.0f / partition_size_ *
-                          kVerityProgressPercent);
-  CHECK(pending_task_id_.PostTask(
-      FROM_HERE,
-      base::BindOnce(&FilesystemVerifierAction::WriteVerityAndHashPartition,
-                     base::Unretained(this),
-                     fd,
-                     start_offset + bytes_read,
-                     end_offset,
-                     buffer,
-                     buffer_size)));
-}
-
-void FilesystemVerifierAction::HashPartition(FileDescriptorPtr fd,
-                                             const off64_t start_offset,
-                                             const off64_t end_offset,
-                                             void* buffer,
-                                             const size_t buffer_size) {
-  if (start_offset >= end_offset) {
-    LOG_IF(WARNING, start_offset > end_offset)
-        << "start_offset is greater than end_offset : " << start_offset << " > "
-        << end_offset;
-    FinishPartitionHashing();
-    return;
-  }
-  const auto cur_offset = fd->Seek(start_offset, SEEK_SET);
-  if (cur_offset != start_offset) {
-    PLOG(ERROR) << "Failed to seek to offset: " << start_offset;
-    Cleanup(ErrorCode::kFilesystemVerifierError);
-    return;
-  }
-  const auto read_size =
-      std::min<size_t>(buffer_size, end_offset - start_offset);
-  const auto bytes_read = fd->Read(buffer, read_size);
-  if (bytes_read < 0 || static_cast<size_t>(bytes_read) != read_size) {
-    PLOG(ERROR) << "Failed to read offset " << start_offset << " expected "
-                << read_size << " bytes, actual: " << bytes_read;
-    Cleanup(ErrorCode::kFilesystemVerifierError);
-    return;
-  }
-  if (!hasher_->Update(buffer, read_size)) {
-    LOG(ERROR) << "Hasher updated failed on offset" << start_offset;
-    Cleanup(ErrorCode::kFilesystemVerifierError);
-    return;
-  }
-  const auto progress = (start_offset + bytes_read) * 1.0f / partition_size_;
-  UpdatePartitionProgress(progress * (1 - kVerityProgressPercent) +
-                          kVerityProgressPercent);
-  CHECK(pending_task_id_.PostTask(
-      FROM_HERE,
-      base::BindOnce(&FilesystemVerifierAction::HashPartition,
-                     base::Unretained(this),
-                     fd,
-                     start_offset + bytes_read,
-                     end_offset,
-                     buffer,
-                     buffer_size)));
-}
-
 void FilesystemVerifierAction::StartPartitionHashing() {
   if (partition_index_ == install_plan_.partitions.size()) {
-    if (!install_plan_.untouched_dynamic_partitions.empty()) {
-      LOG(INFO) << "Verifying extents of untouched dynamic partitions ["
-                << base::JoinString(install_plan_.untouched_dynamic_partitions,
-                                    ", ")
-                << "]";
-      if (!dynamic_control_->VerifyExtentsForUntouchedPartitions(
-              install_plan_.source_slot,
-              install_plan_.target_slot,
-              install_plan_.untouched_dynamic_partitions)) {
-        Cleanup(ErrorCode::kFilesystemVerifierError);
-        return;
-      }
-    }
-
     Cleanup(ErrorCode::kSuccess);
     return;
   }
   const InstallPlan::Partition& partition =
       install_plan_.partitions[partition_index_];
-  const auto& part_path = GetPartitionPath();
-  partition_size_ = GetPartitionSize();
 
-  LOG(INFO) << "Hashing partition " << partition_index_ << " ("
-            << partition.name << ") on device " << part_path;
-  auto success = false;
-  if (IsVABC(partition)) {
-    success = InitializeFdVABC(ShouldWriteVerity());
-  } 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);
+  string part_path;
+  switch (verifier_step_) {
+    case VerifierStep::kVerifySourceHash:
+      part_path = partition.source_path;
+      partition_size_ = partition.source_size;
+      break;
+    case VerifierStep::kVerifyTargetHash:
+      part_path = partition.target_path;
+      partition_size_ = partition.target_size;
+      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;
     }
-    success = InitializeFd(part_path);
-  }
-  if (!success) {
+    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";
+    Cleanup(ErrorCode::kFilesystemVerifierError);
+    return;
+  }
+
   buffer_.resize(kReadFileBufferSize);
   hasher_ = std::make_unique<HashCalculator>();
 
   offset_ = 0;
-  filesystem_data_end_ = partition_size_;
-  if (partition.fec_offset > 0) {
-    CHECK_LE(partition.hash_tree_offset, partition.fec_offset)
-        << " Hash tree is expected to come before FEC data";
-  }
-  if (partition.hash_tree_offset != 0) {
-    filesystem_data_end_ = partition.hash_tree_offset;
-  } else if (partition.fec_offset != 0) {
-    filesystem_data_end_ = partition.fec_offset;
-  }
-  if (ShouldWriteVerity()) {
-    LOG(INFO) << "Verity writes enabled on partition " << partition.name;
+  if (verifier_step_ == VerifierStep::kVerifyTargetHash &&
+      install_plan_.write_verity) {
     if (!verity_writer_->Init(partition)) {
-      LOG(INFO) << "Verity writes enabled on partition " << partition.name;
       Cleanup(ErrorCode::kVerityCalculationError);
       return;
     }
-    WriteVerityAndHashPartition(
-        partition_fd_, 0, filesystem_data_end_, buffer_.data(), buffer_.size());
-  } else {
-    LOG(INFO) << "Verity writes disabled on partition " << partition.name;
-    HashPartition(
-        partition_fd_, 0, partition_size_, buffer_.data(), buffer_.size());
+  }
+
+  // Start the first read.
+  ScheduleRead();
+}
+
+void FilesystemVerifierAction::ScheduleRead() {
+  const InstallPlan::Partition& partition =
+      install_plan_.partitions[partition_index_];
+
+  // We can only start reading anything past |hash_tree_offset| after we have
+  // already read all the data blocks that the hash tree covers. The same
+  // applies to FEC.
+  uint64_t read_end = partition_size_;
+  if (partition.hash_tree_size != 0 &&
+      offset_ < partition.hash_tree_data_offset + partition.hash_tree_data_size)
+    read_end = std::min(read_end, partition.hash_tree_offset);
+  if (partition.fec_size != 0 &&
+      offset_ < partition.fec_data_offset + partition.fec_data_size)
+    read_end = std::min(read_end, partition.fec_offset);
+  size_t bytes_to_read =
+      std::min(static_cast<uint64_t>(buffer_.size()), read_end - offset_);
+  if (!bytes_to_read) {
+    FinishPartitionHashing();
+    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) {
+    LOG(ERROR) << "Unable to schedule an asynchronous read from the stream.";
+    Cleanup(ErrorCode::kError);
   }
 }
 
-bool FilesystemVerifierAction::IsVABC(
-    const InstallPlan::Partition& partition) const {
-  return dynamic_control_->UpdateUsesSnapshotCompression() &&
-         verifier_step_ == VerifierStep::kVerifyTargetHash &&
-         dynamic_control_->IsDynamicPartition(partition.name,
-                                              install_plan_.target_slot);
-}
-
-const std::string& FilesystemVerifierAction::GetPartitionPath() const {
-  const InstallPlan::Partition& partition =
-      install_plan_.partitions[partition_index_];
-  switch (verifier_step_) {
-    case VerifierStep::kVerifySourceHash:
-      return partition.source_path;
-    case VerifierStep::kVerifyTargetHash:
-      if (IsVABC(partition)) {
-        return partition.readonly_target_path;
-      } else {
-        return partition.target_path;
-      }
+void FilesystemVerifierAction::OnReadDoneCallback(size_t bytes_read) {
+  if (cancelled_) {
+    Cleanup(ErrorCode::kError);
+    return;
   }
-}
 
-size_t FilesystemVerifierAction::GetPartitionSize() const {
-  const InstallPlan::Partition& partition =
-      install_plan_.partitions[partition_index_];
-  switch (verifier_step_) {
-    case VerifierStep::kVerifySourceHash:
-      return partition.source_size;
-    case VerifierStep::kVerifyTargetHash:
-      return partition.target_size;
+  if (bytes_read == 0) {
+    LOG(ERROR) << "Failed to read the remaining " << partition_size_ - offset_
+               << " bytes from partition "
+               << install_plan_.partitions[partition_index_].name;
+    Cleanup(ErrorCode::kFilesystemVerifierError);
+    return;
   }
+
+  if (!hasher_->Update(buffer_.data(), bytes_read)) {
+    LOG(ERROR) << "Unable to update the hash.";
+    Cleanup(ErrorCode::kError);
+    return;
+  }
+
+  if (verifier_step_ == VerifierStep::kVerifyTargetHash &&
+      install_plan_.write_verity) {
+    if (!verity_writer_->Update(offset_, buffer_.data(), bytes_read)) {
+      Cleanup(ErrorCode::kVerityCalculationError);
+      return;
+    }
+  }
+
+  offset_ += bytes_read;
+
+  if (offset_ == partition_size_) {
+    FinishPartitionHashing();
+    return;
+  }
+
+  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::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() {
@@ -448,8 +249,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_++;
@@ -485,20 +286,17 @@
       }
       // 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();
-  if (partition_fd_) {
-    partition_fd_->Close();
-    partition_fd_.reset();
-  }
+  src_stream_->CloseBlocking(nullptr);
   StartPartitionHashing();
 }
 
diff --git a/payload_consumer/filesystem_verifier_action.h b/payload_consumer/filesystem_verifier_action.h
index 850abda..83d6668 100644
--- a/payload_consumer/filesystem_verifier_action.h
+++ b/payload_consumer/filesystem_verifier_action.h
@@ -22,15 +22,12 @@
 
 #include <memory>
 #include <string>
-#include <utility>
 #include <vector>
 
-#include <brillo/message_loops/message_loop.h>
+#include <brillo/streams/stream.h>
 
 #include "update_engine/common/action.h"
 #include "update_engine/common/hash_calculator.h"
-#include "update_engine/common/scoped_task_id.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"
 
@@ -52,62 +49,32 @@
   kVerifySourceHash,
 };
 
-class FilesystemVerifyDelegate {
- public:
-  virtual ~FilesystemVerifyDelegate() = default;
-  virtual void OnVerifyProgressUpdate(double progress) = 0;
-};
-
 class FilesystemVerifierAction : public InstallPlanAction {
  public:
-  explicit FilesystemVerifierAction(
-      DynamicPartitionControlInterface* dynamic_control)
-      : verity_writer_(verity_writer::CreateVerityWriter()),
-        dynamic_control_(dynamic_control) {
-    CHECK(dynamic_control_);
-  }
-
+  FilesystemVerifierAction()
+      : verity_writer_(verity_writer::CreateVerityWriter()) {}
   ~FilesystemVerifierAction() override = default;
 
   void PerformAction() override;
   void TerminateProcessing() override;
 
-  // Used for listening to progress updates
-  void set_delegate(FilesystemVerifyDelegate* delegate) {
-    this->delegate_ = delegate;
-  }
-  [[nodiscard]] FilesystemVerifyDelegate* get_delegate() const {
-    return this->delegate_;
-  }
-
   // Debugging/logging
   static std::string StaticType() { return "FilesystemVerifierAction"; }
   std::string Type() const override { return StaticType(); }
 
  private:
   friend class FilesystemVerifierActionTestDelegate;
-  void WriteVerityAndHashPartition(FileDescriptorPtr fd,
-                                   const off64_t start_offset,
-                                   const off64_t end_offset,
-                                   void* buffer,
-                                   const size_t buffer_size);
-  void HashPartition(FileDescriptorPtr fd,
-                     const off64_t start_offset,
-                     const off64_t end_offset,
-                     void* buffer,
-                     const size_t buffer_size);
-
-  // 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();
 
-  const std::string& GetPartitionPath() const;
+  // Schedules the asynchronous read of the filesystem.
+  void ScheduleRead();
 
-  bool IsVABC(const InstallPlan::Partition& partition) const;
-
-  size_t GetPartitionSize() const;
+  // 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);
 
   // When the read is done, finalize the hash checking of the current partition
   // and continue checking the next one.
@@ -118,17 +85,6 @@
   // true if TerminateProcessing() was called.
   void Cleanup(ErrorCode code);
 
-  // Invoke delegate callback to report progress, if delegate is not null
-  void UpdateProgress(double progress);
-
-  // Updates progress of current partition. |progress| should be in range [0,
-  // 1], and it will be scaled appropriately with # of partitions.
-  void UpdatePartitionProgress(double progress);
-
-  // Initialize read_fd_ and write_fd_
-  bool InitializeFd(const std::string& part_path);
-  bool InitializeFdVABC(bool should_write_verity);
-
   // The type of the partition that we are verifying.
   VerifierStep verifier_step_ = VerifierStep::kVerifyTargetHash;
 
@@ -136,24 +92,23 @@
   // being hashed.
   size_t partition_index_{0};
 
-  // If not null, the FileDescriptor used to read from the device.
-  // verity writer might attempt to write to this fd, if verity is enabled.
-  FileDescriptorPtr partition_fd_;
+  // If not null, the FileStream used to read from the device.
+  brillo::StreamPtr src_stream_;
 
   // Buffer for storing data we read.
   brillo::Blob buffer_;
 
   bool cancelled_{false};  // true if the action has been cancelled.
 
+  // The install plan we're passed in via the input pipe.
+  InstallPlan install_plan_;
+
   // Calculates the hash of the data.
   std::unique_ptr<HashCalculator> hasher_;
 
   // Write verity data of the current partition.
   std::unique_ptr<VerityWriterInterface> verity_writer_;
 
-  // Verifies the untouched dynamic partitions for partial updates.
-  DynamicPartitionControlInterface* dynamic_control_{nullptr};
-
   // Reads and hashes this many bytes from the head of the input stream. When
   // the partition starts to be hashed, this field is initialized from the
   // corresponding InstallPlan::Partition size which is the total size
@@ -164,16 +119,6 @@
   // The byte offset that we are reading in the current partition.
   uint64_t offset_{0};
 
-  // The end offset of filesystem data, first byte position of hashtree.
-  uint64_t filesystem_data_end_{0};
-
-  // 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.
-  ScopedTaskId pending_task_id_;
-
   DISALLOW_COPY_AND_ASSIGN(FilesystemVerifierAction);
 };
 
diff --git a/payload_consumer/filesystem_verifier_action_unittest.cc b/payload_consumer/filesystem_verifier_action_unittest.cc
index f2f2954..cb33404 100644
--- a/payload_consumer/filesystem_verifier_action_unittest.cc
+++ b/payload_consumer/filesystem_verifier_action_unittest.cc
@@ -16,8 +16,6 @@
 
 #include "update_engine/payload_consumer/filesystem_verifier_action.h"
 
-#include <algorithm>
-#include <cstring>
 #include <memory>
 #include <string>
 #include <utility>
@@ -27,194 +25,34 @@
 #include <brillo/message_loops/fake_message_loop.h>
 #include <brillo/message_loops/message_loop_utils.h>
 #include <brillo/secure_blob.h>
-#include <fec/ecc.h>
 #include <gtest/gtest.h>
-#include <libsnapshot/snapshot_writer.h>
-#include <sys/stat.h>
 
-#include "update_engine/common/dynamic_partition_control_stub.h"
 #include "update_engine/common/hash_calculator.h"
-#include "update_engine/common/mock_dynamic_partition_control.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/install_plan.h"
-#include "update_engine/payload_consumer/verity_writer_android.h"
 
 using brillo::MessageLoop;
 using std::string;
-using testing::_;
-using testing::AtLeast;
-using testing::DoAll;
-using testing::NiceMock;
-using testing::Return;
-using testing::SetArgPointee;
 
 namespace chromeos_update_engine {
 
 class FilesystemVerifierActionTest : public ::testing::Test {
- public:
-  static constexpr size_t BLOCK_SIZE = 4096;
-  // We use SHA256 for testing, so hash size is 256bits / 8
-  static constexpr size_t HASH_SIZE = 256 / 8;
-  static constexpr size_t PARTITION_SIZE = BLOCK_SIZE * 1024;
-  static constexpr size_t HASH_TREE_START_OFFSET = 800 * BLOCK_SIZE;
-  size_t hash_tree_size = 0;
-  size_t fec_start_offset = 0;
-  size_t fec_data_size = 0;
-  static constexpr size_t FEC_ROOTS = 2;
-  size_t fec_rounds = 0;
-  size_t fec_size = 0;
-
  protected:
-  void SetUp() override {
-    hash_tree_size = HashTreeBuilder::CalculateSize(
-        HASH_TREE_START_OFFSET, BLOCK_SIZE, HASH_SIZE);
-    fec_start_offset = HASH_TREE_START_OFFSET + hash_tree_size;
-    fec_data_size = fec_start_offset;
-    static constexpr size_t FEC_ROOTS = 2;
-    fec_rounds =
-        utils::DivRoundUp(fec_data_size / BLOCK_SIZE, FEC_RSM - FEC_ROOTS);
-    fec_size = fec_rounds * FEC_ROOTS * BLOCK_SIZE;
-
-    fec_data_.resize(fec_size);
-    hash_tree_data_.resize(hash_tree_size);
-    // Globally readable writable, as we want to write data
-    ASSERT_EQ(0, fchmod(source_part_.fd(), 0666))
-        << " Failed to set " << source_part_.path() << " as writable "
-        << strerror(errno);
-    ASSERT_EQ(0, fchmod(target_part_.fd(), 0666))
-        << " Failed to set " << target_part_.path() << " as writable "
-        << strerror(errno);
-    brillo::Blob part_data(PARTITION_SIZE);
-    test_utils::FillWithData(&part_data);
-    ASSERT_TRUE(utils::WriteFile(
-        source_part_.path().c_str(), part_data.data(), part_data.size()));
-    // FillWithData() will fill with different data next call. We want
-    // source/target partitions to contain different data for testing.
-    test_utils::FillWithData(&part_data);
-    ASSERT_TRUE(utils::WriteFile(
-        target_part_.path().c_str(), part_data.data(), part_data.size()));
-    loop_.SetAsCurrent();
-  }
+  void SetUp() override { loop_.SetAsCurrent(); }
 
   void TearDown() override {
     EXPECT_EQ(0, brillo::MessageLoopRunMaxIterations(&loop_, 1));
   }
 
-  void DoTestVABC(bool clear_target_hash, bool enable_verity);
-
   // Returns true iff test has completed successfully.
   bool DoTest(bool terminate_early, bool hash_fail);
 
   void BuildActions(const InstallPlan& install_plan);
-  void BuildActions(const InstallPlan& install_plan,
-                    DynamicPartitionControlInterface* dynamic_control);
-
-  InstallPlan::Partition* AddFakePartition(InstallPlan* install_plan,
-                                           std::string name = "fake_part") {
-    InstallPlan::Partition& part = install_plan->partitions.emplace_back();
-    part.name = name;
-    part.target_path = target_part_.path();
-    part.readonly_target_path = part.target_path;
-    part.target_size = PARTITION_SIZE;
-    part.block_size = BLOCK_SIZE;
-    part.source_path = source_part_.path();
-    part.source_size = PARTITION_SIZE;
-    EXPECT_TRUE(
-        HashCalculator::RawHashOfFile(source_part_.path(), &part.source_hash));
-    EXPECT_TRUE(
-        HashCalculator::RawHashOfFile(target_part_.path(), &part.target_hash));
-    return &part;
-  }
-  static void ZeroRange(FileDescriptorPtr fd,
-                        size_t start_block,
-                        size_t num_blocks) {
-    std::vector<unsigned char> buffer(BLOCK_SIZE);
-    ASSERT_EQ((ssize_t)(start_block * BLOCK_SIZE),
-              fd->Seek(start_block * BLOCK_SIZE, SEEK_SET));
-    for (size_t i = 0; i < num_blocks; i++) {
-      ASSERT_TRUE(utils::WriteAll(fd, buffer.data(), buffer.size()));
-    }
-  }
-
-  void SetHashWithVerity(InstallPlan::Partition* partition) {
-    partition->hash_tree_algorithm = "sha256";
-    partition->hash_tree_size = hash_tree_size;
-    partition->hash_tree_offset = HASH_TREE_START_OFFSET;
-    partition->hash_tree_data_offset = 0;
-    partition->hash_tree_data_size = HASH_TREE_START_OFFSET;
-    partition->fec_size = fec_size;
-    partition->fec_offset = fec_start_offset;
-    partition->fec_data_offset = 0;
-    partition->fec_data_size = fec_data_size;
-    partition->fec_roots = FEC_ROOTS;
-    VerityWriterAndroid verity_writer;
-    ASSERT_TRUE(verity_writer.Init(*partition));
-    LOG(INFO) << "Opening " << partition->readonly_target_path;
-    auto fd = std::make_shared<EintrSafeFileDescriptor>();
-    ASSERT_TRUE(fd->Open(partition->readonly_target_path.c_str(), O_RDWR))
-        << "Failed to open " << partition->target_path.c_str() << " "
-        << strerror(errno);
-    std::vector<unsigned char> buffer(BLOCK_SIZE);
-    // Only need to read up to hash tree
-    auto bytes_to_read = HASH_TREE_START_OFFSET;
-    auto offset = 0;
-    while (bytes_to_read > 0) {
-      const auto bytes_read = fd->Read(
-          buffer.data(), std::min<size_t>(buffer.size(), bytes_to_read));
-      ASSERT_GT(bytes_read, 0)
-          << "offset: " << offset << " bytes to read: " << bytes_to_read
-          << " error: " << strerror(errno);
-      ASSERT_TRUE(verity_writer.Update(offset, buffer.data(), bytes_read));
-      bytes_to_read -= bytes_read;
-      offset += bytes_read;
-    }
-    ASSERT_TRUE(verity_writer.Finalize(fd, fd));
-    ASSERT_TRUE(fd->IsOpen());
-    ASSERT_TRUE(HashCalculator::RawHashOfFile(target_part_.path(),
-                                              &partition->target_hash));
-
-    ASSERT_TRUE(fd->Seek(HASH_TREE_START_OFFSET, SEEK_SET));
-    ASSERT_EQ(fd->Read(hash_tree_data_.data(), hash_tree_data_.size()),
-              static_cast<ssize_t>(hash_tree_data_.size()))
-        << "Failed to read hashtree " << strerror(errno);
-    ASSERT_TRUE(fd->Seek(fec_start_offset, SEEK_SET));
-    ASSERT_EQ(fd->Read(fec_data_.data(), fec_data_.size()),
-              static_cast<ssize_t>(fec_data_.size()))
-        << "Failed to read FEC " << strerror(errno);
-    // Fs verification action is expected to write them, so clear verity data to
-    // ensure that they are re-created correctly.
-    ZeroRange(
-        fd, HASH_TREE_START_OFFSET / BLOCK_SIZE, hash_tree_size / BLOCK_SIZE);
-    ZeroRange(fd, fec_start_offset / BLOCK_SIZE, fec_size / BLOCK_SIZE);
-  }
 
   brillo::FakeMessageLoop loop_{nullptr};
   ActionProcessor processor_;
-  DynamicPartitionControlStub dynamic_control_stub_;
-  std::vector<unsigned char> fec_data_;
-  std::vector<unsigned char> hash_tree_data_;
-  static ScopedTempFile source_part_;
-  static ScopedTempFile target_part_;
-  InstallPlan install_plan_;
 };
 
-ScopedTempFile FilesystemVerifierActionTest::source_part_{
-    "source_part.XXXXXX", true, PARTITION_SIZE};
-ScopedTempFile FilesystemVerifierActionTest::target_part_{
-    "target_part.XXXXXX", true, PARTITION_SIZE};
-
-static void EnableVABC(MockDynamicPartitionControl* dynamic_control,
-                       const std::string& part_name) {
-  ON_CALL(*dynamic_control, GetDynamicPartitionsFeatureFlag())
-      .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
-  ON_CALL(*dynamic_control, UpdateUsesSnapshotCompression())
-      .WillByDefault(Return(true));
-  ON_CALL(*dynamic_control, IsDynamicPartition(part_name, _))
-      .WillByDefault(Return(true));
-}
-
 class FilesystemVerifierActionTestDelegate : public ActionProcessorDelegate {
  public:
   FilesystemVerifierActionTestDelegate()
@@ -232,8 +70,7 @@
     if (action->Type() == FilesystemVerifierAction::StaticType()) {
       ran_ = true;
       code_ = code;
-      EXPECT_FALSE(
-          static_cast<FilesystemVerifierAction*>(action)->partition_fd_);
+      EXPECT_FALSE(static_cast<FilesystemVerifierAction*>(action)->src_stream_);
     } else if (action->Type() ==
                ObjectCollectorAction<InstallPlan>::StaticType()) {
       auto collector_action =
@@ -253,7 +90,7 @@
 
 bool FilesystemVerifierActionTest::DoTest(bool terminate_early,
                                           bool hash_fail) {
-  ScopedTempFile a_loop_file("a_loop_file.XXXXXX");
+  test_utils::ScopedTempFile a_loop_file("a_loop_file.XXXXXX");
 
   // Make random data for a.
   const size_t kLoopFileSize = 10 * 1024 * 1024 + 512;
@@ -280,8 +117,9 @@
   bool success = true;
 
   // Set up the action objects
-  install_plan_.source_slot = 0;
-  install_plan_.target_slot = 1;
+  InstallPlan install_plan;
+  install_plan.source_slot = 0;
+  install_plan.target_slot = 1;
   InstallPlan::Partition part;
   part.name = "part";
   part.target_size = kLoopFileSize - (hash_fail ? 1 : 0);
@@ -296,19 +134,23 @@
     ADD_FAILURE();
     success = false;
   }
-  install_plan_.partitions = {part};
+  install_plan.partitions = {part};
 
-  BuildActions(install_plan_);
+  BuildActions(install_plan);
 
   FilesystemVerifierActionTestDelegate delegate;
   processor_.set_delegate(&delegate);
 
-  loop_.PostTask(base::Bind(&ActionProcessor::StartProcessing,
-                            base::Unretained(&processor_)));
-  if (terminate_early) {
-    loop_.PostTask(base::Bind(&ActionProcessor::StopProcessing,
-                              base::Unretained(&processor_)));
-  }
+  loop_.PostTask(FROM_HERE,
+                 base::Bind(
+                     [](ActionProcessor* processor, bool terminate_early) {
+                       processor->StartProcessing();
+                       if (terminate_early) {
+                         processor->StopProcessing();
+                       }
+                     },
+                     base::Unretained(&processor_),
+                     terminate_early));
   loop_.Run();
 
   if (!terminate_early) {
@@ -337,18 +179,16 @@
   EXPECT_TRUE(is_a_file_reading_eq);
   success = success && is_a_file_reading_eq;
 
-  bool is_install_plan_eq = (*delegate.install_plan_ == install_plan_);
+  bool is_install_plan_eq = (*delegate.install_plan_ == install_plan);
   EXPECT_TRUE(is_install_plan_eq);
   success = success && is_install_plan_eq;
   return success;
 }
 
 void FilesystemVerifierActionTest::BuildActions(
-    const InstallPlan& install_plan,
-    DynamicPartitionControlInterface* dynamic_control) {
+    const InstallPlan& install_plan) {
   auto feeder_action = std::make_unique<ObjectFeederAction<InstallPlan>>();
-  auto verifier_action =
-      std::make_unique<FilesystemVerifierAction>(dynamic_control);
+  auto verifier_action = std::make_unique<FilesystemVerifierAction>();
   auto collector_action =
       std::make_unique<ObjectCollectorAction<InstallPlan>>();
 
@@ -362,11 +202,6 @@
   processor_.EnqueueAction(std::move(collector_action));
 }
 
-void FilesystemVerifierActionTest::BuildActions(
-    const InstallPlan& install_plan) {
-  BuildActions(install_plan, &dynamic_control_stub_);
-}
-
 class FilesystemVerifierActionTest2Delegate : public ActionProcessorDelegate {
  public:
   void ActionCompleted(ActionProcessor* processor,
@@ -382,8 +217,7 @@
 };
 
 TEST_F(FilesystemVerifierActionTest, MissingInputObjectTest) {
-  auto copier_action =
-      std::make_unique<FilesystemVerifierAction>(&dynamic_control_stub_);
+  auto copier_action = std::make_unique<FilesystemVerifierAction>();
   auto collector_action =
       std::make_unique<ObjectCollectorAction<InstallPlan>>();
 
@@ -396,19 +230,20 @@
   processor_.set_delegate(&delegate);
 
   processor_.StartProcessing();
-  ASSERT_FALSE(processor_.IsRunning());
-  ASSERT_TRUE(delegate.ran_);
+  EXPECT_FALSE(processor_.IsRunning());
+  EXPECT_TRUE(delegate.ran_);
   EXPECT_EQ(ErrorCode::kError, delegate.code_);
 }
 
 TEST_F(FilesystemVerifierActionTest, NonExistentDriveTest) {
+  InstallPlan install_plan;
   InstallPlan::Partition part;
   part.name = "nope";
   part.source_path = "/no/such/file";
   part.target_path = "/no/such/file";
-  install_plan_.partitions = {part};
+  install_plan.partitions = {part};
 
-  BuildActions(install_plan_);
+  BuildActions(install_plan);
 
   FilesystemVerifierActionTest2Delegate delegate;
   processor_.set_delegate(&delegate);
@@ -439,7 +274,7 @@
 
 #ifdef __ANDROID__
 TEST_F(FilesystemVerifierActionTest, RunAsRootWriteVerityTest) {
-  ScopedTempFile part_file("part_file.XXXXXX");
+  test_utils::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);
@@ -449,6 +284,7 @@
   test_utils::ScopedLoopbackDeviceBinder target_device(
       part_file.path(), true, &target_path);
 
+  InstallPlan install_plan;
   InstallPlan::Partition part;
   part.name = "part";
   part.target_path = target_path;
@@ -479,9 +315,9 @@
   part.hash_tree_salt = {0x9e, 0xcb, 0xf8, 0xd5, 0x0b, 0xb4, 0x43,
                          0x0a, 0x7a, 0x10, 0xad, 0x96, 0xd7, 0x15,
                          0x70, 0xba, 0xed, 0x27, 0xe2, 0xae};
-  install_plan_.partitions = {part};
+  install_plan.partitions = {part};
 
-  BuildActions(install_plan_);
+  BuildActions(install_plan);
 
   FilesystemVerifierActionTestDelegate delegate;
   processor_.set_delegate(&delegate);
@@ -500,7 +336,7 @@
 #endif  // __ANDROID__
 
 TEST_F(FilesystemVerifierActionTest, RunAsRootSkipWriteVerityTest) {
-  ScopedTempFile part_file("part_file.XXXXXX");
+  test_utils::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);
@@ -510,7 +346,8 @@
   test_utils::ScopedLoopbackDeviceBinder target_device(
       part_file.path(), true, &target_path);
 
-  install_plan_.write_verity = false;
+  InstallPlan install_plan;
+  install_plan.write_verity = false;
   InstallPlan::Partition part;
   part.name = "part";
   part.target_path = target_path;
@@ -525,9 +362,9 @@
   part.fec_offset = part.fec_data_size;
   part.fec_size = 2 * 4096;
   EXPECT_TRUE(HashCalculator::RawHashOfData(part_data, &part.target_hash));
-  install_plan_.partitions = {part};
+  install_plan.partitions = {part};
 
-  BuildActions(install_plan_);
+  BuildActions(install_plan);
 
   FilesystemVerifierActionTestDelegate delegate;
   processor_.set_delegate(&delegate);
@@ -539,151 +376,8 @@
           base::Unretained(&processor_)));
   loop_.Run();
 
-  ASSERT_FALSE(processor_.IsRunning());
-  ASSERT_TRUE(delegate.ran());
-  ASSERT_EQ(ErrorCode::kSuccess, delegate.code());
+  EXPECT_FALSE(processor_.IsRunning());
+  EXPECT_TRUE(delegate.ran());
+  EXPECT_EQ(ErrorCode::kSuccess, delegate.code());
 }
-
-void FilesystemVerifierActionTest::DoTestVABC(bool clear_target_hash,
-                                              bool enable_verity) {
-  auto part_ptr = AddFakePartition(&install_plan_);
-  if (::testing::Test::HasFailure()) {
-    return;
-  }
-  ASSERT_NE(part_ptr, nullptr);
-  InstallPlan::Partition& part = *part_ptr;
-  part.target_path = "Shouldn't attempt to open this path";
-  if (enable_verity) {
-    install_plan_.write_verity = true;
-    ASSERT_NO_FATAL_FAILURE(SetHashWithVerity(&part));
-  }
-  if (clear_target_hash) {
-    part.target_hash.clear();
-  }
-
-  NiceMock<MockDynamicPartitionControl> dynamic_control;
-
-  EnableVABC(&dynamic_control, part.name);
-  auto open_cow = [part]() {
-    auto cow_fd = std::make_shared<EintrSafeFileDescriptor>();
-    EXPECT_TRUE(cow_fd->Open(part.readonly_target_path.c_str(), O_RDWR))
-        << "Failed to open part " << part.readonly_target_path
-        << strerror(errno);
-    return cow_fd;
-  };
-
-  EXPECT_CALL(dynamic_control, UpdateUsesSnapshotCompression())
-      .Times(AtLeast(1));
-  auto cow_fd = open_cow();
-  if (HasFailure()) {
-    return;
-  }
-
-  if (enable_verity) {
-    ON_CALL(dynamic_control, OpenCowFd(part.name, {part.source_path}, _))
-        .WillByDefault(open_cow);
-    EXPECT_CALL(dynamic_control, OpenCowFd(part.name, {part.source_path}, _))
-        .Times(AtLeast(1));
-
-    // fs verification isn't supposed to write to |readonly_target_path|. All
-    // writes should go through fd returned by |OpenCowFd|. Therefore we set
-    // target part as read-only to make sure.
-    ASSERT_EQ(0, chmod(part.readonly_target_path.c_str(), 0444))
-        << " Failed to set " << part.readonly_target_path << " as read-only "
-        << strerror(errno);
-  } else {
-    // Since we are not writing verity, we should not attempt to OpenCowFd()
-    // reads should go through regular file descriptors on mapped partitions.
-    EXPECT_CALL(dynamic_control, OpenCowFd(part.name, {part.source_path}, _))
-        .Times(0);
-    EXPECT_CALL(dynamic_control, MapAllPartitions()).Times(AtLeast(1));
-  }
-  EXPECT_CALL(dynamic_control, ListDynamicPartitionsForSlot(_, _, _))
-      .WillRepeatedly(
-          DoAll(SetArgPointee<2, std::vector<std::string>>({part.name}),
-                Return(true)));
-
-  BuildActions(install_plan_, &dynamic_control);
-
-  FilesystemVerifierActionTestDelegate delegate;
-  processor_.set_delegate(&delegate);
-
-  loop_.PostTask(FROM_HERE,
-                 base::Bind(&ActionProcessor::StartProcessing,
-                            base::Unretained(&processor_)));
-  loop_.Run();
-
-  ASSERT_FALSE(processor_.IsRunning());
-  ASSERT_TRUE(delegate.ran());
-  if (enable_verity) {
-    std::vector<unsigned char> actual_fec(fec_size);
-    ssize_t bytes_read = 0;
-    ASSERT_TRUE(utils::PReadAll(cow_fd,
-                                actual_fec.data(),
-                                actual_fec.size(),
-                                fec_start_offset,
-                                &bytes_read));
-    ASSERT_EQ(actual_fec, fec_data_);
-    std::vector<unsigned char> actual_hash_tree(hash_tree_size);
-    ASSERT_TRUE(utils::PReadAll(cow_fd,
-                                actual_hash_tree.data(),
-                                actual_hash_tree.size(),
-                                HASH_TREE_START_OFFSET,
-                                &bytes_read));
-    ASSERT_EQ(actual_hash_tree, hash_tree_data_);
-  }
-  if (clear_target_hash) {
-    ASSERT_EQ(ErrorCode::kNewRootfsVerificationError, delegate.code());
-  } else {
-    ASSERT_EQ(ErrorCode::kSuccess, delegate.code());
-  }
-}
-
-TEST_F(FilesystemVerifierActionTest, VABC_NoVerity_Success) {
-  DoTestVABC(false, false);
-}
-
-TEST_F(FilesystemVerifierActionTest, VABC_NoVerity_Target_Mismatch) {
-  DoTestVABC(true, false);
-}
-
-TEST_F(FilesystemVerifierActionTest, VABC_Verity_Success) {
-  DoTestVABC(false, true);
-}
-
-TEST_F(FilesystemVerifierActionTest, VABC_Verity_ReadAfterWrite) {
-  ASSERT_NO_FATAL_FAILURE(DoTestVABC(false, true));
-  // Run FS verification again, w/o writing verity. We have seen a bug where
-  // attempting to run fs again will cause previously written verity data to be
-  // dropped, so cover this scenario.
-  ASSERT_GE(install_plan_.partitions.size(), 1UL);
-  auto& part = install_plan_.partitions[0];
-  install_plan_.write_verity = false;
-  part.readonly_target_path = target_part_.path();
-  NiceMock<MockDynamicPartitionControl> dynamic_control;
-  EnableVABC(&dynamic_control, part.name);
-
-  // b/186196758 is only visible if we repeatedely run FS verification w/o
-  // writing verity
-  for (int i = 0; i < 3; i++) {
-    BuildActions(install_plan_, &dynamic_control);
-
-    FilesystemVerifierActionTestDelegate delegate;
-    processor_.set_delegate(&delegate);
-    loop_.PostTask(
-        FROM_HERE,
-        base::Bind(
-            [](ActionProcessor* processor) { processor->StartProcessing(); },
-            base::Unretained(&processor_)));
-    loop_.Run();
-    ASSERT_FALSE(processor_.IsRunning());
-    ASSERT_TRUE(delegate.ran());
-    ASSERT_EQ(ErrorCode::kSuccess, delegate.code());
-  }
-}
-
-TEST_F(FilesystemVerifierActionTest, VABC_Verity_Target_Mismatch) {
-  DoTestVABC(true, true);
-}
-
 }  // namespace chromeos_update_engine
diff --git a/payload_consumer/install_plan.cc b/payload_consumer/install_plan.cc
index 06b7dd8..766b27c 100644
--- a/payload_consumer/install_plan.cc
+++ b/payload_consumer/install_plan.cc
@@ -16,9 +16,6 @@
 
 #include "update_engine/payload_consumer/install_plan.h"
 
-#include <algorithm>
-#include <utility>
-
 #include <base/format_macros.h>
 #include <base/logging.h>
 #include <base/strings/string_number_conversions.h>
@@ -29,29 +26,9 @@
 #include "update_engine/payload_consumer/payload_constants.h"
 
 using std::string;
-using std::vector;
 
 namespace chromeos_update_engine {
 
-namespace {
-string PayloadUrlsToString(
-    const decltype(InstallPlan::Payload::payload_urls)& payload_urls) {
-  return "(" + base::JoinString(payload_urls, ",") + ")";
-}
-
-string VectorToString(const vector<std::pair<string, string>>& input,
-                      const string& separator) {
-  vector<string> vec;
-  std::transform(input.begin(),
-                 input.end(),
-                 std::back_inserter(vec),
-                 [](const auto& pair) {
-                   return base::JoinString({pair.first, pair.second}, ": ");
-                 });
-  return base::JoinString(vec, separator);
-}
-}  // namespace
-
 string InstallPayloadTypeToString(InstallPayloadType type) {
   switch (type) {
     case InstallPayloadType::kUnknown:
@@ -76,10 +53,34 @@
 }
 
 void InstallPlan::Dump() const {
-  LOG(INFO) << "InstallPlan: \n" << ToString();
-}
+  string partitions_str;
+  for (const auto& partition : partitions) {
+    partitions_str +=
+        base::StringPrintf(", part: %s (source_size: %" PRIu64
+                           ", target_size %" PRIu64 ", postinst:%s)",
+                           partition.name.c_str(),
+                           partition.source_size,
+                           partition.target_size,
+                           utils::ToString(partition.run_postinstall).c_str());
+  }
+  string payloads_str;
+  for (const auto& payload : payloads) {
+    payloads_str += base::StringPrintf(
+        ", payload: (size: %" PRIu64 ", metadata_size: %" PRIu64
+        ", metadata signature: %s, hash: %s, payload type: %s)",
+        payload.size,
+        payload.metadata_size,
+        payload.metadata_signature.c_str(),
+        base::HexEncode(payload.hash.data(), payload.hash.size()).c_str(),
+        InstallPayloadTypeToString(payload.type).c_str());
+  }
 
-string InstallPlan::ToString() const {
+  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)) {
@@ -87,66 +88,19 @@
     url_str = utils::GetFilePath(fd);
   }
 
-  vector<string> result_str;
-  result_str.emplace_back(VectorToString(
-      {
-          {"type", (is_resume ? "resume" : "new_update")},
-          {"version", version},
-          {"source_slot", BootControlInterface::SlotName(source_slot)},
-          {"target_slot", BootControlInterface::SlotName(target_slot)},
-          {"initial url", url_str},
-          {"hash_checks_mandatory", utils::ToString(hash_checks_mandatory)},
-          {"powerwash_required", utils::ToString(powerwash_required)},
-          {"switch_slot_on_reboot", utils::ToString(switch_slot_on_reboot)},
-          {"run_post_install", utils::ToString(run_post_install)},
-          {"is_rollback", utils::ToString(is_rollback)},
-          {"rollback_data_save_requested",
-           utils::ToString(rollback_data_save_requested)},
-          {"write_verity", utils::ToString(write_verity)},
-      },
-      "\n"));
-
-  for (const auto& partition : partitions) {
-    result_str.emplace_back(VectorToString(
-        {
-            {"Partition", partition.name},
-            {"source_size", base::NumberToString(partition.source_size)},
-            {"source_path", partition.source_path},
-            {"source_hash",
-             base::HexEncode(partition.source_hash.data(),
-                             partition.source_hash.size())},
-            {"target_size", base::NumberToString(partition.target_size)},
-            {"target_path", partition.target_path},
-            {"target_hash",
-             base::HexEncode(partition.target_hash.data(),
-                             partition.target_hash.size())},
-            {"run_postinstall", utils::ToString(partition.run_postinstall)},
-            {"postinstall_path", partition.postinstall_path},
-            {"readonly_target_path", partition.readonly_target_path},
-            {"filesystem_type", partition.filesystem_type},
-        },
-        "\n  "));
-  }
-
-  for (unsigned int i = 0; i < payloads.size(); ++i) {
-    const auto& payload = payloads[i];
-    result_str.emplace_back(VectorToString(
-        {
-            {"Payload", base::NumberToString(i)},
-            {"urls", PayloadUrlsToString(payload.payload_urls)},
-            {"size", base::NumberToString(payload.size)},
-            {"metadata_size", base::NumberToString(payload.metadata_size)},
-            {"metadata_signature", payload.metadata_signature},
-            {"hash", base::HexEncode(payload.hash.data(), payload.hash.size())},
-            {"type", InstallPayloadTypeToString(payload.type)},
-            {"fingerprint", payload.fp},
-            {"app_id", payload.app_id},
-            {"already_applied", utils::ToString(payload.already_applied)},
-        },
-        "\n  "));
-  }
-
-  return base::JoinString(result_str, "\n");
+  LOG(INFO) << "InstallPlan: " << (is_resume ? "resume" : "new_update")
+            << version_str
+            << ", source_slot: " << BootControlInterface::SlotName(source_slot)
+            << ", target_slot: " << BootControlInterface::SlotName(target_slot)
+            << ", 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: "
+            << utils::ToString(switch_slot_on_reboot)
+            << ", run_post_install: " << utils::ToString(run_post_install)
+            << ", is_rollback: " << utils::ToString(is_rollback)
+            << ", write_verity: " << utils::ToString(write_verity);
 }
 
 bool InstallPlan::LoadPartitionsFromSlots(BootControlInterface* boot_control) {
@@ -154,19 +108,18 @@
   for (Partition& partition : partitions) {
     if (source_slot != BootControlInterface::kInvalidSlot &&
         partition.source_size > 0) {
-      TEST_AND_RETURN_FALSE(boot_control->GetPartitionDevice(
-          partition.name, source_slot, &partition.source_path));
+      result = boot_control->GetPartitionDevice(
+                   partition.name, source_slot, &partition.source_path) &&
+               result;
     } else {
       partition.source_path.clear();
     }
 
     if (target_slot != BootControlInterface::kInvalidSlot &&
         partition.target_size > 0) {
-      auto device = boot_control->GetPartitionDevice(
-          partition.name, target_slot, source_slot);
-      TEST_AND_RETURN_FALSE(device.has_value());
-      partition.target_path = device->rw_device_path;
-      partition.readonly_target_path = device->readonly_device_path;
+      result = boot_control->GetPartitionDevice(
+                   partition.name, target_slot, &partition.target_path) &&
+               result;
     } else {
       partition.target_path.clear();
     }
diff --git a/payload_consumer/install_plan.h b/payload_consumer/install_plan.h
index 7c77789..ede36b3 100644
--- a/payload_consumer/install_plan.h
+++ b/payload_consumer/install_plan.h
@@ -45,7 +45,6 @@
   bool operator!=(const InstallPlan& that) const;
 
   void Dump() const;
-  std::string ToString() const;
 
   // Loads the |source_path| and |target_path| of all |partitions| based on the
   // |source_slot| and |target_slot| if available. Returns whether it succeeded
@@ -55,28 +54,25 @@
   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};
-    std::string fp;      // fingerprint value unique to the payload
-    std::string app_id;  // App ID of the payload
     // Only download manifest and fill in partitions in install plan without
     // apply the payload if true. Will be set by DownloadAction when resuming
     // multi-payload.
     bool already_applied = false;
 
     bool operator==(const Payload& that) const {
-      return payload_urls == that.payload_urls && size == that.size &&
-             metadata_size == that.metadata_size &&
+      return size == that.size && metadata_size == that.metadata_size &&
              metadata_signature == that.metadata_signature &&
              hash == that.hash && type == that.type &&
-             already_applied == that.already_applied && fp == that.fp &&
-             app_id == that.app_id;
+             already_applied == that.already_applied;
     }
   };
   std::vector<Payload> payloads;
@@ -102,17 +98,9 @@
     uint64_t source_size{0};
     brillo::Blob source_hash;
 
-    // |target_path| is intended to be a path to block device, which you can
-    // open with |open| syscall and perform regular unix style read/write.
-    // For VABC, this will be empty. As you can't read/write VABC devices with
-    // regular syscall.
     std::string target_path;
-    // |mountable_target_device| is intended to be a path to block device which
-    // can be used for mounting this block device's underlying filesystem.
-    std::string readonly_target_path;
     uint64_t target_size{0};
     brillo::Blob target_hash;
-
     uint32_t block_size{0};
 
     // Whether we should run the postinstall script from this partition and the
@@ -158,9 +146,6 @@
   // True if this update is a rollback.
   bool is_rollback{false};
 
-  // True if this rollback should preserve some system data.
-  bool rollback_data_save_requested{false};
-
   // True if the update should write verity.
   // False otherwise.
   bool write_verity{true};
@@ -168,10 +153,6 @@
   // If not blank, a base-64 encoded representation of the PEM-encoded
   // public key in the response.
   std::string public_key_rsa;
-
-  // The name of dynamic partitions not included in the payload. Only used
-  // for partial updates.
-  std::vector<std::string> untouched_dynamic_partitions;
 };
 
 class InstallPlanAction;
@@ -209,10 +190,9 @@
   typedef ActionTraits<InstallPlanAction>::InputObjectType InputObjectType;
   typedef ActionTraits<InstallPlanAction>::OutputObjectType OutputObjectType;
 
- protected:
+ private:
   InstallPlan install_plan_;
 
- private:
   DISALLOW_COPY_AND_ASSIGN(InstallPlanAction);
 };
 
diff --git a/payload_consumer/install_plan_unittest.cc b/payload_consumer/install_plan_unittest.cc
deleted file mode 100644
index 7779494..0000000
--- a/payload_consumer/install_plan_unittest.cc
+++ /dev/null
@@ -1,84 +0,0 @@
-//
-// 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 <gtest/gtest.h>
-
-#include "update_engine/payload_consumer/install_plan.h"
-
-namespace chromeos_update_engine {
-
-TEST(InstallPlanTest, Dump) {
-  InstallPlan install_plan{
-      .download_url = "foo-download-url",
-      .version = "foo-version",
-      .payloads = {{
-          .payload_urls = {"url1", "url2"},
-          .metadata_signature = "foo-signature",
-          .hash = {0xb2, 0xb3},
-          .fp = "foo-fp",
-          .app_id = "foo-app-id",
-      }},
-      .source_slot = BootControlInterface::kInvalidSlot,
-      .target_slot = BootControlInterface::kInvalidSlot,
-      .partitions = {{
-          .name = "foo-partition_name",
-          .source_path = "foo-source-path",
-          .source_hash = {0xb1, 0xb2},
-          .target_path = "foo-target-path",
-          .readonly_target_path = "mountable-device",
-          .target_hash = {0xb3, 0xb4},
-          .postinstall_path = "foo-path",
-          .filesystem_type = "foo-type",
-      }},
-  };
-
-  EXPECT_EQ(install_plan.ToString(),
-            R"(type: new_update
-version: foo-version
-source_slot: INVALID
-target_slot: INVALID
-initial url: foo-download-url
-hash_checks_mandatory: false
-powerwash_required: false
-switch_slot_on_reboot: true
-run_post_install: true
-is_rollback: false
-rollback_data_save_requested: false
-write_verity: true
-Partition: foo-partition_name
-  source_size: 0
-  source_path: foo-source-path
-  source_hash: B1B2
-  target_size: 0
-  target_path: foo-target-path
-  target_hash: B3B4
-  run_postinstall: false
-  postinstall_path: foo-path
-  readonly_target_path: mountable-device
-  filesystem_type: foo-type
-Payload: 0
-  urls: (url1,url2)
-  size: 0
-  metadata_size: 0
-  metadata_signature: foo-signature
-  hash: B2B3
-  type: unknown
-  fingerprint: foo-fp
-  app_id: foo-app-id
-  already_applied: false)");
-}
-
-}  // namespace chromeos_update_engine
diff --git a/common/mock_download_action.h b/payload_consumer/mock_download_action.h
similarity index 81%
rename from common/mock_download_action.h
rename to payload_consumer/mock_download_action.h
index ecda9a3..3abb809 100644
--- a/common/mock_download_action.h
+++ b/payload_consumer/mock_download_action.h
@@ -14,15 +14,15 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_COMMON_MOCK_DOWNLOAD_ACTION_H_
-#define UPDATE_ENGINE_COMMON_MOCK_DOWNLOAD_ACTION_H_
+#ifndef UPDATE_ENGINE_PAYLOAD_CONSUMER_MOCK_DOWNLOAD_ACTION_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_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_COMMON_MOCK_DOWNLOAD_ACTION_H_
+#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_MOCK_DOWNLOAD_ACTION_H_
diff --git a/payload_consumer/mock_partition_writer.h b/payload_consumer/mock_partition_writer.h
deleted file mode 100644
index b056010..0000000
--- a/payload_consumer/mock_partition_writer.h
+++ /dev/null
@@ -1,68 +0,0 @@
-//
-// 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_MOCK_PARTITION_WRITER_H_
-#define UPDATE_ENGINE_MOCK_PARTITION_WRITER_H_
-
-#include <gmock/gmock.h>
-
-#include "common/error_code.h"
-#include "payload_generator/delta_diff_generator.h"
-#include "update_engine/payload_consumer/partition_writer.h"
-
-namespace chromeos_update_engine {
-class MockPartitionWriter : public PartitionWriter {
- public:
-  MockPartitionWriter() : PartitionWriter({}, {}, nullptr, kBlockSize, false) {}
-  virtual ~MockPartitionWriter() = default;
-
-  // Perform necessary initialization work before InstallOperation can be
-  // applied to this partition
-  MOCK_METHOD(bool, Init, (const InstallPlan*, bool, size_t), (override));
-
-  // |CheckpointUpdateProgress| will be called after SetNextOpIndex(), but it's
-  // optional. DeltaPerformer may or may not call this everytime an operation is
-  // applied.
-  MOCK_METHOD(void, CheckpointUpdateProgress, (size_t), (override));
-
-  // 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.
-  MOCK_METHOD(bool,
-              PerformReplaceOperation,
-              (const InstallOperation&, const void*, size_t),
-              (override));
-  MOCK_METHOD(bool,
-              PerformZeroOrDiscardOperation,
-              (const InstallOperation&),
-              (override));
-  MOCK_METHOD(bool,
-              PerformSourceCopyOperation,
-              (const InstallOperation&, ErrorCode*),
-              (override));
-  MOCK_METHOD(bool,
-              PerformSourceBsdiffOperation,
-              (const InstallOperation&, ErrorCode*, const void*, size_t),
-              (override));
-  MOCK_METHOD(bool,
-              PerformPuffDiffOperation,
-              (const InstallOperation&, ErrorCode*, const void*, size_t),
-              (override));
-};
-
-}  // namespace chromeos_update_engine
-
-#endif
diff --git a/payload_consumer/mount_history.cc b/payload_consumer/mount_history.cc
index d699ad9..43a75b3 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::ReadAll(
+  if (!utils::PReadAll(
           blockdevice_fd, block0_buffer.data(), kBlockSize, 0, &bytes_read)) {
     LOG(WARNING) << "PReadAll failed";
     return;
@@ -54,33 +54,14 @@
   //   0x30: len32 Write time
   //   0x34: len16 Number of mounts since the last fsck
   //   0x38: len16 Magic signature 0xEF53
-  //   0x40: len32 Time of last check
-  //   0x108: len32 When the filesystem was created
 
   time_t mount_time =
       *reinterpret_cast<uint32_t*>(&block0_buffer[0x400 + 0x2C]);
-  time_t write_time =
-      *reinterpret_cast<uint32_t*>(&block0_buffer[0x400 + 0x30]);
   uint16_t mount_count =
       *reinterpret_cast<uint16_t*>(&block0_buffer[0x400 + 0x34]);
   uint16_t magic = *reinterpret_cast<uint16_t*>(&block0_buffer[0x400 + 0x38]);
-  time_t check_time =
-      *reinterpret_cast<uint32_t*>(&block0_buffer[0x400 + 0x40]);
-  time_t created_time =
-      *reinterpret_cast<uint32_t*>(&block0_buffer[0x400 + 0x108]);
 
   if (magic == 0xEF53) {
-    // Timestamps can be updated by fsck without updating mount count,
-    // log if any timestamp differ
-    if (! (write_time == created_time && check_time == created_time)) {
-      LOG(WARNING) << "Device have been modified after being created. "
-                   << "Filesystem created on "
-                   << base::Time::FromTimeT(created_time) << ", "
-                   << "last written on "
-                   << base::Time::FromTimeT(write_time) << ", "
-                   << "last checked on "
-                   << base::Time::FromTimeT(check_time) << ".";
-    }
     if (mount_count > 0) {
       LOG(WARNING) << "Device was remounted R/W " << mount_count << " times. "
                    << "Last remount happened on "
diff --git a/payload_consumer/mtd_file_descriptor.cc b/payload_consumer/mtd_file_descriptor.cc
new file mode 100644
index 0000000..5d940cb
--- /dev/null
+++ b/payload_consumer/mtd_file_descriptor.cc
@@ -0,0 +1,263 @@
+//
+// Copyright (C) 2014 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/mtd_file_descriptor.h"
+
+#include <fcntl.h>
+#include <mtd/ubi-user.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+
+#include "update_engine/common/subprocess.h"
+#include "update_engine/common/utils.h"
+
+using std::string;
+
+namespace {
+
+static const char kSysfsClassUbi[] = "/sys/class/ubi/";
+static const char kUsableEbSize[] = "/usable_eb_size";
+static const char kReservedEbs[] = "/reserved_ebs";
+
+using chromeos_update_engine::UbiVolumeInfo;
+using chromeos_update_engine::utils::ReadFile;
+
+// Return a UbiVolumeInfo pointer if |path| is a UBI volume. Otherwise, return
+// a null unique pointer.
+std::unique_ptr<UbiVolumeInfo> GetUbiVolumeInfo(const string& path) {
+  base::FilePath device_node(path);
+  base::FilePath ubi_name(device_node.BaseName());
+
+  string sysfs_node(kSysfsClassUbi);
+  sysfs_node.append(ubi_name.MaybeAsASCII());
+
+  std::unique_ptr<UbiVolumeInfo> ret;
+
+  // Obtain volume info from sysfs.
+  string s_reserved_ebs;
+  if (!ReadFile(sysfs_node + kReservedEbs, &s_reserved_ebs)) {
+    LOG(ERROR) << "Cannot read " << sysfs_node + kReservedEbs;
+    return ret;
+  }
+  string s_eb_size;
+  if (!ReadFile(sysfs_node + kUsableEbSize, &s_eb_size)) {
+    LOG(ERROR) << "Cannot read " << sysfs_node + kUsableEbSize;
+    return ret;
+  }
+
+  base::TrimWhitespaceASCII(
+      s_reserved_ebs, base::TRIM_TRAILING, &s_reserved_ebs);
+  base::TrimWhitespaceASCII(s_eb_size, base::TRIM_TRAILING, &s_eb_size);
+
+  uint64_t reserved_ebs, eb_size;
+  if (!base::StringToUint64(s_reserved_ebs, &reserved_ebs)) {
+    LOG(ERROR) << "Cannot parse reserved_ebs: " << s_reserved_ebs;
+    return ret;
+  }
+  if (!base::StringToUint64(s_eb_size, &eb_size)) {
+    LOG(ERROR) << "Cannot parse usable_eb_size: " << s_eb_size;
+    return ret;
+  }
+
+  ret.reset(new UbiVolumeInfo);
+  ret->reserved_ebs = reserved_ebs;
+  ret->eraseblock_size = eb_size;
+  return ret;
+}
+
+}  // namespace
+
+namespace chromeos_update_engine {
+
+MtdFileDescriptor::MtdFileDescriptor()
+    : read_ctx_(nullptr, &mtd_read_close),
+      write_ctx_(nullptr, &mtd_write_close) {}
+
+bool MtdFileDescriptor::IsMtd(const char* path) {
+  uint64_t size;
+  return mtd_node_info(path, &size, nullptr, nullptr) == 0;
+}
+
+bool MtdFileDescriptor::Open(const char* path, int flags, mode_t mode) {
+  // This File Descriptor does not support read and write.
+  TEST_AND_RETURN_FALSE((flags & O_ACCMODE) != O_RDWR);
+  // But we need to open the underlying file descriptor in O_RDWR mode because
+  // during write, we need to read back to verify the write actually sticks or
+  // we have to skip the block. That job is done by mtdutils library.
+  if ((flags & O_ACCMODE) == O_WRONLY) {
+    flags &= ~O_ACCMODE;
+    flags |= O_RDWR;
+  }
+  TEST_AND_RETURN_FALSE(
+      EintrSafeFileDescriptor::Open(path, flags | O_CLOEXEC, mode));
+
+  if ((flags & O_ACCMODE) == O_RDWR) {
+    write_ctx_.reset(mtd_write_descriptor(fd_, path));
+    nr_written_ = 0;
+  } else {
+    read_ctx_.reset(mtd_read_descriptor(fd_, path));
+  }
+
+  if (!read_ctx_ && !write_ctx_) {
+    Close();
+    return false;
+  }
+
+  return true;
+}
+
+bool MtdFileDescriptor::Open(const char* path, int flags) {
+  mode_t cur = umask(022);
+  umask(cur);
+  return Open(path, flags, 0777 & ~cur);
+}
+
+ssize_t MtdFileDescriptor::Read(void* buf, size_t count) {
+  CHECK(read_ctx_);
+  return mtd_read_data(read_ctx_.get(), static_cast<char*>(buf), count);
+}
+
+ssize_t MtdFileDescriptor::Write(const void* buf, size_t count) {
+  CHECK(write_ctx_);
+  ssize_t result =
+      mtd_write_data(write_ctx_.get(), static_cast<const char*>(buf), count);
+  if (result > 0) {
+    nr_written_ += result;
+  }
+  return result;
+}
+
+off64_t MtdFileDescriptor::Seek(off64_t offset, int whence) {
+  if (write_ctx_) {
+    // Ignore seek in write mode.
+    return nr_written_;
+  }
+  return EintrSafeFileDescriptor::Seek(offset, whence);
+}
+
+bool MtdFileDescriptor::Close() {
+  read_ctx_.reset();
+  write_ctx_.reset();
+  return EintrSafeFileDescriptor::Close();
+}
+
+bool UbiFileDescriptor::IsUbi(const char* path) {
+  base::FilePath device_node(path);
+  base::FilePath ubi_name(device_node.BaseName());
+  TEST_AND_RETURN_FALSE(base::StartsWith(
+      ubi_name.MaybeAsASCII(), "ubi", base::CompareCase::SENSITIVE));
+
+  return static_cast<bool>(GetUbiVolumeInfo(path));
+}
+
+bool UbiFileDescriptor::Open(const char* path, int flags, mode_t mode) {
+  std::unique_ptr<UbiVolumeInfo> info = GetUbiVolumeInfo(path);
+  if (!info) {
+    return false;
+  }
+
+  // This File Descriptor does not support read and write.
+  TEST_AND_RETURN_FALSE((flags & O_ACCMODE) != O_RDWR);
+  TEST_AND_RETURN_FALSE(
+      EintrSafeFileDescriptor::Open(path, flags | O_CLOEXEC, mode));
+
+  usable_eb_blocks_ = info->reserved_ebs;
+  eraseblock_size_ = info->eraseblock_size;
+  volume_size_ = usable_eb_blocks_ * eraseblock_size_;
+
+  if ((flags & O_ACCMODE) == O_WRONLY) {
+    // It's best to use volume update ioctl so that UBI layer will mark the
+    // volume as being updated, and only clear that mark if the update is
+    // successful. We will need to pad to the whole volume size at close.
+    uint64_t vsize = volume_size_;
+    if (ioctl(fd_, UBI_IOCVOLUP, &vsize) != 0) {
+      PLOG(ERROR) << "Cannot issue volume update ioctl";
+      EintrSafeFileDescriptor::Close();
+      return false;
+    }
+    mode_ = kWriteOnly;
+    nr_written_ = 0;
+  } else {
+    mode_ = kReadOnly;
+  }
+
+  return true;
+}
+
+bool UbiFileDescriptor::Open(const char* path, int flags) {
+  mode_t cur = umask(022);
+  umask(cur);
+  return Open(path, flags, 0777 & ~cur);
+}
+
+ssize_t UbiFileDescriptor::Read(void* buf, size_t count) {
+  CHECK(mode_ == kReadOnly);
+  return EintrSafeFileDescriptor::Read(buf, count);
+}
+
+ssize_t UbiFileDescriptor::Write(const void* buf, size_t count) {
+  CHECK(mode_ == kWriteOnly);
+  ssize_t nr_chunk = EintrSafeFileDescriptor::Write(buf, count);
+  if (nr_chunk >= 0) {
+    nr_written_ += nr_chunk;
+  }
+  return nr_chunk;
+}
+
+off64_t UbiFileDescriptor::Seek(off64_t offset, int whence) {
+  if (mode_ == kWriteOnly) {
+    // Ignore seek in write mode.
+    return nr_written_;
+  }
+  return EintrSafeFileDescriptor::Seek(offset, whence);
+}
+
+bool UbiFileDescriptor::Close() {
+  bool pad_ok = true;
+  if (IsOpen() && mode_ == kWriteOnly) {
+    char buf[1024];
+    memset(buf, 0xFF, sizeof(buf));
+    while (nr_written_ < volume_size_) {
+      // We have written less than the whole volume. In order for us to clear
+      // the update marker, we need to fill the rest. It is recommended to fill
+      // UBI writes with 0xFF.
+      uint64_t to_write = volume_size_ - nr_written_;
+      if (to_write > sizeof(buf)) {
+        to_write = sizeof(buf);
+      }
+      ssize_t nr_chunk = EintrSafeFileDescriptor::Write(buf, to_write);
+      if (nr_chunk < 0) {
+        LOG(ERROR) << "Cannot 0xFF-pad before closing.";
+        // There is an error, but we can't really do any meaningful thing here.
+        pad_ok = false;
+        break;
+      }
+      nr_written_ += nr_chunk;
+    }
+  }
+  return EintrSafeFileDescriptor::Close() && pad_ok;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/mtd_file_descriptor.h b/payload_consumer/mtd_file_descriptor.h
new file mode 100644
index 0000000..c0170b7
--- /dev/null
+++ b/payload_consumer/mtd_file_descriptor.h
@@ -0,0 +1,103 @@
+//
+// Copyright (C) 2014 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_PAYLOAD_CONSUMER_MTD_FILE_DESCRIPTOR_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_MTD_FILE_DESCRIPTOR_H_
+
+// This module defines file descriptors that deal with NAND media. We are
+// concerned with raw NAND access (as MTD device), and through UBI layer.
+
+#include <memory>
+
+#include <mtdutils.h>
+
+#include "update_engine/payload_consumer/file_descriptor.h"
+
+namespace chromeos_update_engine {
+
+// A class defining the file descriptor API for raw MTD device. This file
+// descriptor supports either random read, or sequential write but not both at
+// once.
+class MtdFileDescriptor : public EintrSafeFileDescriptor {
+ public:
+  MtdFileDescriptor();
+
+  static bool IsMtd(const char* path);
+
+  bool Open(const char* path, int flags, mode_t mode) override;
+  bool Open(const char* path, int flags) override;
+  ssize_t Read(void* buf, size_t count) override;
+  ssize_t Write(const void* buf, size_t count) override;
+  off64_t Seek(off64_t offset, int whence) override;
+  uint64_t BlockDevSize() override { return 0; }
+  bool BlkIoctl(int request,
+                uint64_t start,
+                uint64_t length,
+                int* result) override {
+    return false;
+  }
+  bool Close() override;
+
+ private:
+  std::unique_ptr<MtdReadContext, decltype(&mtd_read_close)> read_ctx_;
+  std::unique_ptr<MtdWriteContext, decltype(&mtd_write_close)> write_ctx_;
+  uint64_t nr_written_;
+};
+
+struct UbiVolumeInfo {
+  // Number of eraseblocks.
+  uint64_t reserved_ebs;
+  // Size of each eraseblock.
+  uint64_t eraseblock_size;
+};
+
+// A file descriptor to update a UBI volume, similar to MtdFileDescriptor.
+// Once the file descriptor is opened for write, the volume is marked as being
+// updated. The volume will not be usable until an update is completed. See
+// UBI_IOCVOLUP ioctl operation.
+class UbiFileDescriptor : public EintrSafeFileDescriptor {
+ public:
+  // Perform some queries about |path| to see if it is a UBI volume.
+  static bool IsUbi(const char* path);
+
+  bool Open(const char* path, int flags, mode_t mode) override;
+  bool Open(const char* path, int flags) override;
+  ssize_t Read(void* buf, size_t count) override;
+  ssize_t Write(const void* buf, size_t count) override;
+  off64_t Seek(off64_t offset, int whence) override;
+  uint64_t BlockDevSize() override { return 0; }
+  bool BlkIoctl(int request,
+                uint64_t start,
+                uint64_t length,
+                int* result) override {
+    return false;
+  }
+  bool Close() override;
+
+ private:
+  enum Mode { kReadOnly, kWriteOnly };
+
+  uint64_t usable_eb_blocks_;
+  uint64_t eraseblock_size_;
+  uint64_t volume_size_;
+  uint64_t nr_written_;
+
+  Mode mode_;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_MTD_FILE_DESCRIPTOR_H_
diff --git a/payload_consumer/partition_update_generator_android.cc b/payload_consumer/partition_update_generator_android.cc
deleted file mode 100644
index 4467182..0000000
--- a/payload_consumer/partition_update_generator_android.cc
+++ /dev/null
@@ -1,183 +0,0 @@
-//
-// 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/partition_update_generator_android.h"
-
-#include <filesystem>
-#include <memory>
-#include <utility>
-
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <base/logging.h>
-#include <base/strings/string_split.h>
-
-#include "update_engine/common/boot_control_interface.h"
-#include "update_engine/common/hash_calculator.h"
-#include "update_engine/common/utils.h"
-
-namespace chromeos_update_engine {
-
-PartitionUpdateGeneratorAndroid::PartitionUpdateGeneratorAndroid(
-    BootControlInterface* boot_control, size_t block_size)
-    : boot_control_(boot_control), block_size_(block_size) {}
-
-bool PartitionUpdateGeneratorAndroid::
-    GenerateOperationsForPartitionsNotInPayload(
-        BootControlInterface::Slot source_slot,
-        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";
-    return false;
-  }
-
-  std::vector<PartitionUpdate> partition_updates;
-  for (const auto& partition_name : ab_partitions) {
-    if (partitions_in_payload.find(partition_name) !=
-        partitions_in_payload.end()) {
-      LOG(INFO) << partition_name << " has included in payload";
-      continue;
-    }
-    bool is_source_dynamic = false;
-    std::string source_device;
-
-    TEST_AND_RETURN_FALSE(
-        boot_control_->GetPartitionDevice(partition_name,
-                                          source_slot,
-                                          true, /* not_in_payload */
-                                          &source_device,
-                                          &is_source_dynamic));
-    bool is_target_dynamic = false;
-    std::string target_device;
-    TEST_AND_RETURN_FALSE(boot_control_->GetPartitionDevice(
-        partition_name, target_slot, true, &target_device, &is_target_dynamic));
-
-    if (is_source_dynamic || is_target_dynamic) {
-      if (is_source_dynamic != is_target_dynamic) {
-        LOG(ERROR) << "Partition " << partition_name << " is expected to be a"
-                   << " static partition. source slot is "
-                   << (is_source_dynamic ? "" : "not")
-                   << " dynamic, and target slot " << target_slot << " is "
-                   << (is_target_dynamic ? "" : "not") << " dynamic.";
-        return false;
-      } else {
-        continue;
-      }
-    }
-
-    auto source_size = utils::FileSize(source_device);
-    auto target_size = utils::FileSize(target_device);
-    if (source_size == -1 || target_size == -1 || source_size != target_size ||
-        source_size % block_size_ != 0) {
-      LOG(ERROR) << "Invalid partition size. source size " << source_size
-                 << ", target size " << target_size;
-      return false;
-    }
-
-    auto partition_update = CreatePartitionUpdate(
-        partition_name, source_device, target_device, source_size);
-    if (!partition_update.has_value()) {
-      LOG(ERROR) << "Failed to create partition update for " << partition_name;
-      return false;
-    }
-    partition_updates.push_back(partition_update.value());
-  }
-  *update_list = std::move(partition_updates);
-  return true;
-}
-
-std::vector<std::string>
-PartitionUpdateGeneratorAndroid::GetAbPartitionsOnDevice() const {
-  auto partition_list_str =
-      android::base::GetProperty("ro.product.ab_ota_partitions", "");
-  return base::SplitString(partition_list_str,
-                           ",",
-                           base::TRIM_WHITESPACE,
-                           base::SPLIT_WANT_NONEMPTY);
-}
-
-std::optional<PartitionUpdate>
-PartitionUpdateGeneratorAndroid::CreatePartitionUpdate(
-    const std::string& partition_name,
-    const std::string& source_device,
-    const std::string& target_device,
-    int64_t partition_size) {
-  PartitionUpdate partition_update;
-  partition_update.set_partition_name(partition_name);
-  auto old_partition_info = partition_update.mutable_old_partition_info();
-  old_partition_info->set_size(partition_size);
-
-  auto raw_hash = CalculateHashForPartition(source_device, partition_size);
-  if (!raw_hash.has_value()) {
-    LOG(ERROR) << "Failed to calculate hash for partition " << source_device
-               << " size: " << partition_size;
-    return {};
-  }
-  old_partition_info->set_hash(raw_hash->data(), raw_hash->size());
-  auto new_partition_info = partition_update.mutable_new_partition_info();
-  new_partition_info->set_size(partition_size);
-  new_partition_info->set_hash(raw_hash->data(), raw_hash->size());
-
-  auto copy_operation = partition_update.add_operations();
-  copy_operation->set_type(InstallOperation::SOURCE_COPY);
-  Extent copy_extent;
-  copy_extent.set_start_block(0);
-  copy_extent.set_num_blocks(partition_size / block_size_);
-
-  *copy_operation->add_src_extents() = copy_extent;
-  *copy_operation->add_dst_extents() = copy_extent;
-
-  return partition_update;
-}
-
-std::optional<brillo::Blob>
-PartitionUpdateGeneratorAndroid::CalculateHashForPartition(
-    const std::string& block_device, int64_t partition_size) {
-  // TODO(xunchang) compute the hash with ecc partitions first, the hashing
-  // behavior should match the one in SOURCE_COPY. Also, we don't have the
-  // correct hash for source partition.
-  // An alternative way is to verify the written bytes match the read bytes
-  // during filesystem verification. This could probably save us a read of
-  // partitions here.
-  brillo::Blob raw_hash;
-  if (HashCalculator::RawHashOfFile(block_device, partition_size, &raw_hash) !=
-      partition_size) {
-    LOG(ERROR) << "Failed to calculate hash for " << block_device;
-    return std::nullopt;
-  }
-
-  return raw_hash;
-}
-
-namespace partition_update_generator {
-std::unique_ptr<PartitionUpdateGeneratorInterface> Create(
-    BootControlInterface* boot_control, size_t block_size) {
-  CHECK(boot_control);
-
-  return std::unique_ptr<PartitionUpdateGeneratorInterface>(
-      new PartitionUpdateGeneratorAndroid(boot_control, block_size));
-}
-}  // namespace partition_update_generator
-
-}  // namespace chromeos_update_engine
diff --git a/payload_consumer/partition_update_generator_android.h b/payload_consumer/partition_update_generator_android.h
deleted file mode 100644
index 0330c99..0000000
--- a/payload_consumer/partition_update_generator_android.h
+++ /dev/null
@@ -1,68 +0,0 @@
-//
-// 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_PAYLOAD_CONSUMER_PARTITION_UPDATE_GENERATOR_ANDROID_H_
-#define UPDATE_ENGINE_PAYLOAD_CONSUMER_PARTITION_UPDATE_GENERATOR_ANDROID_H_
-
-#include <optional>
-#include <set>
-#include <string>
-#include <vector>
-
-#include <brillo/secure_blob.h>
-#include <gtest/gtest_prod.h>  // for FRIEND_TEST
-
-#include "update_engine/common/boot_control_interface.h"
-#include "update_engine/payload_consumer/partition_update_generator_interface.h"
-
-namespace chromeos_update_engine {
-
-class PartitionUpdateGeneratorAndroid
-    : public PartitionUpdateGeneratorInterface {
- public:
-  PartitionUpdateGeneratorAndroid(BootControlInterface* boot_control,
-                                  size_t block_size);
-
-  bool GenerateOperationsForPartitionsNotInPayload(
-      BootControlInterface::Slot source_slot,
-      BootControlInterface::Slot target_slot,
-      const std::set<std::string>& partitions_in_payload,
-      std::vector<PartitionUpdate>* update_list) override;
-  virtual std::vector<std::string> GetAbPartitionsOnDevice() const;
-
- private:
-  friend class PartitionUpdateGeneratorAndroidTest;
-  FRIEND_TEST(PartitionUpdateGeneratorAndroidTest, GetStaticPartitions);
-  FRIEND_TEST(PartitionUpdateGeneratorAndroidTest, CreatePartitionUpdate);
-
-  // Creates a PartitionUpdate object for a given partition to update from
-  // source to target. Returns std::nullopt on failure.
-  std::optional<PartitionUpdate> CreatePartitionUpdate(
-      const std::string& partition_name,
-      const std::string& source_device,
-      const std::string& target_device,
-      int64_t partition_size);
-
-  std::optional<brillo::Blob> CalculateHashForPartition(
-      const std::string& block_device, int64_t partition_size);
-
-  BootControlInterface* boot_control_;
-  size_t block_size_;
-};
-
-}  // namespace chromeos_update_engine
-
-#endif
diff --git a/payload_consumer/partition_update_generator_android_unittest.cc b/payload_consumer/partition_update_generator_android_unittest.cc
deleted file mode 100644
index 86d025e..0000000
--- a/payload_consumer/partition_update_generator_android_unittest.cc
+++ /dev/null
@@ -1,159 +0,0 @@
-//
-// 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/partition_update_generator_android.h"
-
-#include <map>
-#include <memory>
-#include <set>
-#include <utility>
-#include <vector>
-
-#include <android-base/strings.h>
-#include <brillo/secure_blob.h>
-#include <gtest/gtest.h>
-
-#include "update_engine/common/boot_control_interface.h"
-#include "update_engine/common/fake_boot_control.h"
-#include "update_engine/common/hash_calculator.h"
-#include "update_engine/common/test_utils.h"
-#include "update_engine/common/utils.h"
-
-namespace chromeos_update_engine {
-
-class FakePartitionUpdateGenerator : public PartitionUpdateGeneratorAndroid {
- public:
-  std::vector<std::string> GetAbPartitionsOnDevice() const {
-    return ab_partitions_;
-  }
-  using PartitionUpdateGeneratorAndroid::PartitionUpdateGeneratorAndroid;
-  std::vector<std::string> ab_partitions_;
-};
-
-class PartitionUpdateGeneratorAndroidTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    ASSERT_TRUE(device_dir_.CreateUniqueTempDir());
-    boot_control_ = std::make_unique<FakeBootControl>();
-    ASSERT_TRUE(boot_control_);
-    boot_control_->SetNumSlots(2);
-    generator_ = std::make_unique<FakePartitionUpdateGenerator>(
-        boot_control_.get(), 4096);
-    ASSERT_TRUE(generator_);
-  }
-
-  std::unique_ptr<FakePartitionUpdateGenerator> generator_;
-  std::unique_ptr<FakeBootControl> boot_control_;
-
-  base::ScopedTempDir device_dir_;
-  std::map<std::string, std::string> device_map_;
-
-  void SetUpBlockDevice(const std::map<std::string, std::string>& contents) {
-    std::set<std::string> partition_base_names;
-    for (const auto& [name, content] : contents) {
-      auto path = device_dir_.GetPath().value() + "/" + name;
-      ASSERT_TRUE(
-          utils::WriteFile(path.c_str(), content.data(), content.size()));
-
-      if (android::base::EndsWith(name, "_a")) {
-        auto prefix = name.substr(0, name.size() - 2);
-        boot_control_->SetPartitionDevice(prefix, 0, path);
-        partition_base_names.emplace(prefix);
-      } else if (android::base::EndsWith(name, "_b")) {
-        auto prefix = name.substr(0, name.size() - 2);
-        boot_control_->SetPartitionDevice(prefix, 1, path);
-        partition_base_names.emplace(prefix);
-      }
-      device_map_[name] = std::move(path);
-    }
-    generator_->ab_partitions_ = {partition_base_names.begin(),
-                                  partition_base_names.end()};
-  }
-
-  void CheckPartitionUpdate(const std::string& name,
-                            const std::string& content,
-                            const PartitionUpdate& partition_update) {
-    ASSERT_EQ(name, partition_update.partition_name());
-
-    brillo::Blob out_hash;
-    ASSERT_TRUE(HashCalculator::RawHashOfBytes(
-        content.data(), content.size(), &out_hash));
-    ASSERT_EQ(std::string(out_hash.begin(), out_hash.end()),
-              partition_update.old_partition_info().hash());
-    ASSERT_EQ(std::string(out_hash.begin(), out_hash.end()),
-              partition_update.new_partition_info().hash());
-
-    ASSERT_EQ(1, partition_update.operations_size());
-    const auto& operation = partition_update.operations(0);
-    ASSERT_EQ(InstallOperation::SOURCE_COPY, operation.type());
-
-    ASSERT_EQ(1, operation.src_extents_size());
-    ASSERT_EQ(0u, operation.src_extents(0).start_block());
-    ASSERT_EQ(content.size() / 4096, operation.src_extents(0).num_blocks());
-
-    ASSERT_EQ(1, operation.dst_extents_size());
-    ASSERT_EQ(0u, operation.dst_extents(0).start_block());
-    ASSERT_EQ(content.size() / 4096, operation.dst_extents(0).num_blocks());
-  }
-};
-
-TEST_F(PartitionUpdateGeneratorAndroidTest, CreatePartitionUpdate) {
-  auto system_contents = std::string(4096 * 2, '1');
-  auto boot_contents = std::string(4096 * 5, 'b');
-  std::map<std::string, std::string> contents = {
-      {"system_a", system_contents},
-      {"system_b", std::string(4096 * 2, 0)},
-      {"boot_a", boot_contents},
-      {"boot_b", std::string(4096 * 5, 0)},
-  };
-  SetUpBlockDevice(contents);
-
-  auto system_partition_update = generator_->CreatePartitionUpdate(
-      "system", device_map_["system_a"], device_map_["system_b"], 4096 * 2);
-  ASSERT_TRUE(system_partition_update.has_value());
-  CheckPartitionUpdate(
-      "system", system_contents, system_partition_update.value());
-
-  auto boot_partition_update = generator_->CreatePartitionUpdate(
-      "boot", device_map_["boot_a"], device_map_["boot_b"], 4096 * 5);
-  ASSERT_TRUE(boot_partition_update.has_value());
-  CheckPartitionUpdate("boot", boot_contents, boot_partition_update.value());
-}
-
-TEST_F(PartitionUpdateGeneratorAndroidTest, GenerateOperations) {
-  auto system_contents = std::string(4096 * 10, '2');
-  auto boot_contents = std::string(4096 * 5, 'b');
-  std::map<std::string, std::string> contents = {
-      {"system_a", system_contents},
-      {"system_b", std::string(4096 * 10, 0)},
-      {"boot_a", boot_contents},
-      {"boot_b", std::string(4096 * 5, 0)},
-      {"vendor_a", ""},
-      {"vendor_b", ""},
-      {"persist", ""},
-  };
-  SetUpBlockDevice(contents);
-
-  std::vector<PartitionUpdate> update_list;
-  ASSERT_TRUE(generator_->GenerateOperationsForPartitionsNotInPayload(
-      0, 1, std::set<std::string>{"vendor"}, &update_list));
-
-  ASSERT_EQ(2u, update_list.size());
-  CheckPartitionUpdate("boot", boot_contents, update_list[0]);
-  CheckPartitionUpdate("system", system_contents, update_list[1]);
-}
-
-}  // namespace chromeos_update_engine
diff --git a/payload_consumer/partition_update_generator_interface.h b/payload_consumer/partition_update_generator_interface.h
deleted file mode 100644
index 3fa3dfb..0000000
--- a/payload_consumer/partition_update_generator_interface.h
+++ /dev/null
@@ -1,55 +0,0 @@
-//
-// 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_PAYLOAD_CONSUMER_PARTITION_UPDATE_GENERATOR_INTERFACE_H_
-#define UPDATE_ENGINE_PAYLOAD_CONSUMER_PARTITION_UPDATE_GENERATOR_INTERFACE_H_
-
-#include <memory>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "update_engine/common/boot_control_interface.h"
-
-namespace chromeos_update_engine {
-class PartitionUpdate;
-
-// This class parses the partitions that are not included in the payload of a
-// partial A/B update. And it generates additional operations for these
-// partitions to make the update complete.
-class PartitionUpdateGeneratorInterface {
- public:
-  virtual ~PartitionUpdateGeneratorInterface() = default;
-
-  // Adds PartitionUpdate for partitions not included in the payload. For static
-  // partitions, it generates SOURCE_COPY operations to copy the bytes from the
-  // source slot to target slot. For dynamic partitions, it only calculates the
-  // partition hash for the filesystem verification later.
-  virtual bool GenerateOperationsForPartitionsNotInPayload(
-      BootControlInterface::Slot source_slot,
-      BootControlInterface::Slot target_slot,
-      const std::set<std::string>& partitions_in_payload,
-      std::vector<PartitionUpdate>* update_list) = 0;
-};
-
-namespace partition_update_generator {
-std::unique_ptr<PartitionUpdateGeneratorInterface> Create(
-    BootControlInterface* boot_control, size_t block_size);
-}
-
-}  // namespace chromeos_update_engine
-
-#endif
diff --git a/payload_consumer/partition_update_generator_stub.cc b/payload_consumer/partition_update_generator_stub.cc
deleted file mode 100644
index cfbd5e1..0000000
--- a/payload_consumer/partition_update_generator_stub.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-// 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/partition_update_generator_stub.h"
-
-#include <memory>
-
-namespace chromeos_update_engine {
-
-bool PartitionUpdateGeneratorStub::GenerateOperationsForPartitionsNotInPayload(
-    chromeos_update_engine::BootControlInterface::Slot source_slot,
-    chromeos_update_engine::BootControlInterface::Slot target_slot,
-    const std::set<std::string>& partitions_in_payload,
-    std::vector<PartitionUpdate>* update_list) {
-  return true;
-}
-
-namespace partition_update_generator {
-std::unique_ptr<PartitionUpdateGeneratorInterface> Create(
-    BootControlInterface* boot_control, size_t block_size) {
-  return std::make_unique<PartitionUpdateGeneratorStub>();
-}
-}  // namespace partition_update_generator
-
-}  // namespace chromeos_update_engine
diff --git a/payload_consumer/partition_update_generator_stub.h b/payload_consumer/partition_update_generator_stub.h
deleted file mode 100644
index 282875e..0000000
--- a/payload_consumer/partition_update_generator_stub.h
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-// 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_PAYLOAD_CONSUMER_PARTITION_UPDATE_GENERATOR_STUB_H_
-#define UPDATE_ENGINE_PAYLOAD_CONSUMER_PARTITION_UPDATE_GENERATOR_STUB_H_
-
-#include <set>
-#include <string>
-#include <vector>
-
-#include "update_engine/common/boot_control_interface.h"
-#include "update_engine/payload_consumer/partition_update_generator_interface.h"
-
-namespace chromeos_update_engine {
-class PartitionUpdateGeneratorStub : public PartitionUpdateGeneratorInterface {
- public:
-  PartitionUpdateGeneratorStub() = default;
-  bool GenerateOperationsForPartitionsNotInPayload(
-      BootControlInterface::Slot source_slot,
-      BootControlInterface::Slot target_slot,
-      const std::set<std::string>& partitions_in_payload,
-      std::vector<PartitionUpdate>* update_list) override;
-};
-
-}  // namespace chromeos_update_engine
-
-#endif
diff --git a/payload_consumer/partition_writer.cc b/payload_consumer/partition_writer.cc
deleted file mode 100644
index 6f98ba3..0000000
--- a/payload_consumer/partition_writer.cc
+++ /dev/null
@@ -1,661 +0,0 @@
-//
-// 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/partition_writer.h>
-
-#include <fcntl.h>
-#include <linux/fs.h>
-
-#include <algorithm>
-#include <initializer_list>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include <base/strings/string_number_conversions.h>
-#include <bsdiff/bspatch.h>
-#include <puffin/puffpatch.h>
-#include <bsdiff/file_interface.h>
-#include <puffin/stream.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/extent_reader.h"
-#include "update_engine/payload_consumer/extent_writer.h"
-#include "update_engine/payload_consumer/fec_file_descriptor.h"
-#include "update_engine/payload_consumer/file_descriptor_utils.h"
-#include "update_engine/payload_consumer/install_plan.h"
-#include "update_engine/payload_consumer/mount_history.h"
-#include "update_engine/payload_consumer/payload_constants.h"
-#include "update_engine/payload_consumer/xz_extent_writer.h"
-
-namespace chromeos_update_engine {
-
-namespace {
-constexpr uint64_t kCacheSize = 1024 * 1024;  // 1MB
-
-// Discard the tail of the block device referenced by |fd|, from the offset
-// |data_size| until the end of the block device. Returns whether the data was
-// discarded.
-
-bool DiscardPartitionTail(const FileDescriptorPtr& fd, uint64_t data_size) {
-  uint64_t part_size = fd->BlockDevSize();
-  if (!part_size || part_size <= data_size)
-    return false;
-
-  struct blkioctl_request {
-    int number;
-    const char* name;
-  };
-  const std::initializer_list<blkioctl_request> blkioctl_requests = {
-      {BLKDISCARD, "BLKDISCARD"},
-      {BLKSECDISCARD, "BLKSECDISCARD"},
-#ifdef BLKZEROOUT
-      {BLKZEROOUT, "BLKZEROOUT"},
-#endif
-  };
-  for (const auto& req : blkioctl_requests) {
-    int error = 0;
-    if (fd->BlkIoctl(req.number, data_size, part_size - data_size, &error) &&
-        error == 0) {
-      return true;
-    }
-    LOG(WARNING) << "Error discarding the last "
-                 << (part_size - data_size) / 1024 << " KiB using ioctl("
-                 << req.name << ")";
-  }
-  return false;
-}
-
-}  // namespace
-
-// Opens path for read/write. On success returns an open FileDescriptor
-// and sets *err to 0. On failure, sets *err to errno and returns nullptr.
-FileDescriptorPtr OpenFile(const char* path,
-                           int mode,
-                           bool cache_writes,
-                           int* err) {
-  // Try to mark the block device read-only based on the mode. Ignore any
-  // failure since this won't work when passing regular files.
-  bool read_only = (mode & O_ACCMODE) == O_RDONLY;
-  utils::SetBlockDeviceReadOnly(path, read_only);
-
-  FileDescriptorPtr fd(new EintrSafeFileDescriptor());
-  if (cache_writes && !read_only) {
-    fd = FileDescriptorPtr(new CachedFileDescriptor(fd, kCacheSize));
-    LOG(INFO) << "Caching writes.";
-  }
-  if (!fd->Open(path, mode, 000)) {
-    *err = errno;
-    PLOG(ERROR) << "Unable to open file " << path;
-    return nullptr;
-  }
-  *err = 0;
-  return fd;
-}
-
-class BsdiffExtentFile : public bsdiff::FileInterface {
- public:
-  BsdiffExtentFile(std::unique_ptr<ExtentReader> reader, size_t size)
-      : BsdiffExtentFile(std::move(reader), nullptr, size) {}
-  BsdiffExtentFile(std::unique_ptr<ExtentWriter> writer, size_t size)
-      : BsdiffExtentFile(nullptr, std::move(writer), size) {}
-
-  ~BsdiffExtentFile() override = default;
-
-  bool Read(void* buf, size_t count, size_t* bytes_read) override {
-    TEST_AND_RETURN_FALSE(reader_->Read(buf, count));
-    *bytes_read = count;
-    offset_ += count;
-    return true;
-  }
-
-  bool Write(const void* buf, size_t count, size_t* bytes_written) override {
-    TEST_AND_RETURN_FALSE(writer_->Write(buf, count));
-    *bytes_written = count;
-    offset_ += count;
-    return true;
-  }
-
-  bool Seek(off_t pos) override {
-    if (reader_ != nullptr) {
-      TEST_AND_RETURN_FALSE(reader_->Seek(pos));
-      offset_ = pos;
-    } else {
-      // For writes technically there should be no change of position, or it
-      // should be equivalent of current offset.
-      TEST_AND_RETURN_FALSE(offset_ == static_cast<uint64_t>(pos));
-    }
-    return true;
-  }
-
-  bool Close() override { return true; }
-
-  bool GetSize(uint64_t* size) override {
-    *size = size_;
-    return true;
-  }
-
- private:
-  BsdiffExtentFile(std::unique_ptr<ExtentReader> reader,
-                   std::unique_ptr<ExtentWriter> writer,
-                   size_t size)
-      : reader_(std::move(reader)),
-        writer_(std::move(writer)),
-        size_(size),
-        offset_(0) {}
-
-  std::unique_ptr<ExtentReader> reader_;
-  std::unique_ptr<ExtentWriter> writer_;
-  uint64_t size_;
-  uint64_t offset_;
-
-  DISALLOW_COPY_AND_ASSIGN(BsdiffExtentFile);
-};
-// A class to be passed to |puffpatch| for reading from |source_fd_| and writing
-// into |target_fd_|.
-class PuffinExtentStream : public puffin::StreamInterface {
- public:
-  // Constructor for creating a stream for reading from an |ExtentReader|.
-  PuffinExtentStream(std::unique_ptr<ExtentReader> reader, uint64_t size)
-      : PuffinExtentStream(std::move(reader), nullptr, size) {}
-
-  // Constructor for creating a stream for writing to an |ExtentWriter|.
-  PuffinExtentStream(std::unique_ptr<ExtentWriter> writer, uint64_t size)
-      : PuffinExtentStream(nullptr, std::move(writer), size) {}
-
-  ~PuffinExtentStream() override = default;
-
-  bool GetSize(uint64_t* size) const override {
-    *size = size_;
-    return true;
-  }
-
-  bool GetOffset(uint64_t* offset) const override {
-    *offset = offset_;
-    return true;
-  }
-
-  bool Seek(uint64_t offset) override {
-    if (is_read_) {
-      TEST_AND_RETURN_FALSE(reader_->Seek(offset));
-      offset_ = offset;
-    } else {
-      // For writes technically there should be no change of position, or it
-      // should equivalent of current offset.
-      TEST_AND_RETURN_FALSE(offset_ == offset);
-    }
-    return true;
-  }
-
-  bool Read(void* buffer, size_t count) override {
-    TEST_AND_RETURN_FALSE(is_read_);
-    TEST_AND_RETURN_FALSE(reader_->Read(buffer, count));
-    offset_ += count;
-    return true;
-  }
-
-  bool Write(const void* buffer, size_t count) override {
-    TEST_AND_RETURN_FALSE(!is_read_);
-    TEST_AND_RETURN_FALSE(writer_->Write(buffer, count));
-    offset_ += count;
-    return true;
-  }
-
-  bool Close() override { return true; }
-
- private:
-  PuffinExtentStream(std::unique_ptr<ExtentReader> reader,
-                     std::unique_ptr<ExtentWriter> writer,
-                     uint64_t size)
-      : reader_(std::move(reader)),
-        writer_(std::move(writer)),
-        size_(size),
-        offset_(0),
-        is_read_(reader_ ? true : false) {}
-
-  std::unique_ptr<ExtentReader> reader_;
-  std::unique_ptr<ExtentWriter> writer_;
-  uint64_t size_;
-  uint64_t offset_;
-  bool is_read_;
-
-  DISALLOW_COPY_AND_ASSIGN(PuffinExtentStream);
-};
-
-PartitionWriter::PartitionWriter(
-    const PartitionUpdate& partition_update,
-    const InstallPlan::Partition& install_part,
-    DynamicPartitionControlInterface* dynamic_control,
-    size_t block_size,
-    bool is_interactive)
-    : partition_update_(partition_update),
-      install_part_(install_part),
-      dynamic_control_(dynamic_control),
-      interactive_(is_interactive),
-      block_size_(block_size) {}
-
-PartitionWriter::~PartitionWriter() {
-  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,
-                           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.
-
-  target_path_ = install_part_.target_path;
-  int err;
-
-  int flags = O_RDWR;
-  if (!interactive_)
-    flags |= O_DSYNC;
-
-  LOG(INFO) << "Opening " << target_path_ << " partition with"
-            << (interactive_ ? "out" : "") << " O_DSYNC";
-
-  target_fd_ = OpenFile(target_path_.c_str(), flags, true, &err);
-  if (!target_fd_) {
-    LOG(ERROR) << "Unable to open target partition "
-               << partition.partition_name() << " on slot "
-               << BootControlInterface::SlotName(target_slot) << ", file "
-               << target_path_;
-    return false;
-  }
-
-  LOG(INFO) << "Applying " << partition.operations().size()
-            << " operations to partition \"" << partition.partition_name()
-            << "\"";
-
-  // Discard the end of the partition, but ignore failures.
-  DiscardPartitionTail(target_fd_, install_part_.target_size);
-
-  return true;
-}
-
-bool PartitionWriter::PerformReplaceOperation(const InstallOperation& operation,
-                                              const void* data,
-                                              size_t count) {
-  // Setup the ExtentWriter stack based on the operation type.
-  std::unique_ptr<ExtentWriter> writer = CreateBaseExtentWriter();
-
-  if (operation.type() == InstallOperation::REPLACE_BZ) {
-    writer.reset(new BzipExtentWriter(std::move(writer)));
-  } else if (operation.type() == InstallOperation::REPLACE_XZ) {
-    writer.reset(new XzExtentWriter(std::move(writer)));
-  }
-
-  TEST_AND_RETURN_FALSE(writer->Init(operation.dst_extents(), block_size_));
-  TEST_AND_RETURN_FALSE(writer->Write(data, operation.data_length()));
-
-  return true;
-}
-
-bool PartitionWriter::PerformZeroOrDiscardOperation(
-    const InstallOperation& operation) {
-#ifdef BLKZEROOUT
-  bool attempt_ioctl = true;
-  int request =
-      (operation.type() == InstallOperation::ZERO ? BLKZEROOUT : BLKDISCARD);
-#else   // !defined(BLKZEROOUT)
-  bool attempt_ioctl = false;
-  int request = 0;
-#endif  // !defined(BLKZEROOUT)
-
-  brillo::Blob zeros;
-  for (const Extent& extent : operation.dst_extents()) {
-    const uint64_t start = extent.start_block() * block_size_;
-    const uint64_t length = extent.num_blocks() * block_size_;
-    if (attempt_ioctl) {
-      int result = 0;
-      if (target_fd_->BlkIoctl(request, start, length, &result) && result == 0)
-        continue;
-      attempt_ioctl = false;
-    }
-    // In case of failure, we fall back to writing 0 to the selected region.
-    zeros.resize(16 * block_size_);
-    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::WriteAll(
-          target_fd_, zeros.data(), chunk_length, start + offset));
-    }
-  }
-  return true;
-}
-
-bool PartitionWriter::PerformSourceCopyOperation(
-    const InstallOperation& operation, ErrorCode* error) {
-  TEST_AND_RETURN_FALSE(source_fd_ != nullptr);
-
-  // The device may optimize the SOURCE_COPY operation.
-  // Being this a device-specific optimization let DynamicPartitionController
-  // decide it the operation should be skipped.
-  const PartitionUpdate& partition = partition_update_;
-  const auto& partition_control = dynamic_control_;
-
-  InstallOperation buf;
-  const bool should_optimize = partition_control->OptimizeOperation(
-      partition.partition_name(), operation, &buf);
-  const InstallOperation& optimized = should_optimize ? buf : operation;
-
-  if (operation.has_src_sha256_hash()) {
-    bool read_ok;
-    brillo::Blob source_hash;
-    brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
-                                      operation.src_sha256_hash().end());
-
-    // We fall back to use the error corrected device if the hash of the raw
-    // device doesn't match or there was an error reading the source partition.
-    // Note that this code will also fall back if writing the target partition
-    // fails.
-    if (should_optimize) {
-      // Hash operation.src_extents(), then copy optimized.src_extents to
-      // optimized.dst_extents.
-      read_ok =
-          fd_utils::ReadAndHashExtents(
-              source_fd_, operation.src_extents(), block_size_, &source_hash) &&
-          fd_utils::CopyAndHashExtents(source_fd_,
-                                       optimized.src_extents(),
-                                       target_fd_,
-                                       optimized.dst_extents(),
-                                       block_size_,
-                                       nullptr /* skip hashing */);
-    } else {
-      read_ok = fd_utils::CopyAndHashExtents(source_fd_,
-                                             operation.src_extents(),
-                                             target_fd_,
-                                             operation.dst_extents(),
-                                             block_size_,
-                                             &source_hash);
-    }
-    if (read_ok && expected_source_hash == source_hash)
-      return true;
-    LOG(WARNING) << "Source hash from RAW device mismatched, attempting to "
-                    "correct using ECC";
-    if (!OpenCurrentECCPartition()) {
-      // The following function call will return false since the source hash
-      // mismatches, but we still want to call it so it prints the appropriate
-      // log message.
-      return ValidateSourceHash(source_hash, operation, source_fd_, error);
-    }
-
-    LOG(WARNING) << "Source hash from RAW device mismatched: found "
-                 << base::HexEncode(source_hash.data(), source_hash.size())
-                 << ", expected "
-                 << base::HexEncode(expected_source_hash.data(),
-                                    expected_source_hash.size());
-    if (should_optimize) {
-      TEST_AND_RETURN_FALSE(fd_utils::ReadAndHashExtents(
-          source_ecc_fd_, operation.src_extents(), block_size_, &source_hash));
-      TEST_AND_RETURN_FALSE(
-          fd_utils::CopyAndHashExtents(source_ecc_fd_,
-                                       optimized.src_extents(),
-                                       target_fd_,
-                                       optimized.dst_extents(),
-                                       block_size_,
-                                       nullptr /* skip hashing */));
-    } else {
-      TEST_AND_RETURN_FALSE(
-          fd_utils::CopyAndHashExtents(source_ecc_fd_,
-                                       operation.src_extents(),
-                                       target_fd_,
-                                       operation.dst_extents(),
-                                       block_size_,
-                                       &source_hash));
-    }
-    TEST_AND_RETURN_FALSE(
-        ValidateSourceHash(source_hash, operation, source_ecc_fd_, error));
-    // At this point reading from the error corrected device worked, but
-    // reading from the raw device failed, so this is considered a recovered
-    // failure.
-    source_ecc_recovered_failures_++;
-  } else {
-    // When the operation doesn't include a source hash, we attempt the error
-    // corrected device first since we can't verify the block in the raw device
-    // at this point, but we fall back to the raw device since the error
-    // corrected device can be shorter or not available.
-
-    if (OpenCurrentECCPartition() &&
-        fd_utils::CopyAndHashExtents(source_ecc_fd_,
-                                     optimized.src_extents(),
-                                     target_fd_,
-                                     optimized.dst_extents(),
-                                     block_size_,
-                                     nullptr)) {
-      return true;
-    }
-    TEST_AND_RETURN_FALSE(fd_utils::CopyAndHashExtents(source_fd_,
-                                                       optimized.src_extents(),
-                                                       target_fd_,
-                                                       optimized.dst_extents(),
-                                                       block_size_,
-                                                       nullptr));
-  }
-  return true;
-}
-
-bool PartitionWriter::PerformSourceBsdiffOperation(
-    const InstallOperation& operation,
-    ErrorCode* error,
-    const void* data,
-    size_t count) {
-  FileDescriptorPtr source_fd = ChooseSourceFD(operation, error);
-  TEST_AND_RETURN_FALSE(source_fd != nullptr);
-
-  auto reader = std::make_unique<DirectExtentReader>();
-  TEST_AND_RETURN_FALSE(
-      reader->Init(source_fd, operation.src_extents(), block_size_));
-  auto src_file = std::make_unique<BsdiffExtentFile>(
-      std::move(reader),
-      utils::BlocksInExtents(operation.src_extents()) * block_size_);
-
-  auto writer = CreateBaseExtentWriter();
-  TEST_AND_RETURN_FALSE(writer->Init(operation.dst_extents(), block_size_));
-  auto dst_file = std::make_unique<BsdiffExtentFile>(
-      std::move(writer),
-      utils::BlocksInExtents(operation.dst_extents()) * block_size_);
-
-  TEST_AND_RETURN_FALSE(bsdiff::bspatch(std::move(src_file),
-                                        std::move(dst_file),
-                                        reinterpret_cast<const uint8_t*>(data),
-                                        count) == 0);
-  return true;
-}
-
-bool PartitionWriter::PerformPuffDiffOperation(
-    const InstallOperation& operation,
-    ErrorCode* error,
-    const void* data,
-    size_t count) {
-  FileDescriptorPtr source_fd = ChooseSourceFD(operation, error);
-  TEST_AND_RETURN_FALSE(source_fd != nullptr);
-
-  auto reader = std::make_unique<DirectExtentReader>();
-  TEST_AND_RETURN_FALSE(
-      reader->Init(source_fd, operation.src_extents(), block_size_));
-  puffin::UniqueStreamPtr src_stream(new PuffinExtentStream(
-      std::move(reader),
-      utils::BlocksInExtents(operation.src_extents()) * block_size_));
-
-  auto writer = CreateBaseExtentWriter();
-  TEST_AND_RETURN_FALSE(writer->Init(operation.dst_extents(), block_size_));
-  puffin::UniqueStreamPtr dst_stream(new PuffinExtentStream(
-      std::move(writer),
-      utils::BlocksInExtents(operation.dst_extents()) * block_size_));
-
-  constexpr size_t kMaxCacheSize = 5 * 1024 * 1024;  // Total 5MB cache.
-  TEST_AND_RETURN_FALSE(
-      puffin::PuffPatch(std::move(src_stream),
-                        std::move(dst_stream),
-                        reinterpret_cast<const uint8_t*>(data),
-                        count,
-                        kMaxCacheSize));
-  return true;
-}
-
-FileDescriptorPtr PartitionWriter::ChooseSourceFD(
-    const InstallOperation& operation, ErrorCode* error) {
-  if (source_fd_ == nullptr) {
-    LOG(ERROR) << "ChooseSourceFD fail: source_fd_ == nullptr";
-    return nullptr;
-  }
-
-  if (!operation.has_src_sha256_hash()) {
-    // When the operation doesn't include a source hash, we attempt the error
-    // corrected device first since we can't verify the block in the raw device
-    // at this point, but we first need to make sure all extents are readable
-    // since the error corrected device can be shorter or not available.
-    if (OpenCurrentECCPartition() &&
-        fd_utils::ReadAndHashExtents(
-            source_ecc_fd_, operation.src_extents(), block_size_, nullptr)) {
-      return source_ecc_fd_;
-    }
-    return source_fd_;
-  }
-
-  brillo::Blob source_hash;
-  brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
-                                    operation.src_sha256_hash().end());
-  if (fd_utils::ReadAndHashExtents(
-          source_fd_, operation.src_extents(), block_size_, &source_hash) &&
-      source_hash == expected_source_hash) {
-    return source_fd_;
-  }
-  // We fall back to use the error corrected device if the hash of the raw
-  // device doesn't match or there was an error reading the source partition.
-  if (!OpenCurrentECCPartition()) {
-    // The following function call will return false since the source hash
-    // mismatches, but we still want to call it so it prints the appropriate
-    // log message.
-    ValidateSourceHash(source_hash, operation, source_fd_, error);
-    return nullptr;
-  }
-  LOG(WARNING) << "Source hash from RAW device mismatched: found "
-               << base::HexEncode(source_hash.data(), source_hash.size())
-               << ", expected "
-               << base::HexEncode(expected_source_hash.data(),
-                                  expected_source_hash.size());
-
-  if (fd_utils::ReadAndHashExtents(
-          source_ecc_fd_, operation.src_extents(), block_size_, &source_hash) &&
-      ValidateSourceHash(source_hash, operation, source_ecc_fd_, error)) {
-    // At this point reading from the error corrected device worked, but
-    // reading from the raw device failed, so this is considered a recovered
-    // failure.
-    source_ecc_recovered_failures_++;
-    return source_ecc_fd_;
-  }
-  return nullptr;
-}
-
-bool PartitionWriter::OpenCurrentECCPartition() {
-  // No support for ECC for full payloads.
-  // Full payload should not have any opeartion that requires ECC partitions.
-  if (source_ecc_fd_)
-    return true;
-
-  if (source_ecc_open_failure_)
-    return false;
-
-#if USE_FEC
-  const PartitionUpdate& partition = partition_update_;
-  const InstallPlan::Partition& install_part = install_part_;
-  std::string path = install_part.source_path;
-  FileDescriptorPtr fd(new FecFileDescriptor());
-  if (!fd->Open(path.c_str(), O_RDONLY, 0)) {
-    PLOG(ERROR) << "Unable to open ECC source partition "
-                << partition.partition_name() << ", file " << path;
-    source_ecc_open_failure_ = true;
-    return false;
-  }
-  source_ecc_fd_ = fd;
-#else
-  // No support for ECC compiled.
-  source_ecc_open_failure_ = true;
-#endif  // USE_FEC
-
-  return !source_ecc_open_failure_;
-}
-
-int PartitionWriter::Close() {
-  int err = 0;
-  if (source_fd_ && !source_fd_->Close()) {
-    err = errno;
-    PLOG(ERROR) << "Error closing source partition";
-    if (!err)
-      err = 1;
-  }
-  source_fd_.reset();
-  source_path_.clear();
-
-  if (target_fd_ && !target_fd_->Close()) {
-    err = errno;
-    PLOG(ERROR) << "Error closing target partition";
-    if (!err)
-      err = 1;
-  }
-  target_fd_.reset();
-  target_path_.clear();
-
-  if (source_ecc_fd_ && !source_ecc_fd_->Close()) {
-    err = errno;
-    PLOG(ERROR) << "Error closing ECC source partition";
-    if (!err)
-      err = 1;
-  }
-  source_ecc_fd_.reset();
-  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>(target_fd_);
-}
-
-}  // namespace chromeos_update_engine
diff --git a/payload_consumer/partition_writer.h b/payload_consumer/partition_writer.h
deleted file mode 100644
index 82e557a..0000000
--- a/payload_consumer/partition_writer.h
+++ /dev/null
@@ -1,147 +0,0 @@
-//
-// 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_PARTITION_WRITER_H_
-#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"
-
-namespace chromeos_update_engine {
-class PartitionWriter {
- public:
-  PartitionWriter(const PartitionUpdate& partition_update,
-                  const InstallPlan::Partition& install_part,
-                  DynamicPartitionControlInterface* dynamic_control,
-                  size_t block_size,
-                  bool is_interactive);
-  virtual ~PartitionWriter();
-  static bool ValidateSourceHash(const brillo::Blob& calculated_hash,
-                                 const InstallOperation& operation,
-                                 const FileDescriptorPtr source_fd,
-                                 ErrorCode* error);
-
-  // Perform necessary initialization work before InstallOperation can be
-  // applied to this partition
-  [[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]] virtual bool PerformReplaceOperation(
-      const InstallOperation& operation, const void* data, size_t count);
-  [[nodiscard]] virtual bool PerformZeroOrDiscardOperation(
-      const InstallOperation& operation);
-
-  [[nodiscard]] virtual bool PerformSourceCopyOperation(
-      const InstallOperation& operation, ErrorCode* error);
-  [[nodiscard]] virtual bool PerformSourceBsdiffOperation(
-      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);
-
-  // |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.
-  // Returns nullptr if the source hash mismatch cannot be corrected, and set
-  // 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_;
-  DynamicPartitionControlInterface* dynamic_control_;
-  // Path to source partition
-  std::string source_path_;
-  // Path to target partition
-  std::string target_path_;
-  FileDescriptorPtr source_fd_;
-  FileDescriptorPtr target_fd_;
-  const bool interactive_;
-  const size_t block_size_;
-  // File descriptor of the error corrected source partition. Only set while
-  // updating partition using a delta payload for a partition where error
-  // correction is available. The size of the error corrected device is smaller
-  // than the underlying raw device, since it doesn't include the error
-  // correction blocks.
-  FileDescriptorPtr source_ecc_fd_{nullptr};
-
-  // The total number of operations that failed source hash verification but
-  // passed after falling back to the error-corrected |source_ecc_fd_| device.
-  uint64_t source_ecc_recovered_failures_{0};
-
-  // Whether opening the current partition as an error-corrected device failed.
-  // Used to avoid re-opening the same source partition if it is not actually
-  // 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
deleted file mode 100644
index 184e2d5..0000000
--- a/payload_consumer/partition_writer_factory_android.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-//
-// 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->UpdateUsesSnapshotCompression() &&
-      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
deleted file mode 100644
index 609f043..0000000
--- a/payload_consumer/partition_writer_factory_chromeos.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-// 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
deleted file mode 100644
index 263f338..0000000
--- a/payload_consumer/partition_writer_unittest.cc
+++ /dev/null
@@ -1,204 +0,0 @@
-//
-// 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 <memory>
-#include <vector>
-
-#include <brillo/secure_blob.h>
-#include <gtest/gtest.h>
-
-#include "update_engine/common/dynamic_partition_control_stub.h"
-#include "update_engine/common/error_code.h"
-#include "update_engine/common/fake_prefs.h"
-#include "update_engine/common/hash_calculator.h"
-#include "update_engine/common/test_utils.h"
-#include "update_engine/common/utils.h"
-#include "update_engine/payload_consumer/delta_performer.h"
-#include "update_engine/payload_consumer/extent_reader.h"
-#include "update_engine/payload_consumer/extent_writer.h"
-#include "update_engine/payload_consumer/fake_file_descriptor.h"
-#include "update_engine/payload_consumer/file_descriptor.h"
-#include "update_engine/payload_consumer/install_plan.h"
-#include "update_engine/payload_generator/annotated_operation.h"
-#include "update_engine/payload_generator/delta_diff_generator.h"
-#include "update_engine/payload_generator/extent_ranges.h"
-#include "update_engine/payload_generator/payload_file.h"
-#include "update_engine/payload_generator/payload_generation_config.h"
-#include "update_engine/update_metadata.pb.h"
-
-namespace chromeos_update_engine {
-
-class PartitionWriterTest : public testing::Test {
- public:
-  // Helper function to pretend that the ECC file descriptor was already opened.
-  // Returns a pointer to the created file descriptor.
-  FakeFileDescriptor* SetFakeECCFile(size_t size) {
-    EXPECT_FALSE(writer_.source_ecc_fd_) << "source_ecc_fd_ already open.";
-    FakeFileDescriptor* ret = new FakeFileDescriptor();
-    fake_ecc_fd_.reset(ret);
-    // Call open to simulate it was already opened.
-    ret->Open("", 0);
-    ret->SetFileSize(size);
-    writer_.source_ecc_fd_ = fake_ecc_fd_;
-    return ret;
-  }
-
-  uint64_t GetSourceEccRecoveredFailures() const {
-    return writer_.source_ecc_recovered_failures_;
-  }
-
-  AnnotatedOperation GenerateSourceCopyOp(const brillo::Blob& copied_data,
-                                          bool add_hash,
-                                          PartitionConfig* old_part = nullptr) {
-    PayloadGenerationConfig config;
-    const uint64_t kDefaultBlockSize = config.block_size;
-    EXPECT_EQ(0U, copied_data.size() % kDefaultBlockSize);
-    uint64_t num_blocks = copied_data.size() / kDefaultBlockSize;
-    AnnotatedOperation aop;
-    *(aop.op.add_src_extents()) = ExtentForRange(0, num_blocks);
-    *(aop.op.add_dst_extents()) = ExtentForRange(0, num_blocks);
-    aop.op.set_type(InstallOperation::SOURCE_COPY);
-    brillo::Blob src_hash;
-    EXPECT_TRUE(HashCalculator::RawHashOfData(copied_data, &src_hash));
-    if (add_hash)
-      aop.op.set_src_sha256_hash(src_hash.data(), src_hash.size());
-
-    return aop;
-  }
-
-  brillo::Blob PerformSourceCopyOp(const InstallOperation& op,
-                                   const brillo::Blob blob_data) {
-    ScopedTempFile source_partition("Blob-XXXXXX");
-    FileDescriptorPtr fd(new EintrSafeFileDescriptor());
-    DirectExtentWriter extent_writer{fd};
-    EXPECT_TRUE(fd->Open(source_partition.path().c_str(), O_RDWR));
-    EXPECT_TRUE(extent_writer.Init(op.src_extents(), kBlockSize));
-    EXPECT_TRUE(extent_writer.Write(blob_data.data(), blob_data.size()));
-
-    ScopedTempFile target_partition("Blob-XXXXXX");
-
-    install_part_.source_path = source_partition.path();
-    install_part_.target_path = target_partition.path();
-    install_part_.source_size = blob_data.size();
-    install_part_.target_size = blob_data.size();
-
-    ErrorCode error;
-    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));
-    return output_data;
-  }
-
-  FakePrefs prefs_{};
-  InstallPlan install_plan_{};
-  InstallPlan::Payload payload_{};
-  DynamicPartitionControlStub dynamic_control_{};
-  FileDescriptorPtr fake_ecc_fd_{};
-  DeltaArchiveManifest manifest_{};
-  PartitionUpdate partition_update_{};
-  InstallPlan::Partition install_part_{};
-  PartitionWriter writer_{
-      partition_update_, install_part_, &dynamic_control_, kBlockSize, false};
-};
-// Test that the error-corrected file descriptor is used to read a partition
-// when no hash is available for SOURCE_COPY but it falls back to the normal
-// file descriptor when the size of the error corrected one is too small.
-TEST_F(PartitionWriterTest, ErrorCorrectionSourceCopyWhenNoHashFallbackTest) {
-  constexpr size_t kCopyOperationSize = 4 * 4096;
-  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));
-
-  // Setup the fec file descriptor as the fake stream, with smaller data than
-  // the expected.
-  FakeFileDescriptor* fake_fec = SetFakeECCFile(kCopyOperationSize / 2);
-
-  PartitionConfig old_part(kPartitionNameRoot);
-  old_part.path = source.path();
-  old_part.size = expected_data.size();
-
-  // The payload operation doesn't include an operation hash.
-  auto source_copy_op = GenerateSourceCopyOp(expected_data, false, &old_part);
-
-  auto output_data = PerformSourceCopyOp(source_copy_op.op, expected_data);
-  ASSERT_EQ(output_data, expected_data);
-
-  // Verify that the fake_fec was attempted to be used. Since the file
-  // descriptor is shorter it can actually do more than one read to realize it
-  // reached the EOF.
-  EXPECT_LE(1U, fake_fec->GetReadOps().size());
-  // This fallback doesn't count as an error-corrected operation since the
-  // operation hash was not available.
-  EXPECT_EQ(0U, GetSourceEccRecoveredFailures());
-}
-
-// Test that the error-corrected file descriptor is used to read the partition
-// since the source partition doesn't match the operation hash.
-TEST_F(PartitionWriterTest, ErrorCorrectionSourceCopyFallbackTest) {
-  constexpr size_t kCopyOperationSize = 4 * 4096;
-  // Write invalid data to the source image, which doesn't match the expected
-  // hash.
-  brillo::Blob invalid_data(kCopyOperationSize, 0x55);
-
-  // Setup the fec file descriptor as the fake stream, which matches
-  // |expected_data|.
-  FakeFileDescriptor* fake_fec = SetFakeECCFile(kCopyOperationSize);
-  brillo::Blob expected_data = FakeFileDescriptorData(kCopyOperationSize);
-
-  auto source_copy_op = GenerateSourceCopyOp(expected_data, true);
-  auto output_data = PerformSourceCopyOp(source_copy_op.op, invalid_data);
-  ASSERT_EQ(output_data, expected_data);
-
-  // Verify that the fake_fec was actually used.
-  EXPECT_EQ(1U, fake_fec->GetReadOps().size());
-  EXPECT_EQ(1U, GetSourceEccRecoveredFailures());
-}
-
-TEST_F(PartitionWriterTest, ChooseSourceFDTest) {
-  constexpr size_t kSourceSize = 4 * 4096;
-  ScopedTempFile source("Source-XXXXXX");
-  // Write invalid data to the source image, which doesn't match the expected
-  // hash.
-  brillo::Blob invalid_data(kSourceSize, 0x55);
-  EXPECT_TRUE(test_utils::WriteFileVector(source.path(), invalid_data));
-
-  writer_.source_fd_ = std::make_shared<EintrSafeFileDescriptor>();
-  writer_.source_fd_->Open(source.path().c_str(), O_RDONLY);
-
-  // Setup the fec file descriptor as the fake stream, which matches
-  // |expected_data|.
-  FakeFileDescriptor* fake_fec = SetFakeECCFile(kSourceSize);
-  brillo::Blob expected_data = FakeFileDescriptorData(kSourceSize);
-
-  InstallOperation op;
-  *(op.add_src_extents()) = ExtentForRange(0, kSourceSize / 4096);
-  brillo::Blob src_hash;
-  EXPECT_TRUE(HashCalculator::RawHashOfData(expected_data, &src_hash));
-  op.set_src_sha256_hash(src_hash.data(), src_hash.size());
-
-  ErrorCode error = ErrorCode::kSuccess;
-  EXPECT_EQ(writer_.source_ecc_fd_, writer_.ChooseSourceFD(op, &error));
-  EXPECT_EQ(ErrorCode::kSuccess, error);
-  // Verify that the fake_fec was actually used.
-  EXPECT_EQ(1U, fake_fec->GetReadOps().size());
-  EXPECT_EQ(1U, GetSourceEccRecoveredFailures());
-}
-
-}  // namespace chromeos_update_engine
diff --git a/payload_consumer/payload_constants.cc b/payload_consumer/payload_constants.cc
index d62a0ec..a2368a4 100644
--- a/payload_consumer/payload_constants.cc
+++ b/payload_consumer/payload_constants.cc
@@ -16,28 +16,24 @@
 
 #include "update_engine/payload_consumer/payload_constants.h"
 
-#include <base/logging.h>
-
 namespace chromeos_update_engine {
 
-// const uint64_t kChromeOSMajorPayloadVersion = 1;  DEPRECATED
+const uint64_t kChromeOSMajorPayloadVersion = 1;
 const uint64_t kBrilloMajorPayloadVersion = 2;
 
-const uint64_t kMinSupportedMajorPayloadVersion = kBrilloMajorPayloadVersion;
-const uint64_t kMaxSupportedMajorPayloadVersion = kBrilloMajorPayloadVersion;
+const uint32_t kMinSupportedMinorPayloadVersion = 1;
+const uint32_t kMaxSupportedMinorPayloadVersion = 6;
 
 const uint32_t kFullPayloadMinorVersion = 0;
-// const uint32_t kInPlaceMinorPayloadVersion = 1;  DEPRECATED
+const uint32_t kInPlaceMinorPayloadVersion = 1;
 const uint32_t kSourceMinorPayloadVersion = 2;
 const uint32_t kOpSrcHashMinorPayloadVersion = 3;
 const uint32_t kBrotliBsdiffMinorPayloadVersion = 4;
 const uint32_t kPuffdiffMinorPayloadVersion = 5;
 const uint32_t kVerityMinorPayloadVersion = 6;
-const uint32_t kPartialUpdateMinorPayloadVersion = 7;
 
-const uint32_t kMinSupportedMinorPayloadVersion = kSourceMinorPayloadVersion;
-const uint32_t kMaxSupportedMinorPayloadVersion =
-    kPartialUpdateMinorPayloadVersion;
+const uint64_t kMinSupportedMajorPayloadVersion = 1;
+const uint64_t kMaxSupportedMajorPayloadVersion = 2;
 
 const uint64_t kMaxPayloadHeaderSize = 24;
 
@@ -48,6 +44,10 @@
 
 const char* InstallOperationTypeName(InstallOperation::Type op_type) {
   switch (op_type) {
+    case InstallOperation::BSDIFF:
+      return "BSDIFF";
+    case InstallOperation::MOVE:
+      return "MOVE";
     case InstallOperation::REPLACE:
       return "REPLACE";
     case InstallOperation::REPLACE_BZ:
@@ -66,10 +66,6 @@
       return "PUFFDIFF";
     case InstallOperation::BROTLI_BSDIFF:
       return "BROTLI_BSDIFF";
-
-    case InstallOperation::BSDIFF:
-    case InstallOperation::MOVE:
-      NOTREACHED();
   }
   return "<unknown_op>";
 }
diff --git a/payload_consumer/payload_constants.h b/payload_consumer/payload_constants.h
index 03647ee..1642488 100644
--- a/payload_consumer/payload_constants.h
+++ b/payload_consumer/payload_constants.h
@@ -26,7 +26,7 @@
 namespace chromeos_update_engine {
 
 // The major version used by Chrome OS.
-// extern const uint64_t kChromeOSMajorPayloadVersion;  DEPRECATED
+extern const uint64_t kChromeOSMajorPayloadVersion;
 
 // The major version used by Brillo.
 extern const uint64_t kBrilloMajorPayloadVersion;
@@ -39,7 +39,7 @@
 extern const uint32_t kFullPayloadMinorVersion;
 
 // The minor version used by the in-place delta generator algorithm.
-// extern const uint32_t kInPlaceMinorPayloadVersion;  DEPRECATED
+extern const uint32_t kInPlaceMinorPayloadVersion;
 
 // The minor version used by the A to B delta generator algorithm.
 extern const uint32_t kSourceMinorPayloadVersion;
@@ -56,9 +56,6 @@
 // The minor version that allows Verity hash tree and FEC generation.
 extern const uint32_t kVerityMinorPayloadVersion;
 
-// The minor version that allows partial update, e.g. kernel only update.
-extern const uint32_t kPartialUpdateMinorPayloadVersion;
-
 // The minimum and maximum supported minor version.
 extern const uint32_t kMinSupportedMinorPayloadVersion;
 extern const uint32_t kMaxSupportedMinorPayloadVersion;
diff --git a/payload_consumer/payload_metadata.cc b/payload_consumer/payload_metadata.cc
index f797723..0952646 100644
--- a/payload_consumer/payload_metadata.cc
+++ b/payload_consumer/payload_metadata.cc
@@ -18,7 +18,6 @@
 
 #include <endian.h>
 
-#include <base/strings/stringprintf.h>
 #include <brillo/data_encoding.h>
 
 #include "update_engine/common/constants.h"
@@ -38,50 +37,45 @@
 const uint64_t PayloadMetadata::kDeltaManifestSizeSize = 8;
 const uint64_t PayloadMetadata::kDeltaMetadataSignatureSizeSize = 4;
 
-uint64_t PayloadMetadata::GetMetadataSignatureSizeOffset() const {
-  return kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
+bool PayloadMetadata::GetMetadataSignatureSizeOffset(
+    uint64_t* out_offset) const {
+  if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
+    *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
+    return true;
+  }
+  return false;
 }
 
-uint64_t PayloadMetadata::GetManifestOffset() const {
-  // Actual manifest begins right after the metadata signature size field.
-  return kDeltaManifestSizeOffset + kDeltaManifestSizeSize +
-         kDeltaMetadataSignatureSizeSize;
+bool PayloadMetadata::GetManifestOffset(uint64_t* out_offset) const {
+  // Actual manifest begins right after the manifest size field or
+  // metadata signature size field if major version >= 2.
+  if (major_payload_version_ == kChromeOSMajorPayloadVersion) {
+    *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
+    return true;
+  }
+  if (major_payload_version_ == kBrilloMajorPayloadVersion) {
+    *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize +
+                  kDeltaMetadataSignatureSizeSize;
+    return true;
+  }
+  LOG(ERROR) << "Unknown major payload version: " << major_payload_version_;
+  return false;
 }
 
 MetadataParseResult PayloadMetadata::ParsePayloadHeader(
     const brillo::Blob& payload, ErrorCode* error) {
-  return ParsePayloadHeader(payload.data(), payload.size(), error);
-}
-
-MetadataParseResult PayloadMetadata::ParsePayloadHeader(
-    const unsigned char* payload, size_t size, ErrorCode* error) {
+  uint64_t manifest_offset;
   // Ensure we have data to cover the major payload version.
-  if (size < kDeltaManifestSizeOffset)
+  if (payload.size() < kDeltaManifestSizeOffset)
     return MetadataParseResult::kInsufficientData;
 
   // Validate the magic string.
-  if (memcmp(payload, kDeltaMagic, sizeof(kDeltaMagic)) != 0) {
-    LOG(ERROR) << "Bad payload format -- invalid delta magic: "
-               << base::StringPrintf("%02x%02x%02x%02x",
-                                     payload[0],
-                                     payload[1],
-                                     payload[2],
-                                     payload[3])
-               << " Expected: "
-               << base::StringPrintf("%02x%02x%02x%02x",
-                                     kDeltaMagic[0],
-                                     kDeltaMagic[1],
-                                     kDeltaMagic[2],
-                                     kDeltaMagic[3]);
+  if (memcmp(payload.data(), kDeltaMagic, sizeof(kDeltaMagic)) != 0) {
+    LOG(ERROR) << "Bad payload format -- invalid delta magic.";
     *error = ErrorCode::kDownloadInvalidMetadataMagicString;
     return MetadataParseResult::kError;
   }
 
-  uint64_t manifest_offset = GetManifestOffset();
-  // Check again with the manifest offset.
-  if (size < manifest_offset)
-    return MetadataParseResult::kInsufficientData;
-
   // Extract the payload version from the metadata.
   static_assert(sizeof(major_payload_version_) == kDeltaVersionSize,
                 "Major payload version size mismatch");
@@ -99,6 +93,15 @@
     return MetadataParseResult::kError;
   }
 
+  // Get the manifest offset now that we have payload version.
+  if (!GetManifestOffset(&manifest_offset)) {
+    *error = ErrorCode::kUnsupportedMajorPayloadVersion;
+    return MetadataParseResult::kError;
+  }
+  // Check again with the manifest offset.
+  if (payload.size() < manifest_offset)
+    return MetadataParseResult::kInsufficientData;
+
   // Next, parse the manifest size.
   static_assert(sizeof(manifest_size_) == kDeltaManifestSizeSize,
                 "manifest_size size mismatch");
@@ -110,26 +113,30 @@
   metadata_size_ = manifest_offset + manifest_size_;
   if (metadata_size_ < manifest_size_) {
     // Overflow detected.
-    LOG(ERROR) << "Overflow detected on manifest size.";
     *error = ErrorCode::kDownloadInvalidMetadataSize;
     return MetadataParseResult::kError;
   }
 
-  // Parse the metadata signature size.
-  static_assert(
-      sizeof(metadata_signature_size_) == kDeltaMetadataSignatureSizeSize,
-      "metadata_signature_size size mismatch");
-  uint64_t metadata_signature_size_offset = GetMetadataSignatureSizeOffset();
-  memcpy(&metadata_signature_size_,
-         &payload[metadata_signature_size_offset],
-         kDeltaMetadataSignatureSizeSize);
-  metadata_signature_size_ = be32toh(metadata_signature_size_);
+  if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
+    // Parse the metadata signature size.
+    static_assert(
+        sizeof(metadata_signature_size_) == kDeltaMetadataSignatureSizeSize,
+        "metadata_signature_size size mismatch");
+    uint64_t metadata_signature_size_offset;
+    if (!GetMetadataSignatureSizeOffset(&metadata_signature_size_offset)) {
+      *error = ErrorCode::kError;
+      return MetadataParseResult::kError;
+    }
+    memcpy(&metadata_signature_size_,
+           &payload[metadata_signature_size_offset],
+           kDeltaMetadataSignatureSizeSize);
+    metadata_signature_size_ = be32toh(metadata_signature_size_);
 
-  if (metadata_size_ + metadata_signature_size_ < metadata_size_) {
-    // Overflow detected.
-    LOG(ERROR) << "Overflow detected on metadata and signature size.";
-    *error = ErrorCode::kDownloadInvalidMetadataSize;
-    return MetadataParseResult::kError;
+    if (metadata_size_ + metadata_signature_size_ < metadata_size_) {
+      // Overflow detected.
+      *error = ErrorCode::kDownloadInvalidMetadataSize;
+      return MetadataParseResult::kError;
+    }
   }
   return MetadataParseResult::kSuccess;
 }
@@ -141,14 +148,10 @@
 
 bool PayloadMetadata::GetManifest(const brillo::Blob& payload,
                                   DeltaArchiveManifest* out_manifest) const {
-  return GetManifest(payload.data(), payload.size(), out_manifest);
-}
-
-bool PayloadMetadata::GetManifest(const unsigned char* payload,
-                                  size_t size,
-                                  DeltaArchiveManifest* out_manifest) const {
-  uint64_t manifest_offset = GetManifestOffset();
-  CHECK_GE(size, manifest_offset + manifest_size_);
+  uint64_t manifest_offset;
+  if (!GetManifestOffset(&manifest_offset))
+    return false;
+  CHECK_GE(payload.size(), manifest_offset + manifest_size_);
   return out_manifest->ParseFromArray(&payload[manifest_offset],
                                       manifest_size_);
 }
@@ -173,7 +176,7 @@
                  << metadata_signature;
       return ErrorCode::kDownloadMetadataSignatureError;
     }
-  } else {
+  } else if (major_payload_version_ == kBrilloMajorPayloadVersion) {
     metadata_signature_protobuf.assign(
         payload.begin() + metadata_size_,
         payload.begin() + metadata_size_ + metadata_signature_size_);
@@ -222,32 +225,4 @@
   return ErrorCode::kSuccess;
 }
 
-bool PayloadMetadata::ParsePayloadFile(const string& payload_path,
-                                       DeltaArchiveManifest* manifest,
-                                       Signatures* metadata_signatures) {
-  brillo::Blob payload;
-  TEST_AND_RETURN_FALSE(
-      utils::ReadFileChunk(payload_path, 0, kMaxPayloadHeaderSize, &payload));
-  TEST_AND_RETURN_FALSE(ParsePayloadHeader(payload));
-
-  if (manifest != nullptr) {
-    TEST_AND_RETURN_FALSE(
-        utils::ReadFileChunk(payload_path,
-                             kMaxPayloadHeaderSize,
-                             GetMetadataSize() - kMaxPayloadHeaderSize,
-                             &payload));
-    TEST_AND_RETURN_FALSE(GetManifest(payload, manifest));
-  }
-
-  if (metadata_signatures != nullptr) {
-    payload.clear();
-    TEST_AND_RETURN_FALSE(utils::ReadFileChunk(
-        payload_path, GetMetadataSize(), GetMetadataSignatureSize(), &payload));
-    TEST_AND_RETURN_FALSE(
-        metadata_signatures->ParseFromArray(payload.data(), payload.size()));
-  }
-
-  return true;
-}
-
 }  // namespace chromeos_update_engine
diff --git a/payload_consumer/payload_metadata.h b/payload_consumer/payload_metadata.h
index f23b668..75ef8f9 100644
--- a/payload_consumer/payload_metadata.h
+++ b/payload_consumer/payload_metadata.h
@@ -56,9 +56,6 @@
   // the payload.
   MetadataParseResult ParsePayloadHeader(const brillo::Blob& payload,
                                          ErrorCode* error);
-  MetadataParseResult ParsePayloadHeader(const unsigned char* payload,
-                                         size_t size,
-                                         ErrorCode* error);
   // Simpler version of the above, returns true on success.
   bool ParsePayloadHeader(const brillo::Blob& payload);
 
@@ -66,7 +63,7 @@
   // |metadata_signature| (if present) or the metadata signature in payload
   // itself (if present). Returns ErrorCode::kSuccess on match or a suitable
   // error code otherwise. This method must be called before any part of the
-  // metadata is parsed so that an on-path attack on the SSL connection
+  // metadata is parsed so that a man-in-the-middle attack on the SSL connection
   // to the payload server doesn't exploit any vulnerability in the code that
   // parses the protocol buffer.
   ErrorCode ValidateMetadataSignature(
@@ -91,24 +88,15 @@
   bool GetManifest(const brillo::Blob& payload,
                    DeltaArchiveManifest* out_manifest) const;
 
-  bool GetManifest(const unsigned char* payload,
-                   size_t size,
-                   DeltaArchiveManifest* out_manifest) const;
-
-  // Parses a payload file |payload_path| and prepares the metadata properties,
-  // manifest and metadata signatures. Can be used as an easy to use utility to
-  // get the payload information without manually the process.
-  bool ParsePayloadFile(const std::string& payload_path,
-                        DeltaArchiveManifest* manifest,
-                        Signatures* metadata_signatures);
-
  private:
-  // Returns the byte offset at which the manifest protobuf begins in a payload.
-  uint64_t GetManifestOffset() const;
+  // Set |*out_offset| to the byte offset at which the manifest protobuf begins
+  // in a payload. Return true on success, false if the offset is unknown.
+  bool GetManifestOffset(uint64_t* out_offset) const;
 
-  // Returns the byte offset where the size of the metadata signature is stored
-  // in a payload.
-  uint64_t GetMetadataSignatureSizeOffset() const;
+  // Set |*out_offset| to the byte offset where the size of the metadata
+  // signature is stored in a payload. Return true on success, if this field is
+  // not present in the payload, return false.
+  bool GetMetadataSignatureSizeOffset(uint64_t* out_offset) const;
 
   uint64_t metadata_size_{0};
   uint64_t manifest_size_{0};
diff --git a/payload_consumer/payload_verifier.cc b/payload_consumer/payload_verifier.cc
index 8a3ea65..24e337e 100644
--- a/payload_consumer/payload_verifier.cc
+++ b/payload_consumer/payload_verifier.cc
@@ -172,7 +172,9 @@
       if (padded_hash_data == sig_hash_data) {
         return true;
       }
-    } else if (key_type == EVP_PKEY_EC) {
+    }
+
+    if (key_type == EVP_PKEY_EC) {
       EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(public_key.get());
       TEST_AND_RETURN_FALSE(ec_key != nullptr);
       if (ECDSA_verify(0,
@@ -183,10 +185,10 @@
                        ec_key) == 1) {
         return true;
       }
-    } else {
-      LOG(ERROR) << "Unsupported key type " << key_type;
-      return false;
     }
+
+    LOG(ERROR) << "Unsupported key type " << key_type;
+    return false;
   }
   LOG(INFO) << "Failed to verify the signature with " << public_keys_.size()
             << " keys.";
@@ -201,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(const_cast<EVP_PKEY*>(public_key));
+  RSA* rsa = EVP_PKEY_get0_RSA(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 051ccbf..c08cfc2 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -28,7 +28,6 @@
 #include <base/files/file_path.h>
 #include <base/files/file_util.h>
 #include <base/logging.h>
-#include <base/stl_util.h>
 #include <base/strings/string_split.h>
 #include <base/strings/string_util.h>
 
@@ -50,48 +49,17 @@
 
 namespace chromeos_update_engine {
 
+using brillo::MessageLoop;
 using std::string;
 using std::vector;
 
-PostinstallRunnerAction::PostinstallRunnerAction(
-    BootControlInterface* boot_control, HardwareInterface* hardware)
-    : boot_control_(boot_control), hardware_(hardware) {
-#ifdef __ANDROID__
-  fs_mount_dir_ = "/postinstall";
-#else   // __ANDROID__
-  base::FilePath temp_dir;
-  TEST_AND_RETURN(base::CreateNewTempDirectory("au_postint_mount", &temp_dir));
-  fs_mount_dir_ = temp_dir.value();
-#endif  // __ANDROID__
-  CHECK(!fs_mount_dir_.empty());
-  LOG(INFO) << "postinstall mount point: " << fs_mount_dir_;
-}
-
 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->UpdateUsesSnapshotCompression()) {
-    // 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.
+  // Currently we're always powerwashing when rolling back.
   if (install_plan_.powerwash_required || install_plan_.is_rollback) {
-    if (hardware_->SchedulePowerwash(
-            install_plan_.rollback_data_save_requested)) {
+    if (hardware_->SchedulePowerwash(install_plan_.is_rollback)) {
       powerwash_scheduled_ = true;
     } else {
       return CompletePostinstall(ErrorCode::kPostinstallPowerwashError);
@@ -140,7 +108,8 @@
   const InstallPlan::Partition& partition =
       install_plan_.partitions[current_partition_];
 
-  const string mountable_device = partition.readonly_target_path;
+  const string mountable_device =
+      utils::MakePartitionNameForMount(partition.target_path);
   if (mountable_device.empty()) {
     LOG(ERROR) << "Cannot make mountable device from " << partition.target_path;
     return CompletePostinstall(ErrorCode::kPostinstallRunnerError);
@@ -149,12 +118,14 @@
   // Perform post-install for the current_partition_ partition. At this point we
   // need to call CompletePartitionPostinstall to complete the operation and
   // cleanup.
+#ifdef __ANDROID__
+  fs_mount_dir_ = "/postinstall";
+#else   // __ANDROID__
+  base::FilePath temp_dir;
+  TEST_AND_RETURN(base::CreateNewTempDirectory("au_postint_mount", &temp_dir));
+  fs_mount_dir_ = temp_dir.value();
+#endif  // __ANDROID__
 
-  if (!utils::FileExists(fs_mount_dir_.c_str())) {
-    LOG(ERROR) << "Mount point " << fs_mount_dir_
-               << " does not exist, mount call will fail";
-    return CompletePostinstall(ErrorCode::kPostinstallRunnerError);
-  }
   // Double check that the fs_mount_dir is not busy with a previous mounted
   // filesystem from a previous crashed postinstall step.
   if (utils::IsMountpoint(fs_mount_dir_)) {
@@ -191,12 +162,11 @@
   }
 #endif  // __ANDROID__
 
-  if (!utils::MountFilesystem(
-          mountable_device,
-          fs_mount_dir_,
-          MS_RDONLY,
-          partition.filesystem_type,
-          hardware_->GetPartitionMountOptions(partition.name))) {
+  if (!utils::MountFilesystem(mountable_device,
+                              fs_mount_dir_,
+                              MS_RDONLY,
+                              partition.filesystem_type,
+                              constants::kPostinstallMountOptions)) {
     return CompletePartitionPostinstall(
         1, "Error mounting the device " + mountable_device);
   }
@@ -245,10 +215,13 @@
     PLOG(ERROR) << "Unable to set non-blocking I/O mode on fd " << progress_fd_;
   }
 
-  progress_controller_ = base::FileDescriptorWatcher::WatchReadable(
+  progress_task_ = MessageLoop::current()->WatchFileDescriptor(
+      FROM_HERE,
       progress_fd_,
-      base::BindRepeating(&PostinstallRunnerAction::OnProgressFdReady,
-                          base::Unretained(this)));
+      MessageLoop::WatchMode::kWatchRead,
+      true,
+      base::Bind(&PostinstallRunnerAction::OnProgressFdReady,
+                 base::Unretained(this)));
 }
 
 void PostinstallRunnerAction::OnProgressFdReady() {
@@ -258,7 +231,7 @@
     bytes_read = 0;
     bool eof;
     bool ok =
-        utils::ReadAll(progress_fd_, buf, base::size(buf), &bytes_read, &eof);
+        utils::ReadAll(progress_fd_, buf, arraysize(buf), &bytes_read, &eof);
     progress_buffer_.append(buf, bytes_read);
     // Process every line.
     vector<string> lines = base::SplitString(
@@ -273,7 +246,8 @@
     if (!ok || eof) {
       // There was either an error or an EOF condition, so we are done watching
       // the file descriptor.
-      progress_controller_.reset();
+      MessageLoop::current()->CancelTask(progress_task_);
+      progress_task_ = MessageLoop::kTaskIdNull;
       return;
     }
   } while (bytes_read);
@@ -310,18 +284,17 @@
 void PostinstallRunnerAction::Cleanup() {
   utils::UnmountFilesystem(fs_mount_dir_);
 #ifndef __ANDROID__
-#if BASE_VER < 800000
-  if (!base::DeleteFile(base::FilePath(fs_mount_dir_), true)) {
-#else
-  if (!base::DeleteFile(base::FilePath(fs_mount_dir_))) {
-#endif
+  if (!base::DeleteFile(base::FilePath(fs_mount_dir_), false)) {
     PLOG(WARNING) << "Not removing temporary mountpoint " << fs_mount_dir_;
   }
-#endif
+#endif  // !__ANDROID__
+  fs_mount_dir_.clear();
 
   progress_fd_ = -1;
-  progress_controller_.reset();
-
+  if (progress_task_ != MessageLoop::kTaskIdNull) {
+    MessageLoop::current()->CancelTask(progress_task_);
+    progress_task_ = MessageLoop::kTaskIdNull;
+  }
   progress_buffer_.clear();
 }
 
@@ -375,19 +348,12 @@
       } else {
         // Schedules warm reset on next reboot, ignores the error.
         hardware_->SetWarmReset(true);
-        // Sets the vbmeta digest for the other slot to boot into.
-        hardware_->SetVbmetaDigestForInactiveSlot(false);
       }
     } else {
       error_code = ErrorCode::kUpdatedButNotActive;
     }
   }
 
-  auto dynamic_control = boot_control_->GetDynamicPartitionControl();
-  CHECK(dynamic_control);
-  dynamic_control->UnmapAllPartitions();
-  LOG(INFO) << "Unmapped all partitions.";
-
   ScopedActionCompleter completer(processor_, this);
   completer.set_code(error_code);
 
diff --git a/payload_consumer/postinstall_runner_action.h b/payload_consumer/postinstall_runner_action.h
index 178d72a..b9b7069 100644
--- a/payload_consumer/postinstall_runner_action.h
+++ b/payload_consumer/postinstall_runner_action.h
@@ -17,12 +17,9 @@
 #ifndef UPDATE_ENGINE_PAYLOAD_CONSUMER_POSTINSTALL_RUNNER_ACTION_H_
 #define UPDATE_ENGINE_PAYLOAD_CONSUMER_POSTINSTALL_RUNNER_ACTION_H_
 
-#include <memory>
 #include <string>
-#include <utility>
 #include <vector>
 
-#include <base/files/file_descriptor_watcher_posix.h>
 #include <brillo/message_loops/message_loop.h>
 #include <gtest/gtest_prod.h>
 
@@ -41,7 +38,8 @@
 class PostinstallRunnerAction : public InstallPlanAction {
  public:
   PostinstallRunnerAction(BootControlInterface* boot_control,
-                          HardwareInterface* hardware);
+                          HardwareInterface* hardware)
+      : boot_control_(boot_control), hardware_(hardware) {}
 
   // InstallPlanAction overrides.
   void PerformAction() override;
@@ -68,9 +66,6 @@
   friend class PostinstallRunnerActionTest;
   FRIEND_TEST(PostinstallRunnerActionTest, ProcessProgressLineTest);
 
-  // exposed for testing purposes only
-  void SetMountDir(std::string dir) { fs_mount_dir_ = std::move(dir); }
-
   void PerformPartitionPostinstall();
 
   // Called whenever the |progress_fd_| has data available to read.
@@ -100,6 +95,8 @@
   // ready. Called when the post-install script was run for all the partitions.
   void CompletePostinstall(ErrorCode error_code);
 
+  InstallPlan install_plan_;
+
   // The path where the filesystem will be mounted during post-install.
   std::string fs_mount_dir_;
 
@@ -142,8 +139,7 @@
   // The parent progress file descriptor used to watch for progress reports from
   // the postinstall program and the task watching for them.
   int progress_fd_{-1};
-
-  std::unique_ptr<base::FileDescriptorWatcher::Controller> progress_controller_;
+  brillo::MessageLoop::TaskId progress_task_{brillo::MessageLoop::kTaskIdNull};
 
   // A buffer of a partial read line from the progress file descriptor.
   std::string progress_buffer_;
diff --git a/payload_consumer/postinstall_runner_action_unittest.cc b/payload_consumer/postinstall_runner_action_unittest.cc
index 792ee28..e9313f1 100644
--- a/payload_consumer/postinstall_runner_action_unittest.cc
+++ b/payload_consumer/postinstall_runner_action_unittest.cc
@@ -26,14 +26,9 @@
 
 #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>
@@ -45,7 +40,7 @@
 #include "update_engine/common/subprocess.h"
 #include "update_engine/common/test_utils.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/cros/mock_payload_state.h"
+#include "update_engine/mock_payload_state.h"
 
 using brillo::MessageLoop;
 using chromeos_update_engine::test_utils::ScopedLoopbackDeviceBinder;
@@ -105,8 +100,7 @@
   void RunPostinstallAction(const string& device_path,
                             const string& postinstall_program,
                             bool powerwash_required,
-                            bool is_rollback,
-                            bool save_rollback_data);
+                            bool is_rollback);
 
   void RunPostinstallActionWithInstallPlan(const InstallPlan& install_plan);
 
@@ -149,25 +143,13 @@
           base::TimeDelta::FromMilliseconds(10));
     } else {
       CHECK(processor_);
-      // Must |PostDelayedTask()| here to be safe that |FileDescriptorWatcher|
-      // doesn't leak memory, do not directly call |StopProcessing()|.
-      loop_.PostDelayedTask(
-          FROM_HERE,
-          base::Bind(
-              [](ActionProcessor* processor) { processor->StopProcessing(); },
-              base::Unretained(processor_)),
-          base::TimeDelta::FromMilliseconds(100));
+      processor_->StopProcessing();
     }
   }
 
  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_;
 
@@ -190,12 +172,10 @@
     const string& device_path,
     const string& postinstall_program,
     bool powerwash_required,
-    bool is_rollback,
-    bool save_rollback_data) {
+    bool is_rollback) {
   InstallPlan::Partition part;
   part.name = "part";
   part.target_path = device_path;
-  part.readonly_target_path = device_path;
   part.run_postinstall = true;
   part.postinstall_path = postinstall_program;
   InstallPlan install_plan;
@@ -203,7 +183,6 @@
   install_plan.download_url = "http://127.0.0.1:8080/update";
   install_plan.powerwash_required = powerwash_required;
   install_plan.is_rollback = is_rollback;
-  install_plan.rollback_data_save_requested = save_rollback_data;
   RunPostinstallActionWithInstallPlan(install_plan);
 }
 
@@ -216,9 +195,6 @@
   auto runner_action = std::make_unique<PostinstallRunnerAction>(
       &fake_boot_control_, &fake_hardware_);
   postinstall_action_ = runner_action.get();
-  base::FilePath temp_dir;
-  TEST_AND_RETURN(base::CreateNewTempDirectory("postinstall", &temp_dir));
-  postinstall_action_->SetMountDir(temp_dir.value());
   runner_action->set_delegate(setup_action_delegate_);
   BondActions(feeder_action.get(), runner_action.get());
   auto collector_action =
@@ -241,7 +217,7 @@
   EXPECT_TRUE(processor_delegate_.processing_stopped_called_ ||
               processor_delegate_.processing_done_called_);
   if (processor_delegate_.processing_done_called_) {
-    // Validation check that the code was set when the processor finishes.
+    // Sanity check that the code was set when the processor finishes.
     EXPECT_TRUE(processor_delegate_.code_set_);
   }
 }
@@ -280,8 +256,7 @@
 TEST_F(PostinstallRunnerActionTest, RunAsRootSimpleTest) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
 
-  RunPostinstallAction(
-      loop.dev(), kPostinstallDefaultScript, false, false, false);
+  RunPostinstallAction(loop.dev(), kPostinstallDefaultScript, false, false);
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
   EXPECT_TRUE(processor_delegate_.processing_done_called_);
 
@@ -292,7 +267,7 @@
 
 TEST_F(PostinstallRunnerActionTest, RunAsRootRunSymlinkFileTest) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
-  RunPostinstallAction(loop.dev(), "bin/postinst_link", false, false, false);
+  RunPostinstallAction(loop.dev(), "bin/postinst_link", false, false);
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
 }
 
@@ -302,7 +277,6 @@
   RunPostinstallAction(loop.dev(),
                        "bin/postinst_example",
                        /*powerwash_required=*/true,
-                       false,
                        false);
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
 
@@ -311,31 +285,14 @@
   EXPECT_FALSE(fake_hardware_.GetIsRollbackPowerwashScheduled());
 }
 
-TEST_F(PostinstallRunnerActionTest, RunAsRootRollbackTestNoDataSave) {
+TEST_F(PostinstallRunnerActionTest, RunAsRootRollbackTest) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
 
   // Run a simple postinstall program, rollback happened.
   RunPostinstallAction(loop.dev(),
                        "bin/postinst_example",
                        false,
-                       /*is_rollback=*/true,
-                       /*save_rollback_data=*/false);
-  EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
-
-  // Check that powerwash was scheduled and that it's NOT a rollback powerwash.
-  EXPECT_TRUE(fake_hardware_.IsPowerwashScheduled());
-  EXPECT_FALSE(fake_hardware_.GetIsRollbackPowerwashScheduled());
-}
-
-TEST_F(PostinstallRunnerActionTest, RunAsRootRollbackTestWithDataSave) {
-  ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
-
-  // Run a simple postinstall program, rollback happened.
-  RunPostinstallAction(loop.dev(),
-                       "bin/postinst_example",
-                       false,
-                       /*is_rollback=*/true,
-                       /*save_rollback_data=*/true);
+                       /*is_rollback=*/true);
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
 
   // Check that powerwash was scheduled and that it's a rollback powerwash.
@@ -346,8 +303,7 @@
 // Runs postinstall from a partition file that doesn't mount, so it should
 // fail.
 TEST_F(PostinstallRunnerActionTest, RunAsRootCantMountTest) {
-  RunPostinstallAction(
-      "/dev/null", kPostinstallDefaultScript, false, false, false);
+  RunPostinstallAction("/dev/null", kPostinstallDefaultScript, false, false);
   EXPECT_EQ(ErrorCode::kPostinstallRunnerError, processor_delegate_.code_);
 
   // In case of failure, Postinstall should not signal a powerwash even if it
@@ -360,7 +316,6 @@
   InstallPlan::Partition part;
   part.name = "part";
   part.target_path = "/dev/null";
-  part.readonly_target_path = "/dev/null";
   part.run_postinstall = true;
   part.postinstall_path = kPostinstallDefaultScript;
   part.postinstall_optional = true;
@@ -382,7 +337,7 @@
 // fail.
 TEST_F(PostinstallRunnerActionTest, RunAsRootErrScriptTest) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
-  RunPostinstallAction(loop.dev(), "bin/postinst_fail1", false, false, false);
+  RunPostinstallAction(loop.dev(), "bin/postinst_fail1", false, false);
   EXPECT_EQ(ErrorCode::kPostinstallRunnerError, processor_delegate_.code_);
 }
 
@@ -390,7 +345,7 @@
 // UMA with a different error code. Test those cases are properly detected.
 TEST_F(PostinstallRunnerActionTest, RunAsRootFirmwareBErrScriptTest) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
-  RunPostinstallAction(loop.dev(), "bin/postinst_fail3", false, false, false);
+  RunPostinstallAction(loop.dev(), "bin/postinst_fail3", false, false);
   EXPECT_EQ(ErrorCode::kPostinstallBootedFromFirmwareB,
             processor_delegate_.code_);
 }
@@ -398,26 +353,16 @@
 // Check that you can't specify an absolute path.
 TEST_F(PostinstallRunnerActionTest, RunAsRootAbsolutePathNotAllowedTest) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
-  RunPostinstallAction(loop.dev(), "/etc/../bin/sh", false, false, false);
+  RunPostinstallAction(loop.dev(), "/etc/../bin/sh", false, false);
   EXPECT_EQ(ErrorCode::kPostinstallRunnerError, processor_delegate_.code_);
 }
 
 #ifdef __ANDROID__
-// Check that the postinstall file is labeled to the postinstall_exec label.
+// Check that the postinstall file is relabeled to the postinstall label.
 // SElinux labels are only set on Android.
 TEST_F(PostinstallRunnerActionTest, RunAsRootCheckFileContextsTest) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
-  RunPostinstallAction(
-      loop.dev(), "bin/self_check_context", false, false, false);
-  EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
-}
-
-// Check that the postinstall file is relabeled to the default postinstall
-// label. SElinux labels are only set on Android.
-TEST_F(PostinstallRunnerActionTest, RunAsRootCheckDefaultFileContextsTest) {
-  ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
-  RunPostinstallAction(
-      loop.dev(), "bin/self_check_default_context", false, false, false);
+  RunPostinstallAction(loop.dev(), "bin/self_check_context", false, false);
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
 }
 #endif  // __ANDROID__
@@ -430,7 +375,7 @@
   loop_.PostTask(FROM_HERE,
                  base::Bind(&PostinstallRunnerActionTest::SuspendRunningAction,
                             base::Unretained(this)));
-  RunPostinstallAction(loop.dev(), "bin/postinst_suspend", false, false, false);
+  RunPostinstallAction(loop.dev(), "bin/postinst_suspend", false, false);
   // postinst_suspend returns 0 only if it was suspended at some point.
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
   EXPECT_TRUE(processor_delegate_.processing_done_called_);
@@ -442,7 +387,7 @@
 
   // Wait for the action to start and then cancel it.
   CancelWhenStarted();
-  RunPostinstallAction(loop.dev(), "bin/postinst_suspend", false, false, false);
+  RunPostinstallAction(loop.dev(), "bin/postinst_suspend", false, false);
   // When canceling the action, the action never finished and therefore we had
   // a ProcessingStopped call instead.
   EXPECT_FALSE(processor_delegate_.code_set_);
@@ -465,8 +410,7 @@
 
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
   setup_action_delegate_ = &mock_delegate_;
-  RunPostinstallAction(
-      loop.dev(), "bin/postinst_progress", false, false, false);
+  RunPostinstallAction(loop.dev(), "bin/postinst_progress", false, false);
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
 }
 
diff --git a/payload_consumer/snapshot_extent_writer.cc b/payload_consumer/snapshot_extent_writer.cc
deleted file mode 100644
index 242e726..0000000
--- a/payload_consumer/snapshot_extent_writer.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-//
-// 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(
-    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) {
-    if (!cow_writer_->AddRawBlocks(
-            cur_extent.start_block(), data, cur_extent_size)) {
-      LOG(ERROR) << "AddRawBlocks(" << cur_extent.start_block() << ", " << data
-                 << ", " << cur_extent_size << ") failed.";
-      // return value is expected to be greater than 0. Return 0 to signal error
-      // condition
-      return 0;
-    }
-    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) {
-    if (!cow_writer_->AddRawBlocks(
-            cur_extent.start_block(), buffer_.data(), buffer_.size())) {
-      LOG(ERROR) << "AddRawBlocks(" << cur_extent.start_block() << ", "
-                 << buffer_.data() << ", " << buffer_.size() << ") failed.";
-      return 0;
-    }
-    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);
-    TEST_AND_RETURN_FALSE(bytes_written > 0);
-    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
deleted file mode 100644
index c3a948e..0000000
--- a/payload_consumer/snapshot_extent_writer.h
+++ /dev/null
@@ -1,59 +0,0 @@
-//
-// 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_SNAPSHOT_EXTENT_WRITER_H_
-#define UPDATE_ENGINE_SNAPSHOT_EXTENT_WRITER_H_
-
-#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(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();
-  [[nodiscard]] 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
-
-#endif
diff --git a/payload_consumer/snapshot_extent_writer_unittest.cc b/payload_consumer/snapshot_extent_writer_unittest.cc
deleted file mode 100644
index 2201043..0000000
--- a/payload_consumer/snapshot_extent_writer_unittest.cc
+++ /dev/null
@@ -1,180 +0,0 @@
-//
-// 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(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(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(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
deleted file mode 100644
index 0843fff..0000000
--- a/payload_consumer/vabc_partition_writer.cc
+++ /dev/null
@@ -1,175 +0,0 @@
-//
-// 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:
-        if (cow_op.src_block == cow_op.dst_block) {
-          continue;
-        }
-        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.
-  // if cow_writer_ failed, that means Init() failed. This function shouldn't be
-  // called if Init() fails.
-  TEST_AND_RETURN(cow_writer_ != nullptr);
-  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.
-  TEST_AND_RETURN_FALSE(cow_writer_ != nullptr);
-  return cow_writer_->AddLabel(kEndOfInstallLabel);
-}
-
-VABCPartitionWriter::~VABCPartitionWriter() {
-  if (cow_writer_) {
-    cow_writer_->Finalize();
-  }
-}
-
-}  // namespace chromeos_update_engine
diff --git a/payload_consumer/vabc_partition_writer.h b/payload_consumer/vabc_partition_writer.h
deleted file mode 100644
index 7fb2a2c..0000000
--- a/payload_consumer/vabc_partition_writer.h
+++ /dev/null
@@ -1,63 +0,0 @@
-//
-// 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 e2fab7d..d5437b6 100644
--- a/payload_consumer/verity_writer_android.cc
+++ b/payload_consumer/verity_writer_android.cc
@@ -29,8 +29,6 @@
 }
 
 #include "update_engine/common/utils.h"
-#include "update_engine/payload_consumer/cached_file_descriptor.h"
-#include "update_engine/payload_consumer/file_descriptor.h"
 
 namespace chromeos_update_engine {
 
@@ -43,6 +41,9 @@
 bool VerityWriterAndroid::Init(const InstallPlan::Partition& partition) {
   partition_ = &partition;
 
+  if (partition_->hash_tree_size != 0 || partition_->fec_size != 0) {
+    utils::SetBlockDeviceReadOnly(partition_->target_path, false);
+  }
   if (partition_->hash_tree_size != 0) {
     auto hash_function =
         HashTreeBuilder::HashFunction(partition_->hash_tree_algorithm);
@@ -64,86 +65,58 @@
       return false;
     }
   }
-  total_offset_ = 0;
   return true;
 }
 
-bool VerityWriterAndroid::Update(const uint64_t offset,
+bool VerityWriterAndroid::Update(uint64_t offset,
                                  const uint8_t* buffer,
                                  size_t size) {
-  if (offset != total_offset_) {
-    LOG(ERROR) << "Sequential read expected, expected to read at: "
-               << total_offset_ << " actual read occurs at: " << offset;
-    return false;
-  }
   if (partition_->hash_tree_size != 0) {
-    const uint64_t hash_tree_data_end =
+    uint64_t hash_tree_data_end =
         partition_->hash_tree_data_offset + partition_->hash_tree_data_size;
-    const uint64_t start_offset =
-        std::max(offset, partition_->hash_tree_data_offset);
-    if (offset + size > hash_tree_data_end) {
-      LOG(WARNING)
-          << "Reading past hash_tree_data_end, something is probably "
-             "wrong, might cause incorrect hash of partitions. offset: "
-          << offset << " size: " << size
-          << " hash_tree_data_end: " << hash_tree_data_end;
-    }
-    const uint64_t end_offset = std::min(offset + size, hash_tree_data_end);
+    uint64_t start_offset = std::max(offset, partition_->hash_tree_data_offset);
+    uint64_t end_offset = std::min(offset + size, hash_tree_data_end);
     if (start_offset < end_offset) {
       TEST_AND_RETURN_FALSE(hash_tree_builder_->Update(
           buffer + start_offset - offset, end_offset - start_offset));
 
       if (end_offset == hash_tree_data_end) {
-        LOG(INFO)
-            << "Read everything before hash tree. Ready to write hash tree.";
+        // 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));
+        hash_tree_builder_.reset();
       }
     }
   }
-  total_offset_ += size;
-
-  return true;
-}
-
-bool VerityWriterAndroid::Finalize(FileDescriptorPtr read_fd,
-                                   FileDescriptorPtr write_fd) {
-  const auto hash_tree_data_end =
-      partition_->hash_tree_data_offset + partition_->hash_tree_data_size;
-  if (total_offset_ < hash_tree_data_end) {
-    LOG(ERROR) << "Read up to " << total_offset_
-               << " when we are expecting to read everything "
-                  "before "
-               << hash_tree_data_end;
-    return false;
-  }
-  // All hash tree data blocks has been hashed, write hash tree to disk.
-  LOG(INFO) << "Writing verity hash tree to " << partition_->target_path;
-  TEST_AND_RETURN_FALSE(hash_tree_builder_->BuildHashTree());
-  TEST_AND_RETURN_FALSE_ERRNO(
-      write_fd->Seek(partition_->hash_tree_offset, SEEK_SET));
-  auto success =
-      hash_tree_builder_->WriteHashTree([write_fd](auto data, auto size) {
-        return utils::WriteAll(write_fd, data, size);
-      });
-  // hashtree builder already prints error messages.
-  TEST_AND_RETURN_FALSE(success);
-  hash_tree_builder_.reset();
   if (partition_->fec_size != 0) {
-    LOG(INFO) << "Writing verity FEC to " << partition_->target_path;
-    TEST_AND_RETURN_FALSE(EncodeFEC(read_fd,
-                                    write_fd,
-                                    partition_->fec_data_offset,
-                                    partition_->fec_data_size,
-                                    partition_->fec_offset,
-                                    partition_->fec_size,
-                                    partition_->fec_roots,
-                                    partition_->block_size,
-                                    false /* verify_mode */));
+    uint64_t fec_data_end =
+        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,
+                                      partition_->fec_data_offset,
+                                      partition_->fec_data_size,
+                                      partition_->fec_offset,
+                                      partition_->fec_size,
+                                      partition_->fec_roots,
+                                      partition_->block_size,
+                                      false /* verify_mode */));
+    }
   }
   return true;
 }
 
-bool VerityWriterAndroid::EncodeFEC(FileDescriptorPtr read_fd,
-                                    FileDescriptorPtr write_fd,
+bool VerityWriterAndroid::EncodeFEC(const std::string& path,
                                     uint64_t data_offset,
                                     uint64_t data_size,
                                     uint64_t fec_offset,
@@ -162,10 +135,13 @@
       init_rs_char(FEC_PARAMS(fec_roots)), &free_rs_char);
   TEST_AND_RETURN_FALSE(rs_char != nullptr);
 
-  // Cache at most 1MB of fec data, in VABC, we need to re-open fd if we
-  // perform a read() operation after write(). So reduce the number of writes
-  // can save unnecessary re-opens.
-  write_fd = std::make_shared<CachedFileDescriptor>(write_fd, 1 * (1 << 20));
+  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
@@ -178,13 +154,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(read_fd,
+        TEST_AND_RETURN_FALSE(utils::PReadAll(fd,
                                               buffer.data(),
                                               buffer.size(),
                                               data_offset + offset,
                                               &bytes_read));
-        TEST_AND_RETURN_FALSE(bytes_read >= 0);
-        TEST_AND_RETURN_FALSE(static_cast<size_t>(bytes_read) == buffer.size());
+        TEST_AND_RETURN_FALSE(bytes_read ==
+                              static_cast<ssize_t>(buffer.size()));
       }
       for (size_t k = 0; k < buffer.size(); k++) {
         rs_blocks[k * rs_n + j] = buffer[k];
@@ -203,43 +179,17 @@
       brillo::Blob fec_read(fec.size());
       ssize_t bytes_read = 0;
       TEST_AND_RETURN_FALSE(utils::PReadAll(
-          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());
+          fd, fec_read.data(), fec_read.size(), fec_offset, &bytes_read));
+      TEST_AND_RETURN_FALSE(bytes_read ==
+                            static_cast<ssize_t>(fec_read.size()));
       TEST_AND_RETURN_FALSE(fec == fec_read);
     } else {
-      CHECK(write_fd);
-      write_fd->Seek(fec_offset, SEEK_SET);
-      if (!utils::WriteAll(write_fd, fec.data(), fec.size())) {
-        PLOG(ERROR) << "EncodeFEC write() failed";
-        return false;
-      }
+      TEST_AND_RETURN_FALSE(
+          utils::PWriteAll(fd, fec.data(), fec.size(), fec_offset));
     }
     fec_offset += fec.size();
   }
-  write_fd->Flush();
-  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);
+  return true;
 }
 }  // namespace chromeos_update_engine
diff --git a/payload_consumer/verity_writer_android.h b/payload_consumer/verity_writer_android.h
index 8339528..05a5856 100644
--- a/payload_consumer/verity_writer_android.h
+++ b/payload_consumer/verity_writer_android.h
@@ -22,7 +22,6 @@
 
 #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 {
@@ -32,9 +31,8 @@
   VerityWriterAndroid() = default;
   ~VerityWriterAndroid() override = default;
 
-  bool Init(const InstallPlan::Partition& partition);
+  bool Init(const InstallPlan::Partition& partition) override;
   bool Update(uint64_t offset, const uint8_t* buffer, size_t size) override;
-  bool Finalize(FileDescriptorPtr read_fd, FileDescriptorPtr write_fd) override;
 
   // Read [data_offset : data_offset + data_size) from |path| and encode FEC
   // data, if |verify_mode|, then compare the encoded FEC with the one in
@@ -42,15 +40,6 @@
   // 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,
@@ -64,7 +53,7 @@
   const InstallPlan::Partition* partition_ = nullptr;
 
   std::unique_ptr<HashTreeBuilder> hash_tree_builder_;
-  uint64_t total_offset_ = 0;
+
   DISALLOW_COPY_AND_ASSIGN(VerityWriterAndroid);
 };
 
diff --git a/payload_consumer/verity_writer_android_unittest.cc b/payload_consumer/verity_writer_android_unittest.cc
index 75da0ae..f943ce8 100644
--- a/payload_consumer/verity_writer_android_unittest.cc
+++ b/payload_consumer/verity_writer_android_unittest.cc
@@ -16,14 +16,11 @@
 
 #include "update_engine/payload_consumer/verity_writer_android.h"
 
-#include <fcntl.h>
-
 #include <brillo/secure_blob.h>
 #include <gtest/gtest.h>
 
 #include "update_engine/common/test_utils.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/payload_consumer/file_descriptor.h"
 
 namespace chromeos_update_engine {
 
@@ -38,23 +35,19 @@
     partition_.hash_tree_size = 4096;
     partition_.hash_tree_algorithm = "sha1";
     partition_.fec_roots = 2;
-    partition_fd_ = std::make_shared<EintrSafeFileDescriptor>();
-    partition_fd_->Open(partition_.target_path.c_str(), O_RDWR);
   }
 
   VerityWriterAndroid verity_writer_;
   InstallPlan::Partition partition_;
-  FileDescriptorPtr partition_fd_;
-  ScopedTempFile temp_file_;
+  test_utils::ScopedTempFile temp_file_;
 };
 
 TEST_F(VerityWriterAndroidTest, SimpleTest) {
   brillo::Blob part_data(8192);
   test_utils::WriteFileVector(partition_.target_path, part_data);
   ASSERT_TRUE(verity_writer_.Init(partition_));
-  ASSERT_TRUE(verity_writer_.Update(0, part_data.data(), 4096));
-  ASSERT_TRUE(verity_writer_.Update(4096, part_data.data() + 4096, 4096));
-  ASSERT_TRUE(verity_writer_.Finalize(partition_fd_, partition_fd_));
+  EXPECT_TRUE(verity_writer_.Update(0, part_data.data(), 4096));
+  EXPECT_TRUE(verity_writer_.Update(4096, part_data.data() + 4096, 4096));
   brillo::Blob actual_part;
   utils::ReadFile(partition_.target_path, &actual_part);
   // dd if=/dev/zero bs=4096 count=1 2>/dev/null | sha1sum | xxd -r -p |
@@ -63,7 +56,7 @@
                        0x1d, 0xf3, 0xbf, 0xb2, 0x6b, 0x4f, 0xb7,
                        0xcd, 0x95, 0xfb, 0x7b, 0xff, 0x1d};
   memcpy(part_data.data() + 4096, hash.data(), hash.size());
-  ASSERT_EQ(part_data, actual_part);
+  EXPECT_EQ(part_data, actual_part);
 }
 
 TEST_F(VerityWriterAndroidTest, NoOpTest) {
@@ -71,28 +64,19 @@
   partition_.hash_tree_size = 0;
   brillo::Blob part_data(4096);
   ASSERT_TRUE(verity_writer_.Init(partition_));
-  ASSERT_TRUE(verity_writer_.Update(0, part_data.data(), part_data.size()));
-  ASSERT_TRUE(verity_writer_.Update(4096, part_data.data(), part_data.size()));
-  ASSERT_TRUE(verity_writer_.Update(8192, part_data.data(), part_data.size()));
-}
-
-TEST_F(VerityWriterAndroidTest, DiscontinuedRead) {
-  partition_.hash_tree_data_size = 8192;
-  partition_.hash_tree_size = 4096;
-  brillo::Blob part_data(4096);
-  ASSERT_TRUE(verity_writer_.Init(partition_));
-  ASSERT_TRUE(verity_writer_.Update(0, part_data.data(), part_data.size()));
-  ASSERT_FALSE(verity_writer_.Update(8192, part_data.data(), part_data.size()));
+  EXPECT_TRUE(verity_writer_.Update(0, part_data.data(), part_data.size()));
+  EXPECT_TRUE(verity_writer_.Update(4096, part_data.data(), part_data.size()));
+  EXPECT_TRUE(verity_writer_.Update(8192, part_data.data(), part_data.size()));
 }
 
 TEST_F(VerityWriterAndroidTest, InvalidHashAlgorithmTest) {
   partition_.hash_tree_algorithm = "sha123";
-  ASSERT_FALSE(verity_writer_.Init(partition_));
+  EXPECT_FALSE(verity_writer_.Init(partition_));
 }
 
 TEST_F(VerityWriterAndroidTest, WrongHashTreeSizeTest) {
   partition_.hash_tree_size = 8192;
-  ASSERT_FALSE(verity_writer_.Init(partition_));
+  EXPECT_FALSE(verity_writer_.Init(partition_));
 }
 
 TEST_F(VerityWriterAndroidTest, SHA256Test) {
@@ -100,9 +84,8 @@
   brillo::Blob part_data(8192);
   test_utils::WriteFileVector(partition_.target_path, part_data);
   ASSERT_TRUE(verity_writer_.Init(partition_));
-  ASSERT_TRUE(verity_writer_.Update(0, part_data.data(), 4096));
-  ASSERT_TRUE(verity_writer_.Update(4096, part_data.data() + 4096, 4096));
-  ASSERT_TRUE(verity_writer_.Finalize(partition_fd_, partition_fd_));
+  EXPECT_TRUE(verity_writer_.Update(0, part_data.data(), 4096));
+  EXPECT_TRUE(verity_writer_.Update(4096, part_data.data() + 4096, 4096));
   brillo::Blob actual_part;
   utils::ReadFile(partition_.target_path, &actual_part);
   // dd if=/dev/zero bs=4096 count=1 2>/dev/null | sha256sum | xxd -r -p |
@@ -112,33 +95,7 @@
                        0x4f, 0x58, 0x05, 0xff, 0x7c, 0xb4, 0x7c, 0x7a,
                        0x85, 0xda, 0xbd, 0x8b, 0x48, 0x89, 0x2c, 0xa7};
   memcpy(part_data.data() + 4096, hash.data(), hash.size());
-  ASSERT_EQ(part_data, actual_part);
-}
-
-TEST_F(VerityWriterAndroidTest, NonZeroOffsetSHA256Test) {
-  partition_.hash_tree_algorithm = "sha256";
-  partition_.hash_tree_data_offset = 100;
-  partition_.hash_tree_offset =
-      partition_.hash_tree_data_offset + partition_.hash_tree_data_size;
-  brillo::Blob part_data(8192 + partition_.hash_tree_data_offset);
-  test_utils::WriteFileVector(partition_.target_path, part_data);
-  ASSERT_TRUE(verity_writer_.Init(partition_));
-  ASSERT_TRUE(verity_writer_.Update(0, part_data.data(), 4096));
-  ASSERT_TRUE(verity_writer_.Update(4096, part_data.data() + 4096, 4096));
-  ASSERT_TRUE(verity_writer_.Update(
-      8192, part_data.data() + 8192, partition_.hash_tree_data_offset));
-  ASSERT_TRUE(verity_writer_.Finalize(partition_fd_, partition_fd_));
-  brillo::Blob actual_part;
-  utils::ReadFile(partition_.target_path, &actual_part);
-  // dd if=/dev/zero bs=4096 count=1 2>/dev/null | sha256sum | xxd -r -p |
-  //     hexdump -v -e '/1 "0x%02x, "'
-  brillo::Blob hash = {0xad, 0x7f, 0xac, 0xb2, 0x58, 0x6f, 0xc6, 0xe9,
-                       0x66, 0xc0, 0x04, 0xd7, 0xd1, 0xd1, 0x6b, 0x02,
-                       0x4f, 0x58, 0x05, 0xff, 0x7c, 0xb4, 0x7c, 0x7a,
-                       0x85, 0xda, 0xbd, 0x8b, 0x48, 0x89, 0x2c, 0xa7};
-  memcpy(
-      part_data.data() + partition_.hash_tree_offset, hash.data(), hash.size());
-  ASSERT_EQ(part_data, actual_part);
+  EXPECT_EQ(part_data, actual_part);
 }
 
 TEST_F(VerityWriterAndroidTest, FECTest) {
@@ -149,8 +106,7 @@
   brillo::Blob part_data(3 * 4096, 0x1);
   test_utils::WriteFileVector(partition_.target_path, part_data);
   ASSERT_TRUE(verity_writer_.Init(partition_));
-  ASSERT_TRUE(verity_writer_.Update(0, part_data.data(), part_data.size()));
-  ASSERT_TRUE(verity_writer_.Finalize(partition_fd_, partition_fd_));
+  EXPECT_TRUE(verity_writer_.Update(0, part_data.data(), part_data.size()));
   brillo::Blob actual_part;
   utils::ReadFile(partition_.target_path, &actual_part);
   // Write FEC data.
@@ -158,7 +114,7 @@
     part_data[i] = 0x8e;
     part_data[i + 1] = 0x8f;
   }
-  ASSERT_EQ(part_data, actual_part);
+  EXPECT_EQ(part_data, actual_part);
 }
 
 }  // namespace chromeos_update_engine
diff --git a/payload_consumer/verity_writer_interface.h b/payload_consumer/verity_writer_interface.h
index 37ed605..a3ecef3 100644
--- a/payload_consumer/verity_writer_interface.h
+++ b/payload_consumer/verity_writer_interface.h
@@ -22,7 +22,6 @@
 
 #include <base/macros.h>
 
-#include "payload_consumer/file_descriptor.h"
 #include "update_engine/payload_consumer/install_plan.h"
 
 namespace chromeos_update_engine {
@@ -38,10 +37,6 @@
   // blocks has passed.
   virtual bool Update(uint64_t offset, const uint8_t* buffer, size_t size) = 0;
 
-  // Write hash tree && FEC data to underlying fd, if they are present
-  virtual bool Finalize(FileDescriptorPtr read_fd,
-                        FileDescriptorPtr write_fd) = 0;
-
  protected:
   VerityWriterInterface() = default;
 
diff --git a/payload_consumer/verity_writer_stub.cc b/payload_consumer/verity_writer_stub.cc
index 8bff076..a0e2467 100644
--- a/payload_consumer/verity_writer_stub.cc
+++ b/payload_consumer/verity_writer_stub.cc
@@ -36,9 +36,4 @@
   return true;
 }
 
-bool VerityWriterStub::Finalize(FileDescriptorPtr read_fd,
-                                FileDescriptorPtr write_fd) {
-  return true;
-}
-
 }  // namespace chromeos_update_engine
diff --git a/payload_consumer/verity_writer_stub.h b/payload_consumer/verity_writer_stub.h
index a20db03..ea5e574 100644
--- a/payload_consumer/verity_writer_stub.h
+++ b/payload_consumer/verity_writer_stub.h
@@ -28,7 +28,6 @@
 
   bool Init(const InstallPlan::Partition& partition) override;
   bool Update(uint64_t offset, const uint8_t* buffer, size_t size) override;
-  bool Finalize(FileDescriptorPtr read_fd, FileDescriptorPtr write_fd) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(VerityWriterStub);
diff --git a/payload_consumer/xz_extent_writer.cc b/payload_consumer/xz_extent_writer.cc
index a648351..a5b939d 100644
--- a/payload_consumer/xz_extent_writer.cc
+++ b/payload_consumer/xz_extent_writer.cc
@@ -57,11 +57,12 @@
   TEST_AND_RETURN(input_buffer_.empty());
 }
 
-bool XzExtentWriter::Init(const RepeatedPtrField<Extent>& extents,
+bool XzExtentWriter::Init(FileDescriptorPtr fd,
+                          const RepeatedPtrField<Extent>& extents,
                           uint32_t block_size) {
   stream_ = xz_dec_init(XZ_DYNALLOC, kXzMaxDictSize);
   TEST_AND_RETURN_FALSE(stream_ != nullptr);
-  return underlying_writer_->Init(extents, block_size);
+  return underlying_writer_->Init(fd, extents, block_size);
 }
 
 bool XzExtentWriter::Write(const void* bytes, size_t count) {
diff --git a/payload_consumer/xz_extent_writer.h b/payload_consumer/xz_extent_writer.h
index 70338f2..e022274 100644
--- a/payload_consumer/xz_extent_writer.h
+++ b/payload_consumer/xz_extent_writer.h
@@ -39,7 +39,8 @@
       : underlying_writer_(std::move(underlying_writer)) {}
   ~XzExtentWriter() override;
 
-  bool Init(const google::protobuf::RepeatedPtrField<Extent>& extents,
+  bool Init(FileDescriptorPtr fd,
+            const google::protobuf::RepeatedPtrField<Extent>& extents,
             uint32_t block_size) override;
   bool Write(const void* bytes, size_t count) override;
 
diff --git a/payload_consumer/xz_extent_writer_unittest.cc b/payload_consumer/xz_extent_writer_unittest.cc
index 5269dbc..34980a9 100644
--- a/payload_consumer/xz_extent_writer_unittest.cc
+++ b/payload_consumer/xz_extent_writer_unittest.cc
@@ -87,7 +87,7 @@
   }
 
   void WriteAll(const brillo::Blob& compressed) {
-    EXPECT_TRUE(xz_writer_->Init({}, 1024));
+    EXPECT_TRUE(xz_writer_->Init(fd_, {}, 1024));
     EXPECT_TRUE(xz_writer_->Write(compressed.data(), compressed.size()));
 
     EXPECT_TRUE(fake_extent_writer_->InitCalled());
@@ -130,7 +130,7 @@
 }
 
 TEST_F(XzExtentWriterTest, GarbageDataRejected) {
-  EXPECT_TRUE(xz_writer_->Init({}, 1024));
+  EXPECT_TRUE(xz_writer_->Init(fd_, {}, 1024));
   // The sample_data_ is an uncompressed string.
   EXPECT_FALSE(xz_writer_->Write(sample_data_.data(), sample_data_.size()));
 }
@@ -138,7 +138,7 @@
 TEST_F(XzExtentWriterTest, PartialDataIsKept) {
   brillo::Blob compressed(std::begin(kCompressed30KiBofA),
                           std::end(kCompressed30KiBofA));
-  EXPECT_TRUE(xz_writer_->Init({}, 1024));
+  EXPECT_TRUE(xz_writer_->Init(fd_, {}, 1024));
   for (uint8_t byte : compressed) {
     EXPECT_TRUE(xz_writer_->Write(&byte, 1));
   }
diff --git a/payload_generator/ab_generator_unittest.cc b/payload_generator/ab_generator_unittest.cc
index 84eeb77..270657a 100644
--- a/payload_generator/ab_generator_unittest.cc
+++ b/payload_generator/ab_generator_unittest.cc
@@ -30,10 +30,10 @@
 #include "update_engine/common/test_utils.h"
 #include "update_engine/common/utils.h"
 #include "update_engine/payload_generator/annotated_operation.h"
+#include "update_engine/payload_generator/bzip.h"
 #include "update_engine/payload_generator/delta_diff_generator.h"
 #include "update_engine/payload_generator/extent_ranges.h"
 #include "update_engine/payload_generator/extent_utils.h"
-#include "update_engine/payload_generator/xz.h"
 
 using std::string;
 using std::vector;
@@ -48,8 +48,8 @@
   return ext.start_block() == start_block && ext.num_blocks() == num_blocks;
 }
 
-// Tests splitting of a REPLACE/REPLACE_XZ operation.
-void TestSplitReplaceOrReplaceXzOperation(InstallOperation::Type orig_type,
+// Tests splitting of a REPLACE/REPLACE_BZ operation.
+void TestSplitReplaceOrReplaceBzOperation(InstallOperation::Type orig_type,
                                           bool compressible) {
   const size_t op_ex1_start_block = 2;
   const size_t op_ex1_num_blocks = 2;
@@ -70,7 +70,8 @@
       part_data.push_back(dis(gen));
   }
   ASSERT_EQ(part_size, part_data.size());
-  ScopedTempFile part_file("SplitReplaceOrReplaceXzTest_part.XXXXXX");
+  test_utils::ScopedTempFile part_file(
+      "SplitReplaceOrReplaceBzTest_part.XXXXXX");
   ASSERT_TRUE(test_utils::WriteFileVector(part_file.path(), part_data));
 
   // Create original operation and blob data.
@@ -96,7 +97,7 @@
   if (orig_type == InstallOperation::REPLACE) {
     op_blob = op_data;
   } else {
-    ASSERT_TRUE(XzCompress(op_data, &op_blob));
+    ASSERT_TRUE(BzipCompress(op_data, &op_blob));
   }
   op.set_data_offset(0);
   op.set_data_length(op_blob.size());
@@ -106,7 +107,8 @@
   aop.name = "SplitTestOp";
 
   // Create the data file.
-  ScopedTempFile data_file("SplitReplaceOrReplaceXzTest_data.XXXXXX");
+  test_utils::ScopedTempFile data_file(
+      "SplitReplaceOrReplaceBzTest_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);
@@ -116,14 +118,14 @@
 
   // Split the operation.
   vector<AnnotatedOperation> result_ops;
-  PayloadVersion version(kBrilloMajorPayloadVersion,
+  PayloadVersion version(kChromeOSMajorPayloadVersion,
                          kSourceMinorPayloadVersion);
   ASSERT_TRUE(ABGenerator::SplitAReplaceOp(
       version, aop, part_file.path(), &result_ops, &blob_file));
 
   // Check the result.
   InstallOperation::Type expected_type =
-      compressible ? InstallOperation::REPLACE_XZ : InstallOperation::REPLACE;
+      compressible ? InstallOperation::REPLACE_BZ : InstallOperation::REPLACE;
 
   ASSERT_EQ(2U, result_ops.size());
 
@@ -141,7 +143,7 @@
       part_data.begin() + op_ex1_offset + op_ex1_size);
   brillo::Blob first_expected_blob;
   if (compressible) {
-    ASSERT_TRUE(XzCompress(first_expected_data, &first_expected_blob));
+    ASSERT_TRUE(BzipCompress(first_expected_data, &first_expected_blob));
   } else {
     first_expected_blob = first_expected_data;
   }
@@ -171,7 +173,7 @@
       part_data.begin() + op_ex2_offset + op_ex2_size);
   brillo::Blob second_expected_blob;
   if (compressible) {
-    ASSERT_TRUE(XzCompress(second_expected_data, &second_expected_blob));
+    ASSERT_TRUE(BzipCompress(second_expected_data, &second_expected_blob));
   } else {
     second_expected_blob = second_expected_data;
   }
@@ -197,8 +199,8 @@
   }
 }
 
-// Tests merging of REPLACE/REPLACE_XZ operations.
-void TestMergeReplaceOrReplaceXzOperations(InstallOperation::Type orig_type,
+// Tests merging of REPLACE/REPLACE_BZ operations.
+void TestMergeReplaceOrReplaceBzOperations(InstallOperation::Type orig_type,
                                            bool compressible) {
   const size_t first_op_num_blocks = 1;
   const size_t second_op_num_blocks = 2;
@@ -218,7 +220,8 @@
       part_data.push_back(dis(gen));
   }
   ASSERT_EQ(part_size, part_data.size());
-  ScopedTempFile part_file("MergeReplaceOrReplaceXzTest_part.XXXXXX");
+  test_utils::ScopedTempFile part_file(
+      "MergeReplaceOrReplaceBzTest_part.XXXXXX");
   ASSERT_TRUE(test_utils::WriteFileVector(part_file.path(), part_data));
 
   // Create original operations and blob data.
@@ -236,7 +239,7 @@
   if (orig_type == InstallOperation::REPLACE) {
     first_op_blob = first_op_data;
   } else {
-    ASSERT_TRUE(XzCompress(first_op_data, &first_op_blob));
+    ASSERT_TRUE(BzipCompress(first_op_data, &first_op_blob));
   }
   first_op.set_data_offset(0);
   first_op.set_data_length(first_op_blob.size());
@@ -256,7 +259,7 @@
   if (orig_type == InstallOperation::REPLACE) {
     second_op_blob = second_op_data;
   } else {
-    ASSERT_TRUE(XzCompress(second_op_data, &second_op_blob));
+    ASSERT_TRUE(BzipCompress(second_op_data, &second_op_blob));
   }
   second_op.set_data_offset(first_op_blob.size());
   second_op.set_data_length(second_op_blob.size());
@@ -268,7 +271,8 @@
   aops.push_back(second_aop);
 
   // Create the data file.
-  ScopedTempFile data_file("MergeReplaceOrReplaceXzTest_data.XXXXXX");
+  test_utils::ScopedTempFile data_file(
+      "MergeReplaceOrReplaceBzTest_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);
@@ -277,14 +281,14 @@
   BlobFileWriter blob_file(data_fd, &data_file_size);
 
   // Merge the operations.
-  PayloadVersion version(kBrilloMajorPayloadVersion,
+  PayloadVersion version(kChromeOSMajorPayloadVersion,
                          kSourceMinorPayloadVersion);
   EXPECT_TRUE(ABGenerator::MergeOperations(
       &aops, version, 5, part_file.path(), &blob_file));
 
   // Check the result.
   InstallOperation::Type expected_op_type =
-      compressible ? InstallOperation::REPLACE_XZ : InstallOperation::REPLACE;
+      compressible ? InstallOperation::REPLACE_BZ : InstallOperation::REPLACE;
   EXPECT_EQ(1U, aops.size());
   InstallOperation new_op = aops[0].op;
   EXPECT_EQ(expected_op_type, new_op.type());
@@ -299,7 +303,7 @@
                              part_data.begin() + total_op_size);
   brillo::Blob expected_blob;
   if (compressible) {
-    ASSERT_TRUE(XzCompress(expected_data, &expected_blob));
+    ASSERT_TRUE(BzipCompress(expected_data, &expected_blob));
   } else {
     expected_blob = expected_data;
   }
@@ -380,19 +384,19 @@
 }
 
 TEST_F(ABGeneratorTest, SplitReplaceTest) {
-  TestSplitReplaceOrReplaceXzOperation(InstallOperation::REPLACE, false);
+  TestSplitReplaceOrReplaceBzOperation(InstallOperation::REPLACE, false);
 }
 
-TEST_F(ABGeneratorTest, SplitReplaceIntoReplaceXzTest) {
-  TestSplitReplaceOrReplaceXzOperation(InstallOperation::REPLACE, true);
+TEST_F(ABGeneratorTest, SplitReplaceIntoReplaceBzTest) {
+  TestSplitReplaceOrReplaceBzOperation(InstallOperation::REPLACE, true);
 }
 
-TEST_F(ABGeneratorTest, SplitReplaceXzTest) {
-  TestSplitReplaceOrReplaceXzOperation(InstallOperation::REPLACE_XZ, true);
+TEST_F(ABGeneratorTest, SplitReplaceBzTest) {
+  TestSplitReplaceOrReplaceBzOperation(InstallOperation::REPLACE_BZ, true);
 }
 
-TEST_F(ABGeneratorTest, SplitReplaceXzIntoReplaceTest) {
-  TestSplitReplaceOrReplaceXzOperation(InstallOperation::REPLACE_XZ, false);
+TEST_F(ABGeneratorTest, SplitReplaceBzIntoReplaceTest) {
+  TestSplitReplaceOrReplaceBzOperation(InstallOperation::REPLACE_BZ, false);
 }
 
 TEST_F(ABGeneratorTest, SortOperationsByDestinationTest) {
@@ -460,7 +464,7 @@
   aops.push_back(third_aop);
 
   BlobFileWriter blob_file(0, nullptr);
-  PayloadVersion version(kBrilloMajorPayloadVersion,
+  PayloadVersion version(kChromeOSMajorPayloadVersion,
                          kSourceMinorPayloadVersion);
   EXPECT_TRUE(ABGenerator::MergeOperations(&aops, version, 5, "", &blob_file));
 
@@ -480,19 +484,19 @@
 }
 
 TEST_F(ABGeneratorTest, MergeReplaceOperationsTest) {
-  TestMergeReplaceOrReplaceXzOperations(InstallOperation::REPLACE, false);
+  TestMergeReplaceOrReplaceBzOperations(InstallOperation::REPLACE, false);
 }
 
-TEST_F(ABGeneratorTest, MergeReplaceOperationsToReplaceXzTest) {
-  TestMergeReplaceOrReplaceXzOperations(InstallOperation::REPLACE, true);
+TEST_F(ABGeneratorTest, MergeReplaceOperationsToReplaceBzTest) {
+  TestMergeReplaceOrReplaceBzOperations(InstallOperation::REPLACE, true);
 }
 
-TEST_F(ABGeneratorTest, MergeReplaceXzOperationsTest) {
-  TestMergeReplaceOrReplaceXzOperations(InstallOperation::REPLACE_XZ, true);
+TEST_F(ABGeneratorTest, MergeReplaceBzOperationsTest) {
+  TestMergeReplaceOrReplaceBzOperations(InstallOperation::REPLACE_BZ, true);
 }
 
-TEST_F(ABGeneratorTest, MergeReplaceXzOperationsToReplaceTest) {
-  TestMergeReplaceOrReplaceXzOperations(InstallOperation::REPLACE_XZ, false);
+TEST_F(ABGeneratorTest, MergeReplaceBzOperationsToReplaceTest) {
+  TestMergeReplaceOrReplaceBzOperations(InstallOperation::REPLACE_BZ, false);
 }
 
 TEST_F(ABGeneratorTest, NoMergeOperationsTest) {
@@ -533,7 +537,7 @@
   aops.push_back(fourth_aop);
 
   BlobFileWriter blob_file(0, nullptr);
-  PayloadVersion version(kBrilloMajorPayloadVersion,
+  PayloadVersion version(kChromeOSMajorPayloadVersion,
                          kSourceMinorPayloadVersion);
   EXPECT_TRUE(ABGenerator::MergeOperations(&aops, version, 4, "", &blob_file));
 
@@ -557,7 +561,7 @@
   second_aop.op = second_op;
   aops.push_back(second_aop);
 
-  ScopedTempFile src_part_file("AddSourceHashTest_src_part.XXXXXX");
+  test_utils::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.cc b/payload_generator/blob_file_writer.cc
index a1afe87..7cdeb35 100644
--- a/payload_generator/blob_file_writer.cc
+++ b/payload_generator/blob_file_writer.cc
@@ -38,9 +38,9 @@
   return result;
 }
 
-void BlobFileWriter::IncTotalBlobs(size_t increment) {
-  base::AutoLock auto_lock(blob_mutex_);
-  total_blobs_ += increment;
+void BlobFileWriter::SetTotalBlobs(size_t total_blobs) {
+  total_blobs_ = total_blobs;
+  stored_blobs_ = 0;
 }
 
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/blob_file_writer.h b/payload_generator/blob_file_writer.h
index bdd4c08..48553be 100644
--- a/payload_generator/blob_file_writer.h
+++ b/payload_generator/blob_file_writer.h
@@ -35,8 +35,10 @@
   // was stored, or -1 in case of failure.
   off_t StoreBlob(const brillo::Blob& blob);
 
-  // Increase |total_blobs| by |increment|. Thread safe.
-  void IncTotalBlobs(size_t increment);
+  // The number of |total_blobs| is the number of blobs that will be stored but
+  // is only used for logging purposes. If not set or set to 0, logging will be
+  // skipped. This function will also reset the number of stored blobs to 0.
+  void SetTotalBlobs(size_t total_blobs);
 
  private:
   size_t total_blobs_{0};
diff --git a/payload_generator/blob_file_writer_unittest.cc b/payload_generator/blob_file_writer_unittest.cc
index f4dcafb..487bc73 100644
--- a/payload_generator/blob_file_writer_unittest.cc
+++ b/payload_generator/blob_file_writer_unittest.cc
@@ -31,21 +31,24 @@
 class BlobFileWriterTest : public ::testing::Test {};
 
 TEST(BlobFileWriterTest, SimpleTest) {
-  ScopedTempFile blob_file("BlobFileWriterTest.XXXXXX", true);
+  string blob_path;
+  int blob_fd;
+  EXPECT_TRUE(
+      utils::MakeTempFile("BlobFileWriterTest.XXXXXX", &blob_path, &blob_fd));
   off_t blob_file_size = 0;
-  BlobFileWriter blob_file_writer(blob_file.fd(), &blob_file_size);
+  BlobFileWriter blob_file(blob_fd, &blob_file_size);
 
-  const off_t kBlobSize = 1024;
-  brillo::Blob blob(kBlobSize);
+  off_t blob_size = 1024;
+  brillo::Blob blob(blob_size);
   FillWithData(&blob);
-  EXPECT_EQ(0, blob_file_writer.StoreBlob(blob));
-  EXPECT_EQ(kBlobSize, blob_file_writer.StoreBlob(blob));
+  EXPECT_EQ(0, blob_file.StoreBlob(blob));
+  EXPECT_EQ(blob_size, blob_file.StoreBlob(blob));
 
-  brillo::Blob stored_blob(kBlobSize);
+  brillo::Blob stored_blob(blob_size);
   ssize_t bytes_read;
-  ASSERT_TRUE(utils::PReadAll(
-      blob_file.fd(), stored_blob.data(), kBlobSize, 0, &bytes_read));
-  EXPECT_EQ(bytes_read, kBlobSize);
+  ASSERT_TRUE(
+      utils::PReadAll(blob_fd, stored_blob.data(), blob_size, 0, &bytes_read));
+  EXPECT_EQ(bytes_read, blob_size);
   EXPECT_EQ(blob, stored_blob);
 }
 
diff --git a/payload_generator/block_mapping_unittest.cc b/payload_generator/block_mapping_unittest.cc
index 017548a..9b9b4f1 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.
-  ScopedTempFile old_part_{"BlockMappingTest_old.XXXXXX"};
-  ScopedTempFile new_part_{"BlockMappingTest_new.XXXXXX"};
+  test_utils::ScopedTempFile old_part_{"BlockMappingTest_old.XXXXXX"};
+  test_utils::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 7805156..0b115e0 100644
--- a/payload_generator/boot_img_filesystem_unittest.cc
+++ b/payload_generator/boot_img_filesystem_unittest.cc
@@ -63,7 +63,7 @@
     return boot_img;
   }
 
-  ScopedTempFile boot_file_;
+  test_utils::ScopedTempFile boot_file_;
 };
 
 TEST_F(BootImgFilesystemTest, SimpleTest) {
diff --git a/payload_generator/cow_size_estimator.cc b/payload_generator/cow_size_estimator.cc
deleted file mode 100644
index 01e9965..0000000
--- a/payload_generator/cow_size_estimator.cc
+++ /dev/null
@@ -1,167 +0,0 @@
-//
-// 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 <string>
-#include <utility>
-#include <vector>
-
-#include <android-base/unique_fd.h>
-#include <libsnapshot/cow_writer.h>
-
-#include "update_engine/common/cow_operation_convert.h"
-#include "update_engine/common/utils.h"
-#include "update_engine/update_metadata.pb.h"
-
-namespace chromeos_update_engine {
-using android::snapshot::CowWriter;
-
-namespace {
-bool 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);
-    TEST_AND_RETURN_FALSE(success);
-    CHECK_EQ(static_cast<size_t>(bytes_read), buffer.size());
-    TEST_AND_RETURN_FALSE(writer->AddRawBlocks(
-        extent.start_block(), buffer.data(), buffer.size()));
-  }
-  return true;
-}
-
-bool PerformZeroOp(const InstallOperation& op,
-                   CowWriter* writer,
-                   size_t block_size) {
-  for (const auto& extent : op.dst_extents()) {
-    TEST_AND_RETURN_FALSE(
-        writer->AddZeroBlocks(extent.start_block(), extent.num_blocks()));
-  }
-  return true;
-}
-
-bool WriteAllCowOps(size_t block_size,
-                    const std::vector<CowOperation>& converted,
-                    android::snapshot::ICowWriter* cow_writer,
-                    FileDescriptorPtr target_fd) {
-  std::vector<uint8_t> buffer(block_size);
-
-  for (const auto& cow_op : converted) {
-    switch (cow_op.op) {
-      case CowOperation::CowCopy:
-        if (cow_op.src_block == cow_op.dst_block) {
-          continue;
-        }
-        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(chromeos_update_engine::utils::ReadAll(
-            target_fd,
-            buffer.data(),
-            block_size,
-            cow_op.dst_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;
-}
-}  // namespace
-
-size_t EstimateCowSize(
-    FileDescriptorPtr target_fd,
-    const google::protobuf::RepeatedPtrField<InstallOperation>& operations,
-    const google::protobuf::RepeatedPtrField<CowMergeOperation>&
-        merge_operations,
-    size_t block_size,
-    std::string compression) {
-  android::snapshot::CowWriter cow_writer{
-      {.block_size = static_cast<uint32_t>(block_size),
-       .compression = std::move(compression)}};
-  // 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});
-  CHECK(CowDryRun(
-      target_fd, operations, merge_operations, block_size, &cow_writer));
-  CHECK(cow_writer.Finalize());
-  return cow_writer.GetCowSize();
-}
-
-bool CowDryRun(
-    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) {
-  const auto converted = ConvertToCowOperations(operations, merge_operations);
-  WriteAllCowOps(block_size, converted, cow_writer, target_fd);
-  cow_writer->AddLabel(0);
-  for (const auto& op : operations) {
-    switch (op.type()) {
-      case InstallOperation::REPLACE:
-      case InstallOperation::REPLACE_BZ:
-      case InstallOperation::REPLACE_XZ:
-        TEST_AND_RETURN_FALSE(
-            PerformReplaceOp(op, cow_writer, target_fd, block_size));
-        break;
-      case InstallOperation::ZERO:
-      case InstallOperation::DISCARD:
-        TEST_AND_RETURN_FALSE(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.
-        TEST_AND_RETURN_FALSE(
-            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 true;
-}
-}  // namespace chromeos_update_engine
diff --git a/payload_generator/cow_size_estimator.h b/payload_generator/cow_size_estimator.h
deleted file mode 100644
index 850c890..0000000
--- a/payload_generator/cow_size_estimator.h
+++ /dev/null
@@ -1,47 +0,0 @@
-//
-// 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 <string>
-
-#include <libsnapshot/cow_writer.h>
-#include <update_engine/update_metadata.pb.h>
-
-#include "update_engine/payload_consumer/file_descriptor.h"
-
-namespace chromeos_update_engine {
-// Given file descriptor to the 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 target_fd,
-    const google::protobuf::RepeatedPtrField<InstallOperation>& operations,
-    const google::protobuf::RepeatedPtrField<CowMergeOperation>&
-        merge_operations,
-    size_t block_size,
-    std::string compression);
-
-// Convert InstallOps to CowOps and apply the converted cow op to |cow_writer|
-bool CowDryRun(
-    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);
-
-}  // namespace chromeos_update_engine
diff --git a/payload_generator/cow_size_estimator_stub.cc b/payload_generator/cow_size_estimator_stub.cc
deleted file mode 100644
index 9d94d63..0000000
--- a/payload_generator/cow_size_estimator_stub.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-//
-// 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"
-
-namespace chromeos_update_engine {
-
-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 chromeos_update_engine
diff --git a/payload_generator/cycle_breaker.cc b/payload_generator/cycle_breaker.cc
new file mode 100644
index 0000000..d6eeed2
--- /dev/null
+++ b/payload_generator/cycle_breaker.cc
@@ -0,0 +1,218 @@
+//
+// Copyright (C) 2012 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/cycle_breaker.h"
+
+#include <inttypes.h>
+
+#include <limits>
+#include <set>
+#include <string>
+#include <utility>
+
+#include <base/stl_util.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+
+#include "update_engine/payload_generator/graph_utils.h"
+#include "update_engine/payload_generator/tarjan.h"
+
+using std::make_pair;
+using std::set;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+// This is the outer function from the original paper.
+void CycleBreaker::BreakCycles(const Graph& graph, set<Edge>* out_cut_edges) {
+  cut_edges_.clear();
+
+  // Make a copy, which we will modify by removing edges. Thus, in each
+  // iteration subgraph_ is the current subgraph or the original with
+  // vertices we desire. This variable was "A_K" in the original paper.
+  subgraph_ = graph;
+
+  // The paper calls for the "adjacency structure (i.e., graph) of
+  // strong (-ly connected) component K with least vertex in subgraph
+  // induced by {s, s + 1, ..., n}".
+  // We arbitrarily order each vertex by its index in the graph. Thus,
+  // each iteration, we are looking at the subgraph {s, s + 1, ..., n}
+  // and looking for the strongly connected component with vertex s.
+
+  TarjanAlgorithm tarjan;
+  skipped_ops_ = 0;
+
+  for (Graph::size_type i = 0; i < subgraph_.size(); i++) {
+    InstallOperation::Type op_type = graph[i].aop.op.type();
+    if (op_type == InstallOperation::REPLACE ||
+        op_type == InstallOperation::REPLACE_BZ) {
+      skipped_ops_++;
+      continue;
+    }
+
+    if (i > 0) {
+      // Erase node (i - 1) from subgraph_. First, erase what it points to
+      subgraph_[i - 1].out_edges.clear();
+      // Now, erase any pointers to node (i - 1)
+      for (Graph::size_type j = i; j < subgraph_.size(); j++) {
+        subgraph_[j].out_edges.erase(i - 1);
+      }
+    }
+
+    // Calculate SCC (strongly connected component) with vertex i.
+    vector<Vertex::Index> component_indexes;
+    tarjan.Execute(i, &subgraph_, &component_indexes);
+
+    // Set subgraph edges for the components in the SCC.
+    for (vector<Vertex::Index>::iterator it = component_indexes.begin();
+         it != component_indexes.end();
+         ++it) {
+      subgraph_[*it].subgraph_edges.clear();
+      for (vector<Vertex::Index>::iterator jt = component_indexes.begin();
+           jt != component_indexes.end();
+           ++jt) {
+        // If there's a link from *it -> *jt in the graph,
+        // add a subgraph_ edge
+        if (base::ContainsKey(subgraph_[*it].out_edges, *jt))
+          subgraph_[*it].subgraph_edges.insert(*jt);
+      }
+    }
+
+    current_vertex_ = i;
+    blocked_.clear();
+    blocked_.resize(subgraph_.size());
+    blocked_graph_.clear();
+    blocked_graph_.resize(subgraph_.size());
+    Circuit(current_vertex_, 0);
+  }
+
+  out_cut_edges->swap(cut_edges_);
+  LOG(INFO) << "Cycle breaker skipped " << skipped_ops_ << " ops.";
+  DCHECK(stack_.empty());
+}
+
+static const size_t kMaxEdgesToConsider = 2;
+
+void CycleBreaker::HandleCircuit() {
+  stack_.push_back(current_vertex_);
+  CHECK_GE(stack_.size(), static_cast<vector<Vertex::Index>::size_type>(2));
+  Edge min_edge = make_pair(stack_[0], stack_[1]);
+  uint64_t min_edge_weight = std::numeric_limits<uint64_t>::max();
+  size_t edges_considered = 0;
+  for (vector<Vertex::Index>::const_iterator it = stack_.begin();
+       it != (stack_.end() - 1);
+       ++it) {
+    Edge edge = make_pair(*it, *(it + 1));
+    if (cut_edges_.find(edge) != cut_edges_.end()) {
+      stack_.pop_back();
+      return;
+    }
+    uint64_t edge_weight = graph_utils::EdgeWeight(subgraph_, edge);
+    if (edge_weight < min_edge_weight) {
+      min_edge_weight = edge_weight;
+      min_edge = edge;
+    }
+    edges_considered++;
+    if (edges_considered == kMaxEdgesToConsider)
+      break;
+  }
+  cut_edges_.insert(min_edge);
+  stack_.pop_back();
+}
+
+void CycleBreaker::Unblock(Vertex::Index u) {
+  blocked_[u] = false;
+
+  for (Vertex::EdgeMap::iterator it = blocked_graph_[u].out_edges.begin();
+       it != blocked_graph_[u].out_edges.end();) {
+    Vertex::Index w = it->first;
+    blocked_graph_[u].out_edges.erase(it++);
+    if (blocked_[w])
+      Unblock(w);
+  }
+}
+
+bool CycleBreaker::StackContainsCutEdge() const {
+  for (vector<Vertex::Index>::const_iterator it = ++stack_.begin(),
+                                             e = stack_.end();
+       it != e;
+       ++it) {
+    Edge edge = make_pair(*(it - 1), *it);
+    if (base::ContainsKey(cut_edges_, edge)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool CycleBreaker::Circuit(Vertex::Index vertex, Vertex::Index depth) {
+  // "vertex" was "v" in the original paper.
+  bool found = false;  // Was "f" in the original paper.
+  stack_.push_back(vertex);
+  blocked_[vertex] = true;
+  {
+    static int counter = 0;
+    counter++;
+    if (counter == 10000) {
+      counter = 0;
+      std::string stack_str;
+      for (Vertex::Index index : stack_) {
+        stack_str += std::to_string(index);
+        stack_str += " -> ";
+      }
+      LOG(INFO) << "stack: " << stack_str;
+    }
+  }
+
+  for (Vertex::SubgraphEdgeMap::iterator w =
+           subgraph_[vertex].subgraph_edges.begin();
+       w != subgraph_[vertex].subgraph_edges.end();
+       ++w) {
+    if (*w == current_vertex_) {
+      // The original paper called for printing stack_ followed by
+      // current_vertex_ here, which is a cycle. Instead, we call
+      // HandleCircuit() to break it.
+      HandleCircuit();
+      found = true;
+    } else if (!blocked_[*w]) {
+      if (Circuit(*w, depth + 1)) {
+        found = true;
+        if ((depth > kMaxEdgesToConsider) || StackContainsCutEdge())
+          break;
+      }
+    }
+  }
+
+  if (found) {
+    Unblock(vertex);
+  } else {
+    for (Vertex::SubgraphEdgeMap::iterator w =
+             subgraph_[vertex].subgraph_edges.begin();
+         w != subgraph_[vertex].subgraph_edges.end();
+         ++w) {
+      if (blocked_graph_[*w].out_edges.find(vertex) ==
+          blocked_graph_[*w].out_edges.end()) {
+        blocked_graph_[*w].out_edges.insert(
+            make_pair(vertex, EdgeProperties()));
+      }
+    }
+  }
+  CHECK_EQ(vertex, stack_.back());
+  stack_.pop_back();
+  return found;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/cycle_breaker.h b/payload_generator/cycle_breaker.h
new file mode 100644
index 0000000..01518fe
--- /dev/null
+++ b/payload_generator/cycle_breaker.h
@@ -0,0 +1,71 @@
+//
+// Copyright (C) 2010 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_PAYLOAD_GENERATOR_CYCLE_BREAKER_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_CYCLE_BREAKER_H_
+
+// This is a modified implementation of Donald B. Johnson's algorithm for
+// finding all elementary cycles (a.k.a. circuits) in a directed graph.
+// See the paper "Finding All the Elementary Circuits of a Directed Graph"
+// at http://dutta.csc.ncsu.edu/csc791_spring07/wrap/circuits_johnson.pdf
+// for reference.
+
+// Note: this version of the algorithm not only finds cycles, but breaks them.
+// It uses a simple greedy algorithm for cutting: when a cycle is discovered,
+// the edge with the least weight is cut. Longer term we may wish to do
+// something more intelligent, since the goal is (ideally) to minimize the
+// sum of the weights of all cut cycles. In practice, it's intractable
+// to consider all cycles before cutting any; there are simply too many.
+// In a sample graph representative of a typical workload, I found over
+// 5 * 10^15 cycles.
+
+#include <set>
+#include <vector>
+
+#include "update_engine/payload_generator/graph_types.h"
+
+namespace chromeos_update_engine {
+
+class CycleBreaker {
+ public:
+  CycleBreaker() : skipped_ops_(0) {}
+  // out_cut_edges is replaced with the cut edges.
+  void BreakCycles(const Graph& graph, std::set<Edge>* out_cut_edges);
+
+  size_t skipped_ops() const { return skipped_ops_; }
+
+ private:
+  void HandleCircuit();
+  void Unblock(Vertex::Index u);
+  bool Circuit(Vertex::Index vertex, Vertex::Index depth);
+  bool StackContainsCutEdge() const;
+
+  std::vector<bool> blocked_;         // "blocked" in the paper
+  Vertex::Index current_vertex_;      // "s" in the paper
+  std::vector<Vertex::Index> stack_;  // the stack variable in the paper
+  Graph subgraph_;                    // "A_K" in the paper
+  Graph blocked_graph_;               // "B" in the paper
+
+  std::set<Edge> cut_edges_;
+
+  // Number of operations skipped b/c we know they don't have any
+  // incoming edges.
+  size_t skipped_ops_;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_CYCLE_BREAKER_H_
diff --git a/payload_generator/cycle_breaker_unittest.cc b/payload_generator/cycle_breaker_unittest.cc
new file mode 100644
index 0000000..fdcf49b
--- /dev/null
+++ b/payload_generator/cycle_breaker_unittest.cc
@@ -0,0 +1,279 @@
+//
+// Copyright (C) 2010 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/cycle_breaker.h"
+
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/stl_util.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/payload_generator/graph_types.h"
+
+using std::make_pair;
+using std::pair;
+using std::set;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+void SetOpForNodes(Graph* graph) {
+  for (Vertex& vertex : *graph) {
+    vertex.aop.op.set_type(InstallOperation::MOVE);
+  }
+}
+}  // namespace
+
+class CycleBreakerTest : public ::testing::Test {};
+
+TEST(CycleBreakerTest, SimpleTest) {
+  int counter = 0;
+  const Vertex::Index n_a = counter++;
+  const Vertex::Index n_b = counter++;
+  const Vertex::Index n_c = counter++;
+  const Vertex::Index n_d = counter++;
+  const Vertex::Index n_e = counter++;
+  const Vertex::Index n_f = counter++;
+  const Vertex::Index n_g = counter++;
+  const Vertex::Index n_h = counter++;
+  const Graph::size_type kNodeCount = counter++;
+
+  Graph graph(kNodeCount);
+  SetOpForNodes(&graph);
+
+  graph[n_a].out_edges.insert(make_pair(n_e, EdgeProperties()));
+  graph[n_a].out_edges.insert(make_pair(n_f, EdgeProperties()));
+  graph[n_b].out_edges.insert(make_pair(n_a, EdgeProperties()));
+  graph[n_c].out_edges.insert(make_pair(n_d, EdgeProperties()));
+  graph[n_d].out_edges.insert(make_pair(n_e, EdgeProperties()));
+  graph[n_d].out_edges.insert(make_pair(n_f, EdgeProperties()));
+  graph[n_e].out_edges.insert(make_pair(n_b, EdgeProperties()));
+  graph[n_e].out_edges.insert(make_pair(n_c, EdgeProperties()));
+  graph[n_e].out_edges.insert(make_pair(n_f, EdgeProperties()));
+  graph[n_f].out_edges.insert(make_pair(n_g, EdgeProperties()));
+  graph[n_g].out_edges.insert(make_pair(n_h, EdgeProperties()));
+  graph[n_h].out_edges.insert(make_pair(n_g, EdgeProperties()));
+
+  CycleBreaker breaker;
+
+  set<Edge> broken_edges;
+  breaker.BreakCycles(graph, &broken_edges);
+
+  // The following cycles must be cut:
+  // A->E->B
+  // C->D->E
+  // G->H
+
+  EXPECT_TRUE(base::ContainsKey(broken_edges, make_pair(n_a, n_e)) ||
+              base::ContainsKey(broken_edges, make_pair(n_e, n_b)) ||
+              base::ContainsKey(broken_edges, make_pair(n_b, n_a)));
+  EXPECT_TRUE(base::ContainsKey(broken_edges, make_pair(n_c, n_d)) ||
+              base::ContainsKey(broken_edges, make_pair(n_d, n_e)) ||
+              base::ContainsKey(broken_edges, make_pair(n_e, n_c)));
+  EXPECT_TRUE(base::ContainsKey(broken_edges, make_pair(n_g, n_h)) ||
+              base::ContainsKey(broken_edges, make_pair(n_h, n_g)));
+  EXPECT_EQ(3U, broken_edges.size());
+}
+
+namespace {
+pair<Vertex::Index, EdgeProperties> EdgeWithWeight(Vertex::Index dest,
+                                                   uint64_t weight) {
+  EdgeProperties props;
+  props.extents.resize(1);
+  props.extents[0].set_num_blocks(weight);
+  return make_pair(dest, props);
+}
+}  // namespace
+
+// This creates a bunch of cycles like this:
+//
+//               root <------.
+//    (t)->     / | \        |
+//             V  V  V       |
+//             N  N  N       |
+//              \ | /        |
+//               VVV         |
+//                N          |
+//              / | \        |
+//             V  V  V       |
+//             N  N  N       |
+//               ...         |
+//     (s)->    \ | /        |
+//               VVV         |
+//                N          |
+//                 \_________/
+//
+// such that the original cutting algo would cut edges (s). We changed
+// the algorithm to cut cycles (t) instead, since they are closer to the
+// root, and that can massively speed up cycle cutting.
+TEST(CycleBreakerTest, AggressiveCutTest) {
+  size_t counter = 0;
+
+  const int kNodesPerGroup = 4;
+  const int kGroups = 33;
+
+  Graph graph(kGroups * kNodesPerGroup + 1);  // + 1 for the root node
+  SetOpForNodes(&graph);
+
+  const Vertex::Index n_root = counter++;
+
+  Vertex::Index last_hub = n_root;
+  for (int i = 0; i < kGroups; i++) {
+    uint64_t weight = 5;
+    if (i == 0)
+      weight = 2;
+    else if (i == (kGroups - 1))
+      weight = 1;
+
+    const Vertex::Index next_hub = counter++;
+
+    for (int j = 0; j < (kNodesPerGroup - 1); j++) {
+      const Vertex::Index node = counter++;
+      graph[last_hub].out_edges.insert(EdgeWithWeight(node, weight));
+      graph[node].out_edges.insert(EdgeWithWeight(next_hub, weight));
+    }
+    last_hub = next_hub;
+  }
+
+  graph[last_hub].out_edges.insert(EdgeWithWeight(n_root, 5));
+
+  EXPECT_EQ(counter, graph.size());
+
+  CycleBreaker breaker;
+
+  set<Edge> broken_edges;
+  LOG(INFO) << "If this hangs for more than 1 second, the test has failed.";
+  breaker.BreakCycles(graph, &broken_edges);
+
+  set<Edge> expected_cuts;
+
+  for (Vertex::EdgeMap::const_iterator it = graph[n_root].out_edges.begin(),
+                                       e = graph[n_root].out_edges.end();
+       it != e;
+       ++it) {
+    expected_cuts.insert(make_pair(n_root, it->first));
+  }
+
+  EXPECT_TRUE(broken_edges == expected_cuts);
+}
+
+TEST(CycleBreakerTest, WeightTest) {
+  size_t counter = 0;
+  const Vertex::Index n_a = counter++;
+  const Vertex::Index n_b = counter++;
+  const Vertex::Index n_c = counter++;
+  const Vertex::Index n_d = counter++;
+  const Vertex::Index n_e = counter++;
+  const Vertex::Index n_f = counter++;
+  const Vertex::Index n_g = counter++;
+  const Vertex::Index n_h = counter++;
+  const Vertex::Index n_i = counter++;
+  const Vertex::Index n_j = counter++;
+  const Graph::size_type kNodeCount = counter++;
+
+  Graph graph(kNodeCount);
+  SetOpForNodes(&graph);
+
+  graph[n_a].out_edges.insert(EdgeWithWeight(n_b, 4));
+  graph[n_a].out_edges.insert(EdgeWithWeight(n_f, 3));
+  graph[n_a].out_edges.insert(EdgeWithWeight(n_h, 2));
+  graph[n_b].out_edges.insert(EdgeWithWeight(n_a, 3));
+  graph[n_b].out_edges.insert(EdgeWithWeight(n_c, 4));
+  graph[n_c].out_edges.insert(EdgeWithWeight(n_b, 5));
+  graph[n_c].out_edges.insert(EdgeWithWeight(n_d, 3));
+  graph[n_d].out_edges.insert(EdgeWithWeight(n_a, 6));
+  graph[n_d].out_edges.insert(EdgeWithWeight(n_e, 3));
+  graph[n_e].out_edges.insert(EdgeWithWeight(n_d, 4));
+  graph[n_e].out_edges.insert(EdgeWithWeight(n_g, 5));
+  graph[n_f].out_edges.insert(EdgeWithWeight(n_g, 2));
+  graph[n_g].out_edges.insert(EdgeWithWeight(n_f, 3));
+  graph[n_g].out_edges.insert(EdgeWithWeight(n_d, 5));
+  graph[n_h].out_edges.insert(EdgeWithWeight(n_i, 8));
+  graph[n_i].out_edges.insert(EdgeWithWeight(n_e, 4));
+  graph[n_i].out_edges.insert(EdgeWithWeight(n_h, 9));
+  graph[n_i].out_edges.insert(EdgeWithWeight(n_j, 6));
+
+  CycleBreaker breaker;
+
+  set<Edge> broken_edges;
+  breaker.BreakCycles(graph, &broken_edges);
+
+  // These are required to be broken:
+  EXPECT_TRUE(base::ContainsKey(broken_edges, make_pair(n_b, n_a)));
+  EXPECT_TRUE(base::ContainsKey(broken_edges, make_pair(n_b, n_c)));
+  EXPECT_TRUE(base::ContainsKey(broken_edges, make_pair(n_d, n_e)));
+  EXPECT_TRUE(base::ContainsKey(broken_edges, make_pair(n_f, n_g)));
+  EXPECT_TRUE(base::ContainsKey(broken_edges, make_pair(n_h, n_i)));
+}
+
+TEST(CycleBreakerTest, UnblockGraphTest) {
+  size_t counter = 0;
+  const Vertex::Index n_a = counter++;
+  const Vertex::Index n_b = counter++;
+  const Vertex::Index n_c = counter++;
+  const Vertex::Index n_d = counter++;
+  const Graph::size_type kNodeCount = counter++;
+
+  Graph graph(kNodeCount);
+  SetOpForNodes(&graph);
+
+  graph[n_a].out_edges.insert(EdgeWithWeight(n_b, 1));
+  graph[n_a].out_edges.insert(EdgeWithWeight(n_c, 1));
+  graph[n_b].out_edges.insert(EdgeWithWeight(n_c, 2));
+  graph[n_c].out_edges.insert(EdgeWithWeight(n_b, 2));
+  graph[n_b].out_edges.insert(EdgeWithWeight(n_d, 2));
+  graph[n_d].out_edges.insert(EdgeWithWeight(n_a, 2));
+
+  CycleBreaker breaker;
+
+  set<Edge> broken_edges;
+  breaker.BreakCycles(graph, &broken_edges);
+
+  // These are required to be broken:
+  EXPECT_TRUE(base::ContainsKey(broken_edges, make_pair(n_a, n_b)));
+  EXPECT_TRUE(base::ContainsKey(broken_edges, make_pair(n_a, n_c)));
+}
+
+TEST(CycleBreakerTest, SkipOpsTest) {
+  size_t counter = 0;
+  const Vertex::Index n_a = counter++;
+  const Vertex::Index n_b = counter++;
+  const Vertex::Index n_c = counter++;
+  const Graph::size_type kNodeCount = counter++;
+
+  Graph graph(kNodeCount);
+  SetOpForNodes(&graph);
+  graph[n_a].aop.op.set_type(InstallOperation::REPLACE_BZ);
+  graph[n_c].aop.op.set_type(InstallOperation::REPLACE);
+
+  graph[n_a].out_edges.insert(EdgeWithWeight(n_b, 1));
+  graph[n_c].out_edges.insert(EdgeWithWeight(n_b, 1));
+
+  CycleBreaker breaker;
+
+  set<Edge> broken_edges;
+  breaker.BreakCycles(graph, &broken_edges);
+
+  EXPECT_EQ(2U, breaker.skipped_ops());
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/deflate_utils.cc b/payload_generator/deflate_utils.cc
index c874bfd..01402dd 100644
--- a/payload_generator/deflate_utils.cc
+++ b/payload_generator/deflate_utils.cc
@@ -46,7 +46,7 @@
 // TODO(*): Optimize this so we don't have to read all extents into memory in
 // case it is large.
 bool CopyExtentsToFile(const string& in_path,
-                       const vector<Extent>& extents,
+                       const vector<Extent> extents,
                        const string& out_path,
                        size_t block_size) {
   brillo::Blob data(utils::BlocksInExtents(extents) * block_size);
@@ -284,9 +284,8 @@
       TEST_AND_RETURN_FALSE(
           CopyExtentsToFile(part.path, file.extents, path.value(), kBlockSize));
       // Test if it is actually a Squashfs file.
-      auto sqfs = SquashfsFilesystem::CreateFromFile(path.value(),
-                                                     extract_deflates,
-                                                     /*load_settings=*/false);
+      auto sqfs =
+          SquashfsFilesystem::CreateFromFile(path.value(), extract_deflates);
       if (sqfs) {
         // It is an squashfs file. Get its files to replace with itself.
         vector<FilesystemInterface::File> files;
@@ -307,7 +306,7 @@
       }
     }
 
-    if (is_regular_file && extract_deflates && !file.is_compressed) {
+    if (is_regular_file && extract_deflates) {
       // Search for deflates if the file is in zip or gzip format.
       // .zvoice files may eventually move out of rootfs. If that happens,
       // remove ".zvoice" (crbug.com/782918).
diff --git a/payload_generator/delta_diff_generator.cc b/payload_generator/delta_diff_generator.cc
index ab0f036..d484d32 100644
--- a/payload_generator/delta_diff_generator.cc
+++ b/payload_generator/delta_diff_generator.cc
@@ -29,21 +29,16 @@
 #include <vector>
 
 #include <base/logging.h>
-#include <base/threading/simple_thread.h>
 
 #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/inplace_generator.h"
 #include "update_engine/payload_generator/payload_file.h"
-#include "update_engine/update_metadata.pb.h"
 
 using std::string;
 using std::unique_ptr;
@@ -55,110 +50,6 @@
 const size_t kRootFSPartitionSize = static_cast<size_t>(2) * 1024 * 1024 * 1024;
 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,
-      const PartitionConfig& old_part,
-      const PartitionConfig& new_part,
-      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),
-        new_part_(new_part),
-        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 "
-              << new_part_.name;
-    bool success = strategy_->GenerateOperations(
-        config_, old_part_, new_part_, file_writer_, aops_);
-    if (!success) {
-      // ABORT the entire process, so that developer can look
-      // at recent logs and diagnose what happened
-      LOG(FATAL) << "GenerateOperations(" << old_part_.name << ", "
-                 << new_part_.name << ") failed";
-    }
-
-    bool snapshot_enabled =
-        config_.target.dynamic_partition_metadata &&
-        config_.target.dynamic_partition_metadata->snapshot_enabled();
-    if (!snapshot_enabled || !IsDynamicPartition(new_part_.name)) {
-      return;
-    }
-    // Skip cow size estimation if VABC isn't enabled
-    if (!config_.target.dynamic_partition_metadata->vabc_enabled()) {
-      return;
-    }
-    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(
-        std::move(target_fd),
-        std::move(operations),
-        {cow_merge_sequence_->begin(), cow_merge_sequence_->end()},
-        config_.block_size,
-        config_.target.dynamic_partition_metadata->vabc_compression_param());
-    if (!new_part_.disable_fec_computation) {
-      *cow_size_ +=
-          new_part_.verity.fec_extent.num_blocks() * config_.block_size;
-    }
-    *cow_size_ +=
-        new_part_.verity.hash_tree_extent.num_blocks() * config_.block_size;
-    LOG(INFO) << "Estimated COW size for partition: " << new_part_.name << " "
-              << *cow_size_;
-  }
-
- private:
-  const PayloadGenerationConfig& config_;
-  const PartitionConfig& old_part_;
-  const PartitionConfig& new_part_;
-  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);
-};
-
 bool GenerateUpdatePayloadFile(const PayloadGenerationConfig& config,
                                const string& output_path,
                                const string& private_key_path,
@@ -173,28 +64,23 @@
   PayloadFile payload;
   TEST_AND_RETURN_FALSE(payload.Init(config));
 
-  ScopedTempFile data_file("CrAU_temp_data.XXXXXX", true);
+  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);
+
   {
     off_t data_file_size = 0;
-    BlobFileWriter blob_file(data_file.fd(), &data_file_size);
+    ScopedFdCloser data_file_fd_closer(&data_file_fd);
+    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());
     }
     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());
-    base::DelegateSimpleThreadPool thread_pool{"partition-thread-pool",
-                                               thread_count};
     for (size_t i = 0; i < config.target.partitions.size(); i++) {
       const PartitionConfig& old_part =
           config.is_delta ? config.source.partitions[i] : empty_part;
@@ -207,49 +93,36 @@
       unique_ptr<OperationsGenerator> strategy;
       if (!old_part.path.empty()) {
         // Delta update.
-        LOG(INFO) << "Using generator ABGenerator() for partition "
-                  << new_part.name;
-        strategy.reset(new ABGenerator());
+        if (config.version.minor == kInPlaceMinorPayloadVersion) {
+          LOG(INFO) << "Using generator InplaceGenerator().";
+          strategy.reset(new InplaceGenerator());
+        } else {
+          LOG(INFO) << "Using generator ABGenerator().";
+          strategy.reset(new ABGenerator());
+        }
       } else {
-        LOG(INFO) << "Using generator FullUpdateGenerator() for partition "
-                  << new_part.name;
+        LOG(INFO) << "Using generator FullUpdateGenerator().";
         strategy.reset(new FullUpdateGenerator());
       }
 
+      vector<AnnotatedOperation> aops;
       // Generate the operations using the strategy we selected above.
-      partition_tasks.push_back(PartitionProcessor(config,
-                                                   old_part,
-                                                   new_part,
-                                                   &blob_file,
-                                                   &all_aops[i],
-                                                   &all_merge_sequences[i],
-                                                   &all_cow_sizes[i],
-                                                   std::move(strategy)));
-    }
-    thread_pool.Start();
-    for (auto& processor : partition_tasks) {
-      thread_pool.AddWork(&processor);
-    }
-    thread_pool.JoinAll();
+      TEST_AND_RETURN_FALSE(strategy->GenerateOperations(
+          config, old_part, new_part, &blob_file, &aops));
 
-    for (size_t i = 0; i < config.target.partitions.size(); i++) {
-      const PartitionConfig& old_part =
-          config.is_delta ? config.source.partitions[i] : empty_part;
-      const PartitionConfig& new_part = config.target.partitions[i];
-      TEST_AND_RETURN_FALSE(
-          payload.AddPartition(old_part,
-                               new_part,
-                               std::move(all_aops[i]),
-                               std::move(all_merge_sequences[i]),
-                               all_cow_sizes[i]));
+      // Filter the no-operations. OperationsGenerators should not output this
+      // kind of operations normally, but this is an extra step to fix that if
+      // happened.
+      diff_utils::FilterNoopOperations(&aops);
+
+      TEST_AND_RETURN_FALSE(payload.AddPartition(old_part, new_part, aops));
     }
   }
-  data_file.CloseFd();
 
   LOG(INFO) << "Writing payload file...";
   // Write payload file to disk.
   TEST_AND_RETURN_FALSE(payload.WritePayload(
-      output_path, data_file.path(), private_key_path, metadata_size));
+      output_path, temp_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 3c025e1..4ba6e24 100644
--- a/payload_generator/delta_diff_utils.cc
+++ b/payload_generator/delta_diff_utils.cc
@@ -83,6 +83,103 @@
 
 const int kBrotliCompressionQuality = 11;
 
+// Process a range of blocks from |range_start| to |range_end| in the extent at
+// position |*idx_p| of |extents|. If |do_remove| is true, this range will be
+// removed, which may cause the extent to be trimmed, split or removed entirely.
+// The value of |*idx_p| is updated to point to the next extent to be processed.
+// Returns true iff the next extent to process is a new or updated one.
+bool ProcessExtentBlockRange(vector<Extent>* extents,
+                             size_t* idx_p,
+                             const bool do_remove,
+                             uint64_t range_start,
+                             uint64_t range_end) {
+  size_t idx = *idx_p;
+  uint64_t start_block = (*extents)[idx].start_block();
+  uint64_t num_blocks = (*extents)[idx].num_blocks();
+  uint64_t range_size = range_end - range_start;
+
+  if (do_remove) {
+    if (range_size == num_blocks) {
+      // Remove the entire extent.
+      extents->erase(extents->begin() + idx);
+    } else if (range_end == num_blocks) {
+      // Trim the end of the extent.
+      (*extents)[idx].set_num_blocks(num_blocks - range_size);
+      idx++;
+    } else if (range_start == 0) {
+      // Trim the head of the extent.
+      (*extents)[idx].set_start_block(start_block + range_size);
+      (*extents)[idx].set_num_blocks(num_blocks - range_size);
+    } else {
+      // Trim the middle, splitting the remainder into two parts.
+      (*extents)[idx].set_num_blocks(range_start);
+      Extent e;
+      e.set_start_block(start_block + range_end);
+      e.set_num_blocks(num_blocks - range_end);
+      idx++;
+      extents->insert(extents->begin() + idx, e);
+    }
+  } else if (range_end == num_blocks) {
+    // Done with this extent.
+    idx++;
+  } else {
+    return false;
+  }
+
+  *idx_p = idx;
+  return true;
+}
+
+// Remove identical corresponding block ranges in |src_extents| and
+// |dst_extents|. Used for preventing moving of blocks onto themselves during
+// MOVE operations. The value of |total_bytes| indicates the actual length of
+// content; this may be slightly less than the total size of blocks, in which
+// case the last block is only partly occupied with data. Returns the total
+// number of bytes removed.
+size_t RemoveIdenticalBlockRanges(vector<Extent>* src_extents,
+                                  vector<Extent>* dst_extents,
+                                  const size_t total_bytes) {
+  size_t src_idx = 0;
+  size_t dst_idx = 0;
+  uint64_t src_offset = 0, dst_offset = 0;
+  size_t removed_bytes = 0, nonfull_block_bytes;
+  bool do_remove = false;
+  while (src_idx < src_extents->size() && dst_idx < dst_extents->size()) {
+    do_remove = ((*src_extents)[src_idx].start_block() + src_offset ==
+                 (*dst_extents)[dst_idx].start_block() + dst_offset);
+
+    uint64_t src_num_blocks = (*src_extents)[src_idx].num_blocks();
+    uint64_t dst_num_blocks = (*dst_extents)[dst_idx].num_blocks();
+    uint64_t min_num_blocks =
+        std::min(src_num_blocks - src_offset, dst_num_blocks - dst_offset);
+    uint64_t prev_src_offset = src_offset;
+    uint64_t prev_dst_offset = dst_offset;
+    src_offset += min_num_blocks;
+    dst_offset += min_num_blocks;
+
+    bool new_src = ProcessExtentBlockRange(
+        src_extents, &src_idx, do_remove, prev_src_offset, src_offset);
+    bool new_dst = ProcessExtentBlockRange(
+        dst_extents, &dst_idx, do_remove, prev_dst_offset, dst_offset);
+    if (new_src) {
+      src_offset = 0;
+    }
+    if (new_dst) {
+      dst_offset = 0;
+    }
+
+    if (do_remove)
+      removed_bytes += min_num_blocks * kBlockSize;
+  }
+
+  // If we removed the last block and this block is only partly used by file
+  // content, deduct the unused portion from the total removed byte count.
+  if (do_remove && (nonfull_block_bytes = total_bytes % kBlockSize))
+    removed_bytes -= kBlockSize - nonfull_block_bytes;
+
+  return removed_bytes;
+}
+
 // Storing a diff operation has more overhead over replace operation in the
 // manifest, we need to store an additional src_sha256_hash which is 32 bytes
 // and not compressible, and also src_extents which could use anywhere from a
@@ -221,11 +318,13 @@
     return;
   }
 
-  if (!ABGenerator::FragmentOperations(
-          version_, &file_aops_, new_part_, blob_file_)) {
-    LOG(ERROR) << "Failed to fragment operations for " << name_;
-    failed_ = true;
-    return;
+  if (!version_.InplaceUpdate()) {
+    if (!ABGenerator::FragmentOperations(
+            version_, &file_aops_, new_part_, blob_file_)) {
+      LOG(ERROR) << "Failed to fragment operations for " << name_;
+      failed_ = true;
+      return;
+    }
   }
 
   LOG(INFO) << "Encoded file " << name_ << " (" << new_extents_blocks_
@@ -250,13 +349,12 @@
   if (old_file_iter != old_files_map.end())
     return old_file_iter->second;
 
-  // No old file matches the new file name. Use a similar file with the
-  // shortest levenshtein distance instead.
+  // No old file match for the new file name, use a similar file with the
+  // shortest levenshtein distance.
   // This works great if the file has version number in it, but even for
   // a completely new file, using a similar file can still help.
-  int min_distance =
-      LevenshteinDistance(new_file_name, old_files_map.begin()->first);
-  const FilesystemInterface::File* old_file = &old_files_map.begin()->second;
+  int min_distance = new_file_name.size();
+  const FilesystemInterface::File* old_file;
   for (const auto& pair : old_files_map) {
     int distance = LevenshteinDistance(new_file_name, pair.first);
     if (distance < min_distance) {
@@ -349,8 +447,12 @@
     // from the same source blocks. At that time, this code can die. -adlr
     FilesystemInterface::File old_file =
         GetOldFile(old_files_map, new_file.name);
-    auto old_file_extents =
-        FilterExtentRanges(old_file.extents, old_zero_blocks);
+    vector<Extent> old_file_extents;
+    if (version.InplaceUpdate())
+      old_file_extents =
+          FilterExtentRanges(old_file.extents, old_visited_blocks);
+    else
+      old_file_extents = FilterExtentRanges(old_file.extents, old_zero_blocks);
     old_visited_blocks.AddExtents(old_file_extents);
 
     file_delta_processors.emplace_back(old_part.path,
@@ -439,6 +541,21 @@
                                            &old_block_ids,
                                            &new_block_ids));
 
+  // If the update is inplace, we map all the blocks that didn't move,
+  // regardless of the contents since they are already copied and no operation
+  // is required.
+  if (version.InplaceUpdate()) {
+    uint64_t num_blocks = std::min(old_num_blocks, new_num_blocks);
+    for (uint64_t block = 0; block < num_blocks; block++) {
+      if (old_block_ids[block] == new_block_ids[block] &&
+          !old_visited_blocks->ContainsBlock(block) &&
+          !new_visited_blocks->ContainsBlock(block)) {
+        old_visited_blocks->AddBlock(block);
+        new_visited_blocks->AddBlock(block);
+      }
+    }
+  }
+
   // A mapping from the block_id to the list of block numbers with that block id
   // in the old partition. This is used to lookup where in the old partition
   // is a block from the new partition.
@@ -485,6 +602,10 @@
     AppendBlockToExtents(&old_identical_blocks,
                          old_blocks_map_it->second.back());
     AppendBlockToExtents(&new_identical_blocks, block);
+    // We can't reuse source blocks in minor version 1 because the cycle
+    // breaking algorithm used in the in-place update doesn't support that.
+    if (version.InplaceUpdate())
+      old_blocks_map_it->second.pop_back();
   }
 
   if (chunk_blocks == -1)
@@ -536,7 +657,9 @@
       aops->emplace_back();
       AnnotatedOperation* aop = &aops->back();
       aop->name = "<identical-blocks>";
-      aop->op.set_type(InstallOperation::SOURCE_COPY);
+      aop->op.set_type(version.OperationAllowed(InstallOperation::SOURCE_COPY)
+                           ? InstallOperation::SOURCE_COPY
+                           : InstallOperation::MOVE);
 
       uint64_t chunk_num_blocks =
           std::min(static_cast<uint64_t>(extent.num_blocks()) - op_block_offset,
@@ -581,11 +704,6 @@
   InstallOperation operation;
 
   uint64_t total_blocks = utils::BlocksInExtents(new_extents);
-  if (chunk_blocks == 0) {
-    LOG(ERROR) << "Invalid number of chunk_blocks. Cannot be 0.";
-    return false;
-  }
-
   if (chunk_blocks == -1)
     chunk_blocks = total_blocks;
 
@@ -614,8 +732,13 @@
 
     // Check if the operation writes nothing.
     if (operation.dst_extents_size() == 0) {
-      LOG(ERROR) << "Empty non-MOVE operation";
-      return false;
+      if (operation.type() == InstallOperation::MOVE) {
+        LOG(INFO) << "Empty MOVE operation (" << name << "), skipping";
+        continue;
+      } else {
+        LOG(ERROR) << "Empty non-MOVE operation";
+        return false;
+      }
     }
 
     // Now, insert into the list of operations.
@@ -705,18 +828,19 @@
 
   // Disable bsdiff, and puffdiff when the data is too big.
   bool bsdiff_allowed =
-      version.OperationAllowed(InstallOperation::SOURCE_BSDIFF);
+      version.OperationAllowed(InstallOperation::SOURCE_BSDIFF) ||
+      version.OperationAllowed(InstallOperation::BSDIFF);
   if (bsdiff_allowed &&
       blocks_to_read * kBlockSize > kMaxBsdiffDestinationSize) {
-    LOG(INFO) << "bsdiff ignored, data too big: " << blocks_to_read * kBlockSize
-              << " bytes";
+    LOG(INFO) << "bsdiff blacklisted, data too big: "
+              << blocks_to_read * kBlockSize << " bytes";
     bsdiff_allowed = false;
   }
 
   bool puffdiff_allowed = version.OperationAllowed(InstallOperation::PUFFDIFF);
   if (puffdiff_allowed &&
       blocks_to_read * kBlockSize > kMaxPuffdiffDestinationSize) {
-    LOG(INFO) << "puffdiff ignored, data too big: "
+    LOG(INFO) << "puffdiff blacklisted, data too big: "
               << blocks_to_read * kBlockSize << " bytes";
     puffdiff_allowed = false;
   }
@@ -754,7 +878,9 @@
                                              kBlockSize));
     if (old_data == new_data) {
       // No change in data.
-      operation.set_type(InstallOperation::SOURCE_COPY);
+      operation.set_type(version.OperationAllowed(InstallOperation::SOURCE_COPY)
+                             ? InstallOperation::SOURCE_COPY
+                             : InstallOperation::MOVE);
       data_blob = brillo::Blob();
     } else if (IsDiffOperationBetter(
                    operation, data_blob.size(), 0, src_extents.size())) {
@@ -766,7 +892,7 @@
         ScopedPathUnlinker unlinker(patch.value());
 
         std::unique_ptr<bsdiff::PatchWriterInterface> bsdiff_patch_writer;
-        InstallOperation::Type operation_type = InstallOperation::SOURCE_BSDIFF;
+        InstallOperation::Type operation_type = InstallOperation::BSDIFF;
         if (version.OperationAllowed(InstallOperation::BROTLI_BSDIFF)) {
           bsdiff_patch_writer =
               bsdiff::CreateBSDF2PatchWriter(patch.value(),
@@ -775,6 +901,9 @@
           operation_type = InstallOperation::BROTLI_BSDIFF;
         } else {
           bsdiff_patch_writer = bsdiff::CreateBsdiffPatchWriter(patch.value());
+          if (version.OperationAllowed(InstallOperation::SOURCE_BSDIFF)) {
+            operation_type = InstallOperation::SOURCE_BSDIFF;
+          }
         }
 
         brillo::Blob bsdiff_delta;
@@ -822,13 +951,17 @@
         // Only Puffdiff if both files have at least one deflate left.
         if (!src_deflates.empty() && !dst_deflates.empty()) {
           brillo::Blob puffdiff_delta;
-          ScopedTempFile temp_file("puffdiff-delta.XXXXXX");
+          string temp_file_path;
+          TEST_AND_RETURN_FALSE(utils::MakeTempFile(
+              "puffdiff-delta.XXXXXX", &temp_file_path, nullptr));
+          ScopedPathUnlinker temp_file_unlinker(temp_file_path);
+
           // 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,
@@ -843,14 +976,23 @@
     }
   }
 
+  // Remove identical src/dst block ranges in MOVE operations.
+  if (operation.type() == InstallOperation::MOVE) {
+    auto removed_bytes =
+        RemoveIdenticalBlockRanges(&src_extents, &dst_extents, new_data.size());
+    operation.set_src_length(old_data.size() - removed_bytes);
+    operation.set_dst_length(new_data.size() - removed_bytes);
+  }
+
   // WARNING: We always set legacy |src_length| and |dst_length| fields for
   // BSDIFF. For SOURCE_BSDIFF we only set them for minor version 3 and
   // lower. This is needed because we used to use these two parameters in the
   // SOURCE_BSDIFF for minor version 3 and lower, but we do not need them
   // anymore in higher minor versions. This means if we stop adding these
   // parameters for those minor versions, the delta payloads will be invalid.
-  if (operation.type() == InstallOperation::SOURCE_BSDIFF &&
-      version.minor <= kOpSrcHashMinorPayloadVersion) {
+  if (operation.type() == InstallOperation::BSDIFF ||
+      (operation.type() == InstallOperation::SOURCE_BSDIFF &&
+       version.minor <= kOpSrcHashMinorPayloadVersion)) {
     operation.set_src_length(old_data.size());
     operation.set_dst_length(new_data.size());
   }
@@ -879,6 +1021,22 @@
           op_type == InstallOperation::DISCARD);
 }
 
+// Returns true if |op| is a no-op operation that doesn't do any useful work
+// (e.g., a move operation that copies blocks onto themselves).
+bool IsNoopOperation(const InstallOperation& op) {
+  return (op.type() == InstallOperation::MOVE &&
+          ExpandExtents(op.src_extents()) == ExpandExtents(op.dst_extents()));
+}
+
+void FilterNoopOperations(vector<AnnotatedOperation>* ops) {
+  ops->erase(std::remove_if(ops->begin(),
+                            ops->end(),
+                            [](const AnnotatedOperation& aop) {
+                              return IsNoopOperation(aop.op);
+                            }),
+             ops->end());
+}
+
 bool InitializePartitionInfo(const PartitionConfig& part, PartitionInfo* info) {
   info->set_size(part.size);
   HashCalculator hasher;
@@ -934,7 +1092,7 @@
   if (magic != EXT2_SUPER_MAGIC)
     return false;
 
-  // Validation check the parameters.
+  // Sanity check the parameters.
   TEST_AND_RETURN_FALSE(log_block_size >= EXT2_MIN_BLOCK_LOG_SIZE &&
                         log_block_size <= EXT2_MAX_BLOCK_LOG_SIZE);
   TEST_AND_RETURN_FALSE(block_count > 0);
diff --git a/payload_generator/delta_diff_utils.h b/payload_generator/delta_diff_utils.h
index c75d16d..2211b30 100644
--- a/payload_generator/delta_diff_utils.h
+++ b/payload_generator/delta_diff_utils.h
@@ -127,6 +127,14 @@
 // Returns true if an operation with type |op_type| has no |src_extents|.
 bool IsNoSourceOperation(InstallOperation::Type op_type);
 
+// Returns true if |op| is a no-op operation that doesn't do any useful work
+// (e.g., a move operation that copies blocks onto themselves).
+bool IsNoopOperation(const InstallOperation& op);
+
+// Filters all the operations that are no-op, maintaining the relative order
+// of the rest of the operations.
+void FilterNoopOperations(std::vector<AnnotatedOperation>* ops);
+
 bool InitializePartitionInfo(const PartitionConfig& partition,
                              PartitionInfo* info);
 
diff --git a/payload_generator/delta_diff_utils_unittest.cc b/payload_generator/delta_diff_utils_unittest.cc
index f2db1bd..b2950e8 100644
--- a/payload_generator/delta_diff_utils_unittest.cc
+++ b/payload_generator/delta_diff_utils_unittest.cc
@@ -69,12 +69,13 @@
 // Create a fake filesystem of the given |size| and initialize the partition
 // holding it in the PartitionConfig |part|.
 void CreatePartition(PartitionConfig* part,
-                     ScopedTempFile* part_file,
+                     const string& pattern,
                      uint64_t block_size,
                      off_t size) {
-  part->path = part_file->path();
-  ASSERT_EQ(0, ftruncate(part_file->fd(), size));
-  part_file->CloseFd();
+  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->fs_interface.reset(new FakeFilesystem(block_size, size / block_size));
   part->size = size;
 }
@@ -111,21 +112,31 @@
 
   void SetUp() override {
     CreatePartition(&old_part_,
-                    &old_part_file_,
+                    "DeltaDiffUtilsTest-old_part-XXXXXX",
                     block_size_,
                     block_size_ * kDefaultBlockCount);
     CreatePartition(&new_part_,
-                    &new_part_file_,
+                    "DeltaDiffUtilsTest-old_part-XXXXXX",
                     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(tmp_blob_file_.fd(), &blob_size_);
-    PayloadVersion version(kBrilloMajorPayloadVersion, minor_version);
+    BlobFileWriter blob_file(blob_fd_, &blob_size_);
+    PayloadVersion version(kChromeOSMajorPayloadVersion, minor_version);
     ExtentRanges old_zero_blocks;
     return diff_utils::DeltaMovedAndZeroBlocks(&aops_,
                                                old_part_.path,
@@ -144,11 +155,10 @@
   // 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.
-  ScopedTempFile tmp_blob_file_{"DeltaDiffUtilsTest-blob-XXXXXX", true};
+  string blob_path_;
+  int blob_fd_{-1};
   off_t blob_size_{0};
 
   size_t block_size_{kBlockSize};
@@ -163,7 +173,7 @@
   new_part_.verity.hash_tree_extent = ExtentForRange(20, 30);
   new_part_.verity.fec_extent = ExtentForRange(40, 50);
 
-  BlobFileWriter blob_file(tmp_blob_file_.fd(), &blob_size_);
+  BlobFileWriter blob_file(blob_fd_, &blob_size_);
   EXPECT_TRUE(diff_utils::DeltaReadPartition(
       &aops_,
       old_part_,
@@ -184,6 +194,164 @@
   }
 }
 
+TEST_F(DeltaDiffUtilsTest, MoveSmallTest) {
+  brillo::Blob data_blob(block_size_);
+  test_utils::FillWithData(&data_blob);
+
+  // The old file is on a different block than the new one.
+  vector<Extent> old_extents = {ExtentForRange(11, 1)};
+  vector<Extent> new_extents = {ExtentForRange(1, 1)};
+
+  EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
+  EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
+
+  brillo::Blob data;
+  InstallOperation op;
+  EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
+      old_part_.path,
+      new_part_.path,
+      old_extents,
+      new_extents,
+      {},  // old_deflates
+      {},  // new_deflates
+      PayloadVersion(kChromeOSMajorPayloadVersion, kInPlaceMinorPayloadVersion),
+      &data,
+      &op));
+  EXPECT_TRUE(data.empty());
+
+  EXPECT_TRUE(op.has_type());
+  EXPECT_EQ(InstallOperation::MOVE, op.type());
+  EXPECT_FALSE(op.has_data_offset());
+  EXPECT_FALSE(op.has_data_length());
+  EXPECT_EQ(1, op.src_extents_size());
+  EXPECT_EQ(kBlockSize, op.src_length());
+  EXPECT_EQ(1, op.dst_extents_size());
+  EXPECT_EQ(kBlockSize, op.dst_length());
+  EXPECT_EQ(utils::BlocksInExtents(op.src_extents()),
+            utils::BlocksInExtents(op.dst_extents()));
+  EXPECT_EQ(1U, utils::BlocksInExtents(op.dst_extents()));
+}
+
+TEST_F(DeltaDiffUtilsTest, MoveWithSameBlock) {
+  // Setup the old/new files so that it has immobile chunks; we make sure to
+  // utilize all sub-cases of such chunks: blocks 21--22 induce a split (src)
+  // and complete removal (dst), whereas blocks 24--25 induce trimming of the
+  // tail (src) and head (dst) of extents. The final block (29) is used for
+  // ensuring we properly account for the number of bytes removed in cases where
+  // the last block is partly filled. The detailed configuration:
+  //
+  // Old:  [ 20     21 22     23     24 25 ] [ 28     29 ]
+  // New:  [ 18 ] [ 21 22 ] [ 20 ] [ 24 25     26 ] [ 29 ]
+  // Same:          ^^ ^^            ^^ ^^            ^^
+  vector<Extent> old_extents = {ExtentForRange(20, 6), ExtentForRange(28, 2)};
+  vector<Extent> new_extents = {ExtentForRange(18, 1),
+                                ExtentForRange(21, 2),
+                                ExtentForRange(20, 1),
+                                ExtentForRange(24, 3),
+                                ExtentForRange(29, 1)};
+
+  uint64_t num_blocks = utils::BlocksInExtents(old_extents);
+  EXPECT_EQ(num_blocks, utils::BlocksInExtents(new_extents));
+
+  // The size of the data should match the total number of blocks. Each block
+  // has a different content.
+  brillo::Blob file_data;
+  for (uint64_t i = 0; i < num_blocks; ++i) {
+    file_data.resize(file_data.size() + kBlockSize, 'a' + i);
+  }
+
+  EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, file_data));
+  EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, file_data));
+
+  brillo::Blob data;
+  InstallOperation op;
+  EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
+      old_part_.path,
+      new_part_.path,
+      old_extents,
+      new_extents,
+      {},  // old_deflates
+      {},  // new_deflates
+      PayloadVersion(kChromeOSMajorPayloadVersion, kInPlaceMinorPayloadVersion),
+      &data,
+      &op));
+
+  EXPECT_TRUE(data.empty());
+
+  EXPECT_TRUE(op.has_type());
+  EXPECT_EQ(InstallOperation::MOVE, op.type());
+  EXPECT_FALSE(op.has_data_offset());
+  EXPECT_FALSE(op.has_data_length());
+
+  // The expected old and new extents that actually moved. See comment above.
+  old_extents = {
+      ExtentForRange(20, 1), ExtentForRange(23, 1), ExtentForRange(28, 1)};
+  new_extents = {
+      ExtentForRange(18, 1), ExtentForRange(20, 1), ExtentForRange(26, 1)};
+  num_blocks = utils::BlocksInExtents(old_extents);
+
+  EXPECT_EQ(num_blocks * kBlockSize, op.src_length());
+  EXPECT_EQ(num_blocks * kBlockSize, op.dst_length());
+
+  EXPECT_EQ(old_extents.size(), static_cast<size_t>(op.src_extents_size()));
+  for (int i = 0; i < op.src_extents_size(); i++) {
+    EXPECT_EQ(old_extents[i].start_block(), op.src_extents(i).start_block())
+        << "i == " << i;
+    EXPECT_EQ(old_extents[i].num_blocks(), op.src_extents(i).num_blocks())
+        << "i == " << i;
+  }
+
+  EXPECT_EQ(new_extents.size(), static_cast<size_t>(op.dst_extents_size()));
+  for (int i = 0; i < op.dst_extents_size(); i++) {
+    EXPECT_EQ(new_extents[i].start_block(), op.dst_extents(i).start_block())
+        << "i == " << i;
+    EXPECT_EQ(new_extents[i].num_blocks(), op.dst_extents(i).num_blocks())
+        << "i == " << i;
+  }
+}
+
+TEST_F(DeltaDiffUtilsTest, BsdiffSmallTest) {
+  // Test a BSDIFF operation from block 1 to block 2.
+  brillo::Blob data_blob(kBlockSize);
+  test_utils::FillWithData(&data_blob);
+
+  // The old file is on a different block than the new one.
+  vector<Extent> old_extents = {ExtentForRange(1, 1)};
+  vector<Extent> new_extents = {ExtentForRange(2, 1)};
+
+  EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
+  // Modify one byte in the new file.
+  data_blob[0]++;
+  EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
+
+  brillo::Blob data;
+  InstallOperation op;
+  EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
+      old_part_.path,
+      new_part_.path,
+      old_extents,
+      new_extents,
+      {},  // old_deflates
+      {},  // new_deflates
+      PayloadVersion(kChromeOSMajorPayloadVersion, kInPlaceMinorPayloadVersion),
+      &data,
+      &op));
+
+  EXPECT_FALSE(data.empty());
+
+  EXPECT_TRUE(op.has_type());
+  EXPECT_EQ(InstallOperation::BSDIFF, op.type());
+  EXPECT_FALSE(op.has_data_offset());
+  EXPECT_FALSE(op.has_data_length());
+  EXPECT_EQ(1, op.src_extents_size());
+  EXPECT_EQ(kBlockSize, op.src_length());
+  EXPECT_EQ(1, op.dst_extents_size());
+  EXPECT_EQ(kBlockSize, op.dst_length());
+  EXPECT_EQ(utils::BlocksInExtents(op.src_extents()),
+            utils::BlocksInExtents(op.dst_extents()));
+  EXPECT_EQ(1U, utils::BlocksInExtents(op.dst_extents()));
+}
+
 TEST_F(DeltaDiffUtilsTest, ReplaceSmallTest) {
   // The old file is on a different block than the new one.
   vector<Extent> old_extents = {ExtentForRange(1, 1)};
@@ -215,7 +383,8 @@
         new_extents,
         {},  // old_deflates
         {},  // new_deflates
-        PayloadVersion(kBrilloMajorPayloadVersion, kSourceMinorPayloadVersion),
+        PayloadVersion(kChromeOSMajorPayloadVersion,
+                       kInPlaceMinorPayloadVersion),
         &data,
         &op));
     EXPECT_FALSE(data.empty());
@@ -257,7 +426,7 @@
       new_extents,
       {},  // old_deflates
       {},  // new_deflates
-      PayloadVersion(kBrilloMajorPayloadVersion, kSourceMinorPayloadVersion),
+      PayloadVersion(kChromeOSMajorPayloadVersion, kSourceMinorPayloadVersion),
       &data,
       &op));
   EXPECT_TRUE(data.empty());
@@ -291,7 +460,7 @@
       new_extents,
       {},  // old_deflates
       {},  // new_deflates
-      PayloadVersion(kBrilloMajorPayloadVersion, kSourceMinorPayloadVersion),
+      PayloadVersion(kChromeOSMajorPayloadVersion, kSourceMinorPayloadVersion),
       &data,
       &op));
 
@@ -331,6 +500,49 @@
   EXPECT_EQ(InstallOperation::REPLACE_BZ, op.type());
 }
 
+TEST_F(DeltaDiffUtilsTest, IsNoopOperationTest) {
+  InstallOperation op;
+  op.set_type(InstallOperation::REPLACE_BZ);
+  EXPECT_FALSE(diff_utils::IsNoopOperation(op));
+  op.set_type(InstallOperation::MOVE);
+  EXPECT_TRUE(diff_utils::IsNoopOperation(op));
+  *(op.add_src_extents()) = ExtentForRange(3, 2);
+  *(op.add_dst_extents()) = ExtentForRange(3, 2);
+  EXPECT_TRUE(diff_utils::IsNoopOperation(op));
+  *(op.add_src_extents()) = ExtentForRange(7, 5);
+  *(op.add_dst_extents()) = ExtentForRange(7, 5);
+  EXPECT_TRUE(diff_utils::IsNoopOperation(op));
+  *(op.add_src_extents()) = ExtentForRange(20, 2);
+  *(op.add_dst_extents()) = ExtentForRange(20, 1);
+  *(op.add_dst_extents()) = ExtentForRange(21, 1);
+  EXPECT_TRUE(diff_utils::IsNoopOperation(op));
+  *(op.add_src_extents()) = ExtentForRange(24, 1);
+  *(op.add_dst_extents()) = ExtentForRange(25, 1);
+  EXPECT_FALSE(diff_utils::IsNoopOperation(op));
+}
+
+TEST_F(DeltaDiffUtilsTest, FilterNoopOperations) {
+  AnnotatedOperation aop1;
+  aop1.op.set_type(InstallOperation::REPLACE_BZ);
+  *(aop1.op.add_dst_extents()) = ExtentForRange(3, 2);
+  aop1.name = "aop1";
+
+  AnnotatedOperation aop2 = aop1;
+  aop2.name = "aop2";
+
+  AnnotatedOperation noop;
+  noop.op.set_type(InstallOperation::MOVE);
+  *(noop.op.add_src_extents()) = ExtentForRange(3, 2);
+  *(noop.op.add_dst_extents()) = ExtentForRange(3, 2);
+  noop.name = "noop";
+
+  vector<AnnotatedOperation> ops = {noop, aop1, noop, noop, aop2, noop};
+  diff_utils::FilterNoopOperations(&ops);
+  EXPECT_EQ(2u, ops.size());
+  EXPECT_EQ("aop1", ops[0].name);
+  EXPECT_EQ("aop2", ops[1].name);
+}
+
 // Test the simple case where all the blocks are different and no new blocks are
 // zeroed.
 TEST_F(DeltaDiffUtilsTest, NoZeroedOrUniqueBlocksDetected) {
@@ -338,7 +550,7 @@
   InitializePartitionWithUniqueBlocks(new_part_, block_size_, 42);
 
   EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1,  // chunk_blocks
-                                         kSourceMinorPayloadVersion));
+                                         kInPlaceMinorPayloadVersion));
 
   EXPECT_EQ(0U, old_visited_blocks_.blocks());
   EXPECT_EQ(0U, new_visited_blocks_.blocks());
@@ -346,6 +558,29 @@
   EXPECT_TRUE(aops_.empty());
 }
 
+// Test that when the partitions have identical blocks in the same positions no
+// MOVE operation is performed and all the blocks are handled.
+TEST_F(DeltaDiffUtilsTest, IdenticalPartitionsDontMove) {
+  InitializePartitionWithUniqueBlocks(old_part_, block_size_, 42);
+  InitializePartitionWithUniqueBlocks(new_part_, block_size_, 42);
+
+  // Mark some of the blocks as already visited.
+  vector<Extent> already_visited = {ExtentForRange(5, 10),
+                                    ExtentForRange(25, 10)};
+  old_visited_blocks_.AddExtents(already_visited);
+  new_visited_blocks_.AddExtents(already_visited);
+
+  // Most of the blocks rest in the same place, but there's no need for MOVE
+  // operations on those blocks.
+  EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1,  // chunk_blocks
+                                         kInPlaceMinorPayloadVersion));
+
+  EXPECT_EQ(kDefaultBlockCount, old_visited_blocks_.blocks());
+  EXPECT_EQ(kDefaultBlockCount, new_visited_blocks_.blocks());
+  EXPECT_EQ(0, blob_size_);
+  EXPECT_TRUE(aops_.empty());
+}
+
 // Test that when the partitions have identical blocks in the same positions
 // MOVE operation is performed and all the blocks are handled.
 TEST_F(DeltaDiffUtilsTest, IdenticalBlocksAreCopiedFromSource) {
@@ -466,14 +701,16 @@
   EXPECT_TRUE(WriteExtents(old_part_.path, old_zeros, block_size_, zeros_data));
 
   EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(5,  // chunk_blocks
-                                         kSourceMinorPayloadVersion));
+                                         kInPlaceMinorPayloadVersion));
 
-  // Zeroed blocks from |old_visited_blocks_| were copied over.
+  // Zeroed blocks from old_visited_blocks_ were copied over, so me actually
+  // use them regardless of the trivial MOVE operation not being emitted.
   EXPECT_EQ(old_zeros,
             old_visited_blocks_.GetExtentsForBlockCount(
                 old_visited_blocks_.blocks()));
 
-  // All the new zeroed blocks should be used with REPLACE_BZ.
+  // All the new zeroed blocks should be used, part with REPLACE_BZ and part
+  // trivial MOVE operations (not included).
   EXPECT_EQ(new_zeros,
             new_visited_blocks_.GetExtentsForBlockCount(
                 new_visited_blocks_.blocks()));
@@ -484,8 +721,7 @@
       // This range should be split.
       ExtentForRange(30, 5),
       ExtentForRange(35, 5),
-      ExtentForRange(40, 5),
-      ExtentForRange(45, 5),
+      ExtentForRange(40, 3),
   };
 
   EXPECT_EQ(expected_op_extents.size(), aops_.size());
@@ -585,8 +821,6 @@
       "update_engine");
   EXPECT_EQ(diff_utils::GetOldFile(old_files_map, "bin/delta_generator").name,
             "delta_generator");
-  // Check file name with minimum size.
-  EXPECT_EQ(diff_utils::GetOldFile(old_files_map, "a").name, "filename");
 }
 
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/ext2_filesystem_unittest.cc b/payload_generator/ext2_filesystem_unittest.cc
index 88e1538..54600e9 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) {
-  ScopedTempFile fs_filename_{"Ext2FilesystemTest-XXXXXX"};
+  test_utils::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.cc b/payload_generator/extent_ranges.cc
index 2098639..0e3f087 100644
--- a/payload_generator/extent_ranges.cc
+++ b/payload_generator/extent_ranges.cc
@@ -27,6 +27,7 @@
 #include "update_engine/payload_consumer/payload_constants.h"
 #include "update_engine/payload_generator/extent_utils.h"
 
+using std::set;
 using std::vector;
 
 namespace chromeos_update_engine {
@@ -202,15 +203,6 @@
   }
 }
 
-bool ExtentRanges::OverlapsWithExtent(const Extent& extent) const {
-  for (const auto& entry : extent_set_) {
-    if (ExtentsOverlap(entry, extent)) {
-      return true;
-    }
-  }
-  return false;
-}
-
 bool ExtentRanges::ContainsBlock(uint64_t block) const {
   auto lower = extent_set_.lower_bound(ExtentForRange(block, 1));
   // The block could be on the extent before the one in |lower|.
diff --git a/payload_generator/extent_ranges.h b/payload_generator/extent_ranges.h
index 68aa27f..62ffff4 100644
--- a/payload_generator/extent_ranges.h
+++ b/payload_generator/extent_ranges.h
@@ -63,9 +63,6 @@
   void AddRanges(const ExtentRanges& ranges);
   void SubtractRanges(const ExtentRanges& ranges);
 
-  // Returns true if the input extent overlaps with the current ExtentRanges.
-  bool OverlapsWithExtent(const Extent& extent) const;
-
   // Returns whether the block |block| is in this ExtentRange.
   bool ContainsBlock(uint64_t block) const;
 
diff --git a/payload_generator/extent_ranges_unittest.cc b/payload_generator/extent_ranges_unittest.cc
index f55bb73..2bcffed 100644
--- a/payload_generator/extent_ranges_unittest.cc
+++ b/payload_generator/extent_ranges_unittest.cc
@@ -18,7 +18,6 @@
 
 #include <vector>
 
-#include <base/stl_util.h>
 #include <gtest/gtest.h>
 
 #include "update_engine/common/test_utils.h"
@@ -52,9 +51,9 @@
   }
 }
 
-#define EXPECT_RANGE_EQ(ranges, var)                       \
-  do {                                                     \
-    ExpectRangeEq(ranges, var, base::size(var), __LINE__); \
+#define EXPECT_RANGE_EQ(ranges, var)                      \
+  do {                                                    \
+    ExpectRangeEq(ranges, var, arraysize(var), __LINE__); \
   } while (0)
 
 void ExpectRangesOverlapOrTouch(uint64_t a_start,
diff --git a/payload_generator/extent_utils.cc b/payload_generator/extent_utils.cc
index 2efef12..c0c7643 100644
--- a/payload_generator/extent_utils.cc
+++ b/payload_generator/extent_utils.cc
@@ -155,10 +155,4 @@
   return a.start_block() == b.start_block() && a.num_blocks() == b.num_blocks();
 }
 
-std::ostream& operator<<(std::ostream& out, const Extent& extent) {
-  out << "[" << extent.start_block() << " - "
-      << extent.start_block() + extent.num_blocks() - 1 << "]";
-  return out;
-}
-
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/extent_utils.h b/payload_generator/extent_utils.h
index 7aa614a..9763b1f 100644
--- a/payload_generator/extent_utils.h
+++ b/payload_generator/extent_utils.h
@@ -20,8 +20,6 @@
 #include <string>
 #include <vector>
 
-#include <base/logging.h>
-
 #include "update_engine/payload_consumer/payload_constants.h"
 #include "update_engine/update_metadata.pb.h"
 
@@ -85,45 +83,6 @@
 
 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;
-};
-
-std::ostream& operator<<(std::ostream& out, const Extent& extent);
-
 }  // namespace chromeos_update_engine
 
 #endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_EXTENT_UTILS_H_
diff --git a/payload_generator/filesystem_interface.h b/payload_generator/filesystem_interface.h
index 05d387f..d04295c 100644
--- a/payload_generator/filesystem_interface.h
+++ b/payload_generator/filesystem_interface.h
@@ -62,13 +62,6 @@
     // indicating the starting block, and the number of consecutive blocks.
     std::vector<Extent> extents;
 
-    // If true, the file is already compressed on the disk, so we don't need to
-    // parse it again for deflates. For example, image .gz files inside a
-    // compressed SquashFS image. They might have already been compressed by the
-    // mksquashfs, so we can't really parse the file and look for deflate
-    // compressed parts anymore.
-    bool is_compressed = false;
-
     // All the deflate locations in the file. These locations are not relative
     // to the extents. They are relative to the file system itself.
     std::vector<puffin::BitExtent> deflates;
diff --git a/payload_generator/full_update_generator.cc b/payload_generator/full_update_generator.cc
index 4a5f63a..94a43ab 100644
--- a/payload_generator/full_update_generator.cc
+++ b/payload_generator/full_update_generator.cc
@@ -153,7 +153,7 @@
   aops->resize(num_chunks);
   vector<ChunkProcessor> chunk_processors;
   chunk_processors.reserve(num_chunks);
-  blob_file->IncTotalBlobs(num_chunks);
+  blob_file->SetTotalBlobs(num_chunks);
 
   for (size_t i = 0; i < num_chunks; ++i) {
     size_t start_block = i * chunk_blocks;
@@ -187,6 +187,9 @@
     thread_pool.AddWork(&processor);
   thread_pool.JoinAll();
 
+  // All the work done, disable logging.
+  blob_file->SetTotalBlobs(0);
+
   // All the operations must have a type set at this point. Otherwise, a
   // ChunkProcessor failed to complete.
   for (const AnnotatedOperation& aop : *aops) {
diff --git a/payload_generator/full_update_generator_unittest.cc b/payload_generator/full_update_generator_unittest.cc
index d3b3491..e398125 100644
--- a/payload_generator/full_update_generator_unittest.cc
+++ b/payload_generator/full_update_generator_unittest.cc
@@ -41,9 +41,11 @@
     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_writer_.reset(
-        new BlobFileWriter(blob_file_.fd(), &out_blobs_length_));
+    blob_file_.reset(new BlobFileWriter(out_blobs_fd_, &out_blobs_length_));
+    out_blobs_unlinker_.reset(new ScopedPathUnlinker(out_blobs_path_));
   }
 
   PayloadGenerationConfig config_;
@@ -52,11 +54,14 @@
   vector<AnnotatedOperation> aops;
 
   // Output file holding the payload blobs.
+  string out_blobs_path_;
+  int out_blobs_fd_{-1};
   off_t out_blobs_length_{0};
-  ScopedTempFile part_file_{"FullUpdateTest_partition.XXXXXX"};
+  ScopedFdCloser out_blobs_fd_closer_{&out_blobs_fd_};
+  test_utils::ScopedTempFile part_file_{"FullUpdateTest_partition.XXXXXX"};
 
-  ScopedTempFile blob_file_{"FullUpdateTest_blobs.XXXXXX", true};
-  std::unique_ptr<BlobFileWriter> blob_file_writer_;
+  std::unique_ptr<BlobFileWriter> blob_file_;
+  std::unique_ptr<ScopedPathUnlinker> out_blobs_unlinker_;
 
   // FullUpdateGenerator under test.
   FullUpdateGenerator generator_;
@@ -72,7 +77,7 @@
   EXPECT_TRUE(generator_.GenerateOperations(config_,
                                             new_part_conf,  // this is ignored
                                             new_part_conf,
-                                            blob_file_writer_.get(),
+                                            blob_file_.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()));
@@ -85,7 +90,7 @@
     EXPECT_EQ(config_.hard_chunk_size / config_.block_size,
               aops[i].op.dst_extents(0).num_blocks());
     if (aops[i].op.type() != InstallOperation::REPLACE) {
-      EXPECT_EQ(InstallOperation::REPLACE_XZ, aops[i].op.type());
+      EXPECT_EQ(InstallOperation::REPLACE_BZ, aops[i].op.type());
     }
   }
 }
@@ -103,7 +108,7 @@
   EXPECT_TRUE(generator_.GenerateOperations(config_,
                                             new_part_conf,  // this is ignored
                                             new_part_conf,
-                                            blob_file_writer_.get(),
+                                            blob_file_.get(),
                                             &aops));
   // new_part has one chunk and a half.
   EXPECT_EQ(2U, aops.size());
@@ -124,7 +129,7 @@
   EXPECT_TRUE(generator_.GenerateOperations(config_,
                                             new_part_conf,  // this is ignored
                                             new_part_conf,
-                                            blob_file_writer_.get(),
+                                            blob_file_.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 b04fec0..f035ff1 100644
--- a/payload_generator/generate_delta_main.cc
+++ b/payload_generator/generate_delta_main.cc
@@ -14,34 +14,30 @@
 // limitations under the License.
 //
 
-#include <map>
 #include <string>
 #include <vector>
 
-#include <base/bind.h>
 #include <base/files/file_path.h>
 #include <base/files/file_util.h>
 #include <base/logging.h>
 #include <base/strings/string_number_conversions.h>
 #include <base/strings/string_split.h>
-#include <base/strings/string_util.h>
 #include <brillo/flag_helper.h>
 #include <brillo/key_value_store.h>
 #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"
 #include "update_engine/payload_generator/payload_generation_config.h"
-#include "update_engine/payload_generator/payload_properties.h"
 #include "update_engine/payload_generator/payload_signer.h"
 #include "update_engine/payload_generator/xz.h"
 #include "update_engine/update_metadata.pb.h"
@@ -50,7 +46,6 @@
 // and an output file as arguments and the path to an output file and
 // generates a delta that can be sent to Chrome OS clients.
 
-using std::map;
 using std::string;
 using std::vector;
 
@@ -58,9 +53,6 @@
 
 namespace {
 
-constexpr char kPayloadPropertiesFormatKeyValue[] = "key-value";
-constexpr char kPayloadPropertiesFormatJson[] = "json";
-
 void ParseSignatureSizes(const string& signature_sizes_flag,
                          vector<size_t>* signature_sizes) {
   signature_sizes->clear();
@@ -75,6 +67,38 @@
   }
 }
 
+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,
@@ -183,11 +207,8 @@
   install_plan.source_slot =
       config.is_delta ? 0 : BootControlInterface::kInvalidSlot;
   install_plan.target_slot = 1;
-  // 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.type =
+      config.is_delta ? 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.
@@ -224,10 +245,11 @@
       std::make_unique<DownloadAction>(&prefs,
                                        &fake_boot_control,
                                        &fake_hardware,
+                                       nullptr,
                                        new FileFetcher(),
                                        true /* interactive */);
-  auto filesystem_verifier_action = std::make_unique<FilesystemVerifierAction>(
-      fake_boot_control.GetDynamicPartitionControl());
+  auto filesystem_verifier_action =
+      std::make_unique<FilesystemVerifierAction>();
 
   BondActions(install_plan_action.get(), download_action.get());
   BondActions(download_action.get(), filesystem_verifier_action.get());
@@ -237,9 +259,7 @@
   processor.EnqueueAction(std::move(install_plan_action));
   processor.EnqueueAction(std::move(download_action));
   processor.EnqueueAction(std::move(filesystem_verifier_action));
-  loop.PostTask(FROM_HERE,
-                base::Bind(&ActionProcessor::StartProcessing,
-                           base::Unretained(&processor)));
+  processor.StartProcessing();
   loop.Run();
   CHECK_EQ(delegate.code_, ErrorCode::kSuccess);
   LOG(INFO) << "Completed applying " << (config.is_delta ? "delta" : "full")
@@ -247,62 +267,19 @@
   return true;
 }
 
-bool ExtractProperties(const string& payload_path,
-                       const string& props_file,
-                       const string& props_format) {
-  string properties;
-  PayloadProperties payload_props(payload_path);
-  if (props_format == kPayloadPropertiesFormatKeyValue) {
-    TEST_AND_RETURN_FALSE(payload_props.GetPropertiesAsKeyValue(&properties));
-  } else if (props_format == kPayloadPropertiesFormatJson) {
-    TEST_AND_RETURN_FALSE(payload_props.GetPropertiesAsJson(&properties));
-  } else {
-    LOG(FATAL) << "Invalid option " << props_format
-               << " for --properties_format flag.";
-  }
+int ExtractProperties(const string& payload_path, const string& props_file) {
+  brillo::KeyValueStore properties;
+  TEST_AND_RETURN_FALSE(
+      PayloadSigner::ExtractPayloadProperties(payload_path, &properties));
   if (props_file == "-") {
-    printf("%s", properties.c_str());
+    printf("%s", properties.SaveToString().c_str());
   } else {
-    utils::WriteFile(
-        props_file.c_str(), properties.c_str(), properties.length());
+    properties.Save(base::FilePath(props_file));
     LOG(INFO) << "Generated properties file at " << props_file;
   }
   return true;
 }
 
-template <typename Key, typename Val>
-string ToString(const map<Key, Val>& map) {
-  vector<string> result;
-  result.reserve(map.size());
-  for (const auto& it : map) {
-    result.emplace_back(it.first + ": " + it.second);
-  }
-  return "{" + base::JoinString(result, ",") + "}";
-}
-
-bool ParsePerPartitionTimestamps(const string& partition_timestamps,
-                                 PayloadGenerationConfig* config) {
-  base::StringPairs pairs;
-  CHECK(base::SplitStringIntoKeyValuePairs(
-      partition_timestamps, ':', ',', &pairs))
-      << "--partition_timestamps accepts commad "
-         "separated pairs. e.x. system:1234,vendor:5678";
-  map<string, string> partition_timestamps_map{
-      std::move_iterator(pairs.begin()), std::move_iterator(pairs.end())};
-  for (auto&& partition : config->target.partitions) {
-    auto&& it = partition_timestamps_map.find(partition.name);
-    if (it != partition_timestamps_map.end()) {
-      partition.version = std::move(it->second);
-      partition_timestamps_map.erase(it);
-    }
-  }
-  if (!partition_timestamps_map.empty()) {
-    LOG(ERROR) << "Unused timestamps: " << ToString(partition_timestamps_map);
-    return false;
-  }
-  return true;
-}
-
 int Main(int argc, char** argv) {
   DEFINE_string(old_image, "", "Path to the old rootfs");
   DEFINE_string(new_image, "", "Path to the new rootfs");
@@ -384,21 +361,57 @@
   DEFINE_string(properties_file,
                 "",
                 "If passed, dumps the payload properties of the payload passed "
-                "in --in_file and exits. Look at --properties_format.");
-  DEFINE_string(properties_format,
-                kPayloadPropertiesFormatKeyValue,
-                "Defines the format of the --properties_file. The acceptable "
-                "values are: key-value (default) and json");
+                "in --in_file and exits.");
   DEFINE_int64(max_timestamp,
                0,
                "The maximum timestamp of the OS allowed to apply this "
                "payload.");
-  DEFINE_string(
-      partition_timestamps,
-      "",
-      "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. "
@@ -410,23 +423,10 @@
   DEFINE_bool(disable_fec_computation,
               false,
               "Disables the fec data computation on device.");
-  DEFINE_bool(disable_verity_computation,
-              false,
-              "Disables the verity data computation on device.");
   DEFINE_string(
       out_maximum_signature_size_file,
       "",
       "Path to the output maximum signature size given a private key.");
-  DEFINE_bool(is_partial_update,
-              false,
-              "The payload only targets a subset of partitions on the device,"
-              "e.g. generic kernel image update.");
-  DEFINE_bool(
-      disable_vabc,
-      false,
-      "Whether to disable Virtual AB Compression when installing the OTA");
-  DEFINE_string(
-      apex_info_file, "", "Path to META/apex_info.pb found in target build");
 
   brillo::FlagHelper::Init(
       argc,
@@ -438,11 +438,7 @@
   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;
@@ -504,10 +500,7 @@
     return VerifySignedPayload(FLAGS_in_file, FLAGS_public_key);
   }
   if (!FLAGS_properties_file.empty()) {
-    return ExtractProperties(
-               FLAGS_in_file, FLAGS_properties_file, FLAGS_properties_format)
-               ? 0
-               : 1;
+    return ExtractProperties(FLAGS_in_file, FLAGS_properties_file) ? 0 : 1;
   }
 
   // A payload generation was requested. Convert the flags to a
@@ -528,19 +521,16 @@
   partition_names = base::SplitString(
       FLAGS_partition_names, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
   CHECK(!partition_names.empty());
-  if (FLAGS_major_version < kMinSupportedMajorPayloadVersion ||
-      FLAGS_major_version > kMaxSupportedMajorPayloadVersion) {
-    LOG(FATAL) << "Unsupported major version " << FLAGS_major_version;
-    return 1;
-  }
-
-  if (!FLAGS_apex_info_file.empty()) {
-    // apex_info_file should point to a regular file(or symlink to a regular
-    // file)
-    CHECK(utils::FileExists(FLAGS_apex_info_file.c_str()));
-    CHECK(utils::IsRegFile(FLAGS_apex_info_file.c_str()) ||
-          utils::IsSymlink(FLAGS_apex_info_file.c_str()));
-    payload_config.apex_info_file = FLAGS_apex_info_file;
+  if (FLAGS_major_version == kChromeOSMajorPayloadVersion ||
+      FLAGS_new_partitions.empty()) {
+    LOG_IF(FATAL, partition_names.size() != 2)
+        << "To support more than 2 partitions, please use the "
+        << "--new_partitions flag and major version 2.";
+    LOG_IF(FATAL,
+           partition_names[0] != kPartitionNameRoot ||
+               partition_names[1] != kPartitionNameKernel)
+        << "To support non-default partition name, please use the "
+        << "--new_partitions flag and major version 2.";
   }
 
   if (!FLAGS_new_partitions.empty()) {
@@ -596,15 +586,13 @@
     }
   }
 
-  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;
   }
 
   if (!FLAGS_new_postinstall_config_file.empty()) {
+    LOG_IF(FATAL, FLAGS_major_version == kChromeOSMajorPayloadVersion)
+        << "Postinstall config is only allowed in major version 2 or newer.";
     brillo::KeyValueStore store;
     CHECK(store.Load(base::FilePath(FLAGS_new_postinstall_config_file)));
     CHECK(payload_config.target.LoadPostInstallConfig(store));
@@ -622,20 +610,35 @@
   CHECK(payload_config.target.LoadImageSize());
 
   if (!FLAGS_dynamic_partition_info_file.empty()) {
+    LOG_IF(FATAL, FLAGS_major_version == kChromeOSMajorPayloadVersion)
+        << "Dynamic partition info is only allowed in major version 2 or "
+           "newer.";
     brillo::KeyValueStore store;
     CHECK(store.Load(base::FilePath(FLAGS_dynamic_partition_info_file)));
     CHECK(payload_config.target.LoadDynamicPartitionMetadata(store));
     CHECK(payload_config.target.ValidateDynamicPartitionMetadata());
-    if (FLAGS_disable_vabc) {
-      LOG(INFO) << "Disabling VABC";
-      payload_config.target.dynamic_partition_metadata->set_vabc_enabled(false);
-      payload_config.target.dynamic_partition_metadata
-          ->set_vabc_compression_param("");
-    }
   }
 
   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) {
@@ -653,49 +656,28 @@
     // Autodetect minor_version by looking at the update_engine.conf in the old
     // image.
     if (payload_config.is_delta) {
+      payload_config.version.minor = kInPlaceMinorPayloadVersion;
       brillo::KeyValueStore store;
       uint32_t minor_version;
-      bool minor_version_found = false;
       for (const PartitionConfig& part : payload_config.source.partitions) {
         if (part.fs_interface && part.fs_interface->LoadSettings(&store) &&
             utils::GetMinorVersion(store, &minor_version)) {
           payload_config.version.minor = minor_version;
-          minor_version_found = true;
-          LOG(INFO) << "Auto-detected minor_version="
-                    << payload_config.version.minor;
           break;
         }
       }
-      if (!minor_version_found) {
-        LOG(FATAL) << "Failed to detect the minor version.";
-        return 1;
-      }
     } else {
       payload_config.version.minor = kFullPayloadMinorVersion;
-      LOG(INFO) << "Using non-delta minor_version="
-                << payload_config.version.minor;
     }
+    LOG(INFO) << "Auto-detected minor_version=" << payload_config.version.minor;
   } else {
     payload_config.version.minor = FLAGS_minor_version;
     LOG(INFO) << "Using provided minor_version=" << FLAGS_minor_version;
   }
 
-  if (payload_config.version.minor != kFullPayloadMinorVersion &&
-      (payload_config.version.minor < kMinSupportedMinorPayloadVersion ||
-       payload_config.version.minor > kMaxSupportedMinorPayloadVersion)) {
-    LOG(FATAL) << "Unsupported minor version " << payload_config.version.minor;
-    return 1;
-  }
-
   payload_config.max_timestamp = FLAGS_max_timestamp;
-  if (!FLAGS_partition_timestamps.empty()) {
-    CHECK(ParsePerPartitionTimestamps(FLAGS_partition_timestamps,
-                                      &payload_config));
-  }
 
-  if (payload_config.is_delta &&
-      payload_config.version.minor >= kVerityMinorPayloadVersion &&
-      !FLAGS_disable_verity_computation)
+  if (payload_config.version.minor >= kVerityMinorPayloadVersion)
     CHECK(payload_config.target.LoadVerityConfig());
 
   LOG(INFO) << "Generating " << (payload_config.is_delta ? "delta" : "full")
diff --git a/common/system_state.cc b/payload_generator/graph_types.cc
similarity index 76%
rename from common/system_state.cc
rename to payload_generator/graph_types.cc
index cff1dfe..c03766d 100644
--- a/common/system_state.cc
+++ b/payload_generator/graph_types.cc
@@ -1,5 +1,5 @@
 //
-// Copyright (C) 2020 The Android Open Source Project
+// 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.
@@ -14,10 +14,10 @@
 // limitations under the License.
 //
 
-#include "update_engine/common/system_state.h"
+#include "update_engine/payload_generator/graph_types.h"
 
 namespace chromeos_update_engine {
 
-SystemState* SystemState::g_pointer_ = nullptr;
+const Vertex::Index Vertex::kInvalidIndex = static_cast<Vertex::Index>(-1);
 
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/graph_types.h b/payload_generator/graph_types.h
new file mode 100644
index 0000000..f96b0f3
--- /dev/null
+++ b/payload_generator/graph_types.h
@@ -0,0 +1,87 @@
+//
+// Copyright (C) 2009 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_PAYLOAD_GENERATOR_GRAPH_TYPES_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_GRAPH_TYPES_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <base/macros.h>
+
+#include "update_engine/payload_generator/annotated_operation.h"
+#include "update_engine/payload_generator/extent_utils.h"
+#include "update_engine/update_metadata.pb.h"
+
+// A few classes that help in generating delta images use these types
+// for the graph work.
+
+namespace chromeos_update_engine {
+
+struct EdgeProperties {
+  // Read-before extents. I.e., blocks in |extents| must be read by the
+  // node pointed to before the pointing node runs (presumably b/c it
+  // overwrites these blocks).
+  std::vector<Extent> extents;
+
+  // Write before extents. I.e., blocks in |write_extents| must be written
+  // by the node pointed to before the pointing node runs (presumably
+  // b/c it reads the data written by the other node).
+  std::vector<Extent> write_extents;
+
+  bool operator==(const EdgeProperties& that) const {
+    return extents == that.extents && write_extents == that.write_extents;
+  }
+};
+
+struct Vertex {
+  Vertex() : valid(true), index(-1), lowlink(-1) {}
+  bool valid;
+
+  typedef std::map<std::vector<Vertex>::size_type, EdgeProperties> EdgeMap;
+  EdgeMap out_edges;
+
+  // We sometimes wish to consider a subgraph of a graph. A subgraph would have
+  // a subset of the vertices from the graph and a subset of the edges.
+  // When considering this vertex within a subgraph, subgraph_edges stores
+  // the out-edges.
+  typedef std::set<std::vector<Vertex>::size_type> SubgraphEdgeMap;
+  SubgraphEdgeMap subgraph_edges;
+
+  // For Tarjan's algorithm:
+  std::vector<Vertex>::size_type index;
+  std::vector<Vertex>::size_type lowlink;
+
+  // Other Vertex properties:
+  AnnotatedOperation aop;
+
+  typedef std::vector<Vertex>::size_type Index;
+  static const Vertex::Index kInvalidIndex;
+};
+
+typedef std::vector<Vertex> Graph;
+
+typedef std::pair<Vertex::Index, Vertex::Index> Edge;
+
+const uint64_t kTempBlockStart = 1ULL << 60;
+static_assert(kTempBlockStart != 0, "kTempBlockStart invalid");
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_GRAPH_TYPES_H_
diff --git a/payload_generator/graph_utils.cc b/payload_generator/graph_utils.cc
new file mode 100644
index 0000000..7f5cf8f
--- /dev/null
+++ b/payload_generator/graph_utils.cc
@@ -0,0 +1,142 @@
+//
+// Copyright (C) 2009 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/graph_utils.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/macros.h>
+
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_generator/annotated_operation.h"
+#include "update_engine/payload_generator/extent_utils.h"
+
+using std::make_pair;
+using std::pair;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+namespace graph_utils {
+
+uint64_t EdgeWeight(const Graph& graph, const Edge& edge) {
+  uint64_t weight = 0;
+  const vector<Extent>& extents =
+      graph[edge.first].out_edges.find(edge.second)->second.extents;
+  for (vector<Extent>::const_iterator it = extents.begin(); it != extents.end();
+       ++it) {
+    if (it->start_block() != kSparseHole)
+      weight += it->num_blocks();
+  }
+  return weight;
+}
+
+void AddReadBeforeDep(Vertex* src, Vertex::Index dst, uint64_t block) {
+  Vertex::EdgeMap::iterator edge_it = src->out_edges.find(dst);
+  if (edge_it == src->out_edges.end()) {
+    // Must create new edge
+    pair<Vertex::EdgeMap::iterator, bool> result =
+        src->out_edges.insert(make_pair(dst, EdgeProperties()));
+    CHECK(result.second);
+    edge_it = result.first;
+  }
+  AppendBlockToExtents(&edge_it->second.extents, block);
+}
+
+void AddReadBeforeDepExtents(Vertex* src,
+                             Vertex::Index dst,
+                             const vector<Extent>& extents) {
+  // TODO(adlr): Be more efficient than adding each block individually.
+  for (vector<Extent>::const_iterator it = extents.begin(), e = extents.end();
+       it != e;
+       ++it) {
+    const Extent& extent = *it;
+    for (uint64_t block = extent.start_block(),
+                  block_end = extent.start_block() + extent.num_blocks();
+         block != block_end;
+         ++block) {
+      AddReadBeforeDep(src, dst, block);
+    }
+  }
+}
+
+void DropWriteBeforeDeps(Vertex::EdgeMap* edge_map) {
+  // Specially crafted for-loop for the map-iterate-delete dance.
+  for (Vertex::EdgeMap::iterator it = edge_map->begin();
+       it != edge_map->end();) {
+    if (!it->second.write_extents.empty())
+      it->second.write_extents.clear();
+    if (it->second.extents.empty()) {
+      // Erase *it, as it contains no blocks
+      edge_map->erase(it++);
+    } else {
+      ++it;
+    }
+  }
+}
+
+// For each node N in graph, drop all edges N->|index|.
+void DropIncomingEdgesTo(Graph* graph, Vertex::Index index) {
+  // This would be much more efficient if we had doubly-linked
+  // edges in the graph.
+  for (Graph::iterator it = graph->begin(), e = graph->end(); it != e; ++it) {
+    it->out_edges.erase(index);
+  }
+}
+
+namespace {
+template <typename T>
+void DumpExtents(const T& field, int prepend_space_count) {
+  string header(prepend_space_count, ' ');
+  for (const auto& extent : field) {
+    LOG(INFO) << header << "(" << extent.start_block() << ", "
+              << extent.num_blocks() << ")";
+  }
+}
+
+void DumpOutEdges(const Vertex::EdgeMap& out_edges) {
+  for (Vertex::EdgeMap::const_iterator it = out_edges.begin(),
+                                       e = out_edges.end();
+       it != e;
+       ++it) {
+    LOG(INFO) << "    " << it->first << " read-before:";
+    DumpExtents(it->second.extents, 6);
+    LOG(INFO) << "      write-before:";
+    DumpExtents(it->second.write_extents, 6);
+  }
+}
+}  // namespace
+
+void DumpGraph(const Graph& graph) {
+  LOG(INFO) << "Graph length: " << graph.size();
+  for (Graph::size_type i = 0, e = graph.size(); i != e; ++i) {
+    LOG(INFO) << i << (graph[i].valid ? "" : "-INV") << ": "
+              << graph[i].aop.name << ": "
+              << InstallOperationTypeName(graph[i].aop.op.type());
+    LOG(INFO) << "  src_extents:";
+    DumpExtents(graph[i].aop.op.src_extents(), 4);
+    LOG(INFO) << "  dst_extents:";
+    DumpExtents(graph[i].aop.op.dst_extents(), 4);
+    LOG(INFO) << "  out edges:";
+    DumpOutEdges(graph[i].out_edges);
+  }
+}
+
+}  // namespace graph_utils
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/graph_utils.h b/payload_generator/graph_utils.h
new file mode 100644
index 0000000..7024215
--- /dev/null
+++ b/payload_generator/graph_utils.h
@@ -0,0 +1,54 @@
+//
+// Copyright (C) 2009 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_PAYLOAD_GENERATOR_GRAPH_UTILS_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_GRAPH_UTILS_H_
+
+#include <vector>
+
+#include <base/macros.h>
+
+#include "update_engine/payload_generator/graph_types.h"
+#include "update_engine/update_metadata.pb.h"
+
+// A few utility functions for graphs
+
+namespace chromeos_update_engine {
+
+namespace graph_utils {
+
+// Returns the number of blocks represented by all extents in the edge.
+uint64_t EdgeWeight(const Graph& graph, const Edge& edge);
+
+// These add a read-before dependency from graph[src] -> graph[dst]. If the dep
+// already exists, the block/s is/are added to the existing edge.
+void AddReadBeforeDep(Vertex* src, Vertex::Index dst, uint64_t block);
+void AddReadBeforeDepExtents(Vertex* src,
+                             Vertex::Index dst,
+                             const std::vector<Extent>& extents);
+
+void DropWriteBeforeDeps(Vertex::EdgeMap* edge_map);
+
+// For each node N in graph, drop all edges N->|index|.
+void DropIncomingEdgesTo(Graph* graph, Vertex::Index index);
+
+void DumpGraph(const Graph& graph);
+
+}  // namespace graph_utils
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_GRAPH_UTILS_H_
diff --git a/payload_generator/graph_utils_unittest.cc b/payload_generator/graph_utils_unittest.cc
new file mode 100644
index 0000000..07e7664
--- /dev/null
+++ b/payload_generator/graph_utils_unittest.cc
@@ -0,0 +1,94 @@
+//
+// Copyright (C) 2009 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/graph_utils.h"
+
+#include <utility>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_generator/extent_ranges.h"
+#include "update_engine/payload_generator/extent_utils.h"
+
+using std::make_pair;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+class GraphUtilsTest : public ::testing::Test {};
+
+TEST(GraphUtilsTest, SimpleTest) {
+  Graph graph(2);
+
+  graph[0].out_edges.insert(make_pair(1, EdgeProperties()));
+
+  vector<Extent>& extents = graph[0].out_edges[1].extents;
+
+  EXPECT_EQ(0U, extents.size());
+  AppendBlockToExtents(&extents, 0);
+  EXPECT_EQ(1U, extents.size());
+  AppendBlockToExtents(&extents, 1);
+  AppendBlockToExtents(&extents, 2);
+  EXPECT_EQ(1U, extents.size());
+  AppendBlockToExtents(&extents, 4);
+
+  EXPECT_EQ(2U, extents.size());
+  EXPECT_EQ(0U, extents[0].start_block());
+  EXPECT_EQ(3U, extents[0].num_blocks());
+  EXPECT_EQ(4U, extents[1].start_block());
+  EXPECT_EQ(1U, extents[1].num_blocks());
+
+  EXPECT_EQ(4U, graph_utils::EdgeWeight(graph, make_pair(0, 1)));
+}
+
+TEST(GraphUtilsTest, DepsTest) {
+  Graph graph(3);
+
+  graph_utils::AddReadBeforeDep(&graph[0], 1, 3);
+  EXPECT_EQ(1U, graph[0].out_edges.size());
+  {
+    Extent& extent = graph[0].out_edges[1].extents[0];
+    EXPECT_EQ(3U, extent.start_block());
+    EXPECT_EQ(1U, extent.num_blocks());
+  }
+  graph_utils::AddReadBeforeDep(&graph[0], 1, 4);
+  EXPECT_EQ(1U, graph[0].out_edges.size());
+  {
+    Extent& extent = graph[0].out_edges[1].extents[0];
+    EXPECT_EQ(3U, extent.start_block());
+    EXPECT_EQ(2U, extent.num_blocks());
+  }
+  graph_utils::AddReadBeforeDepExtents(
+      &graph[2], 1, vector<Extent>(1, ExtentForRange(5, 2)));
+  EXPECT_EQ(1U, graph[2].out_edges.size());
+  {
+    Extent& extent = graph[2].out_edges[1].extents[0];
+    EXPECT_EQ(5U, extent.start_block());
+    EXPECT_EQ(2U, extent.num_blocks());
+  }
+  // Change most recent edge from read-before to write-before
+  graph[2].out_edges[1].write_extents.swap(graph[2].out_edges[1].extents);
+  graph_utils::DropWriteBeforeDeps(&graph[2].out_edges);
+  EXPECT_EQ(0U, graph[2].out_edges.size());
+
+  EXPECT_EQ(1U, graph[0].out_edges.size());
+  graph_utils::DropIncomingEdgesTo(&graph, 1);
+  EXPECT_EQ(0U, graph[0].out_edges.size());
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/inplace_generator.cc b/payload_generator/inplace_generator.cc
new file mode 100644
index 0000000..d553cc4
--- /dev/null
+++ b/payload_generator/inplace_generator.cc
@@ -0,0 +1,798 @@
+//
+// 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/payload_generator/inplace_generator.h"
+
+#include <algorithm>
+#include <map>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <base/stl_util.h>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_generator/cycle_breaker.h"
+#include "update_engine/payload_generator/delta_diff_generator.h"
+#include "update_engine/payload_generator/delta_diff_utils.h"
+#include "update_engine/payload_generator/extent_ranges.h"
+#include "update_engine/payload_generator/graph_types.h"
+#include "update_engine/payload_generator/graph_utils.h"
+#include "update_engine/payload_generator/topological_sort.h"
+#include "update_engine/update_metadata.pb.h"
+
+using std::make_pair;
+using std::map;
+using std::pair;
+using std::set;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+using Block = InplaceGenerator::Block;
+
+namespace {
+
+// The only PayloadVersion supported by this implementation.
+const PayloadVersion kInPlacePayloadVersion{kChromeOSMajorPayloadVersion,
+                                            kInPlaceMinorPayloadVersion};
+
+// This class allocates non-existent temp blocks, starting from
+// kTempBlockStart. Other code is responsible for converting these
+// temp blocks into real blocks, as the client can't read or write to
+// these blocks.
+class DummyExtentAllocator {
+ public:
+  vector<Extent> Allocate(const uint64_t block_count) {
+    vector<Extent> ret(1);
+    ret[0].set_start_block(next_block_);
+    ret[0].set_num_blocks(block_count);
+    next_block_ += block_count;
+    return ret;
+  }
+
+ private:
+  uint64_t next_block_{kTempBlockStart};
+};
+
+// Takes a vector of blocks and returns an equivalent vector of Extent
+// objects.
+vector<Extent> CompressExtents(const vector<uint64_t>& blocks) {
+  vector<Extent> new_extents;
+  for (uint64_t block : blocks) {
+    AppendBlockToExtents(&new_extents, block);
+  }
+  return new_extents;
+}
+
+// Helper class to compare two operations by start block of the first Extent in
+// their destination extents given the index of the operations in the graph.
+class IndexedInstallOperationsDstComparator {
+ public:
+  explicit IndexedInstallOperationsDstComparator(Graph* graph)
+      : graph_(graph) {}
+
+  // Compares the operations in the vertex a and b of graph_.
+  bool operator()(size_t a, size_t b) const {
+    return diff_utils::CompareAopsByDestination((*graph_)[a].aop,
+                                                (*graph_)[b].aop);
+  }
+
+ private:
+  const Graph* const graph_;
+};
+
+}  // namespace
+
+void InplaceGenerator::CheckGraph(const Graph& graph) {
+  for (const Vertex& v : graph) {
+    CHECK(v.aop.op.has_type());
+  }
+}
+
+void InplaceGenerator::SubstituteBlocks(Vertex* vertex,
+                                        const vector<Extent>& remove_extents,
+                                        const vector<Extent>& replace_extents) {
+  // First, expand out the blocks that op reads from
+  vector<uint64_t> read_blocks = ExpandExtents(vertex->aop.op.src_extents());
+  {
+    // Expand remove_extents and replace_extents
+    vector<uint64_t> remove_extents_expanded = ExpandExtents(remove_extents);
+    vector<uint64_t> replace_extents_expanded = ExpandExtents(replace_extents);
+    CHECK_EQ(remove_extents_expanded.size(), replace_extents_expanded.size());
+    map<uint64_t, uint64_t> conversion;
+    for (vector<uint64_t>::size_type i = 0; i < replace_extents_expanded.size();
+         i++) {
+      conversion[remove_extents_expanded[i]] = replace_extents_expanded[i];
+    }
+    ApplyMap(&read_blocks, conversion);
+    for (auto& edge_prop_pair : vertex->out_edges) {
+      vector<uint64_t> write_before_deps_expanded =
+          ExpandExtents(edge_prop_pair.second.write_extents);
+      ApplyMap(&write_before_deps_expanded, conversion);
+      edge_prop_pair.second.write_extents =
+          CompressExtents(write_before_deps_expanded);
+    }
+  }
+  // Convert read_blocks back to extents
+  vertex->aop.op.clear_src_extents();
+  vector<Extent> new_extents = CompressExtents(read_blocks);
+  StoreExtents(new_extents, vertex->aop.op.mutable_src_extents());
+}
+
+bool InplaceGenerator::CutEdges(Graph* graph,
+                                const set<Edge>& edges,
+                                vector<CutEdgeVertexes>* out_cuts) {
+  DummyExtentAllocator scratch_allocator;
+  vector<CutEdgeVertexes> cuts;
+  cuts.reserve(edges.size());
+
+  uint64_t scratch_blocks_used = 0;
+  for (const Edge& edge : edges) {
+    cuts.resize(cuts.size() + 1);
+    vector<Extent> old_extents =
+        (*graph)[edge.first].out_edges[edge.second].extents;
+    // Choose some scratch space
+    scratch_blocks_used += graph_utils::EdgeWeight(*graph, edge);
+    cuts.back().tmp_extents =
+        scratch_allocator.Allocate(graph_utils::EdgeWeight(*graph, edge));
+    // create vertex to copy original->scratch
+    cuts.back().new_vertex = graph->size();
+    graph->emplace_back();
+    cuts.back().old_src = edge.first;
+    cuts.back().old_dst = edge.second;
+
+    EdgeProperties& cut_edge_properties =
+        (*graph)[edge.first].out_edges.find(edge.second)->second;
+
+    // This should never happen, as we should only be cutting edges between
+    // real file nodes, and write-before relationships are created from
+    // a real file node to a temp copy node:
+    CHECK(cut_edge_properties.write_extents.empty())
+        << "Can't cut edge that has write-before relationship.";
+
+    // make node depend on the copy operation
+    (*graph)[edge.first].out_edges.insert(
+        make_pair(graph->size() - 1, cut_edge_properties));
+
+    // Set src/dst extents and other proto variables for copy operation
+    graph->back().aop.op.set_type(InstallOperation::MOVE);
+    StoreExtents(cut_edge_properties.extents,
+                 graph->back().aop.op.mutable_src_extents());
+    StoreExtents(cuts.back().tmp_extents,
+                 graph->back().aop.op.mutable_dst_extents());
+    graph->back().aop.op.set_src_length(graph_utils::EdgeWeight(*graph, edge) *
+                                        kBlockSize);
+    graph->back().aop.op.set_dst_length(graph->back().aop.op.src_length());
+
+    // make the dest node read from the scratch space
+    SubstituteBlocks(&((*graph)[edge.second]),
+                     (*graph)[edge.first].out_edges[edge.second].extents,
+                     cuts.back().tmp_extents);
+
+    // delete the old edge
+    CHECK_EQ(static_cast<Graph::size_type>(1),
+             (*graph)[edge.first].out_edges.erase(edge.second));
+
+    // Add an edge from dst to copy operation
+    EdgeProperties write_before_edge_properties;
+    write_before_edge_properties.write_extents = cuts.back().tmp_extents;
+    (*graph)[edge.second].out_edges.insert(
+        make_pair(graph->size() - 1, write_before_edge_properties));
+  }
+  out_cuts->swap(cuts);
+  return true;
+}
+
+// Creates all the edges for the graph. Writers of a block point to
+// readers of the same block. This is because for an edge A->B, B
+// must complete before A executes.
+void InplaceGenerator::CreateEdges(Graph* graph, const vector<Block>& blocks) {
+  for (vector<Block>::size_type i = 0; i < blocks.size(); i++) {
+    // Blocks with both a reader and writer get an edge
+    if (blocks[i].reader == Vertex::kInvalidIndex ||
+        blocks[i].writer == Vertex::kInvalidIndex)
+      continue;
+    // Don't have a node depend on itself
+    if (blocks[i].reader == blocks[i].writer)
+      continue;
+    // See if there's already an edge we can add onto
+    Vertex::EdgeMap::iterator edge_it =
+        (*graph)[blocks[i].writer].out_edges.find(blocks[i].reader);
+    if (edge_it == (*graph)[blocks[i].writer].out_edges.end()) {
+      // No existing edge. Create one
+      (*graph)[blocks[i].writer].out_edges.insert(
+          make_pair(blocks[i].reader, EdgeProperties()));
+      edge_it = (*graph)[blocks[i].writer].out_edges.find(blocks[i].reader);
+      CHECK(edge_it != (*graph)[blocks[i].writer].out_edges.end());
+    }
+    AppendBlockToExtents(&edge_it->second.extents, i);
+  }
+}
+
+namespace {
+
+class SortCutsByTopoOrderLess {
+ public:
+  explicit SortCutsByTopoOrderLess(
+      const vector<vector<Vertex::Index>::size_type>& table)
+      : table_(table) {}
+  bool operator()(const CutEdgeVertexes& a, const CutEdgeVertexes& b) {
+    return table_[a.old_dst] < table_[b.old_dst];
+  }
+
+ private:
+  const vector<vector<Vertex::Index>::size_type>& table_;
+};
+
+}  // namespace
+
+void InplaceGenerator::GenerateReverseTopoOrderMap(
+    const vector<Vertex::Index>& op_indexes,
+    vector<vector<Vertex::Index>::size_type>* reverse_op_indexes) {
+  vector<vector<Vertex::Index>::size_type> table(op_indexes.size());
+  for (vector<Vertex::Index>::size_type i = 0, e = op_indexes.size(); i != e;
+       ++i) {
+    Vertex::Index node = op_indexes[i];
+    if (table.size() < (node + 1)) {
+      table.resize(node + 1);
+    }
+    table[node] = i;
+  }
+  reverse_op_indexes->swap(table);
+}
+
+void InplaceGenerator::SortCutsByTopoOrder(
+    const vector<Vertex::Index>& op_indexes, vector<CutEdgeVertexes>* cuts) {
+  // first, make a reverse lookup table.
+  vector<vector<Vertex::Index>::size_type> table;
+  GenerateReverseTopoOrderMap(op_indexes, &table);
+  SortCutsByTopoOrderLess less(table);
+  sort(cuts->begin(), cuts->end(), less);
+}
+
+void InplaceGenerator::MoveAndSortFullOpsToBack(
+    Graph* graph, vector<Vertex::Index>* op_indexes) {
+  vector<Vertex::Index> ret;
+  vector<Vertex::Index> full_ops;
+  ret.reserve(op_indexes->size());
+  for (auto op_index : *op_indexes) {
+    InstallOperation::Type type = (*graph)[op_index].aop.op.type();
+    if (type == InstallOperation::REPLACE ||
+        type == InstallOperation::REPLACE_BZ) {
+      full_ops.push_back(op_index);
+    } else {
+      ret.push_back(op_index);
+    }
+  }
+  LOG(INFO) << "Stats: " << full_ops.size() << " full ops out of "
+            << (full_ops.size() + ret.size()) << " total ops.";
+  // Sort full ops according to their dst_extents.
+  sort(full_ops.begin(),
+       full_ops.end(),
+       IndexedInstallOperationsDstComparator(graph));
+  ret.insert(ret.end(), full_ops.begin(), full_ops.end());
+  op_indexes->swap(ret);
+}
+
+namespace {
+
+template <typename T>
+bool TempBlocksExistInExtents(const T& extents) {
+  for (const auto& extent : extents) {
+    uint64_t start = extent.start_block();
+    uint64_t num = extent.num_blocks();
+    if (start >= kTempBlockStart || (start + num) >= kTempBlockStart) {
+      LOG(ERROR) << "temp block!";
+      LOG(ERROR) << "start: " << start << ", num: " << num;
+      LOG(ERROR) << "kTempBlockStart: " << kTempBlockStart;
+      LOG(ERROR) << "returning true";
+      return true;
+    }
+    // check for wrap-around, which would be a bug:
+    CHECK(start <= (start + num));
+  }
+  return false;
+}
+
+// Converts the cuts, which must all have the same |old_dst| member,
+// to full. It does this by converting the |old_dst| to REPLACE or
+// REPLACE_BZ, dropping all incoming edges to |old_dst|, and marking
+// all temp nodes invalid.
+bool ConvertCutsToFull(
+    Graph* graph,
+    const string& new_part,
+    BlobFileWriter* blob_file,
+    vector<Vertex::Index>* op_indexes,
+    vector<vector<Vertex::Index>::size_type>* reverse_op_indexes,
+    const vector<CutEdgeVertexes>& cuts) {
+  CHECK(!cuts.empty());
+  set<Vertex::Index> deleted_nodes;
+  for (const CutEdgeVertexes& cut : cuts) {
+    TEST_AND_RETURN_FALSE(
+        InplaceGenerator::ConvertCutToFullOp(graph, cut, new_part, blob_file));
+    deleted_nodes.insert(cut.new_vertex);
+  }
+  deleted_nodes.insert(cuts[0].old_dst);
+
+  vector<Vertex::Index> new_op_indexes;
+  new_op_indexes.reserve(op_indexes->size());
+  for (Vertex::Index vertex_index : *op_indexes) {
+    if (base::ContainsKey(deleted_nodes, vertex_index))
+      continue;
+    new_op_indexes.push_back(vertex_index);
+  }
+  new_op_indexes.push_back(cuts[0].old_dst);
+  op_indexes->swap(new_op_indexes);
+  InplaceGenerator::GenerateReverseTopoOrderMap(*op_indexes,
+                                                reverse_op_indexes);
+  return true;
+}
+
+// Tries to assign temp blocks for a collection of cuts, all of which share
+// the same old_dst member. If temp blocks can't be found, old_dst will be
+// converted to a REPLACE or REPLACE_BZ operation. Returns true on success,
+// which can happen even if blocks are converted to full. Returns false
+// on exceptional error cases.
+bool AssignBlockForAdjoiningCuts(
+    Graph* graph,
+    const string& new_part,
+    BlobFileWriter* blob_file,
+    vector<Vertex::Index>* op_indexes,
+    vector<vector<Vertex::Index>::size_type>* reverse_op_indexes,
+    const vector<CutEdgeVertexes>& cuts) {
+  CHECK(!cuts.empty());
+  const Vertex::Index old_dst = cuts[0].old_dst;
+  // Calculate # of blocks needed
+  uint64_t blocks_needed = 0;
+  vector<uint64_t> cuts_blocks_needed(cuts.size());
+  for (vector<CutEdgeVertexes>::size_type i = 0; i < cuts.size(); ++i) {
+    uint64_t cut_blocks_needed = 0;
+    for (const Extent& extent : cuts[i].tmp_extents) {
+      cut_blocks_needed += extent.num_blocks();
+    }
+    blocks_needed += cut_blocks_needed;
+    cuts_blocks_needed[i] = cut_blocks_needed;
+  }
+
+  // Find enough blocks
+  ExtentRanges scratch_ranges;
+  // Each block that's supplying temp blocks and the corresponding blocks:
+  typedef vector<pair<Vertex::Index, ExtentRanges>> SupplierVector;
+  SupplierVector block_suppliers;
+  uint64_t scratch_blocks_found = 0;
+  for (vector<Vertex::Index>::size_type i = (*reverse_op_indexes)[old_dst] + 1,
+                                        e = op_indexes->size();
+       i < e;
+       ++i) {
+    Vertex::Index test_node = (*op_indexes)[i];
+    if (!(*graph)[test_node].valid)
+      continue;
+    // See if this node has sufficient blocks
+    ExtentRanges ranges;
+    ranges.AddRepeatedExtents((*graph)[test_node].aop.op.dst_extents());
+    ranges.SubtractExtent(
+        ExtentForRange(kTempBlockStart, kSparseHole - kTempBlockStart));
+    ranges.SubtractRepeatedExtents((*graph)[test_node].aop.op.src_extents());
+    // For now, for simplicity, subtract out all blocks in read-before
+    // dependencies.
+    for (Vertex::EdgeMap::const_iterator
+             edge_i = (*graph)[test_node].out_edges.begin(),
+             edge_e = (*graph)[test_node].out_edges.end();
+         edge_i != edge_e;
+         ++edge_i) {
+      ranges.SubtractExtents(edge_i->second.extents);
+    }
+
+    // Prevent using the block 0 as scratch space due to crbug.com/480751.
+    if (ranges.ContainsBlock(0)) {
+      LOG(INFO) << "Removing block 0 from the selected scratch range in vertex "
+                << i;
+      ranges.SubtractBlock(0);
+    }
+
+    if (ranges.blocks() == 0)
+      continue;
+
+    if (ranges.blocks() + scratch_blocks_found > blocks_needed) {
+      // trim down ranges
+      vector<Extent> new_ranges =
+          ranges.GetExtentsForBlockCount(blocks_needed - scratch_blocks_found);
+      ranges = ExtentRanges();
+      ranges.AddExtents(new_ranges);
+    }
+    scratch_ranges.AddRanges(ranges);
+    block_suppliers.push_back(make_pair(test_node, ranges));
+    scratch_blocks_found += ranges.blocks();
+    if (scratch_ranges.blocks() >= blocks_needed)
+      break;
+  }
+  if (scratch_ranges.blocks() < blocks_needed) {
+    LOG(INFO) << "Unable to find sufficient scratch";
+    TEST_AND_RETURN_FALSE(ConvertCutsToFull(
+        graph, new_part, blob_file, op_indexes, reverse_op_indexes, cuts));
+    return true;
+  }
+  // Use the scratch we found
+  TEST_AND_RETURN_FALSE(scratch_ranges.blocks() == scratch_blocks_found);
+
+  // Make all the suppliers depend on this node
+  for (const auto& index_range_pair : block_suppliers) {
+    graph_utils::AddReadBeforeDepExtents(
+        &(*graph)[index_range_pair.first],
+        old_dst,
+        index_range_pair.second.GetExtentsForBlockCount(
+            index_range_pair.second.blocks()));
+  }
+
+  // Replace temp blocks in each cut
+  for (vector<CutEdgeVertexes>::size_type i = 0; i < cuts.size(); ++i) {
+    const CutEdgeVertexes& cut = cuts[i];
+    vector<Extent> real_extents =
+        scratch_ranges.GetExtentsForBlockCount(cuts_blocks_needed[i]);
+    scratch_ranges.SubtractExtents(real_extents);
+
+    // Fix the old dest node w/ the real blocks
+    InplaceGenerator::SubstituteBlocks(
+        &(*graph)[old_dst], cut.tmp_extents, real_extents);
+
+    // Fix the new node w/ the real blocks. Since the new node is just a
+    // copy operation, we can replace all the dest extents w/ the real
+    // blocks.
+    InstallOperation* op = &(*graph)[cut.new_vertex].aop.op;
+    op->clear_dst_extents();
+    StoreExtents(real_extents, op->mutable_dst_extents());
+  }
+  return true;
+}
+
+}  // namespace
+
+bool InplaceGenerator::AssignTempBlocks(
+    Graph* graph,
+    const string& new_part,
+    BlobFileWriter* blob_file,
+    vector<Vertex::Index>* op_indexes,
+    vector<vector<Vertex::Index>::size_type>* reverse_op_indexes,
+    const vector<CutEdgeVertexes>& cuts) {
+  CHECK(!cuts.empty());
+
+  // group of cuts w/ the same old_dst:
+  vector<CutEdgeVertexes> cuts_group;
+
+  for (vector<CutEdgeVertexes>::size_type i = cuts.size() - 1, e = 0; true;
+       --i) {
+    LOG(INFO) << "Fixing temp blocks in cut " << i
+              << ": old dst: " << cuts[i].old_dst
+              << " new vertex: " << cuts[i].new_vertex
+              << " path: " << (*graph)[cuts[i].old_dst].aop.name;
+
+    if (cuts_group.empty() || (cuts_group[0].old_dst == cuts[i].old_dst)) {
+      cuts_group.push_back(cuts[i]);
+    } else {
+      CHECK(!cuts_group.empty());
+      TEST_AND_RETURN_FALSE(AssignBlockForAdjoiningCuts(graph,
+                                                        new_part,
+                                                        blob_file,
+                                                        op_indexes,
+                                                        reverse_op_indexes,
+                                                        cuts_group));
+      cuts_group.clear();
+      cuts_group.push_back(cuts[i]);
+    }
+
+    if (i == e) {
+      // break out of for() loop
+      break;
+    }
+  }
+  CHECK(!cuts_group.empty());
+  TEST_AND_RETURN_FALSE(AssignBlockForAdjoiningCuts(
+      graph, new_part, blob_file, op_indexes, reverse_op_indexes, cuts_group));
+  return true;
+}
+
+bool InplaceGenerator::NoTempBlocksRemain(const Graph& graph) {
+  size_t idx = 0;
+  for (Graph::const_iterator it = graph.begin(), e = graph.end(); it != e;
+       ++it, ++idx) {
+    if (!it->valid)
+      continue;
+    const InstallOperation& op = it->aop.op;
+    if (TempBlocksExistInExtents(op.dst_extents()) ||
+        TempBlocksExistInExtents(op.src_extents())) {
+      LOG(INFO) << "bad extents in node " << idx;
+      LOG(INFO) << "so yeah";
+      return false;
+    }
+
+    // Check out-edges:
+    for (const auto& edge_prop_pair : it->out_edges) {
+      if (TempBlocksExistInExtents(edge_prop_pair.second.extents) ||
+          TempBlocksExistInExtents(edge_prop_pair.second.write_extents)) {
+        LOG(INFO) << "bad out edge in node " << idx;
+        LOG(INFO) << "so yeah";
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+bool InplaceGenerator::ConvertCutToFullOp(Graph* graph,
+                                          const CutEdgeVertexes& cut,
+                                          const string& new_part,
+                                          BlobFileWriter* blob_file) {
+  // Drop all incoming edges, keep all outgoing edges
+
+  // Keep all outgoing edges
+  if ((*graph)[cut.old_dst].aop.op.type() != InstallOperation::REPLACE_BZ &&
+      (*graph)[cut.old_dst].aop.op.type() != InstallOperation::REPLACE) {
+    Vertex::EdgeMap out_edges = (*graph)[cut.old_dst].out_edges;
+    graph_utils::DropWriteBeforeDeps(&out_edges);
+
+    // Replace the operation with a REPLACE or REPLACE_BZ to generate the same
+    // |new_extents| list of blocks and update the graph.
+    vector<AnnotatedOperation> new_aop;
+    vector<Extent> new_extents;
+    ExtentsToVector((*graph)[cut.old_dst].aop.op.dst_extents(), &new_extents);
+    TEST_AND_RETURN_FALSE(diff_utils::DeltaReadFile(
+        &new_aop,
+        "",  // old_part
+        new_part,
+        vector<Extent>(),  // old_extents
+        new_extents,
+        {},  // old_deflates
+        {},  // new_deflates
+        (*graph)[cut.old_dst].aop.name,
+        -1,  // chunk_blocks, forces to have a single operation.
+        kInPlacePayloadVersion,
+        blob_file));
+    TEST_AND_RETURN_FALSE(new_aop.size() == 1);
+    TEST_AND_RETURN_FALSE(AddInstallOpToGraph(
+        graph, cut.old_dst, nullptr, new_aop.front().op, new_aop.front().name));
+
+    (*graph)[cut.old_dst].out_edges = out_edges;
+
+    // Right now we don't have doubly-linked edges, so we have to scan
+    // the whole graph.
+    graph_utils::DropIncomingEdgesTo(graph, cut.old_dst);
+  }
+
+  // Delete temp node
+  (*graph)[cut.old_src].out_edges.erase(cut.new_vertex);
+  CHECK((*graph)[cut.old_dst].out_edges.find(cut.new_vertex) ==
+        (*graph)[cut.old_dst].out_edges.end());
+  (*graph)[cut.new_vertex].valid = false;
+  LOG(INFO) << "marked node invalid: " << cut.new_vertex;
+  return true;
+}
+
+bool InplaceGenerator::ConvertGraphToDag(Graph* graph,
+                                         const string& new_part,
+                                         BlobFileWriter* blob_file,
+                                         vector<Vertex::Index>* final_order,
+                                         Vertex::Index scratch_vertex) {
+  CycleBreaker cycle_breaker;
+  LOG(INFO) << "Finding cycles...";
+  set<Edge> cut_edges;
+  cycle_breaker.BreakCycles(*graph, &cut_edges);
+  LOG(INFO) << "done finding cycles";
+  CheckGraph(*graph);
+
+  // Calculate number of scratch blocks needed
+
+  LOG(INFO) << "Cutting cycles...";
+  vector<CutEdgeVertexes> cuts;
+  TEST_AND_RETURN_FALSE(CutEdges(graph, cut_edges, &cuts));
+  LOG(INFO) << "done cutting cycles";
+  LOG(INFO) << "There are " << cuts.size() << " cuts.";
+  CheckGraph(*graph);
+
+  LOG(INFO) << "Creating initial topological order...";
+  TopologicalSort(*graph, final_order);
+  LOG(INFO) << "done with initial topo order";
+  CheckGraph(*graph);
+
+  LOG(INFO) << "Moving full ops to the back";
+  MoveAndSortFullOpsToBack(graph, final_order);
+  LOG(INFO) << "done moving full ops to back";
+
+  vector<vector<Vertex::Index>::size_type> inverse_final_order;
+  GenerateReverseTopoOrderMap(*final_order, &inverse_final_order);
+
+  SortCutsByTopoOrder(*final_order, &cuts);
+
+  if (!cuts.empty())
+    TEST_AND_RETURN_FALSE(AssignTempBlocks(
+        graph, new_part, blob_file, final_order, &inverse_final_order, cuts));
+  LOG(INFO) << "Making sure all temp blocks have been allocated";
+
+  // Remove the scratch node, if any
+  if (scratch_vertex != Vertex::kInvalidIndex) {
+    final_order->erase(final_order->begin() +
+                       inverse_final_order[scratch_vertex]);
+    (*graph)[scratch_vertex].valid = false;
+    GenerateReverseTopoOrderMap(*final_order, &inverse_final_order);
+  }
+
+  graph_utils::DumpGraph(*graph);
+  CHECK(NoTempBlocksRemain(*graph));
+  LOG(INFO) << "done making sure all temp blocks are allocated";
+  return true;
+}
+
+void InplaceGenerator::CreateScratchNode(uint64_t start_block,
+                                         uint64_t num_blocks,
+                                         Vertex* vertex) {
+  vertex->aop.name = "<scratch>";
+  vertex->aop.op.set_type(InstallOperation::REPLACE_BZ);
+  vertex->aop.op.set_data_offset(0);
+  vertex->aop.op.set_data_length(0);
+  Extent* extent = vertex->aop.op.add_dst_extents();
+  extent->set_start_block(start_block);
+  extent->set_num_blocks(num_blocks);
+}
+
+bool InplaceGenerator::AddInstallOpToBlocksVector(
+    const InstallOperation& operation,
+    const Graph& graph,
+    Vertex::Index vertex,
+    vector<Block>* blocks) {
+  // See if this is already present.
+  TEST_AND_RETURN_FALSE(operation.dst_extents_size() > 0);
+
+  enum BlockField { READER = 0, WRITER, BLOCK_FIELD_COUNT };
+  for (int field = READER; field < BLOCK_FIELD_COUNT; field++) {
+    const char* past_participle = (field == READER) ? "read" : "written";
+    const google::protobuf::RepeatedPtrField<Extent>& extents =
+        (field == READER) ? operation.src_extents() : operation.dst_extents();
+    Vertex::Index Block::*access_type =
+        (field == READER) ? &Block::reader : &Block::writer;
+
+    for (const Extent& extent : extents) {
+      for (uint64_t block = extent.start_block();
+           block < (extent.start_block() + extent.num_blocks());
+           block++) {
+        if ((*blocks)[block].*access_type != Vertex::kInvalidIndex) {
+          LOG(FATAL) << "Block " << block << " is already " << past_participle
+                     << " by " << (*blocks)[block].*access_type << "("
+                     << graph[(*blocks)[block].*access_type].aop.name
+                     << ") and also " << vertex << "(" << graph[vertex].aop.name
+                     << ")";
+        }
+        (*blocks)[block].*access_type = vertex;
+      }
+    }
+  }
+  return true;
+}
+
+bool InplaceGenerator::AddInstallOpToGraph(Graph* graph,
+                                           Vertex::Index existing_vertex,
+                                           vector<Block>* blocks,
+                                           const InstallOperation& operation,
+                                           const string& op_name) {
+  Vertex::Index vertex = existing_vertex;
+  if (vertex == Vertex::kInvalidIndex) {
+    graph->emplace_back();
+    vertex = graph->size() - 1;
+  }
+  (*graph)[vertex].aop.op = operation;
+  CHECK((*graph)[vertex].aop.op.has_type());
+  (*graph)[vertex].aop.name = op_name;
+
+  if (blocks)
+    TEST_AND_RETURN_FALSE(InplaceGenerator::AddInstallOpToBlocksVector(
+        (*graph)[vertex].aop.op, *graph, vertex, blocks));
+  return true;
+}
+
+void InplaceGenerator::ApplyMap(vector<uint64_t>* collection,
+                                const map<uint64_t, uint64_t>& the_map) {
+  for (uint64_t& elem : *collection) {
+    const auto& map_it = the_map.find(elem);
+    if (map_it != the_map.end())
+      elem = map_it->second;
+  }
+}
+
+bool InplaceGenerator::ResolveReadAfterWriteDependencies(
+    const PartitionConfig& old_part,
+    const PartitionConfig& new_part,
+    uint64_t partition_size,
+    size_t block_size,
+    BlobFileWriter* blob_file,
+    vector<AnnotatedOperation>* aops) {
+  // Convert the operations to the graph.
+  Graph graph;
+  CheckGraph(graph);
+  vector<Block> blocks(std::max(old_part.size, new_part.size) / block_size);
+  for (const auto& aop : *aops) {
+    AddInstallOpToGraph(
+        &graph, Vertex::kInvalidIndex, &blocks, aop.op, aop.name);
+  }
+  CheckGraph(graph);
+
+  // Final scratch block (if there's space)
+  Vertex::Index scratch_vertex = Vertex::kInvalidIndex;
+  if (blocks.size() < (partition_size / block_size)) {
+    scratch_vertex = graph.size();
+    graph.emplace_back();
+    size_t scratch_blocks = (partition_size / block_size) - blocks.size();
+    LOG(INFO) << "Added " << scratch_blocks << " scratch space blocks.";
+    CreateScratchNode(blocks.size(), scratch_blocks, &graph.back());
+  }
+  CheckGraph(graph);
+
+  LOG(INFO) << "Creating edges...";
+  CreateEdges(&graph, blocks);
+  LOG(INFO) << "Done creating edges";
+  CheckGraph(graph);
+
+  vector<Vertex::Index> final_order;
+  TEST_AND_RETURN_FALSE(ConvertGraphToDag(
+      &graph, new_part.path, blob_file, &final_order, scratch_vertex));
+
+  // Copy operations over to the |aops| vector in the final_order generated by
+  // the topological sort.
+  aops->clear();
+  aops->reserve(final_order.size());
+  for (const Vertex::Index vertex_index : final_order) {
+    const Vertex& vertex = graph[vertex_index];
+    aops->push_back(vertex.aop);
+  }
+  return true;
+}
+
+bool InplaceGenerator::GenerateOperations(const PayloadGenerationConfig& config,
+                                          const PartitionConfig& old_part,
+                                          const PartitionConfig& new_part,
+                                          BlobFileWriter* blob_file,
+                                          vector<AnnotatedOperation>* aops) {
+  TEST_AND_RETURN_FALSE(old_part.name == new_part.name);
+  TEST_AND_RETURN_FALSE(config.version.major == kInPlacePayloadVersion.major);
+  TEST_AND_RETURN_FALSE(config.version.minor == kInPlacePayloadVersion.minor);
+
+  ssize_t hard_chunk_blocks =
+      (config.hard_chunk_size == -1
+           ? -1
+           : config.hard_chunk_size / config.block_size);
+  size_t soft_chunk_blocks = config.soft_chunk_size / config.block_size;
+  uint64_t partition_size = new_part.size;
+  if (new_part.name == kPartitionNameRoot)
+    partition_size = config.rootfs_partition_size;
+
+  LOG(INFO) << "Delta compressing " << new_part.name << " partition...";
+  TEST_AND_RETURN_FALSE(diff_utils::DeltaReadPartition(aops,
+                                                       old_part,
+                                                       new_part,
+                                                       hard_chunk_blocks,
+                                                       soft_chunk_blocks,
+                                                       config.version,
+                                                       blob_file));
+  LOG(INFO) << "Done reading " << new_part.name;
+
+  TEST_AND_RETURN_FALSE(ResolveReadAfterWriteDependencies(
+      old_part, new_part, partition_size, config.block_size, blob_file, aops));
+  LOG(INFO) << "Done reordering " << new_part.name;
+  return true;
+}
+
+};  // namespace chromeos_update_engine
diff --git a/payload_generator/inplace_generator.h b/payload_generator/inplace_generator.h
new file mode 100644
index 0000000..e7298d2
--- /dev/null
+++ b/payload_generator/inplace_generator.h
@@ -0,0 +1,240 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_PAYLOAD_GENERATOR_INPLACE_GENERATOR_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_INPLACE_GENERATOR_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "update_engine/payload_generator/blob_file_writer.h"
+#include "update_engine/payload_generator/delta_diff_generator.h"
+#include "update_engine/payload_generator/graph_types.h"
+#include "update_engine/payload_generator/operations_generator.h"
+
+// InplaceGenerator contains all functionality related to the inplace algorithm
+// for generating update payloads. These are the functions used when delta minor
+// version is 1.
+
+namespace chromeos_update_engine {
+
+// This struct stores all relevant info for an edge that is cut between
+// nodes old_src -> old_dst by creating new vertex new_vertex. The new
+// relationship is:
+// old_src -(read before)-> new_vertex <-(write before)- old_dst
+// new_vertex is a MOVE operation that moves some existing blocks into
+// temp space. The temp extents are, by necessity, stored in new_vertex
+// (as dst extents) and old_dst (as src extents), but they are also broken
+// out into tmp_extents, as the nodes themselves may contain many more
+// extents.
+struct CutEdgeVertexes {
+  Vertex::Index new_vertex;
+  Vertex::Index old_src;
+  Vertex::Index old_dst;
+  std::vector<Extent> tmp_extents;
+};
+
+class InplaceGenerator : public OperationsGenerator {
+ public:
+  // Represents a disk block on the install partition.
+  struct Block {
+    // During install, each block on the install partition will be written
+    // and some may be read (in all likelihood, many will be read).
+    // The reading and writing will be performed by InstallOperations,
+    // each of which has a corresponding vertex in a graph.
+    // A Block object tells which vertex will read or write this block
+    // at install time.
+    // Generally, there will be a vector of Block objects whose length
+    // is the number of blocks on the install partition.
+    Block() : reader(Vertex::kInvalidIndex), writer(Vertex::kInvalidIndex) {}
+    Vertex::Index reader;
+    Vertex::Index writer;
+  };
+
+  InplaceGenerator() = default;
+
+  // Checks all the operations in the graph have a type assigned.
+  static void CheckGraph(const Graph& graph);
+
+  // Modifies blocks read by 'op' so that any blocks referred to by
+  // 'remove_extents' are replaced with blocks from 'replace_extents'.
+  // 'remove_extents' and 'replace_extents' must be the same number of blocks.
+  // Blocks will be substituted in the order listed in the vectors.
+  // E.g. if 'op' reads blocks 1, 2, 3, 4, 5, 6, 7, 8, remove_extents
+  // contains blocks 6, 2, 3, 5, and replace blocks contains
+  // 12, 13, 14, 15, then op will be changed to read from:
+  // 1, 13, 14, 4, 15, 12, 7, 8
+  static void SubstituteBlocks(Vertex* vertex,
+                               const std::vector<Extent>& remove_extents,
+                               const std::vector<Extent>& replace_extents);
+
+  // Cuts 'edges' from 'graph' according to the AU algorithm. This means
+  // for each edge A->B, remove the dependency that B occur before A.
+  // Do this by creating a new operation X that copies from the blocks
+  // specified by the edge's properties to temp space T. Modify B to read
+  // from T rather than the blocks in the edge. Modify A to depend on X,
+  // but not on B. Free space is found by looking in 'blocks'.
+  // Returns true on success.
+  static bool CutEdges(Graph* graph,
+                       const std::set<Edge>& edges,
+                       std::vector<CutEdgeVertexes>* out_cuts);
+
+  // Creates all the edges for the graph. Writers of a block point to
+  // readers of the same block. This is because for an edge A->B, B
+  // must complete before A executes.
+  static void CreateEdges(Graph* graph, const std::vector<Block>& blocks);
+
+  // Takes |op_indexes|, which is effectively a mapping from order in
+  // which the op is performed -> graph vertex index, and produces the
+  // reverse: a mapping from graph vertex index -> op_indexes index.
+  static void GenerateReverseTopoOrderMap(
+      const std::vector<Vertex::Index>& op_indexes,
+      std::vector<std::vector<Vertex::Index>::size_type>* reverse_op_indexes);
+
+  // Sorts the vector |cuts| by its |cuts[].old_dest| member. Order is
+  // determined by the order of elements in op_indexes.
+  static void SortCutsByTopoOrder(const std::vector<Vertex::Index>& op_indexes,
+                                  std::vector<CutEdgeVertexes>* cuts);
+
+  // Given a topologically sorted graph |op_indexes| and |graph|, alters
+  // |op_indexes| to move all the full operations to the end of the vector.
+  // Full operations should not be depended on, so this is safe.
+  static void MoveAndSortFullOpsToBack(Graph* graph,
+                                       std::vector<Vertex::Index>* op_indexes);
+
+  // Returns true iff there are no extents in the graph that refer to temp
+  // blocks. Temp blocks are in the range [kTempBlockStart, kSparseHole).
+  static bool NoTempBlocksRemain(const Graph& graph);
+
+  // Takes a |graph|, which has edges that must be cut, as listed in
+  // |cuts|.  Cuts the edges. Maintains a list in which the operations
+  // will be performed (in |op_indexes|) and the reverse (in
+  // |reverse_op_indexes|).  Cutting edges requires scratch space, and
+  // if insufficient scratch is found, the file is reread and will be
+  // send down (either as REPLACE or REPLACE_BZ).  Returns true on
+  // success.
+  static bool AssignTempBlocks(
+      Graph* graph,
+      const std::string& new_part,
+      BlobFileWriter* blob_file,
+      std::vector<Vertex::Index>* op_indexes,
+      std::vector<std::vector<Vertex::Index>::size_type>* reverse_op_indexes,
+      const std::vector<CutEdgeVertexes>& cuts);
+
+  // Handles allocation of temp blocks to a cut edge by converting the
+  // dest node to a full op. This removes the need for temp blocks, but
+  // comes at the cost of a worse compression ratio.
+  // For example, say we have A->B->A. It would first be cut to form:
+  // A->B->N<-A, where N copies blocks to temp space. If there are no
+  // temp blocks, this function can be called to convert it to the form:
+  // A->B. Now, A is a full operation.
+  static bool ConvertCutToFullOp(Graph* graph,
+                                 const CutEdgeVertexes& cut,
+                                 const std::string& new_part,
+                                 BlobFileWriter* blob_file);
+
+  // Takes a graph, which is not a DAG, which represents the files just
+  // read from disk, and converts it into a DAG by breaking all cycles
+  // and finding temp space to resolve broken edges.
+  // The final order of the nodes is given in |final_order|
+  // Some files may need to be reread from disk, thus |fd| and
+  // |data_file_size| are be passed.
+  // If |scratch_vertex| is not kInvalidIndex, removes it from
+  // |final_order| before returning.
+  // Returns true on success.
+  static bool ConvertGraphToDag(Graph* graph,
+                                const std::string& new_part,
+                                BlobFileWriter* blob_file,
+                                std::vector<Vertex::Index>* final_order,
+                                Vertex::Index scratch_vertex);
+
+  // Creates a dummy REPLACE_BZ node in the given |vertex|. This can be used
+  // to provide scratch space. The node writes |num_blocks| blocks starting at
+  // |start_block|The node should be marked invalid before writing all nodes to
+  // the output file.
+  static void CreateScratchNode(uint64_t start_block,
+                                uint64_t num_blocks,
+                                Vertex* vertex);
+
+  // The |blocks| vector contains a reader and writer for each block on the
+  // filesystem that's being in-place updated. We populate the reader/writer
+  // fields of |blocks| by calling this function.
+  // For each block in |operation| that is read or written, find that block
+  // in |blocks| and set the reader/writer field to the vertex passed.
+  // |graph| is not strictly necessary, but useful for printing out
+  // error messages.
+  static bool AddInstallOpToBlocksVector(const InstallOperation& operation,
+                                         const Graph& graph,
+                                         Vertex::Index vertex,
+                                         std::vector<Block>* blocks);
+
+  // Add a vertex (if |existing_vertex| is kInvalidVertex) or update an
+  // |existing_vertex| with the passed |operation|.
+  // This method will also register the vertex as the reader or writer of the
+  // blocks involved in the operation updating the |blocks| vector. The
+  // |op_name| associated with the Vertex is used for logging purposes.
+  static bool AddInstallOpToGraph(Graph* graph,
+                                  Vertex::Index existing_vertex,
+                                  std::vector<Block>* blocks,
+                                  const InstallOperation& operation,
+                                  const std::string& op_name);
+
+  // Apply the transformation stored in |the_map| to the |collection| vector
+  // replacing the map keys found in |collection| with its associated value in
+  // |the_map|.
+  static void ApplyMap(std::vector<uint64_t>* collection,
+                       const std::map<uint64_t, uint64_t>& the_map);
+
+  // Resolve all read-after-write dependencies in the operation list |aops|. The
+  // operations in |aops| are such that they generate the desired |new_part| if
+  // applied reading always from the original image. This function reorders the
+  // operations and generates new operations when needed to make these
+  // operations produce the same |new_part| result when applied in-place.
+  // The new operations will create blobs in |data_file_fd| and update
+  // the file size pointed by |data_file_size| if needed.
+  // On success, stores the new operations in |aops| in the right order and
+  // returns true.
+  static bool ResolveReadAfterWriteDependencies(
+      const PartitionConfig& old_part,
+      const PartitionConfig& new_part,
+      uint64_t partition_size,
+      size_t block_size,
+      BlobFileWriter* blob_file,
+      std::vector<AnnotatedOperation>* aops);
+
+  // Generate the update payload operations for the given partition using
+  // only operations that read from the target and/or write to the target,
+  // hence, applying the payload "in-place" in the target partition. This method
+  // assumes that the contents of the source image are pre-copied to the target
+  // partition, up to the size of the source image. Use this method to generate
+  // a delta update with the minor version kInPlaceMinorPayloadVersion.
+  // The operations are stored in |aops|. All the offsets in the operations
+  // reference the data written to |blob_file|.
+  bool GenerateOperations(const PayloadGenerationConfig& config,
+                          const PartitionConfig& old_part,
+                          const PartitionConfig& new_part,
+                          BlobFileWriter* blob_file,
+                          std::vector<AnnotatedOperation>* aops) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(InplaceGenerator);
+};
+
+};  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_INPLACE_GENERATOR_H_
diff --git a/payload_generator/inplace_generator_unittest.cc b/payload_generator/inplace_generator_unittest.cc
new file mode 100644
index 0000000..8028f36
--- /dev/null
+++ b/payload_generator/inplace_generator_unittest.cc
@@ -0,0 +1,752 @@
+//
+// 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/payload_generator/inplace_generator.h"
+
+#include <map>
+#include <memory>
+#include <set>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <base/format_macros.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_generator/cycle_breaker.h"
+#include "update_engine/payload_generator/delta_diff_generator.h"
+#include "update_engine/payload_generator/delta_diff_utils.h"
+#include "update_engine/payload_generator/extent_ranges.h"
+#include "update_engine/payload_generator/graph_types.h"
+#include "update_engine/payload_generator/graph_utils.h"
+
+using std::map;
+using std::set;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+using Block = InplaceGenerator::Block;
+
+namespace {
+
+void GenVertex(Vertex* out,
+               const vector<Extent>& src_extents,
+               const vector<Extent>& dst_extents,
+               const string& path,
+               InstallOperation::Type type) {
+  out->aop.op.set_type(type);
+  out->aop.name = path;
+  StoreExtents(src_extents, out->aop.op.mutable_src_extents());
+  StoreExtents(dst_extents, out->aop.op.mutable_dst_extents());
+}
+
+vector<Extent> VectOfExt(uint64_t start_block, uint64_t num_blocks) {
+  return vector<Extent>(1, ExtentForRange(start_block, num_blocks));
+}
+
+EdgeProperties EdgeWithReadDep(const vector<Extent>& extents) {
+  EdgeProperties ret;
+  ret.extents = extents;
+  return ret;
+}
+
+EdgeProperties EdgeWithWriteDep(const vector<Extent>& extents) {
+  EdgeProperties ret;
+  ret.write_extents = extents;
+  return ret;
+}
+
+template <typename T>
+void DumpVect(const vector<T>& vect) {
+  stringstream ss(stringstream::out);
+  for (typename vector<T>::const_iterator it = vect.begin(), e = vect.end();
+       it != e;
+       ++it) {
+    ss << *it << ", ";
+  }
+  LOG(INFO) << "{" << ss.str() << "}";
+}
+
+void AppendExtent(vector<Extent>* vect, uint64_t start, uint64_t length) {
+  vect->resize(vect->size() + 1);
+  vect->back().set_start_block(start);
+  vect->back().set_num_blocks(length);
+}
+
+void OpAppendExtent(InstallOperation* op, uint64_t start, uint64_t length) {
+  Extent* extent = op->add_src_extents();
+  extent->set_start_block(start);
+  extent->set_num_blocks(length);
+}
+
+}  // namespace
+
+class InplaceGeneratorTest : public ::testing::Test {
+ protected:
+  // Initialize |blob_path_|, |blob_file_size_| and |blob_file_fd_| variables
+  // with a new blob file. The file is closed and removed automatically when
+  // the test finishes.
+  void CreateBlobFile() {
+    // blob_fd_closer_ takes a pointer to blob_fd_. Make sure we destroy a
+    // previous instance before overriding blob_fd_.
+    blob_fd_closer_.reset();
+    EXPECT_TRUE(utils::MakeTempFile(
+        "InplaceGenerator_blob_file.XXXXXX", &blob_path_, &blob_fd_));
+    blob_path_unlinker_.reset(new ScopedPathUnlinker(blob_path_));
+    blob_fd_closer_.reset(new ScopedFdCloser(&blob_fd_));
+    blob_file_size_ = 0;
+    EXPECT_GE(blob_fd_, 0);
+    blob_file_.reset(new BlobFileWriter(blob_fd_, &blob_file_size_));
+  }
+
+  // Dump the list of operations |aops| in case of test failure.
+  void DumpAopsOnFailure(const vector<AnnotatedOperation>& aops) {
+    if (HasNonfatalFailure()) {
+      LOG(INFO) << "Result operation list:";
+      for (const auto& aop : aops) {
+        LOG(INFO) << aop;
+      }
+    }
+  }
+
+  // Blob file name, file descriptor and file size used to store operation
+  // blobs.
+  string blob_path_;
+  int blob_fd_{-1};
+  off_t blob_file_size_{0};
+  std::unique_ptr<BlobFileWriter> blob_file_;
+  std::unique_ptr<ScopedPathUnlinker> blob_path_unlinker_;
+  std::unique_ptr<ScopedFdCloser> blob_fd_closer_;
+};
+
+TEST_F(InplaceGeneratorTest, BlockDefaultValues) {
+  // Tests that a Block is initialized with the default values as a
+  // Vertex::kInvalidIndex. This is required by the delta generators.
+  Block block;
+  EXPECT_EQ(Vertex::kInvalidIndex, block.reader);
+  EXPECT_EQ(Vertex::kInvalidIndex, block.writer);
+}
+
+TEST_F(InplaceGeneratorTest, SubstituteBlocksTest) {
+  vector<Extent> remove_blocks;
+  AppendExtent(&remove_blocks, 3, 3);
+  AppendExtent(&remove_blocks, 7, 1);
+  vector<Extent> replace_blocks;
+  AppendExtent(&replace_blocks, 10, 2);
+  AppendExtent(&replace_blocks, 13, 2);
+  Vertex vertex;
+  InstallOperation& op = vertex.aop.op;
+  OpAppendExtent(&op, 4, 3);
+  OpAppendExtent(&op, kSparseHole, 4);  // Sparse hole in file
+  OpAppendExtent(&op, 3, 1);
+  OpAppendExtent(&op, 7, 3);
+
+  InplaceGenerator::SubstituteBlocks(&vertex, remove_blocks, replace_blocks);
+
+  EXPECT_EQ(7, op.src_extents_size());
+  EXPECT_EQ(11U, op.src_extents(0).start_block());
+  EXPECT_EQ(1U, op.src_extents(0).num_blocks());
+  EXPECT_EQ(13U, op.src_extents(1).start_block());
+  EXPECT_EQ(1U, op.src_extents(1).num_blocks());
+  EXPECT_EQ(6U, op.src_extents(2).start_block());
+  EXPECT_EQ(1U, op.src_extents(2).num_blocks());
+  EXPECT_EQ(kSparseHole, op.src_extents(3).start_block());
+  EXPECT_EQ(4U, op.src_extents(3).num_blocks());
+  EXPECT_EQ(10U, op.src_extents(4).start_block());
+  EXPECT_EQ(1U, op.src_extents(4).num_blocks());
+  EXPECT_EQ(14U, op.src_extents(5).start_block());
+  EXPECT_EQ(1U, op.src_extents(5).num_blocks());
+  EXPECT_EQ(8U, op.src_extents(6).start_block());
+  EXPECT_EQ(2U, op.src_extents(6).num_blocks());
+}
+
+TEST_F(InplaceGeneratorTest, CutEdgesTest) {
+  Graph graph;
+  vector<Block> blocks(9);
+
+  // Create nodes in graph
+  {
+    graph.resize(graph.size() + 1);
+    graph.back().aop.op.set_type(InstallOperation::MOVE);
+    // Reads from blocks 3, 5, 7
+    vector<Extent> extents;
+    AppendBlockToExtents(&extents, 3);
+    AppendBlockToExtents(&extents, 5);
+    AppendBlockToExtents(&extents, 7);
+    StoreExtents(extents, graph.back().aop.op.mutable_src_extents());
+    blocks[3].reader = graph.size() - 1;
+    blocks[5].reader = graph.size() - 1;
+    blocks[7].reader = graph.size() - 1;
+
+    // Writes to blocks 1, 2, 4
+    extents.clear();
+    AppendBlockToExtents(&extents, 1);
+    AppendBlockToExtents(&extents, 2);
+    AppendBlockToExtents(&extents, 4);
+    StoreExtents(extents, graph.back().aop.op.mutable_dst_extents());
+    blocks[1].writer = graph.size() - 1;
+    blocks[2].writer = graph.size() - 1;
+    blocks[4].writer = graph.size() - 1;
+  }
+  {
+    graph.resize(graph.size() + 1);
+    graph.back().aop.op.set_type(InstallOperation::MOVE);
+    // Reads from blocks 1, 2, 4
+    vector<Extent> extents;
+    AppendBlockToExtents(&extents, 1);
+    AppendBlockToExtents(&extents, 2);
+    AppendBlockToExtents(&extents, 4);
+    StoreExtents(extents, graph.back().aop.op.mutable_src_extents());
+    blocks[1].reader = graph.size() - 1;
+    blocks[2].reader = graph.size() - 1;
+    blocks[4].reader = graph.size() - 1;
+
+    // Writes to blocks 3, 5, 6
+    extents.clear();
+    AppendBlockToExtents(&extents, 3);
+    AppendBlockToExtents(&extents, 5);
+    AppendBlockToExtents(&extents, 6);
+    StoreExtents(extents, graph.back().aop.op.mutable_dst_extents());
+    blocks[3].writer = graph.size() - 1;
+    blocks[5].writer = graph.size() - 1;
+    blocks[6].writer = graph.size() - 1;
+  }
+
+  // Create edges
+  InplaceGenerator::CreateEdges(&graph, blocks);
+
+  // Find cycles
+  CycleBreaker cycle_breaker;
+  set<Edge> cut_edges;
+  cycle_breaker.BreakCycles(graph, &cut_edges);
+
+  EXPECT_EQ(1U, cut_edges.size());
+  EXPECT_TRUE(cut_edges.end() !=
+              cut_edges.find(std::pair<Vertex::Index, Vertex::Index>(1, 0)));
+
+  vector<CutEdgeVertexes> cuts;
+  EXPECT_TRUE(InplaceGenerator::CutEdges(&graph, cut_edges, &cuts));
+
+  EXPECT_EQ(3U, graph.size());
+
+  // Check new node in graph:
+  EXPECT_EQ(InstallOperation::MOVE, graph.back().aop.op.type());
+  EXPECT_EQ(2, graph.back().aop.op.src_extents_size());
+  EXPECT_EQ(1, graph.back().aop.op.dst_extents_size());
+  EXPECT_EQ(kTempBlockStart, graph.back().aop.op.dst_extents(0).start_block());
+  EXPECT_EQ(2U, graph.back().aop.op.dst_extents(0).num_blocks());
+  EXPECT_TRUE(graph.back().out_edges.empty());
+
+  // Check that old node reads from new blocks
+  EXPECT_EQ(2, graph[0].aop.op.src_extents_size());
+  EXPECT_EQ(kTempBlockStart, graph[0].aop.op.src_extents(0).start_block());
+  EXPECT_EQ(2U, graph[0].aop.op.src_extents(0).num_blocks());
+  EXPECT_EQ(7U, graph[0].aop.op.src_extents(1).start_block());
+  EXPECT_EQ(1U, graph[0].aop.op.src_extents(1).num_blocks());
+
+  // And that the old dst extents haven't changed
+  EXPECT_EQ(2, graph[0].aop.op.dst_extents_size());
+  EXPECT_EQ(1U, graph[0].aop.op.dst_extents(0).start_block());
+  EXPECT_EQ(2U, graph[0].aop.op.dst_extents(0).num_blocks());
+  EXPECT_EQ(4U, graph[0].aop.op.dst_extents(1).start_block());
+  EXPECT_EQ(1U, graph[0].aop.op.dst_extents(1).num_blocks());
+
+  // Ensure it only depends on the next node and the new temp node
+  EXPECT_EQ(2U, graph[0].out_edges.size());
+  EXPECT_TRUE(graph[0].out_edges.end() != graph[0].out_edges.find(1));
+  EXPECT_TRUE(graph[0].out_edges.end() !=
+              graph[0].out_edges.find(graph.size() - 1));
+
+  // Check second node has unchanged extents
+  EXPECT_EQ(2, graph[1].aop.op.src_extents_size());
+  EXPECT_EQ(1U, graph[1].aop.op.src_extents(0).start_block());
+  EXPECT_EQ(2U, graph[1].aop.op.src_extents(0).num_blocks());
+  EXPECT_EQ(4U, graph[1].aop.op.src_extents(1).start_block());
+  EXPECT_EQ(1U, graph[1].aop.op.src_extents(1).num_blocks());
+
+  EXPECT_EQ(2, graph[1].aop.op.dst_extents_size());
+  EXPECT_EQ(3U, graph[1].aop.op.dst_extents(0).start_block());
+  EXPECT_EQ(1U, graph[1].aop.op.dst_extents(0).num_blocks());
+  EXPECT_EQ(5U, graph[1].aop.op.dst_extents(1).start_block());
+  EXPECT_EQ(2U, graph[1].aop.op.dst_extents(1).num_blocks());
+
+  // Ensure it only depends on the next node
+  EXPECT_EQ(1U, graph[1].out_edges.size());
+  EXPECT_TRUE(graph[1].out_edges.end() != graph[1].out_edges.find(2));
+}
+
+TEST_F(InplaceGeneratorTest, AssignTempBlocksReuseTest) {
+  Graph graph(9);
+
+  const vector<Extent> empt;
+  uint64_t tmp = kTempBlockStart;
+  const string kFilename = "/foo";
+
+  vector<CutEdgeVertexes> cuts;
+  cuts.resize(3);
+
+  // Simple broken loop:
+  GenVertex(
+      &graph[0], VectOfExt(0, 1), VectOfExt(1, 1), "", InstallOperation::MOVE);
+  GenVertex(&graph[1],
+            VectOfExt(tmp, 1),
+            VectOfExt(0, 1),
+            "",
+            InstallOperation::MOVE);
+  GenVertex(&graph[2],
+            VectOfExt(1, 1),
+            VectOfExt(tmp, 1),
+            "",
+            InstallOperation::MOVE);
+  // Corresponding edges:
+  graph[0].out_edges[2] = EdgeWithReadDep(VectOfExt(1, 1));
+  graph[1].out_edges[2] = EdgeWithWriteDep(VectOfExt(tmp, 1));
+  graph[1].out_edges[0] = EdgeWithReadDep(VectOfExt(0, 1));
+  // Store the cut:
+  cuts[0].old_dst = 1;
+  cuts[0].old_src = 0;
+  cuts[0].new_vertex = 2;
+  cuts[0].tmp_extents = VectOfExt(tmp, 1);
+  tmp++;
+
+  // Slightly more complex pair of loops:
+  GenVertex(
+      &graph[3], VectOfExt(4, 2), VectOfExt(2, 2), "", InstallOperation::MOVE);
+  GenVertex(
+      &graph[4], VectOfExt(6, 1), VectOfExt(7, 1), "", InstallOperation::MOVE);
+  GenVertex(&graph[5],
+            VectOfExt(tmp, 3),
+            VectOfExt(4, 3),
+            kFilename,
+            InstallOperation::MOVE);
+  GenVertex(&graph[6],
+            VectOfExt(2, 2),
+            VectOfExt(tmp, 2),
+            "",
+            InstallOperation::MOVE);
+  GenVertex(&graph[7],
+            VectOfExt(7, 1),
+            VectOfExt(tmp + 2, 1),
+            "",
+            InstallOperation::MOVE);
+  // Corresponding edges:
+  graph[3].out_edges[6] = EdgeWithReadDep(VectOfExt(2, 2));
+  graph[4].out_edges[7] = EdgeWithReadDep(VectOfExt(7, 1));
+  graph[5].out_edges[6] = EdgeWithWriteDep(VectOfExt(tmp, 2));
+  graph[5].out_edges[7] = EdgeWithWriteDep(VectOfExt(tmp + 2, 1));
+  graph[5].out_edges[3] = EdgeWithReadDep(VectOfExt(4, 2));
+  graph[5].out_edges[4] = EdgeWithReadDep(VectOfExt(6, 1));
+  // Store the cuts:
+  cuts[1].old_dst = 5;
+  cuts[1].old_src = 3;
+  cuts[1].new_vertex = 6;
+  cuts[1].tmp_extents = VectOfExt(tmp, 2);
+  cuts[2].old_dst = 5;
+  cuts[2].old_src = 4;
+  cuts[2].new_vertex = 7;
+  cuts[2].tmp_extents = VectOfExt(tmp + 2, 1);
+
+  // Supplier of temp block:
+  GenVertex(&graph[8], empt, VectOfExt(8, 1), "", InstallOperation::REPLACE);
+
+  // Specify the final order:
+  vector<Vertex::Index> op_indexes;
+  op_indexes.push_back(2);
+  op_indexes.push_back(0);
+  op_indexes.push_back(1);
+  op_indexes.push_back(6);
+  op_indexes.push_back(3);
+  op_indexes.push_back(7);
+  op_indexes.push_back(4);
+  op_indexes.push_back(5);
+  op_indexes.push_back(8);
+
+  vector<vector<Vertex::Index>::size_type> reverse_op_indexes;
+  InplaceGenerator::GenerateReverseTopoOrderMap(op_indexes,
+                                                &reverse_op_indexes);
+
+  CreateBlobFile();
+  EXPECT_TRUE(InplaceGenerator::AssignTempBlocks(&graph,
+                                                 "/dev/zero",
+                                                 blob_file_.get(),
+                                                 &op_indexes,
+                                                 &reverse_op_indexes,
+                                                 cuts));
+  EXPECT_FALSE(graph[6].valid);
+  EXPECT_FALSE(graph[7].valid);
+  EXPECT_EQ(1, graph[1].aop.op.src_extents_size());
+  EXPECT_EQ(2U, graph[1].aop.op.src_extents(0).start_block());
+  EXPECT_EQ(1U, graph[1].aop.op.src_extents(0).num_blocks());
+  EXPECT_EQ(InstallOperation::REPLACE_BZ, graph[5].aop.op.type());
+}
+
+TEST_F(InplaceGeneratorTest, MoveAndSortFullOpsToBackTest) {
+  Graph graph(4);
+  graph[0].aop.name = "A";
+  graph[0].aop.op.set_type(InstallOperation::REPLACE);
+  graph[1].aop.name = "B";
+  graph[1].aop.op.set_type(InstallOperation::BSDIFF);
+  graph[2].aop.name = "C";
+  graph[2].aop.op.set_type(InstallOperation::REPLACE_BZ);
+  graph[3].aop.name = "D";
+  graph[3].aop.op.set_type(InstallOperation::MOVE);
+
+  vector<Vertex::Index> vect(graph.size());
+
+  for (vector<Vertex::Index>::size_type i = 0; i < vect.size(); ++i) {
+    vect[i] = i;
+  }
+  InplaceGenerator::MoveAndSortFullOpsToBack(&graph, &vect);
+  EXPECT_EQ(vect.size(), graph.size());
+  EXPECT_EQ(graph[vect[0]].aop.name, "B");
+  EXPECT_EQ(graph[vect[1]].aop.name, "D");
+  EXPECT_EQ(graph[vect[2]].aop.name, "A");
+  EXPECT_EQ(graph[vect[3]].aop.name, "C");
+}
+
+TEST_F(InplaceGeneratorTest, AssignTempBlocksTest) {
+  Graph graph(9);
+  const vector<Extent> empt;  // empty
+  const string kFilename = "/foo";
+
+  // Some scratch space:
+  GenVertex(&graph[0], empt, VectOfExt(200, 1), "", InstallOperation::REPLACE);
+  GenVertex(&graph[1], empt, VectOfExt(210, 10), "", InstallOperation::REPLACE);
+  GenVertex(&graph[2], empt, VectOfExt(220, 1), "", InstallOperation::REPLACE);
+
+  // A cycle that requires 10 blocks to break:
+  GenVertex(&graph[3],
+            VectOfExt(10, 11),
+            VectOfExt(0, 9),
+            "",
+            InstallOperation::BSDIFF);
+  graph[3].out_edges[4] = EdgeWithReadDep(VectOfExt(0, 9));
+  GenVertex(&graph[4],
+            VectOfExt(0, 9),
+            VectOfExt(10, 11),
+            "",
+            InstallOperation::BSDIFF);
+  graph[4].out_edges[3] = EdgeWithReadDep(VectOfExt(10, 11));
+
+  // A cycle that requires 9 blocks to break:
+  GenVertex(&graph[5],
+            VectOfExt(40, 11),
+            VectOfExt(30, 10),
+            "",
+            InstallOperation::BSDIFF);
+  graph[5].out_edges[6] = EdgeWithReadDep(VectOfExt(30, 10));
+  GenVertex(&graph[6],
+            VectOfExt(30, 10),
+            VectOfExt(40, 11),
+            "",
+            InstallOperation::BSDIFF);
+  graph[6].out_edges[5] = EdgeWithReadDep(VectOfExt(40, 11));
+
+  // A cycle that requires 40 blocks to break (which is too many):
+  GenVertex(&graph[7],
+            VectOfExt(120, 50),
+            VectOfExt(60, 40),
+            "",
+            InstallOperation::BSDIFF);
+  graph[7].out_edges[8] = EdgeWithReadDep(VectOfExt(60, 40));
+  GenVertex(&graph[8],
+            VectOfExt(60, 40),
+            VectOfExt(120, 50),
+            kFilename,
+            InstallOperation::BSDIFF);
+  graph[8].out_edges[7] = EdgeWithReadDep(VectOfExt(120, 50));
+
+  graph_utils::DumpGraph(graph);
+
+  vector<Vertex::Index> final_order;
+
+  CreateBlobFile();
+  EXPECT_TRUE(InplaceGenerator::ConvertGraphToDag(&graph,
+                                                  "/dev/zero",
+                                                  blob_file_.get(),
+                                                  &final_order,
+                                                  Vertex::kInvalidIndex));
+
+  Graph expected_graph(12);
+  GenVertex(&expected_graph[0],
+            empt,
+            VectOfExt(200, 1),
+            "",
+            InstallOperation::REPLACE);
+  GenVertex(&expected_graph[1],
+            empt,
+            VectOfExt(210, 10),
+            "",
+            InstallOperation::REPLACE);
+  GenVertex(&expected_graph[2],
+            empt,
+            VectOfExt(220, 1),
+            "",
+            InstallOperation::REPLACE);
+  GenVertex(&expected_graph[3],
+            VectOfExt(10, 11),
+            VectOfExt(0, 9),
+            "",
+            InstallOperation::BSDIFF);
+  expected_graph[3].out_edges[9] = EdgeWithReadDep(VectOfExt(0, 9));
+  GenVertex(&expected_graph[4],
+            VectOfExt(60, 9),
+            VectOfExt(10, 11),
+            "",
+            InstallOperation::BSDIFF);
+  expected_graph[4].out_edges[3] = EdgeWithReadDep(VectOfExt(10, 11));
+  expected_graph[4].out_edges[9] = EdgeWithWriteDep(VectOfExt(60, 9));
+  GenVertex(&expected_graph[5],
+            VectOfExt(40, 11),
+            VectOfExt(30, 10),
+            "",
+            InstallOperation::BSDIFF);
+  expected_graph[5].out_edges[10] = EdgeWithReadDep(VectOfExt(30, 10));
+
+  GenVertex(&expected_graph[6],
+            VectOfExt(60, 10),
+            VectOfExt(40, 11),
+            "",
+            InstallOperation::BSDIFF);
+  expected_graph[6].out_edges[5] = EdgeWithReadDep(VectOfExt(40, 11));
+  expected_graph[6].out_edges[10] = EdgeWithWriteDep(VectOfExt(60, 10));
+
+  GenVertex(&expected_graph[7],
+            VectOfExt(120, 50),
+            VectOfExt(60, 40),
+            "",
+            InstallOperation::BSDIFF);
+  expected_graph[7].out_edges[6] = EdgeWithReadDep(VectOfExt(60, 10));
+
+  GenVertex(&expected_graph[8],
+            empt,
+            VectOfExt(0, 50),
+            "/foo",
+            InstallOperation::REPLACE_BZ);
+  expected_graph[8].out_edges[7] = EdgeWithReadDep(VectOfExt(120, 50));
+
+  GenVertex(&expected_graph[9],
+            VectOfExt(0, 9),
+            VectOfExt(60, 9),
+            "",
+            InstallOperation::MOVE);
+
+  GenVertex(&expected_graph[10],
+            VectOfExt(30, 10),
+            VectOfExt(60, 10),
+            "",
+            InstallOperation::MOVE);
+  expected_graph[10].out_edges[4] = EdgeWithReadDep(VectOfExt(60, 9));
+
+  EXPECT_EQ(12U, graph.size());
+  EXPECT_FALSE(graph.back().valid);
+  for (Graph::size_type i = 0; i < graph.size() - 1; i++) {
+    EXPECT_TRUE(graph[i].out_edges == expected_graph[i].out_edges);
+    if (i == 8) {
+      // special case
+    } else {
+      // EXPECT_TRUE(graph[i] == expected_graph[i]) << "i = " << i;
+    }
+  }
+}
+
+TEST_F(InplaceGeneratorTest, CreateScratchNodeTest) {
+  Vertex vertex;
+  InplaceGenerator::CreateScratchNode(12, 34, &vertex);
+  EXPECT_EQ(InstallOperation::REPLACE_BZ, vertex.aop.op.type());
+  EXPECT_EQ(0U, vertex.aop.op.data_offset());
+  EXPECT_EQ(0U, vertex.aop.op.data_length());
+  EXPECT_EQ(1, vertex.aop.op.dst_extents_size());
+  EXPECT_EQ(12U, vertex.aop.op.dst_extents(0).start_block());
+  EXPECT_EQ(34U, vertex.aop.op.dst_extents(0).num_blocks());
+}
+
+TEST_F(InplaceGeneratorTest, ApplyMapTest) {
+  vector<uint64_t> collection = {1, 2, 3, 4, 6};
+  vector<uint64_t> expected_values = {1, 2, 5, 4, 8};
+  map<uint64_t, uint64_t> value_map;
+  value_map[3] = 5;
+  value_map[6] = 8;
+  value_map[5] = 10;
+
+  InplaceGenerator::ApplyMap(&collection, value_map);
+  EXPECT_EQ(expected_values, collection);
+}
+
+// We can't produce MOVE operations with a source or destination in the block 0.
+// This test checks that the cycle breaker procedure doesn't produce such
+// operations.
+TEST_F(InplaceGeneratorTest, ResolveReadAfterWriteDependenciesAvoidMoveToZero) {
+  size_t block_size = 4096;
+  size_t num_blocks = 4;
+  vector<AnnotatedOperation> aops;
+
+  // Create a REPLACE_BZ for block 0, and a circular dependency among all other
+  // blocks. This situation would prefer to issue a MOVE to scratch space and
+  // the only available block is 0.
+  aops.emplace_back();
+  aops.back().name = base::StringPrintf("<bz-block-0>");
+  aops.back().op.set_type(InstallOperation::REPLACE_BZ);
+  StoreExtents({ExtentForRange(0, 1)}, aops.back().op.mutable_dst_extents());
+
+  for (size_t i = 1; i < num_blocks; i++) {
+    AnnotatedOperation aop;
+    aop.name = base::StringPrintf("<op-%" PRIuS ">", i);
+    aop.op.set_type(InstallOperation::BSDIFF);
+    StoreExtents({ExtentForRange(1 + i % (num_blocks - 1), 1)},
+                 aop.op.mutable_src_extents());
+    StoreExtents({ExtentForRange(i, 1)}, aop.op.mutable_dst_extents());
+    aops.push_back(aop);
+  }
+
+  PartitionConfig part("part");
+  part.path = "/dev/zero";
+  part.size = num_blocks * block_size;
+
+  CreateBlobFile();
+
+  // We ran two tests here. The first one without enough blocks for the scratch
+  // space, forcing it to create a new full operation and the second case with
+  // one extra block in the partition that can be used for the move operation.
+  for (const auto part_blocks : vector<uint64_t>{num_blocks, num_blocks + 1}) {
+    SCOPED_TRACE(
+        base::StringPrintf("Using partition_blocks=%" PRIu64, part_blocks));
+    vector<AnnotatedOperation> result_aops = aops;
+    EXPECT_TRUE(InplaceGenerator::ResolveReadAfterWriteDependencies(
+        part,
+        part,
+        part_blocks * block_size,
+        block_size,
+        blob_file_.get(),
+        &result_aops));
+
+    size_t full_ops = 0;
+    for (const auto& aop : result_aops) {
+      if (diff_utils::IsAReplaceOperation(aop.op.type()))
+        full_ops++;
+
+      if (aop.op.type() != InstallOperation::MOVE)
+        continue;
+      for (const Extent& extent : aop.op.src_extents()) {
+        EXPECT_NE(0U, extent.start_block())
+            << "On src extents for aop: " << aop;
+      }
+      for (const Extent& extent : aop.op.dst_extents()) {
+        EXPECT_NE(0U, extent.start_block())
+            << "On dst extents for aop: " << aop;
+      }
+    }
+
+    // If there's extra space in the partition, it should not use a new full
+    // operation for it.
+    EXPECT_EQ(part_blocks == num_blocks ? 2U : 1U, full_ops);
+
+    DumpAopsOnFailure(result_aops);
+  }
+}
+
+// Test that we can shrink a filesystem and break cycles.
+TEST_F(InplaceGeneratorTest, ResolveReadAfterWriteDependenciesShrinkData) {
+  size_t block_size = 4096;
+  size_t old_blocks = 10;
+  size_t new_blocks = 8;
+  vector<AnnotatedOperation> aops;
+
+  // Create a loop using the blocks 1-6 and one other operation writing to the
+  // block 7 from outside the new partition. The loop in the blocks 1-6 uses
+  // two-block operations, so it needs two blocks of scratch space. It can't use
+  // the block 0 as scratch space (see previous test) and it can't use the
+  // blocks 7 or 8 due the last move operation.
+
+  aops.emplace_back();
+  aops.back().name = base::StringPrintf("<bz-block-0>");
+  aops.back().op.set_type(InstallOperation::REPLACE_BZ);
+  StoreExtents({ExtentForRange(0, 1)}, aops.back().op.mutable_dst_extents());
+
+  const size_t num_ops = 3;
+  for (size_t i = 0; i < num_ops; i++) {
+    AnnotatedOperation aop;
+    aop.name = base::StringPrintf("<op-%" PRIuS ">", i);
+    aop.op.set_type(InstallOperation::BSDIFF);
+    StoreExtents({ExtentForRange(1 + 2 * i, 2)}, aop.op.mutable_src_extents());
+    StoreExtents({ExtentForRange(1 + 2 * ((i + 1) % num_ops), 2)},
+                 aop.op.mutable_dst_extents());
+    aops.push_back(aop);
+  }
+
+  {
+    AnnotatedOperation aop;
+    aop.name = "<op-shrink>";
+    aop.op.set_type(InstallOperation::BSDIFF);
+    StoreExtents({ExtentForRange(8, 1)}, aop.op.mutable_src_extents());
+    StoreExtents({ExtentForRange(7, 1)}, aop.op.mutable_dst_extents());
+    aops.push_back(aop);
+  }
+
+  PartitionConfig old_part("part");
+  old_part.path = "/dev/zero";
+  old_part.size = old_blocks * block_size;
+
+  PartitionConfig new_part("part");
+  new_part.path = "/dev/zero";
+  new_part.size = new_blocks * block_size;
+
+  CreateBlobFile();
+
+  EXPECT_TRUE(InplaceGenerator::ResolveReadAfterWriteDependencies(
+      old_part,
+      new_part,
+      (old_blocks + 2) * block_size,  // enough scratch space.
+      block_size,
+      blob_file_.get(),
+      &aops));
+
+  size_t full_ops = 0;
+  for (const auto& aop : aops) {
+    if (diff_utils::IsAReplaceOperation(aop.op.type()))
+      full_ops++;
+  }
+  // There should be only one REPLACE* operation, the one we added for block 0.
+  EXPECT_EQ(1U, full_ops);
+
+  // There should be only one MOVE operation, the one used to break the loop
+  // which should write to scratch space past the block 7 (the last block of the
+  // new partition) which is being written later.
+  size_t move_ops = 0;
+  for (const auto& aop : aops) {
+    if (aop.op.type() == InstallOperation::MOVE) {
+      move_ops++;
+      for (const Extent& extent : aop.op.dst_extents()) {
+        EXPECT_LE(7U, extent.start_block())
+            << "On dst extents for aop: " << aop;
+      }
+    }
+  }
+  EXPECT_EQ(1U, move_ops);
+
+  DumpAopsOnFailure(aops);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/mapfile_filesystem_unittest.cc b/payload_generator/mapfile_filesystem_unittest.cc
index 57b672b..36ae3bf 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:
-  ScopedTempFile temp_file_{"mapfile_file.XXXXXX"};
-  ScopedTempFile temp_mapfile_{"mapfile_mapfile.XXXXXX"};
+  test_utils::ScopedTempFile temp_file_{"mapfile_file.XXXXXX"};
+  test_utils::ScopedTempFile temp_mapfile_{"mapfile_mapfile.XXXXXX"};
 };
 
 TEST_F(MapfileFilesystemTest, EmptyFilesystem) {
diff --git a/payload_generator/merge_sequence_generator.cc b/payload_generator/merge_sequence_generator.cc
deleted file mode 100644
index 289e2f8..0000000
--- a/payload_generator/merge_sequence_generator.cc
+++ /dev/null
@@ -1,301 +0,0 @@
-//
-// 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/merge_sequence_generator.h"
-
-#include <algorithm>
-
-#include "update_engine/payload_generator/extent_utils.h"
-
-namespace chromeos_update_engine {
-
-CowMergeOperation CreateCowMergeOperation(const Extent& src_extent,
-                                          const Extent& dst_extent) {
-  CowMergeOperation ret;
-  ret.set_type(CowMergeOperation::COW_COPY);
-  *ret.mutable_src_extent() = src_extent;
-  *ret.mutable_dst_extent() = dst_extent;
-  return ret;
-}
-
-std::ostream& operator<<(std::ostream& os,
-                         const CowMergeOperation& merge_operation) {
-  os << "CowMergeOperation src extent: "
-     << ExtentsToString({merge_operation.src_extent()})
-     << ", dst extent: " << ExtentsToString({merge_operation.dst_extent()});
-  return os;
-}
-
-// The OTA generation guarantees that all blocks in the dst extent will be
-// written only once. So we can use it to order the CowMergeOperation.
-bool operator<(const CowMergeOperation& op1, const CowMergeOperation& op2) {
-  return op1.dst_extent().start_block() < op2.dst_extent().start_block();
-}
-
-bool operator==(const CowMergeOperation& op1, const CowMergeOperation& op2) {
-  return op1.type() == op2.type() && op1.src_extent() == op2.src_extent() &&
-         op1.dst_extent() == op2.dst_extent();
-}
-
-template <typename T>
-constexpr T GetDifference(T first, T second) {
-  T abs_diff = (first > second) ? (first - second) : (second - first);
-  return abs_diff;
-}
-
-void SplitSelfOverlapping(const Extent& src_extent,
-                          const Extent& dst_extent,
-                          std::vector<CowMergeOperation>* sequence) {
-  CHECK_EQ(src_extent.num_blocks(), dst_extent.num_blocks());
-  if (src_extent.start_block() == dst_extent.start_block()) {
-    sequence->emplace_back(CreateCowMergeOperation(src_extent, dst_extent));
-    return;
-  }
-
-  const size_t diff =
-      GetDifference(src_extent.start_block(), dst_extent.start_block());
-  for (size_t i = 0; i < src_extent.num_blocks(); i += diff) {
-    auto num_blocks = std::min<size_t>(diff, src_extent.num_blocks() - i);
-    sequence->emplace_back(CreateCowMergeOperation(
-        ExtentForRange(i + src_extent.start_block(), num_blocks),
-        ExtentForRange(i + dst_extent.start_block(), num_blocks)));
-  }
-}
-
-std::unique_ptr<MergeSequenceGenerator> MergeSequenceGenerator::Create(
-    const std::vector<AnnotatedOperation>& aops) {
-  std::vector<CowMergeOperation> sequence;
-  for (const auto& aop : aops) {
-    // Only handle SOURCE_COPY now for the cow size optimization.
-    if (aop.op.type() != InstallOperation::SOURCE_COPY) {
-      continue;
-    }
-    if (aop.op.dst_extents().size() != 1) {
-      std::vector<Extent> out_extents;
-      ExtentsToVector(aop.op.dst_extents(), &out_extents);
-      LOG(ERROR) << "The dst extents for source_copy expects to be contiguous,"
-                 << " dst extents: " << ExtentsToString(out_extents);
-      return nullptr;
-    }
-
-    // Split the source extents.
-    size_t used_blocks = 0;
-    for (const auto& src_extent : aop.op.src_extents()) {
-      // The dst_extent in the merge sequence will be a subset of
-      // InstallOperation's dst_extent. This will simplify the OTA -> COW
-      // conversion when we install the payload.
-      Extent dst_extent =
-          ExtentForRange(aop.op.dst_extents(0).start_block() + used_blocks,
-                         src_extent.num_blocks());
-
-      // Self-overlapping SOURCE_COPY, must split into multiple non
-      // self-overlapping ops
-      if (ExtentRanges::ExtentsOverlap(src_extent, dst_extent)) {
-        SplitSelfOverlapping(src_extent, dst_extent, &sequence);
-      } else {
-        sequence.emplace_back(CreateCowMergeOperation(src_extent, dst_extent));
-      }
-      used_blocks += src_extent.num_blocks();
-    }
-
-    if (used_blocks != aop.op.dst_extents(0).num_blocks()) {
-      LOG(ERROR) << "Number of blocks in src extents doesn't equal to the"
-                 << " ones in the dst extents, src blocks " << used_blocks
-                 << ", dst blocks " << aop.op.dst_extents(0).num_blocks();
-      return nullptr;
-    }
-  }
-
-  std::sort(sequence.begin(), sequence.end());
-  return std::unique_ptr<MergeSequenceGenerator>(
-      new MergeSequenceGenerator(sequence));
-}
-
-bool MergeSequenceGenerator::FindDependency(
-    std::map<CowMergeOperation, std::set<CowMergeOperation>>* result) const {
-  CHECK(result);
-  LOG(INFO) << "Finding dependencies";
-
-  // Since the OTA operation may reuse some source blocks, use the binary
-  // search on sorted dst extents to find overlaps.
-  std::map<CowMergeOperation, std::set<CowMergeOperation>> merge_after;
-  for (const auto& op : operations_) {
-    // lower bound (inclusive): dst extent's end block >= src extent's start
-    // block.
-    const auto lower_it = std::lower_bound(
-        operations_.begin(),
-        operations_.end(),
-        op,
-        [](const CowMergeOperation& it, const CowMergeOperation& op) {
-          auto dst_end_block =
-              it.dst_extent().start_block() + it.dst_extent().num_blocks() - 1;
-          return dst_end_block < op.src_extent().start_block();
-        });
-    // upper bound: dst extent's start block > src extent's end block
-    const auto upper_it = std::upper_bound(
-        lower_it,
-        operations_.end(),
-        op,
-        [](const CowMergeOperation& op, const CowMergeOperation& it) {
-          auto src_end_block =
-              op.src_extent().start_block() + op.src_extent().num_blocks() - 1;
-          return src_end_block < it.dst_extent().start_block();
-        });
-
-    // TODO(xunchang) skip inserting the empty set to merge_after.
-    if (lower_it == upper_it) {
-      merge_after.insert({op, {}});
-    } else {
-      std::set<CowMergeOperation> operations(lower_it, upper_it);
-      auto it = operations.find(op);
-      if (it != operations.end()) {
-        LOG(INFO) << "Self overlapping " << op;
-        operations.erase(it);
-      }
-      auto ret = merge_after.emplace(op, std::move(operations));
-      // Check the insertion indeed happens.
-      CHECK(ret.second);
-    }
-  }
-
-  *result = std::move(merge_after);
-  return true;
-}
-
-bool MergeSequenceGenerator::Generate(
-    std::vector<CowMergeOperation>* sequence) const {
-  sequence->clear();
-  std::map<CowMergeOperation, std::set<CowMergeOperation>> merge_after;
-  if (!FindDependency(&merge_after)) {
-    LOG(ERROR) << "Failed to find dependencies";
-    return false;
-  }
-
-  LOG(INFO) << "Generating sequence";
-
-  // Use the non-DFS version of the topology sort. So we can control the
-  // operations to discard to break cycles; thus yielding a deterministic
-  // sequence.
-  std::map<CowMergeOperation, int> incoming_edges;
-  for (const auto& it : merge_after) {
-    for (const auto& blocked : it.second) {
-      // Value is default initialized to 0.
-      incoming_edges[blocked] += 1;
-    }
-  }
-
-  std::set<CowMergeOperation> free_operations;
-  for (const auto& op : operations_) {
-    if (incoming_edges.find(op) == incoming_edges.end()) {
-      free_operations.insert(op);
-    }
-  }
-
-  std::vector<CowMergeOperation> merge_sequence;
-  std::set<CowMergeOperation> convert_to_raw;
-  while (!incoming_edges.empty()) {
-    if (!free_operations.empty()) {
-      merge_sequence.insert(
-          merge_sequence.end(), free_operations.begin(), free_operations.end());
-    } else {
-      auto to_convert = incoming_edges.begin()->first;
-      free_operations.insert(to_convert);
-      convert_to_raw.insert(to_convert);
-      LOG(INFO) << "Converting operation to raw " << to_convert;
-    }
-
-    std::set<CowMergeOperation> next_free_operations;
-    for (const auto& op : free_operations) {
-      incoming_edges.erase(op);
-
-      // Now that this particular operation is merged, other operations blocked
-      // by this one may be free. Decrement the count of blocking operations,
-      // and set up the free operations for the next iteration.
-      for (const auto& blocked : merge_after[op]) {
-        auto it = incoming_edges.find(blocked);
-        if (it == incoming_edges.end()) {
-          continue;
-        }
-
-        auto blocking_transfer_count = &it->second;
-        if (*blocking_transfer_count <= 0) {
-          LOG(ERROR) << "Unexpected count in merge after map "
-                     << blocking_transfer_count;
-          return false;
-        }
-        // This operation is no longer blocked by anyone. Add it to the merge
-        // sequence in the next iteration.
-        *blocking_transfer_count -= 1;
-        if (*blocking_transfer_count == 0) {
-          next_free_operations.insert(blocked);
-        }
-      }
-    }
-
-    LOG(INFO) << "Remaining transfers " << incoming_edges.size()
-              << ", free transfers " << free_operations.size()
-              << ", merge_sequence size " << merge_sequence.size();
-    free_operations = std::move(next_free_operations);
-  }
-
-  if (!free_operations.empty()) {
-    merge_sequence.insert(
-        merge_sequence.end(), free_operations.begin(), free_operations.end());
-  }
-
-  CHECK_EQ(operations_.size(), merge_sequence.size() + convert_to_raw.size());
-
-  size_t blocks_in_sequence = 0;
-  for (const CowMergeOperation& transfer : merge_sequence) {
-    blocks_in_sequence += transfer.dst_extent().num_blocks();
-  }
-
-  size_t blocks_in_raw = 0;
-  for (const CowMergeOperation& transfer : convert_to_raw) {
-    blocks_in_raw += transfer.dst_extent().num_blocks();
-  }
-
-  LOG(INFO) << "Blocks in merge sequence " << blocks_in_sequence
-            << ", blocks in raw " << blocks_in_raw;
-  if (!ValidateSequence(merge_sequence)) {
-    return false;
-  }
-
-  *sequence = std::move(merge_sequence);
-  return true;
-}
-
-bool MergeSequenceGenerator::ValidateSequence(
-    const std::vector<CowMergeOperation>& sequence) {
-  LOG(INFO) << "Validating merge sequence";
-  ExtentRanges visited;
-  for (const auto& op : sequence) {
-    if (visited.OverlapsWithExtent(op.src_extent())) {
-      LOG(ERROR) << "Transfer violates the merge sequence " << op
-                 << "Visited extent ranges: ";
-      visited.Dump();
-      return false;
-    }
-
-    CHECK(!visited.OverlapsWithExtent(op.dst_extent()))
-        << "dst extent should write only once.";
-    visited.AddExtent(op.dst_extent());
-  }
-
-  return true;
-}
-
-}  // namespace chromeos_update_engine
diff --git a/payload_generator/merge_sequence_generator.h b/payload_generator/merge_sequence_generator.h
deleted file mode 100644
index 385fcc3..0000000
--- a/payload_generator/merge_sequence_generator.h
+++ /dev/null
@@ -1,78 +0,0 @@
-//
-// 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_PAYLOAD_GENERATOR_MERGE_SEQUENCE_GENERATOR_H_
-#define UPDATE_ENGINE_PAYLOAD_GENERATOR_MERGE_SEQUENCE_GENERATOR_H_
-
-#include <map>
-#include <memory>
-#include <set>
-#include <utility>
-#include <vector>
-
-#include "update_engine/payload_generator/annotated_operation.h"
-#include "update_engine/payload_generator/extent_ranges.h"
-#include "update_engine/payload_generator/extent_utils.h"
-#include "update_engine/update_metadata.pb.h"
-
-namespace chromeos_update_engine {
-// Constructs CowMergeOperation from src & dst extents
-CowMergeOperation CreateCowMergeOperation(const Extent& src_extent,
-                                          const Extent& dst_extent);
-
-// Comparator for CowMergeOperation.
-bool operator<(const CowMergeOperation& op1, const CowMergeOperation& op2);
-bool operator==(const CowMergeOperation& op1, const CowMergeOperation& op2);
-
-std::ostream& operator<<(std::ostream& os,
-                         const CowMergeOperation& merge_operation);
-
-// This class takes a list of CowMergeOperations; and sorts them so that no
-// read after write will happen by following the sequence. When there is a
-// cycle, we will omit some operations in the list. Therefore, the result
-// sequence may not contain all blocks in the input list.
-class MergeSequenceGenerator {
- public:
-  // Creates an object from a list of OTA InstallOperations. Returns nullptr on
-  // failure.
-  static std::unique_ptr<MergeSequenceGenerator> Create(
-      const std::vector<AnnotatedOperation>& aops);
-  // Checks that no read after write happens in the given sequence.
-  static bool ValidateSequence(const std::vector<CowMergeOperation>& sequence);
-
-  // Generates a merge sequence from |operations_|, puts the result in
-  // |sequence|. Returns false on failure.
-  bool Generate(std::vector<CowMergeOperation>* sequence) const;
-
- private:
-  friend class MergeSequenceGeneratorTest;
-  explicit MergeSequenceGenerator(std::vector<CowMergeOperation> transfers)
-      : operations_(std::move(transfers)) {}
-
-  // For a given merge operation, finds all the operations that should merge
-  // after myself. Put the result in |merge_after|.
-  bool FindDependency(std::map<CowMergeOperation, std::set<CowMergeOperation>>*
-                          merge_after) const;
-  // The list of CowMergeOperations to sort.
-  std::vector<CowMergeOperation> operations_;
-};
-
-void SplitSelfOverlapping(const Extent& src_extent,
-                          const Extent& dst_extent,
-                          std::vector<CowMergeOperation>* sequence);
-
-}  // namespace chromeos_update_engine
-#endif
diff --git a/payload_generator/merge_sequence_generator_unittest.cc b/payload_generator/merge_sequence_generator_unittest.cc
deleted file mode 100644
index b8507ed..0000000
--- a/payload_generator/merge_sequence_generator_unittest.cc
+++ /dev/null
@@ -1,250 +0,0 @@
-//
-// 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 <vector>
-
-#include <gtest/gtest.h>
-
-#include "update_engine/payload_consumer/payload_constants.h"
-#include "update_engine/payload_generator/extent_ranges.h"
-#include "update_engine/payload_generator/extent_utils.h"
-#include "update_engine/payload_generator/merge_sequence_generator.h"
-
-namespace chromeos_update_engine {
-class MergeSequenceGeneratorTest : public ::testing::Test {
- protected:
-  void VerifyTransfers(MergeSequenceGenerator* generator,
-                       const std::vector<CowMergeOperation>& expected) {
-    ASSERT_EQ(expected, generator->operations_);
-  }
-
-  void FindDependency(
-      std::vector<CowMergeOperation> transfers,
-      std::map<CowMergeOperation, std::set<CowMergeOperation>>* result) {
-    std::sort(transfers.begin(), transfers.end());
-    MergeSequenceGenerator generator(std::move(transfers));
-    ASSERT_TRUE(generator.FindDependency(result));
-  }
-
-  void GenerateSequence(std::vector<CowMergeOperation> transfers,
-                        const std::vector<CowMergeOperation>& expected) {
-    std::sort(transfers.begin(), transfers.end());
-    MergeSequenceGenerator generator(std::move(transfers));
-    std::vector<CowMergeOperation> sequence;
-    ASSERT_TRUE(generator.Generate(&sequence));
-    ASSERT_EQ(expected, sequence);
-  }
-};
-
-TEST_F(MergeSequenceGeneratorTest, Create) {
-  std::vector<AnnotatedOperation> aops{{"file1", {}}, {"file2", {}}};
-  aops[0].op.set_type(InstallOperation::SOURCE_COPY);
-  *aops[0].op.add_src_extents() = ExtentForRange(10, 10);
-  *aops[0].op.add_dst_extents() = ExtentForRange(30, 10);
-
-  aops[1].op.set_type(InstallOperation::SOURCE_COPY);
-  *aops[1].op.add_src_extents() = ExtentForRange(20, 10);
-  *aops[1].op.add_dst_extents() = ExtentForRange(40, 10);
-
-  auto generator = MergeSequenceGenerator::Create(aops);
-  ASSERT_TRUE(generator);
-  std::vector<CowMergeOperation> expected = {
-      CreateCowMergeOperation(ExtentForRange(10, 10), ExtentForRange(30, 10)),
-      CreateCowMergeOperation(ExtentForRange(20, 10), ExtentForRange(40, 10))};
-  VerifyTransfers(generator.get(), expected);
-
-  *aops[1].op.add_src_extents() = ExtentForRange(30, 5);
-  *aops[1].op.add_dst_extents() = ExtentForRange(50, 5);
-  generator = MergeSequenceGenerator::Create(aops);
-  ASSERT_FALSE(generator);
-}
-
-TEST_F(MergeSequenceGeneratorTest, Create_SplitSource) {
-  InstallOperation op;
-  op.set_type(InstallOperation::SOURCE_COPY);
-  *(op.add_src_extents()) = ExtentForRange(2, 3);
-  *(op.add_src_extents()) = ExtentForRange(6, 1);
-  *(op.add_src_extents()) = ExtentForRange(8, 4);
-  *(op.add_dst_extents()) = ExtentForRange(10, 8);
-
-  AnnotatedOperation aop{"file1", op};
-  auto generator = MergeSequenceGenerator::Create({aop});
-  ASSERT_TRUE(generator);
-  std::vector<CowMergeOperation> expected = {
-      CreateCowMergeOperation(ExtentForRange(2, 3), ExtentForRange(10, 3)),
-      CreateCowMergeOperation(ExtentForRange(6, 1), ExtentForRange(13, 1)),
-      CreateCowMergeOperation(ExtentForRange(8, 4), ExtentForRange(14, 4))};
-  VerifyTransfers(generator.get(), expected);
-}
-
-TEST_F(MergeSequenceGeneratorTest, FindDependency) {
-  std::vector<CowMergeOperation> transfers = {
-      CreateCowMergeOperation(ExtentForRange(10, 10), ExtentForRange(15, 10)),
-      CreateCowMergeOperation(ExtentForRange(40, 10), ExtentForRange(50, 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]));
-
-  transfers = {
-      CreateCowMergeOperation(ExtentForRange(10, 10), ExtentForRange(25, 10)),
-      CreateCowMergeOperation(ExtentForRange(24, 5), ExtentForRange(35, 5)),
-      CreateCowMergeOperation(ExtentForRange(30, 10), ExtentForRange(15, 10)),
-  };
-
-  FindDependency(transfers, &merge_after);
-  ASSERT_EQ(std::set<CowMergeOperation>({transfers[2]}),
-            merge_after.at(transfers[0]));
-  ASSERT_EQ(std::set<CowMergeOperation>({transfers[0], transfers[2]}),
-            merge_after.at(transfers[1]));
-  ASSERT_EQ(std::set<CowMergeOperation>({transfers[0], transfers[1]}),
-            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)),
-      CreateCowMergeOperation(ExtentForRange(6, 5), ExtentForRange(30, 5)),
-      CreateCowMergeOperation(ExtentForRange(50, 5), ExtentForRange(5, 5)),
-  };
-
-  std::map<CowMergeOperation, std::set<CowMergeOperation>> merge_after;
-  FindDependency(transfers, &merge_after);
-  ASSERT_EQ(std::set<CowMergeOperation>({transfers[2]}),
-            merge_after.at(transfers[0]));
-  ASSERT_EQ(std::set<CowMergeOperation>({transfers[2]}),
-            merge_after.at(transfers[1]));
-}
-
-TEST_F(MergeSequenceGeneratorTest, ValidateSequence) {
-  std::vector<CowMergeOperation> transfers = {
-      CreateCowMergeOperation(ExtentForRange(10, 10), ExtentForRange(15, 10)),
-      CreateCowMergeOperation(ExtentForRange(30, 10), ExtentForRange(40, 10)),
-  };
-
-  // Self overlapping
-  ASSERT_TRUE(MergeSequenceGenerator::ValidateSequence(transfers));
-
-  transfers = {
-      CreateCowMergeOperation(ExtentForRange(30, 10), ExtentForRange(20, 10)),
-      CreateCowMergeOperation(ExtentForRange(15, 10), ExtentForRange(10, 10)),
-  };
-  ASSERT_FALSE(MergeSequenceGenerator::ValidateSequence(transfers));
-}
-
-TEST_F(MergeSequenceGeneratorTest, GenerateSequenceNoCycles) {
-  std::vector<CowMergeOperation> transfers = {
-      CreateCowMergeOperation(ExtentForRange(10, 10), ExtentForRange(15, 10)),
-      // file3 should merge before file2
-      CreateCowMergeOperation(ExtentForRange(40, 5), ExtentForRange(25, 5)),
-      CreateCowMergeOperation(ExtentForRange(25, 10), ExtentForRange(30, 10)),
-  };
-
-  std::vector<CowMergeOperation> expected{
-      transfers[0], transfers[2], transfers[1]};
-  GenerateSequence(transfers, expected);
-}
-
-TEST_F(MergeSequenceGeneratorTest, GenerateSequenceWithCycles) {
-  std::vector<CowMergeOperation> transfers = {
-      CreateCowMergeOperation(ExtentForRange(25, 10), ExtentForRange(30, 10)),
-      CreateCowMergeOperation(ExtentForRange(30, 10), ExtentForRange(40, 10)),
-      CreateCowMergeOperation(ExtentForRange(40, 10), ExtentForRange(25, 10)),
-      CreateCowMergeOperation(ExtentForRange(10, 10), ExtentForRange(15, 10)),
-  };
-
-  // file 1,2,3 form a cycle. And file3, whose dst ext has smallest offset, will
-  // be converted to raw blocks
-  std::vector<CowMergeOperation> expected{
-      transfers[3], transfers[1], transfers[0]};
-  GenerateSequence(transfers, expected);
-}
-
-TEST_F(MergeSequenceGeneratorTest, GenerateSequenceMultipleCycles) {
-  std::vector<CowMergeOperation> transfers = {
-      // cycle 1
-      CreateCowMergeOperation(ExtentForRange(10, 10), ExtentForRange(25, 10)),
-      CreateCowMergeOperation(ExtentForRange(24, 5), ExtentForRange(35, 5)),
-      CreateCowMergeOperation(ExtentForRange(30, 10), ExtentForRange(15, 10)),
-      // cycle 2
-      CreateCowMergeOperation(ExtentForRange(55, 10), ExtentForRange(60, 10)),
-      CreateCowMergeOperation(ExtentForRange(60, 10), ExtentForRange(70, 10)),
-      CreateCowMergeOperation(ExtentForRange(70, 10), ExtentForRange(55, 10)),
-  };
-
-  // file 3, 6 will be converted to raw.
-  std::vector<CowMergeOperation> expected{
-      transfers[1], transfers[0], transfers[4], transfers[3]};
-  GenerateSequence(transfers, expected);
-}
-
-void ValidateSplitSequence(const Extent& src_extent, const Extent& dst_extent) {
-  std::vector<CowMergeOperation> sequence;
-  SplitSelfOverlapping(src_extent, dst_extent, &sequence);
-  ExtentRanges src_extent_set;
-  src_extent_set.AddExtent(src_extent);
-  ExtentRanges dst_extent_set;
-  dst_extent_set.AddExtent(dst_extent);
-
-  size_t src_block_count = 0;
-  size_t dst_block_count = 0;
-  std::cout << "src_extent: " << src_extent << " dst_extent: " << dst_extent
-            << '\n';
-  for (const auto& merge_op : sequence) {
-    src_extent_set.SubtractExtent(merge_op.src_extent());
-    dst_extent_set.SubtractExtent(merge_op.dst_extent());
-    src_block_count += merge_op.src_extent().num_blocks();
-    dst_block_count += merge_op.dst_extent().num_blocks();
-    std::cout << merge_op.src_extent() << " -> " << merge_op.dst_extent()
-              << '\n';
-    ASSERT_FALSE(ExtentRanges::ExtentsOverlap(merge_op.src_extent(),
-                                              merge_op.dst_extent()));
-  }
-  std::cout << '\n';
-  // Check that all blocks are covered
-  ASSERT_EQ(src_extent_set.extent_set().size(), 0UL);
-  ASSERT_EQ(dst_extent_set.extent_set().size(), 0UL);
-
-  // Check that the split didn't cover extra blocks
-  ASSERT_EQ(src_block_count, src_extent.num_blocks());
-  ASSERT_EQ(dst_block_count, dst_extent.num_blocks());
-}
-
-TEST_F(MergeSequenceGeneratorTest, SplitSelfOverlappingTest) {
-  auto a = ExtentForRange(25, 16);
-  auto b = ExtentForRange(30, 16);
-  ValidateSplitSequence(a, b);
-  ValidateSplitSequence(b, a);
-}
-
-}  // namespace chromeos_update_engine
diff --git a/payload_generator/payload_file.cc b/payload_generator/payload_file.cc
index 6ec219f..a111fd6 100644
--- a/payload_generator/payload_file.cc
+++ b/payload_generator/payload_file.cc
@@ -25,7 +25,6 @@
 #include <base/strings/stringprintf.h>
 
 #include "update_engine/common/hash_calculator.h"
-#include "update_engine/common/utils.h"
 #include "update_engine/payload_consumer/delta_performer.h"
 #include "update_engine/payload_consumer/file_writer.h"
 #include "update_engine/payload_consumer/payload_constants.h"
@@ -65,46 +64,40 @@
   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);
 
-  if (config.target.dynamic_partition_metadata != nullptr)
-    *(manifest_.mutable_dynamic_partition_metadata()) =
-        *(config.target.dynamic_partition_metadata);
-
-  if (config.is_partial_update) {
-    manifest_.set_partial_update(true);
+  if (major_version_ == kBrilloMajorPayloadVersion) {
+    if (config.target.dynamic_partition_metadata != nullptr)
+      *(manifest_.mutable_dynamic_partition_metadata()) =
+          *(config.target.dynamic_partition_metadata);
   }
 
-  if (!config.apex_info_file.empty()) {
-    ApexMetadata apex_metadata;
-    int fd = open(config.apex_info_file.c_str(), O_RDONLY);
-    if (fd < 0) {
-      PLOG(FATAL) << "Failed to open " << config.apex_info_file << " for read.";
-    }
-    ScopedFdCloser closer{&fd};
-    CHECK(apex_metadata.ParseFromFileDescriptor(fd));
-    if (apex_metadata.apex_info_size() > 0) {
-      *manifest_.mutable_apex_info() =
-          std::move(*apex_metadata.mutable_apex_info());
-    }
-  }
   return true;
 }
 
 bool PayloadFile::AddPartition(const PartitionConfig& old_conf,
                                const PartitionConfig& new_conf,
-                               vector<AnnotatedOperation> aops,
-                               vector<CowMergeOperation> merge_sequence,
-                               size_t cow_size) {
+                               const vector<AnnotatedOperation>& aops) {
+  // Check partitions order for Chrome OS
+  if (major_version_ == kChromeOSMajorPayloadVersion) {
+    const vector<const char*> part_order = {kPartitionNameRoot,
+                                            kPartitionNameKernel};
+    TEST_AND_RETURN_FALSE(part_vec_.size() < part_order.size());
+    TEST_AND_RETURN_FALSE(new_conf.name == part_order[part_vec_.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);
+  part.aops = aops;
   part.postinstall = new_conf.postinstall;
   part.verity = new_conf.verity;
-  part.version = new_conf.version;
   // Initialize the PartitionInfo objects if present.
   if (!old_conf.path.empty())
     TEST_AND_RETURN_FALSE(
@@ -120,9 +113,11 @@
                                const string& private_key_path,
                                uint64_t* metadata_size_out) {
   // Reorder the data blobs with the manifest_.
-  ScopedTempFile ordered_blobs_file("CrAU_temp_data.ordered.XXXXXX");
-  TEST_AND_RETURN_FALSE(
-      ReorderDataBlobs(data_blobs_path, ordered_blobs_file.path()));
+  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));
 
   // Check that install op blobs are in order.
   uint64_t next_blob_offset = 0;
@@ -139,61 +134,75 @@
   }
 
   // Copy the operations and partition info from the part_vec_ to the manifest.
+  manifest_.clear_install_operations();
+  manifest_.clear_kernel_install_operations();
   manifest_.clear_partitions();
   for (const auto& part : part_vec_) {
-    PartitionUpdate* partition = manifest_.add_partitions();
-    partition->set_partition_name(part.name);
-    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())
-        partition->set_postinstall_path(part.postinstall.path);
-      if (!part.postinstall.filesystem_type.empty())
-        partition->set_filesystem_type(part.postinstall.filesystem_type);
-      partition->set_postinstall_optional(part.postinstall.optional);
-    }
-    if (!part.verity.IsEmpty()) {
-      if (part.verity.hash_tree_extent.num_blocks() != 0) {
-        *partition->mutable_hash_tree_data_extent() =
-            part.verity.hash_tree_data_extent;
-        *partition->mutable_hash_tree_extent() = part.verity.hash_tree_extent;
-        partition->set_hash_tree_algorithm(part.verity.hash_tree_algorithm);
-        if (!part.verity.hash_tree_salt.empty())
-          partition->set_hash_tree_salt(part.verity.hash_tree_salt.data(),
-                                        part.verity.hash_tree_salt.size());
+    if (major_version_ == kBrilloMajorPayloadVersion) {
+      PartitionUpdate* partition = manifest_.add_partitions();
+      partition->set_partition_name(part.name);
+      if (part.postinstall.run) {
+        partition->set_run_postinstall(true);
+        if (!part.postinstall.path.empty())
+          partition->set_postinstall_path(part.postinstall.path);
+        if (!part.postinstall.filesystem_type.empty())
+          partition->set_filesystem_type(part.postinstall.filesystem_type);
+        partition->set_postinstall_optional(part.postinstall.optional);
       }
-      if (part.verity.fec_extent.num_blocks() != 0) {
-        *partition->mutable_fec_data_extent() = part.verity.fec_data_extent;
-        *partition->mutable_fec_extent() = part.verity.fec_extent;
-        partition->set_fec_roots(part.verity.fec_roots);
+      if (!part.verity.IsEmpty()) {
+        if (part.verity.hash_tree_extent.num_blocks() != 0) {
+          *partition->mutable_hash_tree_data_extent() =
+              part.verity.hash_tree_data_extent;
+          *partition->mutable_hash_tree_extent() = part.verity.hash_tree_extent;
+          partition->set_hash_tree_algorithm(part.verity.hash_tree_algorithm);
+          if (!part.verity.hash_tree_salt.empty())
+            partition->set_hash_tree_salt(part.verity.hash_tree_salt.data(),
+                                          part.verity.hash_tree_salt.size());
+        }
+        if (part.verity.fec_extent.num_blocks() != 0) {
+          *partition->mutable_fec_data_extent() = part.verity.fec_data_extent;
+          *partition->mutable_fec_extent() = part.verity.fec_extent;
+          partition->set_fec_roots(part.verity.fec_roots);
+        }
+      }
+      for (const AnnotatedOperation& aop : part.aops) {
+        *partition->add_operations() = aop.op;
+      }
+      if (part.old_info.has_size() || part.old_info.has_hash())
+        *(partition->mutable_old_partition_info()) = part.old_info;
+      if (part.new_info.has_size() || part.new_info.has_hash())
+        *(partition->mutable_new_partition_info()) = part.new_info;
+    } else {
+      // major_version_ == kChromeOSMajorPayloadVersion
+      if (part.name == kPartitionNameKernel) {
+        for (const AnnotatedOperation& aop : part.aops)
+          *manifest_.add_kernel_install_operations() = aop.op;
+        if (part.old_info.has_size() || part.old_info.has_hash())
+          *manifest_.mutable_old_kernel_info() = part.old_info;
+        if (part.new_info.has_size() || part.new_info.has_hash())
+          *manifest_.mutable_new_kernel_info() = part.new_info;
+      } else {
+        for (const AnnotatedOperation& aop : part.aops)
+          *manifest_.add_install_operations() = aop.op;
+        if (part.old_info.has_size() || part.old_info.has_hash())
+          *manifest_.mutable_old_rootfs_info() = part.old_info;
+        if (part.new_info.has_size() || part.new_info.has_hash())
+          *manifest_.mutable_new_rootfs_info() = part.new_info;
       }
     }
-    for (const AnnotatedOperation& aop : part.aops) {
-      *partition->add_operations() = aop.op;
-    }
-    for (const auto& merge_op : part.cow_merge_sequence) {
-      *partition->add_merge_operations() = merge_op;
-    }
-
-    if (part.old_info.has_size() || part.old_info.has_hash())
-      *(partition->mutable_old_partition_info()) = part.old_info;
-    if (part.new_info.has_size() || part.new_info.has_hash())
-      *(partition->mutable_new_partition_info()) = part.new_info;
   }
 
   // Signatures appear at the end of the blobs. Note the offset in the
-  // |manifest_|.
+  // manifest_.
   uint64_t signature_blob_length = 0;
   if (!private_key_path.empty()) {
     TEST_AND_RETURN_FALSE(PayloadSigner::SignatureBlobLength(
         {private_key_path}, &signature_blob_length));
     PayloadSigner::AddSignatureToManifest(
-        next_blob_offset, signature_blob_length, &manifest_);
+        next_blob_offset,
+        signature_blob_length,
+        major_version_ == kChromeOSMajorPayloadVersion,
+        &manifest_);
   }
 
   // Serialize protobuf
@@ -220,14 +229,18 @@
   TEST_AND_RETURN_FALSE(
       WriteUint64AsBigEndian(&writer, serialized_manifest.size()));
 
-  // Metadata signature has the same size as payload signature, because they
-  // are both the same kind of signature for the same kind of hash.
-  uint32_t metadata_signature_size = htobe32(signature_blob_length);
-  TEST_AND_RETURN_FALSE_ERRNO(
-      writer.Write(&metadata_signature_size, sizeof(metadata_signature_size)));
-  metadata_size += sizeof(metadata_signature_size);
-  // Set correct size instead of big endian size.
-  metadata_signature_size = signature_blob_length;
+  // Write metadata signature size.
+  uint32_t metadata_signature_size = 0;
+  if (major_version_ == kBrilloMajorPayloadVersion) {
+    // Metadata signature has the same size as payload signature, because they
+    // are both the same kind of signature for the same kind of hash.
+    uint32_t metadata_signature_size = htobe32(signature_blob_length);
+    TEST_AND_RETURN_FALSE_ERRNO(writer.Write(&metadata_signature_size,
+                                             sizeof(metadata_signature_size)));
+    metadata_size += sizeof(metadata_signature_size);
+    // Set correct size instead of big endian size.
+    metadata_signature_size = signature_blob_length;
+  }
 
   // Write protobuf
   LOG(INFO) << "Writing final delta file protobuf... "
@@ -236,7 +249,8 @@
       writer.Write(serialized_manifest.data(), serialized_manifest.size()));
 
   // Write metadata signature blob.
-  if (!private_key_path.empty()) {
+  if (major_version_ == kBrilloMajorPayloadVersion &&
+      !private_key_path.empty()) {
     brillo::Blob metadata_hash;
     TEST_AND_RETURN_FALSE(HashCalculator::RawHashOfFile(
         payload_file, metadata_size, &metadata_hash));
@@ -247,9 +261,9 @@
         writer.Write(metadata_signature.data(), metadata_signature.size()));
   }
 
-  // Append the data blobs.
+  // Append the data blobs
   LOG(INFO) << "Writing final delta file data blobs...";
-  int blobs_fd = open(ordered_blobs_file.path().c_str(), O_RDONLY, 0);
+  int blobs_fd = open(ordered_blobs_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 3a45793..9dc80a7 100644
--- a/payload_generator/payload_file.h
+++ b/payload_generator/payload_file.h
@@ -43,9 +43,7 @@
   // reference a blob stored in the file provided to WritePayload().
   bool AddPartition(const PartitionConfig& old_conf,
                     const PartitionConfig& new_conf,
-                    std::vector<AnnotatedOperation> aops,
-                    std::vector<CowMergeOperation> merge_sequence,
-                    size_t cow_size);
+                    const std::vector<AnnotatedOperation>& aops);
 
   // 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
@@ -62,9 +60,9 @@
   // Computes a SHA256 hash of the given buf and sets the hash value in the
   // operation so that update_engine could verify. This hash should be set
   // for all operations that have a non-zero data blob. One exception is the
-  // fake operation for signature blob because the contents of the signature
+  // dummy operation for signature blob because the contents of the signature
   // blob will not be available at payload creation time. So, update_engine will
-  // gracefully ignore the fake signature operation.
+  // gracefully ignore the dummy signature operation.
   static bool AddOperationHash(InstallOperation* op, const brillo::Blob& buf);
 
   // Install operations in the manifest may reference data blobs, which
@@ -92,16 +90,12 @@
 
     // The operations to be performed to this partition.
     std::vector<AnnotatedOperation> aops;
-    std::vector<CowMergeOperation> cow_merge_sequence;
 
     PartitionInfo old_info;
     PartitionInfo new_info;
 
     PostInstallConfig postinstall;
     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 1fd36f5..45faebb 100644
--- a/payload_generator/payload_file_unittest.cc
+++ b/payload_generator/payload_file_unittest.cc
@@ -36,7 +36,7 @@
 };
 
 TEST_F(PayloadFileTest, ReorderBlobsTest) {
-  ScopedTempFile orig_blobs("ReorderBlobsTest.orig.XXXXXX");
+  test_utils::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));
 
-  ScopedTempFile new_blobs("ReorderBlobsTest.new.XXXXXX");
+  test_utils::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 2cd2ebc..88cca30 100644
--- a/payload_generator/payload_generation_config.cc
+++ b/payload_generator/payload_generation_config.cc
@@ -23,7 +23,6 @@
 #include <base/logging.h>
 #include <base/strings/string_number_conversions.h>
 #include <brillo/strings/string_utils.h>
-#include <libsnapshot/cow_format.h>
 
 #include "update_engine/common/utils.h"
 #include "update_engine/payload_consumer/delta_performer.h"
@@ -33,7 +32,6 @@
 #include "update_engine/payload_generator/ext2_filesystem.h"
 #include "update_engine/payload_generator/mapfile_filesystem.h"
 #include "update_engine/payload_generator/raw_filesystem.h"
-#include "update_engine/payload_generator/squashfs_filesystem.h"
 
 using std::string;
 
@@ -88,14 +86,6 @@
     return true;
   }
 
-  fs_interface = SquashfsFilesystem::CreateFromFile(path,
-                                                    /*extract_deflates=*/true,
-                                                    /*load_settings=*/true);
-  if (fs_interface) {
-    TEST_AND_RETURN_FALSE(fs_interface->GetBlockSize() == kBlockSize);
-    return true;
-  }
-
   // Fall back to a RAW filesystem.
   TEST_AND_RETURN_FALSE(size % kBlockSize == 0);
   fs_interface = RawFilesystem::Create(
@@ -104,6 +94,7 @@
 }
 
 bool ImageConfig::ValidateIsEmpty() const {
+  TEST_AND_RETURN_FALSE(ImageInfoIsEmpty());
   return partitions.empty();
 }
 
@@ -177,17 +168,7 @@
   bool snapshot_enabled = false;
   store.GetBoolean("virtual_ab", &snapshot_enabled);
   metadata->set_snapshot_enabled(snapshot_enabled);
-  bool vabc_enabled = false;
-  if (store.GetBoolean("virtual_ab_compression", &vabc_enabled) &&
-      vabc_enabled) {
-    LOG(INFO) << "Target build supports VABC";
-    metadata->set_vabc_enabled(vabc_enabled);
-  }
-  // We use "gz" compression by default for VABC.
-  if (metadata->vabc_enabled()) {
-    metadata->set_vabc_compression_param("gz");
-    metadata->set_cow_version(android::snapshot::kCowVersionManifest);
-  }
+
   dynamic_partition_metadata = std::move(metadata);
   return true;
 }
@@ -225,20 +206,28 @@
   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;
 }
 
 bool PayloadVersion::Validate() const {
-  TEST_AND_RETURN_FALSE(major == kBrilloMajorPayloadVersion);
+  TEST_AND_RETURN_FALSE(major == kChromeOSMajorPayloadVersion ||
+                        major == kBrilloMajorPayloadVersion);
   TEST_AND_RETURN_FALSE(minor == kFullPayloadMinorVersion ||
+                        minor == kInPlaceMinorPayloadVersion ||
                         minor == kSourceMinorPayloadVersion ||
                         minor == kOpSrcHashMinorPayloadVersion ||
                         minor == kBrotliBsdiffMinorPayloadVersion ||
                         minor == kPuffdiffMinorPayloadVersion ||
-                        minor == kVerityMinorPayloadVersion ||
-                        minor == kPartialUpdateMinorPayloadVersion);
+                        minor == kVerityMinorPayloadVersion);
   return true;
 }
 
@@ -248,11 +237,14 @@
     case InstallOperation::REPLACE:
     case InstallOperation::REPLACE_BZ:
       // These operations were included in the original payload format.
-    case InstallOperation::REPLACE_XZ:
-      // These operations are included minor version 3 or newer and full
-      // payloads.
       return true;
 
+    case InstallOperation::REPLACE_XZ:
+      // These operations are included in the major version used in Brillo, but
+      // can also be used with minor version 3 or newer.
+      return major == kBrilloMajorPayloadVersion ||
+             minor >= kOpSrcHashMinorPayloadVersion;
+
     case InstallOperation::ZERO:
     case InstallOperation::DISCARD:
       // The implementation of these operations had a bug in earlier versions
@@ -260,6 +252,14 @@
       // them for delta payloads for now.
       return minor >= kBrotliBsdiffMinorPayloadVersion;
 
+    // Delta operations:
+    case InstallOperation::MOVE:
+    case InstallOperation::BSDIFF:
+      // MOVE and BSDIFF were replaced by SOURCE_COPY and SOURCE_BSDIFF and
+      // should not be used in newer delta versions, since the idempotent checks
+      // were removed.
+      return minor == kInPlaceMinorPayloadVersion;
+
     case InstallOperation::SOURCE_COPY:
     case InstallOperation::SOURCE_BSDIFF:
       return minor >= kSourceMinorPayloadVersion;
@@ -269,22 +269,21 @@
 
     case InstallOperation::PUFFDIFF:
       return minor >= kPuffdiffMinorPayloadVersion;
-
-    case InstallOperation::MOVE:
-    case InstallOperation::BSDIFF:
-      NOTREACHED();
   }
   return false;
 }
 
-bool PayloadVersion::IsDeltaOrPartial() const {
+bool PayloadVersion::IsDelta() const {
   return minor != kFullPayloadMinorVersion;
 }
 
+bool PayloadVersion::InplaceUpdate() const {
+  return minor == kInPlaceMinorPayloadVersion;
+}
+
 bool PayloadGenerationConfig::Validate() const {
   TEST_AND_RETURN_FALSE(version.Validate());
-  TEST_AND_RETURN_FALSE(version.IsDeltaOrPartial() ==
-                        (is_delta || is_partial_update));
+  TEST_AND_RETURN_FALSE(version.IsDelta() == is_delta);
   if (is_delta) {
     for (const PartitionConfig& part : source.partitions) {
       if (!part.path.empty()) {
@@ -296,6 +295,9 @@
       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());
@@ -305,14 +307,15 @@
   for (const PartitionConfig& part : target.partitions) {
     TEST_AND_RETURN_FALSE(part.ValidateExists());
     TEST_AND_RETURN_FALSE(part.size % block_size == 0);
+    if (version.minor == kInPlaceMinorPayloadVersion &&
+        part.name == kPartitionNameRoot)
+      TEST_AND_RETURN_FALSE(rootfs_partition_size >= part.size);
+    if (version.major == kChromeOSMajorPayloadVersion)
+      TEST_AND_RETURN_FALSE(part.postinstall.IsEmpty());
     if (version.minor < kVerityMinorPayloadVersion)
       TEST_AND_RETURN_FALSE(part.verity.IsEmpty());
   }
 
-  if (version.minor < kPartialUpdateMinorPayloadVersion) {
-    TEST_AND_RETURN_FALSE(!is_partial_update);
-  }
-
   TEST_AND_RETURN_FALSE(hard_chunk_size == -1 ||
                         hard_chunk_size % block_size == 0);
   TEST_AND_RETURN_FALSE(soft_chunk_size % block_size == 0);
diff --git a/payload_generator/payload_generation_config.h b/payload_generator/payload_generation_config.h
index 9c8c59f..e90edde 100644
--- a/payload_generator/payload_generation_config.h
+++ b/payload_generator/payload_generation_config.h
@@ -119,9 +119,6 @@
 
   // Enables the on device fec data computation by default.
   bool disable_fec_computation = false;
-
-  // Per-partition version, usually a number representing timestamp.
-  std::string version;
 };
 
 // The ImageConfig struct describes a pair of binaries kernel and rootfs and the
@@ -149,6 +146,13 @@
   // 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;
 
@@ -166,8 +170,12 @@
   // Return whether the passed |operation| is allowed by this payload.
   bool OperationAllowed(InstallOperation::Type operation) const;
 
-  // Whether this payload version is a delta or partial payload.
-  bool IsDeltaOrPartial() const;
+  // Whether this payload version is a delta payload.
+  bool IsDelta() const;
+
+  // Tells whether the update is done in-place, that is, whether the operations
+  // read and write from the same partition.
+  bool InplaceUpdate() const;
 
   // The major version of the payload.
   uint64_t major;
@@ -194,10 +202,6 @@
   // Whether the requested payload is a delta payload.
   bool is_delta = false;
 
-  // Whether the requested payload is a partial payload, i.e. only update a
-  // subset of partitions on device.
-  bool is_partial_update = false;
-
   // The major/minor version of the payload.
   PayloadVersion version;
 
@@ -230,9 +234,6 @@
 
   // The maximum timestamp of the OS allowed to apply this payload.
   int64_t max_timestamp = 0;
-
-  // Path to apex_info.pb, extracted from target_file.zip
-  std::string apex_info_file;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/payload_generation_config_android_unittest.cc b/payload_generator/payload_generation_config_android_unittest.cc
index e87b034..44eaf55 100644
--- a/payload_generator/payload_generation_config_android_unittest.cc
+++ b/payload_generator/payload_generation_config_android_unittest.cc
@@ -138,7 +138,8 @@
   }
 
   ImageConfig image_config_;
-  ScopedTempFile temp_file_{"PayloadGenerationConfigAndroidTest.XXXXXX"};
+  test_utils::ScopedTempFile temp_file_{
+      "PayloadGenerationConfigAndroidTest.XXXXXX"};
 };
 
 TEST_F(PayloadGenerationConfigAndroidTest, LoadVerityConfigSimpleTest) {
diff --git a/payload_generator/payload_properties.cc b/payload_generator/payload_properties.cc
deleted file mode 100644
index bcf4fbd..0000000
--- a/payload_generator/payload_properties.cc
+++ /dev/null
@@ -1,124 +0,0 @@
-//
-// Copyright 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include "update_engine/payload_generator/payload_properties.h"
-
-#include <algorithm>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <base/json/json_writer.h>
-#include <base/strings/string_util.h>
-#include <base/values.h>
-#include <brillo/data_encoding.h>
-
-#include "update_engine/common/constants.h"
-#include "update_engine/common/hash_calculator.h"
-#include "update_engine/common/utils.h"
-#include "update_engine/payload_consumer/payload_metadata.h"
-#include "update_engine/update_metadata.pb.h"
-
-using std::string;
-using std::vector;
-
-namespace chromeos_update_engine {
-
-namespace {
-// These ones are needed by the GoldenEye.
-const char kPayloadPropertyJsonVersion[] = "version";
-const char kPayloadPropertyJsonPayloadHash[] = "sha256_hex";
-const char kPayloadPropertyJsonMetadataSize[] = "metadata_size";
-const char kPayloadPropertyJsonMetadataSignature[] = "metadata_signature";
-
-// These are needed by the Nebraska and devserver.
-const char kPayloadPropertyJsonPayloadSize[] = "size";
-const char kPayloadPropertyJsonIsDelta[] = "is_delta";
-}  // namespace
-
-PayloadProperties::PayloadProperties(const string& payload_path)
-    : payload_path_(payload_path) {}
-
-bool PayloadProperties::GetPropertiesAsJson(string* json_str) {
-  TEST_AND_RETURN_FALSE(LoadFromPayload());
-
-  base::DictionaryValue properties;
-  properties.SetInteger(kPayloadPropertyJsonVersion, version_);
-  properties.SetInteger(kPayloadPropertyJsonMetadataSize, metadata_size_);
-  properties.SetString(kPayloadPropertyJsonMetadataSignature,
-                       metadata_signatures_);
-  properties.SetInteger(kPayloadPropertyJsonPayloadSize, payload_size_);
-  properties.SetString(kPayloadPropertyJsonPayloadHash, payload_hash_);
-  properties.SetBoolean(kPayloadPropertyJsonIsDelta, is_delta_);
-
-  return base::JSONWriter::Write(properties, json_str);
-}
-
-bool PayloadProperties::GetPropertiesAsKeyValue(string* key_value_str) {
-  TEST_AND_RETURN_FALSE(LoadFromPayload());
-
-  brillo::KeyValueStore properties;
-  properties.SetString(kPayloadPropertyFileSize, std::to_string(payload_size_));
-  properties.SetString(kPayloadPropertyMetadataSize,
-                       std::to_string(metadata_size_));
-  properties.SetString(kPayloadPropertyFileHash, payload_hash_);
-  properties.SetString(kPayloadPropertyMetadataHash, metadata_hash_);
-
-  *key_value_str = properties.SaveToString();
-  return true;
-}
-
-bool PayloadProperties::LoadFromPayload() {
-  PayloadMetadata payload_metadata;
-  DeltaArchiveManifest manifest;
-  Signatures metadata_signatures;
-  TEST_AND_RETURN_FALSE(payload_metadata.ParsePayloadFile(
-      payload_path_, &manifest, &metadata_signatures));
-
-  metadata_size_ = payload_metadata.GetMetadataSize();
-  payload_size_ = utils::FileSize(payload_path_);
-
-  brillo::Blob metadata_hash;
-  TEST_AND_RETURN_FALSE(HashCalculator::RawHashOfFile(
-                            payload_path_, metadata_size_, &metadata_hash) ==
-                        static_cast<off_t>(metadata_size_));
-  metadata_hash_ = brillo::data_encoding::Base64Encode(metadata_hash);
-
-  brillo::Blob payload_hash;
-  TEST_AND_RETURN_FALSE(HashCalculator::RawHashOfFile(
-                            payload_path_, payload_size_, &payload_hash) ==
-                        static_cast<off_t>(payload_size_));
-  payload_hash_ = brillo::data_encoding::Base64Encode(payload_hash);
-
-  if (payload_metadata.GetMetadataSignatureSize() > 0) {
-    TEST_AND_RETURN_FALSE(metadata_signatures.signatures_size() > 0);
-    vector<string> base64_signatures;
-    for (const auto& sig : metadata_signatures.signatures()) {
-      base64_signatures.push_back(
-          brillo::data_encoding::Base64Encode(sig.data()));
-    }
-    metadata_signatures_ = base::JoinString(base64_signatures, ":");
-  }
-
-  is_delta_ = std::any_of(manifest.partitions().begin(),
-                          manifest.partitions().end(),
-                          [](const PartitionUpdate& part) {
-                            return part.has_old_partition_info();
-                          });
-  return true;
-}
-
-}  // namespace chromeos_update_engine
diff --git a/payload_generator/payload_properties.h b/payload_generator/payload_properties.h
deleted file mode 100644
index 846b181..0000000
--- a/payload_generator/payload_properties.h
+++ /dev/null
@@ -1,70 +0,0 @@
-//
-// Copyright 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef UPDATE_ENGINE_PAYLOAD_GENERATOR_PAYLOAD_PROPERTIES_H_
-#define UPDATE_ENGINE_PAYLOAD_GENERATOR_PAYLOAD_PROPERTIES_H_
-
-#include <string>
-
-#include <brillo/key_value_store.h>
-#include <brillo/secure_blob.h>
-
-namespace chromeos_update_engine {
-
-// A class for extracting information about a payload from the payload file
-// itself. Currently the metadata can be exported as a json file or a key/value
-// properties file. But more can be added if required.
-class PayloadProperties {
- public:
-  explicit PayloadProperties(const std::string& payload_path);
-  ~PayloadProperties() = default;
-
-  // Get the properties in a json format. The json file will be used in
-  // autotests, cros flash, etc. Mainly in Chrome OS.
-  bool GetPropertiesAsJson(std::string* json_str);
-
-  // Get the properties of the payload as a key/value store. This is mainly used
-  // in Android.
-  bool GetPropertiesAsKeyValue(std::string* key_value_str);
-
- private:
-  // Does the main job of reading the payload and extracting information from
-  // it.
-  bool LoadFromPayload();
-
-  // The path to the payload file.
-  std::string payload_path_;
-
-  // The version of the metadata json format. If the output json file changes
-  // format, this needs to be increased.
-  int version_{2};
-
-  size_t metadata_size_;
-  std::string metadata_hash_;
-  std::string metadata_signatures_;
-
-  size_t payload_size_;
-  std::string payload_hash_;
-
-  // Whether the payload is a delta (true) or full (false).
-  bool is_delta_;
-
-  DISALLOW_COPY_AND_ASSIGN(PayloadProperties);
-};
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_PAYLOAD_PROPERTIES_H_
diff --git a/payload_generator/payload_properties_unittest.cc b/payload_generator/payload_properties_unittest.cc
deleted file mode 100644
index 0ff364f..0000000
--- a/payload_generator/payload_properties_unittest.cc
+++ /dev/null
@@ -1,132 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include "update_engine/payload_generator/payload_properties.h"
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <base/files/file_util.h>
-#include <base/files/scoped_file.h>
-#include <base/files/scoped_temp_dir.h>
-#include <base/rand_util.h>
-#include <base/strings/stringprintf.h>
-#include <brillo/data_encoding.h>
-
-#include <gtest/gtest.h>
-
-#include "update_engine/common/hash_calculator.h"
-#include "update_engine/common/test_utils.h"
-#include "update_engine/common/utils.h"
-#include "update_engine/payload_consumer/install_plan.h"
-#include "update_engine/payload_generator/delta_diff_generator.h"
-#include "update_engine/payload_generator/delta_diff_utils.h"
-#include "update_engine/payload_generator/full_update_generator.h"
-#include "update_engine/payload_generator/operations_generator.h"
-#include "update_engine/payload_generator/payload_file.h"
-#include "update_engine/payload_generator/payload_generation_config.h"
-
-using std::string;
-using std::unique_ptr;
-using std::vector;
-
-namespace chromeos_update_engine {
-
-// TODO(kimjae): current implementation is very specific to a static way of
-// producing a deterministic test. It would definitely be beneficial to
-// extend the |PayloadPropertiesTest::SetUp()| into a generic helper or
-// seperate class that can handle creation of different |PayloadFile|s.
-class PayloadPropertiesTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    PayloadGenerationConfig config;
-    config.version.major = kBrilloMajorPayloadVersion;
-    config.version.minor = kSourceMinorPayloadVersion;
-    PayloadFile payload;
-    EXPECT_TRUE(payload.Init(config));
-
-    const auto SetupPartitionConfig =
-        [](PartitionConfig* config, const string& path, size_t size) {
-          config->path = path;
-          config->size = size;
-        };
-    const auto WriteZerosToFile = [](const char path[], size_t size) {
-      string zeros(size, '\0');
-      EXPECT_TRUE(utils::WriteFile(path, zeros.c_str(), zeros.size()));
-    };
-    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);
-    SetupPartitionConfig(&new_part, new_part_file.path(), 10000);
-    WriteZerosToFile(old_part_file.path().c_str(), old_part.size);
-    WriteZerosToFile(new_part_file.path().c_str(), new_part.size);
-
-    // Select payload generation strategy based on the config.
-    unique_ptr<OperationsGenerator> strategy(new FullUpdateGenerator());
-
-    vector<AnnotatedOperation> aops;
-    off_t data_file_size = 0;
-    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, {}, 0);
-
-    uint64_t metadata_size;
-    EXPECT_TRUE(payload.WritePayload(
-        payload_file_.path(), data_file.path(), "", &metadata_size));
-  }
-
-  ScopedTempFile payload_file_{"payload_file.XXXXXX"};
-};
-
-// Validate the hash of file exists within the output.
-TEST_F(PayloadPropertiesTest, GetPropertiesAsJsonTestHash) {
-  constexpr char kJsonProperties[] =
-      "{"
-      R"("is_delta":true,)"
-      R"("metadata_signature":"",)"
-      R"("metadata_size":165,)"
-      R"("sha256_hex":"cV7kfZBH3K0B6QJHxxykDh6b6x0WgVOmc63whPLOy7U=",)"
-      R"("size":211,)"
-      R"("version":2)"
-      "}";
-  string json;
-  EXPECT_TRUE(
-      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=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(
-      &key_value));
-  EXPECT_EQ(kKeyValueProperties, key_value) << "Key Value contents:\n"
-                                            << key_value;
-}
-
-}  // namespace chromeos_update_engine
diff --git a/payload_generator/payload_signer.cc b/payload_generator/payload_signer.cc
index d9f0dd7..72780b1 100644
--- a/payload_generator/payload_signer.cc
+++ b/payload_generator/payload_signer.cc
@@ -82,7 +82,7 @@
 // Given an unsigned payload under |payload_path| and the |payload_signature|
 // and |metadata_signature| generates an updated payload that includes the
 // signatures. It populates |out_metadata_size| with the size of the final
-// manifest after adding the fake signature operation, and
+// manifest after adding the dummy signature operation, and
 // |out_signatures_offset| with the expected offset for the new blob, and
 // |out_metadata_signature_size| which will be size of |metadata_signature|
 // if the payload major version supports metadata signature, 0 otherwise.
@@ -104,20 +104,22 @@
   uint64_t metadata_size = payload_metadata.GetMetadataSize();
   uint32_t metadata_signature_size =
       payload_metadata.GetMetadataSignatureSize();
-  // Write metadata signature size in header.
-  uint32_t metadata_signature_size_be = htobe32(metadata_signature.size());
-  memcpy(payload.data() + manifest_offset,
-         &metadata_signature_size_be,
-         sizeof(metadata_signature_size_be));
-  manifest_offset += sizeof(metadata_signature_size_be);
-  // Replace metadata signature.
-  payload.erase(payload.begin() + metadata_size,
-                payload.begin() + metadata_size + metadata_signature_size);
-  payload.insert(payload.begin() + metadata_size,
-                 metadata_signature.begin(),
-                 metadata_signature.end());
-  metadata_signature_size = metadata_signature.size();
-  LOG(INFO) << "Metadata signature size: " << metadata_signature_size;
+  if (payload_metadata.GetMajorVersion() == kBrilloMajorPayloadVersion) {
+    // Write metadata signature size in header.
+    uint32_t metadata_signature_size_be = htobe32(metadata_signature.size());
+    memcpy(payload.data() + manifest_offset,
+           &metadata_signature_size_be,
+           sizeof(metadata_signature_size_be));
+    manifest_offset += sizeof(metadata_signature_size_be);
+    // Replace metadata signature.
+    payload.erase(payload.begin() + metadata_size,
+                  payload.begin() + metadata_size + metadata_signature_size);
+    payload.insert(payload.begin() + metadata_size,
+                   metadata_signature.begin(),
+                   metadata_signature.end());
+    metadata_signature_size = metadata_signature.size();
+    LOG(INFO) << "Metadata signature size: " << metadata_signature_size;
+  }
 
   DeltaArchiveManifest manifest;
   TEST_AND_RETURN_FALSE(payload_metadata.GetManifest(payload, &manifest));
@@ -141,6 +143,7 @@
     PayloadSigner::AddSignatureToManifest(
         payload.size() - metadata_size - metadata_signature_size,
         payload_signature.size(),
+        payload_metadata.GetMajorVersion() == kChromeOSMajorPayloadVersion,
         &manifest);
 
     // Updates the payload to include the new manifest.
@@ -238,10 +241,25 @@
 
 void PayloadSigner::AddSignatureToManifest(uint64_t signature_blob_offset,
                                            uint64_t signature_blob_length,
+                                           bool add_dummy_op,
                                            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);
+  // Add a dummy op at the end to appease older clients
+  if (add_dummy_op) {
+    InstallOperation* dummy_op = manifest->add_kernel_install_operations();
+    dummy_op->set_type(InstallOperation::REPLACE);
+    dummy_op->set_data_offset(signature_blob_offset);
+    dummy_op->set_data_length(signature_blob_length);
+    Extent* dummy_extent = dummy_op->add_dst_extents();
+    // Tell the dummy op to write this data to a big sparse hole
+    dummy_extent->set_start_block(kSparseHole);
+    dummy_extent->set_num_blocks(
+        utils::DivRoundUp(signature_blob_length, kBlockSize));
+  }
 }
 
 bool PayloadSigner::VerifySignedPayload(const string& payload_path,
@@ -319,6 +337,7 @@
                                                  signature.data(),
                                                  rsa,
                                                  RSA_NO_PADDING);
+
     if (signature_size < 0) {
       LOG(ERROR) << "Signing hash failed: "
                  << ERR_error_string(ERR_get_error(), nullptr);
@@ -493,4 +512,35 @@
   return true;
 }
 
+bool PayloadSigner::ExtractPayloadProperties(
+    const string& payload_path, brillo::KeyValueStore* properties) {
+  brillo::Blob payload;
+  TEST_AND_RETURN_FALSE(
+      utils::ReadFileChunk(payload_path, 0, kMaxPayloadHeaderSize, &payload));
+
+  PayloadMetadata payload_metadata;
+  TEST_AND_RETURN_FALSE(payload_metadata.ParsePayloadHeader(payload));
+  uint64_t metadata_size = payload_metadata.GetMetadataSize();
+
+  uint64_t file_size = utils::FileSize(payload_path);
+  properties->SetString(kPayloadPropertyFileSize, std::to_string(file_size));
+  properties->SetString(kPayloadPropertyMetadataSize,
+                        std::to_string(metadata_size));
+
+  brillo::Blob file_hash, metadata_hash;
+  TEST_AND_RETURN_FALSE(
+      HashCalculator::RawHashOfFile(payload_path, file_size, &file_hash) ==
+      static_cast<off_t>(file_size));
+
+  TEST_AND_RETURN_FALSE(HashCalculator::RawHashOfFile(
+                            payload_path, metadata_size, &metadata_hash) ==
+                        static_cast<off_t>(metadata_size));
+
+  properties->SetString(kPayloadPropertyFileHash,
+                        brillo::data_encoding::Base64Encode(file_hash));
+  properties->SetString(kPayloadPropertyMetadataHash,
+                        brillo::data_encoding::Base64Encode(metadata_hash));
+  return true;
+}
+
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/payload_signer.h b/payload_generator/payload_signer.h
index 9676b71..bd1e32f 100644
--- a/payload_generator/payload_signer.h
+++ b/payload_generator/payload_signer.h
@@ -39,9 +39,12 @@
   static bool VerifySignedPayload(const std::string& payload_path,
                                   const std::string& public_key_path);
 
-  // Adds specified signature offset/length to given |manifest|.
+  // Adds specified signature offset/length to given |manifest|, also adds a
+  // dummy operation that points to a signature blob located at the specified
+  // offset/length if |add_dummy_op| is true.
   static void AddSignatureToManifest(uint64_t signature_blob_offset,
                                      uint64_t signature_blob_length,
+                                     bool add_dummy_op,
                                      DeltaArchiveManifest* manifest);
 
   // Given a raw |hash| and a private key in |private_key_path| calculates the
@@ -62,7 +65,7 @@
   // size in |metadata_signature_size| and signatures offset in
   // |signatures_offset|, calculates the payload signature blob into
   // |out_serialized_signature|. Note that the payload must already have an
-  // updated manifest that includes the fake signature op and correct metadata
+  // updated manifest that includes the dummy signature op and correct metadata
   // signature size in header. Returns true on success, false otherwise.
   static bool SignPayload(const std::string& unsigned_payload_path,
                           const std::vector<std::string>& private_key_paths,
@@ -92,7 +95,7 @@
                                     brillo::Blob* out_payload_hash_data,
                                     brillo::Blob* out_metadata_hash);
 
-  // Given an unsigned payload in |payload_path| (with no fake signature op)
+  // Given an unsigned payload in |payload_path| (with no dummy signature op)
   // and the raw |payload_signatures| and |metadata_signatures| updates the
   // payload to include the signature thus turning it into a signed payload. The
   // new payload is stored in |signed_payload_path|. |payload_path| and
diff --git a/payload_generator/payload_signer_unittest.cc b/payload_generator/payload_signer_unittest.cc
index 2a0b394..bf7100b 100644
--- a/payload_generator/payload_signer_unittest.cc
+++ b/payload_generator/payload_signer_unittest.cc
@@ -20,7 +20,6 @@
 #include <vector>
 
 #include <base/logging.h>
-#include <base/stl_util.h>
 #include <gtest/gtest.h>
 
 #include "update_engine/common/hash_calculator.h"
@@ -119,8 +118,8 @@
   EXPECT_EQ(1, signatures.signatures_size());
   const Signatures::Signature& sig = signatures.signatures(0);
   const string& sig_data = sig.data();
-  ASSERT_EQ(base::size(kDataSignature), sig_data.size());
-  for (size_t i = 0; i < base::size(kDataSignature); i++) {
+  ASSERT_EQ(arraysize(kDataSignature), sig_data.size());
+  for (size_t i = 0; i < arraysize(kDataSignature); i++) {
     EXPECT_EQ(kDataSignature[i], static_cast<uint8_t>(sig_data[i]));
   }
 }
@@ -167,7 +166,7 @@
 }
 
 TEST_F(PayloadSignerTest, SkipMetadataSignatureTest) {
-  ScopedTempFile payload_file("payload.XXXXXX");
+  test_utils::ScopedTempFile payload_file("payload.XXXXXX");
   PayloadGenerationConfig config;
   config.version.major = kBrilloMajorPayloadVersion;
   PayloadFile payload;
@@ -194,7 +193,7 @@
 }
 
 TEST_F(PayloadSignerTest, VerifySignedPayloadTest) {
-  ScopedTempFile payload_file("payload.XXXXXX");
+  test_utils::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 a41e283..6c892f5 100644
--- a/payload_generator/squashfs_filesystem.cc
+++ b/payload_generator/squashfs_filesystem.cc
@@ -23,7 +23,6 @@
 #include <utility>
 
 #include <base/files/file_util.h>
-#include <base/files/scoped_temp_dir.h>
 #include <base/logging.h>
 #include <base/strings/string_number_conversions.h>
 #include <base/strings/string_split.h>
@@ -37,8 +36,6 @@
 #include "update_engine/payload_generator/extent_utils.h"
 #include "update_engine/update_metadata.pb.h"
 
-using base::FilePath;
-using base::ScopedTempDir;
 using std::string;
 using std::unique_ptr;
 using std::vector;
@@ -52,8 +49,6 @@
 constexpr uint64_t kSquashfsCompressedBit = 1 << 24;
 constexpr uint32_t kSquashfsZlibCompression = 1;
 
-constexpr char kUpdateEngineConf[] = "etc/update_engine.conf";
-
 bool ReadSquashfsHeader(const brillo::Blob blob,
                         SquashfsFilesystem::SquashfsHeader* header) {
   if (blob.size() < kSquashfsSuperBlockSize) {
@@ -72,60 +67,24 @@
 }
 
 bool GetFileMapContent(const string& sqfs_path, string* map) {
-  ScopedTempFile map_file("squashfs_file_map.XXXXXX");
+  // 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);
+
   // Run unsquashfs to get the system file map.
   // unsquashfs -m <map-file> <squashfs-file>
-  vector<string> cmd = {"unsquashfs", "-m", map_file.path(), sqfs_path};
-  string stdout, stderr;
+  vector<string> cmd = {"unsquashfs", "-m", map_file, sqfs_path};
+  string stdout;
   int exit_code;
-  if (!Subprocess::SynchronousExec(cmd, &exit_code, &stdout, &stderr) ||
+  if (!Subprocess::SynchronousExec(cmd, &exit_code, &stdout) ||
       exit_code != 0) {
-    LOG(ERROR) << "Failed to run `unsquashfs -m` with stdout content: "
-               << stdout << " and stderr content: " << stderr;
+    LOG(ERROR) << "Failed to run unsquashfs -m. The stdout content was: "
+               << stdout;
     return false;
   }
-  TEST_AND_RETURN_FALSE(utils::ReadFile(map_file.path(), map));
-  return true;
-}
-
-bool GetUpdateEngineConfig(const std::string& sqfs_path, string* config) {
-  ScopedTempDir unsquash_dir;
-  if (!unsquash_dir.CreateUniqueTempDir()) {
-    PLOG(ERROR) << "Failed to create a temporary directory.";
-    return false;
-  }
-
-  // Run unsquashfs to extract update_engine.conf
-  // -f: To force overriding if the target directory exists.
-  // -d: The directory to unsquash the files.
-  vector<string> cmd = {"unsquashfs",
-                        "-f",
-                        "-d",
-                        unsquash_dir.GetPath().value(),
-                        sqfs_path,
-                        kUpdateEngineConf};
-  string stdout, stderr;
-  int exit_code;
-  if (!Subprocess::SynchronousExec(cmd, &exit_code, &stdout, &stderr) ||
-      exit_code != 0) {
-    PLOG(ERROR) << "Failed to unsquashfs etc/update_engine.conf with stdout: "
-                << stdout << " and stderr: " << stderr;
-    return false;
-  }
-
-  auto config_path = unsquash_dir.GetPath().Append(kUpdateEngineConf);
-  string config_content;
-  if (!utils::ReadFile(config_path.value(), &config_content)) {
-    PLOG(ERROR) << "Failed to read " << config_path.value();
-    return false;
-  }
-
-  if (config_content.empty()) {
-    LOG(ERROR) << "update_engine config file was empty!!";
-    return false;
-  }
-
-  *config = std::move(config_content);
+  TEST_AND_RETURN_FALSE(utils::ReadFile(map_file, map));
   return true;
 }
 
@@ -161,7 +120,6 @@
     uint64_t start;
     TEST_AND_RETURN_FALSE(base::StringToUint64(splits[1], &start));
     uint64_t cur_offset = start;
-    bool is_compressed = false;
     for (size_t i = 2; i < splits.size(); ++i) {
       uint64_t blk_size;
       TEST_AND_RETURN_FALSE(base::StringToUint64(splits[i], &blk_size));
@@ -169,11 +127,10 @@
       auto new_blk_size = blk_size & ~kSquashfsCompressedBit;
       TEST_AND_RETURN_FALSE(new_blk_size <= header.block_size);
       if (new_blk_size > 0 && !(blk_size & kSquashfsCompressedBit)) {
-        // It is a compressed block.
+        // Compressed block
         if (is_zlib && extract_deflates) {
           zlib_blks.emplace_back(cur_offset, new_blk_size);
         }
-        is_compressed = true;
       }
       cur_offset += new_blk_size;
     }
@@ -183,7 +140,6 @@
       File file;
       file.name = splits[0].as_string();
       file.extents = {ExtentForBytes(kBlockSize, start, cur_offset - start)};
-      file.is_compressed = is_compressed;
       files_.emplace_back(file);
     }
   }
@@ -195,8 +151,7 @@
   // If there is any overlap between two consecutive extents, remove them. Here
   // we are assuming all files have exactly one extent. If this assumption
   // changes then this implementation needs to change too.
-  for (auto first = files_.begin(),
-            second = first + (first == files_.end() ? 0 : 1);
+  for (auto first = files_.begin(), second = first + 1;
        first != files_.end() && second != files_.end();
        second = first + 1) {
     auto first_begin = first->extents[0].start_block();
@@ -262,15 +217,7 @@
                 return a.offset < b.offset;
               });
 
-    // Sometimes a squashfs can have a two files that are hard linked. In this
-    // case both files will have the same starting offset in the image and hence
-    // the same zlib blocks. So we need to remove these duplicates to eliminate
-    // further potential probems. As a matter of fact the next statement will
-    // fail if there are duplicates (there will be overlap between two blocks).
-    auto last = std::unique(zlib_blks.begin(), zlib_blks.end());
-    zlib_blks.erase(last, zlib_blks.end());
-
-    // Make sure zlib blocks are not overlapping.
+    // Sanity check. Make sure zlib blocks are not overlapping.
     auto result = std::adjacent_find(
         zlib_blks.begin(),
         zlib_blks.end(),
@@ -292,12 +239,12 @@
 }
 
 unique_ptr<SquashfsFilesystem> SquashfsFilesystem::CreateFromFile(
-    const string& sqfs_path, bool extract_deflates, bool load_settings) {
+    const string& sqfs_path, bool extract_deflates) {
   if (sqfs_path.empty())
     return nullptr;
 
   brillo::StreamPtr sqfs_file =
-      brillo::FileStream::Open(FilePath(sqfs_path),
+      brillo::FileStream::Open(base::FilePath(sqfs_path),
                                brillo::Stream::AccessMode::READ,
                                brillo::FileStream::Disposition::OPEN_EXISTING,
                                nullptr);
@@ -331,12 +278,6 @@
     return nullptr;
   }
 
-  if (load_settings) {
-    if (!GetUpdateEngineConfig(sqfs_path, &sqfs->update_engine_config_)) {
-      return nullptr;
-    }
-  }
-
   return sqfs;
 }
 
@@ -370,12 +311,9 @@
 }
 
 bool SquashfsFilesystem::LoadSettings(brillo::KeyValueStore* store) const {
-  if (!store->LoadFromString(update_engine_config_)) {
-    LOG(ERROR) << "Failed to load the settings with config: "
-               << update_engine_config_;
-    return false;
-  }
-  return true;
+  // Settings not supported in squashfs.
+  LOG(ERROR) << "squashfs doesn't support LoadSettings().";
+  return false;
 }
 
 bool SquashfsFilesystem::IsSquashfsImage(const brillo::Blob& blob) {
diff --git a/payload_generator/squashfs_filesystem.h b/payload_generator/squashfs_filesystem.h
index 5045dfc..b79f8c7 100644
--- a/payload_generator/squashfs_filesystem.h
+++ b/payload_generator/squashfs_filesystem.h
@@ -59,7 +59,7 @@
   // |extract_deflates| is true, it will process files to find location of all
   // deflate streams.
   static std::unique_ptr<SquashfsFilesystem> CreateFromFile(
-      const std::string& sqfs_path, bool extract_deflates, bool load_settings);
+      const std::string& sqfs_path, bool extract_deflates);
 
   // Creates the file system from a file map |filemap| which is a multi-line
   // string with each line with the following format:
@@ -113,9 +113,6 @@
   // All the files in the filesystem.
   std::vector<File> files_;
 
-  // The content of /etc/update_engine.conf.
-  std::string update_engine_config_;
-
   DISALLOW_COPY_AND_ASSIGN(SquashfsFilesystem);
 };
 
diff --git a/payload_generator/squashfs_filesystem_unittest.cc b/payload_generator/squashfs_filesystem_unittest.cc
index 68ca9df..29fcf1c 100644
--- a/payload_generator/squashfs_filesystem_unittest.cc
+++ b/payload_generator/squashfs_filesystem_unittest.cc
@@ -112,7 +112,7 @@
 #ifdef __CHROMEOS__
 TEST_F(SquashfsFilesystemTest, EmptyFilesystemTest) {
   unique_ptr<SquashfsFilesystem> fs = SquashfsFilesystem::CreateFromFile(
-      GetBuildArtifactsPath("gen/disk_sqfs_empty.img"), true, false);
+      GetBuildArtifactsPath("gen/disk_sqfs_empty.img"), true);
   CheckSquashfs(fs);
 
   // Even an empty squashfs filesystem is rounded up to 4K.
@@ -133,7 +133,7 @@
 
 TEST_F(SquashfsFilesystemTest, DefaultFilesystemTest) {
   unique_ptr<SquashfsFilesystem> fs = SquashfsFilesystem::CreateFromFile(
-      GetBuildArtifactsPath("gen/disk_sqfs_default.img"), true, false);
+      GetBuildArtifactsPath("gen/disk_sqfs_default.img"), true);
   CheckSquashfs(fs);
 
   vector<FilesystemInterface::File> files;
@@ -148,18 +148,6 @@
   EXPECT_EQ(files[0].name, file.name);
   EXPECT_EQ(files[0].extents, file.extents);
 }
-
-TEST_F(SquashfsFilesystemTest, UpdateEngineConfigTest) {
-  unique_ptr<SquashfsFilesystem> fs = SquashfsFilesystem::CreateFromFile(
-      GetBuildArtifactsPath("gen/disk_sqfs_unittest.img"), true, true);
-  CheckSquashfs(fs);
-
-  brillo::KeyValueStore kvs;
-  EXPECT_TRUE(fs->LoadSettings(&kvs));
-  string minor_version;
-  EXPECT_TRUE(kvs.GetString("PAYLOAD_MINOR_VERSION", &minor_version));
-  EXPECT_EQ(minor_version, "1234");
-}
 #endif  // __CHROMEOS__
 
 TEST_F(SquashfsFilesystemTest, SimpleFileMapTest) {
diff --git a/payload_generator/tarjan.cc b/payload_generator/tarjan.cc
new file mode 100644
index 0000000..2d4ca31
--- /dev/null
+++ b/payload_generator/tarjan.cc
@@ -0,0 +1,83 @@
+//
+// Copyright (C) 2010 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/tarjan.h"
+
+#include <algorithm>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/stl_util.h>
+
+using std::min;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+const vector<Vertex>::size_type kInvalidIndex = -1;
+}
+
+void TarjanAlgorithm::Execute(Vertex::Index vertex,
+                              Graph* graph,
+                              vector<Vertex::Index>* out) {
+  stack_.clear();
+  components_.clear();
+  index_ = 0;
+  for (Graph::iterator it = graph->begin(); it != graph->end(); ++it)
+    it->index = it->lowlink = kInvalidIndex;
+  required_vertex_ = vertex;
+
+  Tarjan(vertex, graph);
+  if (!components_.empty())
+    out->swap(components_[0]);
+}
+
+void TarjanAlgorithm::Tarjan(Vertex::Index vertex, Graph* graph) {
+  CHECK_EQ((*graph)[vertex].index, kInvalidIndex);
+  (*graph)[vertex].index = index_;
+  (*graph)[vertex].lowlink = index_;
+  index_++;
+  stack_.push_back(vertex);
+  for (Vertex::EdgeMap::iterator it = (*graph)[vertex].out_edges.begin();
+       it != (*graph)[vertex].out_edges.end();
+       ++it) {
+    Vertex::Index vertex_next = it->first;
+    if ((*graph)[vertex_next].index == kInvalidIndex) {
+      Tarjan(vertex_next, graph);
+      (*graph)[vertex].lowlink =
+          min((*graph)[vertex].lowlink, (*graph)[vertex_next].lowlink);
+    } else if (base::ContainsValue(stack_, vertex_next)) {
+      (*graph)[vertex].lowlink =
+          min((*graph)[vertex].lowlink, (*graph)[vertex_next].index);
+    }
+  }
+  if ((*graph)[vertex].lowlink == (*graph)[vertex].index) {
+    vector<Vertex::Index> component;
+    Vertex::Index other_vertex;
+    do {
+      other_vertex = stack_.back();
+      stack_.pop_back();
+      component.push_back(other_vertex);
+    } while (other_vertex != vertex && !stack_.empty());
+
+    if (base::ContainsValue(component, required_vertex_)) {
+      components_.resize(components_.size() + 1);
+      component.swap(components_.back());
+    }
+  }
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/tarjan.h b/payload_generator/tarjan.h
new file mode 100644
index 0000000..39ac4e4
--- /dev/null
+++ b/payload_generator/tarjan.h
@@ -0,0 +1,53 @@
+//
+// Copyright (C) 2010 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_PAYLOAD_GENERATOR_TARJAN_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_TARJAN_H_
+
+// This is an implementation of Tarjan's algorithm which finds all
+// Strongly Connected Components in a graph.
+
+// Note: a true Tarjan algorithm would find all strongly connected components
+// in the graph. This implementation will only find the strongly connected
+// component containing the vertex passed in.
+
+#include <vector>
+
+#include "update_engine/payload_generator/graph_types.h"
+
+namespace chromeos_update_engine {
+
+class TarjanAlgorithm {
+ public:
+  TarjanAlgorithm() : index_(0), required_vertex_(0) {}
+
+  // 'out' is set to the result if there is one, otherwise it's untouched.
+  void Execute(Vertex::Index vertex,
+               Graph* graph,
+               std::vector<Vertex::Index>* out);
+
+ private:
+  void Tarjan(Vertex::Index vertex, Graph* graph);
+
+  Vertex::Index index_;
+  Vertex::Index required_vertex_;
+  std::vector<Vertex::Index> stack_;
+  std::vector<std::vector<Vertex::Index>> components_;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_TARJAN_H_
diff --git a/payload_generator/tarjan_unittest.cc b/payload_generator/tarjan_unittest.cc
new file mode 100644
index 0000000..b271227
--- /dev/null
+++ b/payload_generator/tarjan_unittest.cc
@@ -0,0 +1,94 @@
+//
+// Copyright (C) 2010 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/tarjan.h"
+
+#include <string>
+#include <utility>
+
+#include <base/logging.h>
+#include <base/stl_util.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/payload_generator/graph_types.h"
+
+using std::make_pair;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+class TarjanAlgorithmTest : public ::testing::Test {};
+
+TEST(TarjanAlgorithmTest, SimpleTest) {
+  const Vertex::Index n_a = 0;
+  const Vertex::Index n_b = 1;
+  const Vertex::Index n_c = 2;
+  const Vertex::Index n_d = 3;
+  const Vertex::Index n_e = 4;
+  const Vertex::Index n_f = 5;
+  const Vertex::Index n_g = 6;
+  const Vertex::Index n_h = 7;
+  const Graph::size_type kNodeCount = 8;
+
+  Graph graph(kNodeCount);
+
+  graph[n_a].out_edges.insert(make_pair(n_e, EdgeProperties()));
+  graph[n_a].out_edges.insert(make_pair(n_f, EdgeProperties()));
+  graph[n_b].out_edges.insert(make_pair(n_a, EdgeProperties()));
+  graph[n_c].out_edges.insert(make_pair(n_d, EdgeProperties()));
+  graph[n_d].out_edges.insert(make_pair(n_e, EdgeProperties()));
+  graph[n_d].out_edges.insert(make_pair(n_f, EdgeProperties()));
+  graph[n_e].out_edges.insert(make_pair(n_b, EdgeProperties()));
+  graph[n_e].out_edges.insert(make_pair(n_c, EdgeProperties()));
+  graph[n_e].out_edges.insert(make_pair(n_f, EdgeProperties()));
+  graph[n_f].out_edges.insert(make_pair(n_g, EdgeProperties()));
+  graph[n_g].out_edges.insert(make_pair(n_h, EdgeProperties()));
+  graph[n_h].out_edges.insert(make_pair(n_g, EdgeProperties()));
+
+  TarjanAlgorithm tarjan;
+
+  for (Vertex::Index i = n_a; i <= n_e; i++) {
+    vector<Vertex::Index> vertex_indexes;
+    tarjan.Execute(i, &graph, &vertex_indexes);
+
+    EXPECT_EQ(5U, vertex_indexes.size());
+    EXPECT_TRUE(base::ContainsValue(vertex_indexes, n_a));
+    EXPECT_TRUE(base::ContainsValue(vertex_indexes, n_b));
+    EXPECT_TRUE(base::ContainsValue(vertex_indexes, n_c));
+    EXPECT_TRUE(base::ContainsValue(vertex_indexes, n_d));
+    EXPECT_TRUE(base::ContainsValue(vertex_indexes, n_e));
+  }
+
+  {
+    vector<Vertex::Index> vertex_indexes;
+    tarjan.Execute(n_f, &graph, &vertex_indexes);
+
+    EXPECT_EQ(1U, vertex_indexes.size());
+    EXPECT_TRUE(base::ContainsValue(vertex_indexes, n_f));
+  }
+
+  for (Vertex::Index i = n_g; i <= n_h; i++) {
+    vector<Vertex::Index> vertex_indexes;
+    tarjan.Execute(i, &graph, &vertex_indexes);
+
+    EXPECT_EQ(2U, vertex_indexes.size());
+    EXPECT_TRUE(base::ContainsValue(vertex_indexes, n_g));
+    EXPECT_TRUE(base::ContainsValue(vertex_indexes, n_h));
+  }
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/topological_sort.cc b/payload_generator/topological_sort.cc
new file mode 100644
index 0000000..0abd708
--- /dev/null
+++ b/payload_generator/topological_sort.cc
@@ -0,0 +1,57 @@
+//
+// Copyright (C) 2010 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/topological_sort.h"
+
+#include <set>
+#include <vector>
+
+#include <base/logging.h>
+
+using std::set;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+void TopologicalSortVisit(const Graph& graph,
+                          set<Vertex::Index>* visited_nodes,
+                          vector<Vertex::Index>* nodes,
+                          Vertex::Index node) {
+  if (visited_nodes->find(node) != visited_nodes->end())
+    return;
+
+  visited_nodes->insert(node);
+  // Visit all children.
+  for (Vertex::EdgeMap::const_iterator it = graph[node].out_edges.begin();
+       it != graph[node].out_edges.end();
+       ++it) {
+    TopologicalSortVisit(graph, visited_nodes, nodes, it->first);
+  }
+  // Visit this node.
+  nodes->push_back(node);
+}
+}  // namespace
+
+void TopologicalSort(const Graph& graph, vector<Vertex::Index>* out) {
+  set<Vertex::Index> visited_nodes;
+
+  for (Vertex::Index i = 0; i < graph.size(); i++) {
+    TopologicalSortVisit(graph, &visited_nodes, out, i);
+  }
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/topological_sort.h b/payload_generator/topological_sort.h
new file mode 100644
index 0000000..461cbe1
--- /dev/null
+++ b/payload_generator/topological_sort.h
@@ -0,0 +1,42 @@
+//
+// Copyright (C) 2010 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_PAYLOAD_GENERATOR_TOPOLOGICAL_SORT_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_TOPOLOGICAL_SORT_H_
+
+#include <vector>
+
+#include "update_engine/payload_generator/graph_types.h"
+
+namespace chromeos_update_engine {
+
+// Performs a topological sort on the directed graph 'graph' and stores
+// the nodes, in order visited, in 'out'.
+// For example, this graph:
+// A ---> C ----.
+//  \           v
+//   `--> B --> D
+// Might result in this in 'out':
+// out[0] = D
+// out[1] = B
+// out[2] = C
+// out[3] = A
+// Note: results are undefined if there is a cycle in the graph.
+void TopologicalSort(const Graph& graph, std::vector<Vertex::Index>* out);
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_TOPOLOGICAL_SORT_H_
diff --git a/payload_generator/topological_sort_unittest.cc b/payload_generator/topological_sort_unittest.cc
new file mode 100644
index 0000000..aa296d8
--- /dev/null
+++ b/payload_generator/topological_sort_unittest.cc
@@ -0,0 +1,96 @@
+//
+// Copyright (C) 2010 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/topological_sort.h"
+
+#include <utility>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "update_engine/payload_generator/graph_types.h"
+
+using std::make_pair;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+class TopologicalSortTest : public ::testing::Test {};
+
+namespace {
+// Returns true if the value is found in vect. If found, the index is stored
+// in out_index if out_index is not null.
+template <typename T>
+bool IndexOf(const vector<T>& vect,
+             const T& value,
+             typename vector<T>::size_type* out_index) {
+  for (typename vector<T>::size_type i = 0; i < vect.size(); i++) {
+    if (vect[i] == value) {
+      if (out_index) {
+        *out_index = i;
+      }
+      return true;
+    }
+  }
+  return false;
+}
+}  // namespace
+
+TEST(TopologicalSortTest, SimpleTest) {
+  int counter = 0;
+  const Vertex::Index n_a = counter++;
+  const Vertex::Index n_b = counter++;
+  const Vertex::Index n_c = counter++;
+  const Vertex::Index n_d = counter++;
+  const Vertex::Index n_e = counter++;
+  const Vertex::Index n_f = counter++;
+  const Vertex::Index n_g = counter++;
+  const Vertex::Index n_h = counter++;
+  const Vertex::Index n_i = counter++;
+  const Vertex::Index n_j = counter++;
+  const Graph::size_type kNodeCount = counter++;
+
+  Graph graph(kNodeCount);
+
+  graph[n_i].out_edges.insert(make_pair(n_j, EdgeProperties()));
+  graph[n_i].out_edges.insert(make_pair(n_c, EdgeProperties()));
+  graph[n_i].out_edges.insert(make_pair(n_e, EdgeProperties()));
+  graph[n_i].out_edges.insert(make_pair(n_h, EdgeProperties()));
+  graph[n_c].out_edges.insert(make_pair(n_b, EdgeProperties()));
+  graph[n_b].out_edges.insert(make_pair(n_a, EdgeProperties()));
+  graph[n_e].out_edges.insert(make_pair(n_d, EdgeProperties()));
+  graph[n_e].out_edges.insert(make_pair(n_g, EdgeProperties()));
+  graph[n_g].out_edges.insert(make_pair(n_d, EdgeProperties()));
+  graph[n_g].out_edges.insert(make_pair(n_f, EdgeProperties()));
+  graph[n_d].out_edges.insert(make_pair(n_a, EdgeProperties()));
+
+  vector<Vertex::Index> sorted;
+  TopologicalSort(graph, &sorted);
+
+  for (Vertex::Index i = 0; i < graph.size(); i++) {
+    vector<Vertex::Index>::size_type src_index = 0;
+    EXPECT_TRUE(IndexOf(sorted, i, &src_index));
+    for (Vertex::EdgeMap::const_iterator it = graph[i].out_edges.begin();
+         it != graph[i].out_edges.end();
+         ++it) {
+      vector<Vertex::Index>::size_type dst_index = 0;
+      EXPECT_TRUE(IndexOf(sorted, it->first, &dst_index));
+      EXPECT_LT(dst_index, src_index);
+    }
+  }
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/zip_unittest.cc b/payload_generator/zip_unittest.cc
index 10e899b..e357b15 100644
--- a/payload_generator/zip_unittest.cc
+++ b/payload_generator/zip_unittest.cc
@@ -33,6 +33,7 @@
 using chromeos_update_engine::test_utils::kRandomString;
 using google::protobuf::RepeatedPtrField;
 using std::string;
+using std::vector;
 
 namespace chromeos_update_engine {
 
@@ -49,7 +50,8 @@
   }
   ~MemoryExtentWriter() override = default;
 
-  bool Init(const RepeatedPtrField<Extent>& extents,
+  bool Init(FileDescriptorPtr fd,
+            const RepeatedPtrField<Extent>& extents,
             uint32_t block_size) override {
     return true;
   }
@@ -70,7 +72,7 @@
   std::unique_ptr<ExtentWriter> writer(
       new W(std::make_unique<MemoryExtentWriter>(out)));
   // Init() parameters are ignored by the testing MemoryExtentWriter.
-  bool ok = writer->Init({}, 1);
+  bool ok = writer->Init(nullptr, {}, 1);
   ok = writer->Write(in.data(), in.size()) && ok;
   return ok;
 }
diff --git a/cros/payload_state.cc b/payload_state.cc
similarity index 91%
rename from cros/payload_state.cc
rename to payload_state.cc
index d417ef5..3ba6391 100644
--- a/cros/payload_state.cc
+++ b/payload_state.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/payload_state.h"
+#include "update_engine/payload_state.h"
 
 #include <algorithm>
 #include <string>
@@ -29,15 +29,14 @@
 #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/cros/connection_manager_interface.h"
-#include "update_engine/cros/omaha_request_params.h"
-#include "update_engine/cros/update_attempter.h"
+#include "update_engine/connection_manager_interface.h"
+#include "update_engine/metrics_reporter_interface.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"
 
 using base::Time;
 using base::TimeDelta;
@@ -61,8 +60,6 @@
 
 PayloadState::PayloadState()
     : prefs_(nullptr),
-      powerwash_safe_prefs_(nullptr),
-      excluder_(nullptr),
       using_p2p_for_downloading_(false),
       p2p_num_attempts_(0),
       payload_attempt_number_(0),
@@ -78,10 +75,10 @@
     total_bytes_downloaded_[i] = current_bytes_downloaded_[i] = 0;
 }
 
-bool PayloadState::Initialize() {
-  prefs_ = SystemState::Get()->prefs();
-  powerwash_safe_prefs_ = SystemState::Get()->powerwash_safe_prefs();
-  excluder_ = SystemState::Get()->update_attempter()->GetExcluder();
+bool PayloadState::Initialize(SystemState* system_state) {
+  system_state_ = system_state;
+  prefs_ = system_state_->prefs();
+  powerwash_safe_prefs_ = system_state_->powerwash_safe_prefs();
   LoadResponseSignature();
   LoadPayloadAttemptNumber();
   LoadFullPayloadAttemptNumber();
@@ -196,7 +193,7 @@
 
   attempt_type_ = attempt_type;
 
-  const auto* clock = SystemState::Get()->clock();
+  ClockInterface* clock = system_state_->clock();
   attempt_start_time_boot_ = clock->GetBootTime();
   attempt_start_time_monotonic_ = clock->GetMonotonicTime();
   attempt_num_bytes_downloaded_ = 0;
@@ -205,7 +202,7 @@
   ConnectionType network_connection_type;
   ConnectionTethering tethering;
   ConnectionManagerInterface* connection_manager =
-      SystemState::Get()->connection_manager();
+      system_state_->connection_manager();
   if (!connection_manager->GetConnectionProperties(&network_connection_type,
                                                    &tethering)) {
     LOG(ERROR) << "Failed to determine connection type.";
@@ -235,7 +232,7 @@
 void PayloadState::UpdateSucceeded() {
   // Send the relevant metrics that are tracked in this class to UMA.
   CalculateUpdateDurationUptime();
-  SetUpdateTimestampEnd(SystemState::Get()->clock()->GetWallclockTime());
+  SetUpdateTimestampEnd(system_state_->clock()->GetWallclockTime());
 
   switch (attempt_type_) {
     case AttemptType::kUpdate:
@@ -245,7 +242,7 @@
       break;
 
     case AttemptType::kRollback:
-      SystemState::Get()->metrics_reporter()->ReportRollbackMetrics(
+      system_state_->metrics_reporter()->ReportRollbackMetrics(
           metrics::RollbackResult::kSuccess);
       break;
   }
@@ -255,7 +252,7 @@
   SetNumResponsesSeen(0);
   SetPayloadIndex(0);
 
-  metrics_utils::SetSystemUpdatedMarker(SystemState::Get()->clock(), prefs_);
+  metrics_utils::SetSystemUpdatedMarker(system_state_->clock(), prefs_);
 }
 
 void PayloadState::UpdateFailed(ErrorCode error) {
@@ -278,7 +275,7 @@
       break;
 
     case AttemptType::kRollback:
-      SystemState::Get()->metrics_reporter()->ReportRollbackMetrics(
+      system_state_->metrics_reporter()->ReportRollbackMetrics(
           metrics::RollbackResult::kFailed);
       break;
   }
@@ -311,7 +308,6 @@
     case ErrorCode::kUnsupportedMinorPayloadVersion:
     case ErrorCode::kPayloadTimestampError:
     case ErrorCode::kVerityCalculationError:
-      ExcludeCurrentPayload();
       IncrementUrlIndex();
       break;
 
@@ -374,7 +370,6 @@
     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;
 
@@ -408,7 +403,7 @@
               << "will happen from local peer (via p2p).";
     return false;
   }
-  if (SystemState::Get()->request_params()->interactive()) {
+  if (system_state_->request_params()->interactive()) {
     LOG(INFO) << "Payload backoff disabled for interactive update checks.";
     return false;
   }
@@ -424,7 +419,7 @@
     }
   }
 
-  if (!SystemState::Get()->hardware()->IsOfficialBuild() &&
+  if (!system_state_->hardware()->IsOfficialBuild() &&
       !prefs_->Exists(kPrefsNoIgnoreBackoff)) {
     // Backoffs are needed only for official builds. We do not want any delays
     // or update failures due to backoffs during testing or development. Unless
@@ -453,7 +448,7 @@
 }
 
 void PayloadState::Rollback() {
-  SetRollbackVersion(SystemState::Get()->request_params()->app_version());
+  SetRollbackVersion(system_state_->request_params()->app_version());
   AttemptStarted(AttemptType::kRollback);
 }
 
@@ -463,7 +458,6 @@
 }
 
 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";
@@ -476,9 +470,10 @@
 }
 
 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();
+  size_t max_url_size = 0;
+  for (const auto& urls : candidate_urls_)
+    max_url_size = std::max(max_url_size, urls.size());
   if (next_url_index < max_url_size) {
     LOG(INFO) << "Incrementing the URL index for next attempt";
     SetUrlIndex(next_url_index);
@@ -507,33 +502,10 @@
   } else {
     LOG(INFO) << "Reached max number of failures for Url" << GetUrlIndex()
               << ". Trying next available URL";
-    ExcludeCurrentPayload();
     IncrementUrlIndex();
   }
 }
 
-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="
-              << package.hash;
-    return;
-  }
-  auto exclusion_name = utils::GetExclusionName(GetCurrentUrl());
-  if (!excluder_->Exclude(exclusion_name))
-    LOG(WARNING) << "Failed to exclude "
-                 << " Package Hash=" << package.hash
-                 << " CurrentUrl=" << GetCurrentUrl();
-  else
-    LOG(INFO) << "Excluded "
-              << " Package Hash=" << package.hash
-              << " CurrentUrl=" << GetCurrentUrl();
-}
-
 void PayloadState::UpdateBackoffExpiryTime() {
   if (response_.disable_payload_backoff) {
     LOG(INFO) << "Resetting backoff expiry time as payload backoff is disabled";
@@ -611,7 +583,7 @@
       return kPayloadTypeDelta;
     }
   }
-  OmahaRequestParams* params = SystemState::Get()->request_params();
+  OmahaRequestParams* params = system_state_->request_params();
   if (params->delta_okay()) {
     return kPayloadTypeFull;
   }
@@ -619,6 +591,10 @@
   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();
 
@@ -628,7 +604,7 @@
 
   int64_t payload_bytes_downloaded = attempt_num_bytes_downloaded_;
 
-  const auto* clock = SystemState::Get()->clock();
+  ClockInterface* clock = system_state_->clock();
   TimeDelta duration = clock->GetBootTime() - attempt_start_time_boot_;
   TimeDelta duration_uptime =
       clock->GetMonotonicTime() - attempt_start_time_monotonic_;
@@ -673,13 +649,13 @@
     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;
   }
 
-  SystemState::Get()->metrics_reporter()->ReportUpdateAttemptMetrics(
+  system_state_->metrics_reporter()->ReportUpdateAttemptMetrics(
+      system_state_,
       attempt_number,
       payload_type,
       duration,
@@ -688,7 +664,7 @@
       attempt_result,
       internal_error_code);
 
-  SystemState::Get()->metrics_reporter()->ReportUpdateAttemptDownloadMetrics(
+  system_state_->metrics_reporter()->ReportUpdateAttemptDownloadMetrics(
       payload_bytes_downloaded,
       payload_download_speed_bps,
       download_source,
@@ -718,8 +694,7 @@
   if (!attempt_in_progress)
     return;
 
-  SystemState::Get()
-      ->metrics_reporter()
+  system_state_->metrics_reporter()
       ->ReportAbnormallyTerminatedUpdateAttemptMetrics();
 
   ClearPersistedAttemptMetrics();
@@ -783,7 +758,7 @@
 
   int updates_abandoned_count = num_responses_seen_ - 1;
 
-  SystemState::Get()->metrics_reporter()->ReportSuccessfulUpdateMetrics(
+  system_state_->metrics_reporter()->ReportSuccessfulUpdateMetrics(
       attempt_count,
       updates_abandoned_count,
       payload_type,
@@ -799,7 +774,7 @@
 void PayloadState::UpdateNumReboots() {
   // We only update the reboot count when the system has been detected to have
   // been rebooted.
-  if (!SystemState::Get()->system_rebooted()) {
+  if (!system_state_->system_rebooted()) {
     return;
   }
 
@@ -819,7 +794,7 @@
   SetUrlFailureCount(0);
   SetUrlSwitchCount(0);
   UpdateBackoffExpiryTime();  // This will reset the backoff expiry time.
-  SetUpdateTimestampStart(SystemState::Get()->clock()->GetWallclockTime());
+  SetUpdateTimestampStart(system_state_->clock()->GetWallclockTime());
   SetUpdateTimestampEnd(Time());  // Set to null time
   SetUpdateDurationUptime(TimeDelta::FromSeconds(0));
   ResetDownloadSourcesOnNewUpdate();
@@ -831,6 +806,7 @@
 }
 
 void PayloadState::ResetRollbackVersion() {
+  CHECK(powerwash_safe_prefs_);
   rollback_version_ = "";
   powerwash_safe_prefs_->Delete(kPrefsRollbackVersion);
 }
@@ -879,6 +855,7 @@
 }
 
 void PayloadState::LoadResponseSignature() {
+  CHECK(prefs_);
   string stored_value;
   if (prefs_->Exists(kPrefsCurrentResponseSignature) &&
       prefs_->GetString(kPrefsCurrentResponseSignature, &stored_value)) {
@@ -887,6 +864,7 @@
 }
 
 void PayloadState::SetResponseSignature(const string& response_signature) {
+  CHECK(prefs_);
   response_signature_ = response_signature;
   LOG(INFO) << "Current Response Signature = \n" << response_signature_;
   prefs_->SetString(kPrefsCurrentResponseSignature, response_signature_);
@@ -909,6 +887,7 @@
 
 void PayloadState::SetFullPayloadAttemptNumber(
     int full_payload_attempt_number) {
+  CHECK(prefs_);
   full_payload_attempt_number_ = full_payload_attempt_number;
   LOG(INFO) << "Full Payload Attempt Number = " << full_payload_attempt_number_;
   prefs_->SetInt64(kPrefsFullPayloadAttemptNumber,
@@ -916,18 +895,16 @@
 }
 
 void PayloadState::SetPayloadIndex(size_t payload_index) {
+  CHECK(prefs_);
   payload_index_ = payload_index;
   LOG(INFO) << "Payload Index = " << payload_index_;
   prefs_->SetInt64(kPrefsUpdateStatePayloadIndex, payload_index_);
 }
 
 bool PayloadState::NextPayload() {
-  if (payload_index_ >= candidate_urls_.size())
+  if (payload_index_ + 1 >= candidate_urls_.size())
     return false;
   SetPayloadIndex(payload_index_ + 1);
-  if (payload_index_ >= candidate_urls_.size())
-    return false;
-  SetUrlIndex(0);
   return true;
 }
 
@@ -936,6 +913,7 @@
 }
 
 void PayloadState::SetUrlIndex(uint32_t url_index) {
+  CHECK(prefs_);
   url_index_ = url_index;
   LOG(INFO) << "Current URL Index = " << url_index_;
   prefs_->SetInt64(kPrefsCurrentUrlIndex, url_index_);
@@ -951,6 +929,7 @@
 }
 
 void PayloadState::SetScatteringWaitPeriod(TimeDelta wait_period) {
+  CHECK(prefs_);
   scattering_wait_period_ = wait_period;
   LOG(INFO) << "Scattering Wait Period (seconds) = "
             << scattering_wait_period_.InSeconds();
@@ -968,6 +947,7 @@
 }
 
 void PayloadState::SetStagingWaitPeriod(TimeDelta wait_period) {
+  CHECK(prefs_);
   staging_wait_period_ = wait_period;
   LOG(INFO) << "Staging Wait Period (days) =" << staging_wait_period_.InDays();
   if (staging_wait_period_.InSeconds() > 0) {
@@ -983,6 +963,7 @@
 }
 
 void PayloadState::SetUrlSwitchCount(uint32_t url_switch_count) {
+  CHECK(prefs_);
   url_switch_count_ = url_switch_count;
   LOG(INFO) << "URL Switch Count = " << url_switch_count_;
   prefs_->SetInt64(kPrefsUrlSwitchCount, url_switch_count_);
@@ -993,6 +974,7 @@
 }
 
 void PayloadState::SetUrlFailureCount(uint32_t url_failure_count) {
+  CHECK(prefs_);
   url_failure_count_ = url_failure_count;
   LOG(INFO) << "Current URL (Url" << GetUrlIndex()
             << ")'s Failure Count = " << url_failure_count_;
@@ -1000,6 +982,7 @@
 }
 
 void PayloadState::LoadBackoffExpiryTime() {
+  CHECK(prefs_);
   int64_t stored_value;
   if (!prefs_->Exists(kPrefsBackoffExpiryTime))
     return;
@@ -1018,6 +1001,7 @@
 }
 
 void PayloadState::SetBackoffExpiryTime(const Time& new_time) {
+  CHECK(prefs_);
   backoff_expiry_time_ = new_time;
   LOG(INFO) << "Backoff Expiry Time = "
             << utils::ToString(backoff_expiry_time_);
@@ -1027,7 +1011,7 @@
 
 TimeDelta PayloadState::GetUpdateDuration() {
   Time end_time = update_timestamp_end_.is_null()
-                      ? SystemState::Get()->clock()->GetWallclockTime()
+                      ? system_state_->clock()->GetWallclockTime()
                       : update_timestamp_end_;
   return end_time - update_timestamp_start_;
 }
@@ -1035,7 +1019,10 @@
 void PayloadState::LoadUpdateTimestampStart() {
   int64_t stored_value;
   Time stored_time;
-  Time now = SystemState::Get()->clock()->GetWallclockTime();
+
+  CHECK(prefs_);
+
+  Time now = system_state_->clock()->GetWallclockTime();
 
   if (!prefs_->Exists(kPrefsUpdateTimestampStart)) {
     // The preference missing is not unexpected - in that case, just
@@ -1048,7 +1035,7 @@
     stored_time = Time::FromInternalValue(stored_value);
   }
 
-  // Validation check: If the time read from disk is in the future
+  // Sanity check: If the time read from disk is in the future
   // (modulo some slack to account for possible NTP drift
   // adjustments), something is fishy and we should report and
   // reset.
@@ -1083,6 +1070,8 @@
   int64_t stored_value;
   TimeDelta stored_delta;
 
+  CHECK(prefs_);
+
   if (!prefs_->Exists(kPrefsUpdateDurationUptime)) {
     // The preference missing is not unexpected - in that case, just
     // we'll use zero as the delta
@@ -1093,7 +1082,7 @@
     stored_delta = TimeDelta::FromInternalValue(stored_value);
   }
 
-  // Validation check: Uptime can never be greater than the wall-clock
+  // Sanity-check: Uptime can never be greater than the wall-clock
   // difference (modulo some slack). If it is, report and reset
   // to the wall-clock difference.
   TimeDelta diff = GetUpdateDuration() - stored_delta;
@@ -1113,12 +1102,14 @@
 }
 
 void PayloadState::LoadRollbackHappened() {
+  CHECK(powerwash_safe_prefs_);
   bool rollback_happened = false;
   powerwash_safe_prefs_->GetBoolean(kPrefsRollbackHappened, &rollback_happened);
   SetRollbackHappened(rollback_happened);
 }
 
 void PayloadState::SetRollbackHappened(bool rollback_happened) {
+  CHECK(powerwash_safe_prefs_);
   LOG(INFO) << "Setting rollback-happened to " << rollback_happened << ".";
   rollback_happened_ = rollback_happened;
   if (rollback_happened) {
@@ -1130,6 +1121,7 @@
 }
 
 void PayloadState::LoadRollbackVersion() {
+  CHECK(powerwash_safe_prefs_);
   string rollback_version;
   if (powerwash_safe_prefs_->GetString(kPrefsRollbackVersion,
                                        &rollback_version)) {
@@ -1138,7 +1130,8 @@
 }
 
 void PayloadState::SetRollbackVersion(const string& rollback_version) {
-  LOG(INFO) << "Excluding version " << rollback_version;
+  CHECK(powerwash_safe_prefs_);
+  LOG(INFO) << "Blacklisting version " << rollback_version;
   rollback_version_ = rollback_version;
   powerwash_safe_prefs_->SetString(kPrefsRollbackVersion, rollback_version);
 }
@@ -1146,6 +1139,7 @@
 void PayloadState::SetUpdateDurationUptimeExtended(const TimeDelta& value,
                                                    const Time& timestamp,
                                                    bool use_logging) {
+  CHECK(prefs_);
   update_duration_uptime_ = value;
   update_duration_uptime_timestamp_ = timestamp;
   prefs_->SetInt64(kPrefsUpdateDurationUptime,
@@ -1157,12 +1151,12 @@
 }
 
 void PayloadState::SetUpdateDurationUptime(const TimeDelta& value) {
-  Time now = SystemState::Get()->clock()->GetMonotonicTime();
+  Time now = system_state_->clock()->GetMonotonicTime();
   SetUpdateDurationUptimeExtended(value, now, true);
 }
 
 void PayloadState::CalculateUpdateDurationUptime() {
-  Time now = SystemState::Get()->clock()->GetMonotonicTime();
+  Time now = system_state_->clock()->GetMonotonicTime();
   TimeDelta uptime_since_last_update = now - update_duration_uptime_timestamp_;
 
   if (uptime_since_last_update > TimeDelta::FromSeconds(kUptimeResolution)) {
@@ -1184,6 +1178,8 @@
 void PayloadState::SetCurrentBytesDownloaded(DownloadSource source,
                                              uint64_t current_bytes_downloaded,
                                              bool log) {
+  CHECK(prefs_);
+
   if (source >= kNumDownloadSources)
     return;
 
@@ -1205,6 +1201,8 @@
 void PayloadState::SetTotalBytesDownloaded(DownloadSource source,
                                            uint64_t total_bytes_downloaded,
                                            bool log) {
+  CHECK(prefs_);
+
   if (source >= kNumDownloadSources)
     return;
 
@@ -1223,6 +1221,7 @@
 }
 
 void PayloadState::SetNumResponsesSeen(int num_responses_seen) {
+  CHECK(prefs_);
   num_responses_seen_ = num_responses_seen;
   LOG(INFO) << "Num Responses Seen = " << num_responses_seen_;
   prefs_->SetInt64(kPrefsNumResponsesSeen, num_responses_seen_);
@@ -1231,8 +1230,8 @@
 void PayloadState::ComputeCandidateUrls() {
   bool http_url_ok = true;
 
-  if (SystemState::Get()->hardware()->IsOfficialBuild()) {
-    const policy::DevicePolicy* policy = SystemState::Get()->device_policy();
+  if (system_state_->hardware()->IsOfficialBuild()) {
+    const policy::DevicePolicy* policy = system_state_->device_policy();
     if (policy && policy->GetHttpDownloadsEnabled(&http_url_ok) && !http_url_ok)
       LOG(INFO) << "Downloads via HTTP Url are not enabled by device policy";
   } else {
@@ -1265,14 +1264,12 @@
 
   // Avoid the UpdateEngineStarted actions if this is not the first time we
   // run the update engine since reboot.
-  if (!SystemState::Get()->system_rebooted())
+  if (!system_state_->system_rebooted())
     return;
 
   // Report time_to_reboot if we booted into a new update.
   metrics_utils::LoadAndReportTimeToReboot(
-      SystemState::Get()->metrics_reporter(),
-      prefs_,
-      SystemState::Get()->clock());
+      system_state_->metrics_reporter(), prefs_, system_state_->clock());
   prefs_->Delete(kPrefsSystemUpdatedMarker);
 
   // Check if it is needed to send metrics about a failed reboot into a new
@@ -1297,8 +1294,7 @@
     // since we successfully booted the new update in that case. If the boot
     // failed, we will read this value from the same version, so it will always
     // be compatible.
-    if (installed_from ==
-        SystemState::Get()->boot_control()->GetCurrentSlot()) {
+    if (installed_from == system_state_->boot_control()->GetCurrentSlot()) {
       // A reboot was pending, but the chromebook is again in the same
       // BootDevice where the update was installed from.
       int64_t target_attempt;
@@ -1309,7 +1305,7 @@
       }
 
       // Report the UMA metric of the current boot failure.
-      SystemState::Get()->metrics_reporter()->ReportFailedUpdateCount(
+      system_state_->metrics_reporter()->ReportFailedUpdateCount(
           target_attempt);
     } else {
       prefs_->Delete(kPrefsTargetVersionAttempt);
@@ -1340,7 +1336,7 @@
   prefs_->SetInt64(kPrefsTargetVersionAttempt, target_attempt + 1);
 
   prefs_->SetInt64(kPrefsTargetVersionInstalledFrom,
-                   SystemState::Get()->boot_control()->GetCurrentSlot());
+                   system_state_->boot_control()->GetCurrentSlot());
 }
 
 void PayloadState::ResetUpdateStatus() {
@@ -1362,6 +1358,7 @@
 void PayloadState::SetP2PNumAttempts(int value) {
   p2p_num_attempts_ = value;
   LOG(INFO) << "p2p Num Attempts = " << p2p_num_attempts_;
+  CHECK(prefs_);
   prefs_->SetInt64(kPrefsP2PNumAttempts, value);
 }
 
@@ -1377,6 +1374,7 @@
   p2p_first_attempt_timestamp_ = time;
   LOG(INFO) << "p2p First Attempt Timestamp = "
             << utils::ToString(p2p_first_attempt_timestamp_);
+  CHECK(prefs_);
   int64_t stored_value = time.ToInternalValue();
   prefs_->SetInt64(kPrefsP2PFirstAttemptTimestamp, stored_value);
 }
@@ -1389,10 +1387,10 @@
 }
 
 void PayloadState::P2PNewAttempt() {
+  CHECK(prefs_);
   // Set timestamp, if it hasn't been set already
   if (p2p_first_attempt_timestamp_.is_null()) {
-    SetP2PFirstAttemptTimestamp(
-        SystemState::Get()->clock()->GetWallclockTime());
+    SetP2PFirstAttemptTimestamp(system_state_->clock()->GetWallclockTime());
   }
   // Increase number of attempts
   SetP2PNumAttempts(GetP2PNumAttempts() + 1);
@@ -1407,7 +1405,7 @@
   }
 
   if (!p2p_first_attempt_timestamp_.is_null()) {
-    Time now = SystemState::Get()->clock()->GetWallclockTime();
+    Time now = system_state_->clock()->GetWallclockTime();
     TimeDelta time_spent_attempting_p2p = now - p2p_first_attempt_timestamp_;
     if (time_spent_attempting_p2p.InSeconds() < 0) {
       LOG(ERROR) << "Time spent attempting p2p is negative"
diff --git a/cros/payload_state.h b/payload_state.h
similarity index 93%
rename from cros/payload_state.h
rename to payload_state.h
index db54865..5ef1220 100644
--- a/cros/payload_state.h
+++ b/payload_state.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_PAYLOAD_STATE_H_
-#define UPDATE_ENGINE_CROS_PAYLOAD_STATE_H_
+#ifndef UPDATE_ENGINE_PAYLOAD_STATE_H_
+#define UPDATE_ENGINE_PAYLOAD_STATE_H_
 
 #include <algorithm>
 #include <string>
@@ -24,10 +24,9 @@
 #include <base/time/time.h>
 #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/cros/payload_state_interface.h"
+#include "update_engine/metrics_constants.h"
+#include "update_engine/payload_state_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -48,7 +47,7 @@
   // It performs the initial loading of all persisted state into memory and
   // dumps the initial state for debugging purposes.  Note: the other methods
   // should be called only after calling Initialize on this object.
-  bool Initialize();
+  bool Initialize(SystemState* system_state);
 
   // Implementation of PayloadStateInterface methods.
   void SetResponse(const OmahaResponse& response) override;
@@ -157,12 +156,6 @@
   FRIEND_TEST(PayloadStateTest, RollbackHappened);
   FRIEND_TEST(PayloadStateTest, RollbackVersion);
   FRIEND_TEST(PayloadStateTest, UpdateSuccessWithWipedPrefs);
-  FRIEND_TEST(PayloadStateTest, NextPayloadResetsUrlIndex);
-  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().
@@ -187,12 +180,6 @@
   // to the next URL and resets the failure count for that URL.
   void IncrementFailureCount();
 
-  // Excludes the current payload + current candidate URL from being part of
-  // future updates/retries. Whenever |SetResponse()| or |NextPayload()| decide
-  // on the initial current URL index and the next payload respectively, it will
-  // advanced based on exclusions.
-  void ExcludeCurrentPayload();
-
   // Updates the backoff expiry time exponentially based on the current
   // payload attempt number.
   void UpdateBackoffExpiryTime();
@@ -368,14 +355,14 @@
   // check where policy was available. This info is preserved over powerwash.
   void LoadRollbackHappened();
 
-  // Loads the excluded version from our prefs file.
+  // Loads the blacklisted version from our prefs file.
   void LoadRollbackVersion();
 
-  // Excludes this version from getting AU'd to until we receive a new update
+  // Blacklists this version from getting AU'd to until we receive a new update
   // response.
   void SetRollbackVersion(const std::string& rollback_version);
 
-  // Clears any excluded version.
+  // Clears any blacklisted version.
   void ResetRollbackVersion();
 
   inline uint32_t GetUrlIndex() {
@@ -429,6 +416,9 @@
   // Get the total size of all payloads.
   int64_t GetPayloadSize();
 
+  // The global state of the system.
+  SystemState* system_state_;
+
   // Interface object with which we read/write persisted state. This must
   // be set by calling the Initialize method before calling any other method.
   PrefsInterface* prefs_;
@@ -438,11 +428,6 @@
   // This object persists across powerwashes.
   PrefsInterface* powerwash_safe_prefs_;
 
-  // Interface object with which we determine exclusion decisions for
-  // payloads/partitions during the update. This must be set by calling the
-  // Initialize method before calling any other method.
-  ExcluderInterface* excluder_;
-
   // This is the current response object from Omaha.
   OmahaResponse response_;
 
@@ -483,9 +468,10 @@
   size_t payload_index_ = 0;
 
   // The index of the current URL.  This type is different from the one in the
-  // accessor methods because |PrefsInterface| supports only int64_t but we want
+  // accessor methods because PrefsInterface supports only int64_t but we want
   // to provide a stronger abstraction of uint32_t.  Each update to this value
-  // is persisted so we resume from the same value in case of a process restart.
+  // is persisted so we resume from the same value in case of a process
+  // restart.
   size_t url_index_;
 
   // The count of failures encountered in the current attempt to download using
@@ -563,7 +549,7 @@
   // forced updates to avoid update-rollback loops.
   bool rollback_happened_;
 
-  // This stores an excluded version set as part of rollback. When we rollback
+  // This stores a blacklisted version set as part of rollback. When we rollback
   // we store the version of the os from which we are rolling back from in order
   // to guarantee that we do not re-update to it on the next au attempt after
   // reboot.
@@ -595,4 +581,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_PAYLOAD_STATE_H_
+#endif  // UPDATE_ENGINE_PAYLOAD_STATE_H_
diff --git a/cros/payload_state_interface.h b/payload_state_interface.h
similarity index 97%
rename from cros/payload_state_interface.h
rename to payload_state_interface.h
index 9ead650..d384a0e 100644
--- a/cros/payload_state_interface.h
+++ b/payload_state_interface.h
@@ -14,14 +14,14 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_PAYLOAD_STATE_INTERFACE_H_
-#define UPDATE_ENGINE_CROS_PAYLOAD_STATE_INTERFACE_H_
+#ifndef UPDATE_ENGINE_PAYLOAD_STATE_INTERFACE_H_
+#define UPDATE_ENGINE_PAYLOAD_STATE_INTERFACE_H_
 
 #include <string>
 
 #include "update_engine/common/action_processor.h"
 #include "update_engine/common/constants.h"
-#include "update_engine/cros/omaha_response.h"
+#include "update_engine/omaha_response.h"
 
 namespace chromeos_update_engine {
 
@@ -212,4 +212,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_PAYLOAD_STATE_INTERFACE_H_
+#endif  // UPDATE_ENGINE_PAYLOAD_STATE_INTERFACE_H_
diff --git a/cros/payload_state_unittest.cc b/payload_state_unittest.cc
similarity index 76%
rename from cros/payload_state_unittest.cc
rename to payload_state_unittest.cc
index 5478fca..869c24e 100644
--- a/cros/payload_state_unittest.cc
+++ b/payload_state_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/payload_state.h"
+#include "update_engine/payload_state.h"
 
 #include <base/files/file_path.h>
 #include <base/files/file_util.h>
@@ -23,16 +23,16 @@
 #include <gtest/gtest.h>
 
 #include "update_engine/common/constants.h"
-#include "update_engine/common/excluder_interface.h"
+#include "update_engine/common/fake_clock.h"
 #include "update_engine/common/fake_hardware.h"
-#include "update_engine/common/metrics_reporter_interface.h"
-#include "update_engine/common/mock_excluder.h"
+#include "update_engine/common/fake_prefs.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/cros/fake_system_state.h"
-#include "update_engine/cros/omaha_request_action.h"
+#include "update_engine/fake_system_state.h"
+#include "update_engine/metrics_reporter_interface.h"
+#include "update_engine/omaha_request_action.h"
 
 using base::Time;
 using base::TimeDelta;
@@ -44,7 +44,6 @@
 using testing::NiceMock;
 using testing::Return;
 using testing::SetArgPointee;
-using testing::StrictMock;
 
 namespace chromeos_update_engine {
 
@@ -104,18 +103,12 @@
   EXPECT_EQ(expected_response_sign, stored_response_sign);
 }
 
-class PayloadStateTest : public ::testing::Test {
- public:
-  void SetUp() { FakeSystemState::CreateInstance(); }
+class PayloadStateTest : public ::testing::Test {};
 
-  // TODO(b/171829801): Replace all the |MockPrefs| in this file with
-  // |FakePrefs| so we don't have to catch every single unimportant mock call.
-};
-
-TEST_F(PayloadStateTest, SetResponseWorksWithEmptyResponse) {
+TEST(PayloadStateTest, SetResponseWorksWithEmptyResponse) {
   OmahaResponse response;
-  FakeSystemState::Get()->set_prefs(nullptr);
-  auto* prefs = FakeSystemState::Get()->mock_prefs();
+  FakeSystemState fake_system_state;
+  NiceMock<MockPrefs>* prefs = fake_system_state.mock_prefs();
   EXPECT_CALL(*prefs, SetInt64(_, _)).Times(AnyNumber());
   EXPECT_CALL(*prefs, SetInt64(kPrefsPayloadAttemptNumber, 0))
       .Times(AtLeast(1));
@@ -137,7 +130,7 @@
       .Times(AtLeast(1));
   EXPECT_CALL(*prefs, SetInt64(kPrefsNumReboots, 0)).Times(AtLeast(1));
   PayloadState payload_state;
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   payload_state.SetResponse(response);
   string stored_response_sign = payload_state.GetResponseSignature();
   string expected_response_sign =
@@ -150,15 +143,15 @@
   EXPECT_EQ(1, payload_state.GetNumResponsesSeen());
 }
 
-TEST_F(PayloadStateTest, SetResponseWorksWithSingleUrl) {
+TEST(PayloadStateTest, SetResponseWorksWithSingleUrl) {
   OmahaResponse response;
   response.packages.push_back({.payload_urls = {"https://single.url.test"},
                                .size = 123456789,
                                .metadata_size = 58123,
                                .metadata_signature = "msign",
                                .hash = "hash"});
-  FakeSystemState::Get()->set_prefs(nullptr);
-  auto* prefs = FakeSystemState::Get()->mock_prefs();
+  FakeSystemState fake_system_state;
+  NiceMock<MockPrefs>* prefs = fake_system_state.mock_prefs();
   EXPECT_CALL(*prefs, SetInt64(_, _)).Times(AnyNumber());
   EXPECT_CALL(*prefs, SetInt64(kPrefsPayloadAttemptNumber, 0))
       .Times(AtLeast(1));
@@ -180,7 +173,7 @@
       .Times(AtLeast(1));
   EXPECT_CALL(*prefs, SetInt64(kPrefsNumReboots, 0)).Times(AtLeast(1));
   PayloadState payload_state;
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   payload_state.SetResponse(response);
   string stored_response_sign = payload_state.GetResponseSignature();
   string expected_response_sign =
@@ -201,7 +194,7 @@
   EXPECT_EQ(1, payload_state.GetNumResponsesSeen());
 }
 
-TEST_F(PayloadStateTest, SetResponseWorksWithMultipleUrls) {
+TEST(PayloadStateTest, SetResponseWorksWithMultipleUrls) {
   OmahaResponse response;
   response.packages.push_back({.payload_urls = {"http://multiple.url.test",
                                                 "https://multiple.url.test"},
@@ -209,8 +202,8 @@
                                .metadata_size = 558123,
                                .metadata_signature = "metasign",
                                .hash = "rhash"});
-  FakeSystemState::Get()->set_prefs(nullptr);
-  auto* prefs = FakeSystemState::Get()->mock_prefs();
+  FakeSystemState fake_system_state;
+  NiceMock<MockPrefs>* prefs = fake_system_state.mock_prefs();
   EXPECT_CALL(*prefs, SetInt64(_, _)).Times(AnyNumber());
   EXPECT_CALL(*prefs, SetInt64(kPrefsPayloadAttemptNumber, 0))
       .Times(AtLeast(1));
@@ -229,7 +222,7 @@
   EXPECT_CALL(*prefs, SetInt64(kPrefsNumReboots, 0)).Times(AtLeast(1));
 
   PayloadState payload_state;
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   payload_state.SetResponse(response);
   string stored_response_sign = payload_state.GetResponseSignature();
   string expected_response_sign =
@@ -251,10 +244,10 @@
   EXPECT_EQ(1, payload_state.GetNumResponsesSeen());
 }
 
-TEST_F(PayloadStateTest, CanAdvanceUrlIndexCorrectly) {
+TEST(PayloadStateTest, CanAdvanceUrlIndexCorrectly) {
   OmahaResponse response;
-  FakeSystemState::Get()->set_prefs(nullptr);
-  auto* prefs = FakeSystemState::Get()->mock_prefs();
+  FakeSystemState fake_system_state;
+  NiceMock<MockPrefs>* prefs = fake_system_state.mock_prefs();
   PayloadState payload_state;
 
   EXPECT_CALL(*prefs, SetInt64(_, _)).Times(AnyNumber());
@@ -281,7 +274,7 @@
   EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlFailureCount, 0))
       .Times(AtLeast(4));
 
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
 
   // This does a SetResponse which causes all the states to be set to 0 for
   // the first time.
@@ -306,11 +299,12 @@
   EXPECT_EQ(3U, payload_state.GetUrlSwitchCount());
 }
 
-TEST_F(PayloadStateTest, NewResponseResetsPayloadState) {
+TEST(PayloadStateTest, NewResponseResetsPayloadState) {
   OmahaResponse response;
+  FakeSystemState fake_system_state;
   PayloadState payload_state;
 
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
 
   // Set the first response.
   SetupPayloadStateWith2Urls(
@@ -352,12 +346,12 @@
             payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpsServer));
 }
 
-TEST_F(PayloadStateTest, AllCountersGetUpdatedProperlyOnErrorCodesAndEvents) {
+TEST(PayloadStateTest, AllCountersGetUpdatedProperlyOnErrorCodesAndEvents) {
   OmahaResponse response;
   PayloadState payload_state;
+  FakeSystemState fake_system_state;
   int progress_bytes = 100;
-  FakeSystemState::Get()->set_prefs(nullptr);
-  auto* prefs = FakeSystemState::Get()->mock_prefs();
+  NiceMock<MockPrefs>* prefs = fake_system_state.mock_prefs();
 
   EXPECT_CALL(*prefs, SetInt64(_, _)).Times(AnyNumber());
   EXPECT_CALL(*prefs, SetInt64(kPrefsPayloadAttemptNumber, 0))
@@ -403,7 +397,7 @@
       .Times(AtLeast(1));
   EXPECT_CALL(*prefs, SetInt64(kPrefsNumReboots, 0)).Times(AtLeast(1));
 
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
 
   SetupPayloadStateWith2Urls(
       "Hash5873", true, false, &payload_state, &response);
@@ -498,12 +492,11 @@
   EXPECT_FALSE(payload_state.ShouldBackoffDownload());
 }
 
-TEST_F(PayloadStateTest,
-       PayloadAttemptNumberIncreasesOnSuccessfulFullDownload) {
+TEST(PayloadStateTest, PayloadAttemptNumberIncreasesOnSuccessfulFullDownload) {
   OmahaResponse response;
   PayloadState payload_state;
-  FakeSystemState::Get()->set_prefs(nullptr);
-  auto* prefs = FakeSystemState::Get()->mock_prefs();
+  FakeSystemState fake_system_state;
+  NiceMock<MockPrefs>* prefs = fake_system_state.mock_prefs();
 
   EXPECT_CALL(*prefs, SetInt64(_, _)).Times(AnyNumber());
   EXPECT_CALL(*prefs, SetInt64(kPrefsPayloadAttemptNumber, 0))
@@ -522,7 +515,7 @@
   EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlFailureCount, 0))
       .Times(AtLeast(1));
 
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
 
   SetupPayloadStateWith2Urls(
       "Hash8593", true, false, &payload_state, &response);
@@ -538,12 +531,11 @@
   EXPECT_EQ(0U, payload_state.GetUrlSwitchCount());
 }
 
-TEST_F(PayloadStateTest,
-       PayloadAttemptNumberIncreasesOnSuccessfulDeltaDownload) {
+TEST(PayloadStateTest, PayloadAttemptNumberIncreasesOnSuccessfulDeltaDownload) {
   OmahaResponse response;
   PayloadState payload_state;
-  FakeSystemState::Get()->set_prefs(nullptr);
-  auto* prefs = FakeSystemState::Get()->mock_prefs();
+  FakeSystemState fake_system_state;
+  NiceMock<MockPrefs>* prefs = fake_system_state.mock_prefs();
 
   EXPECT_CALL(*prefs, SetInt64(_, _)).Times(AnyNumber());
   EXPECT_CALL(*prefs, SetInt64(kPrefsPayloadAttemptNumber, 0))
@@ -561,7 +553,7 @@
   EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlFailureCount, 0))
       .Times(AtLeast(1));
 
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
 
   SetupPayloadStateWith2Urls("Hash8593", true, true, &payload_state, &response);
 
@@ -576,11 +568,12 @@
   EXPECT_EQ(0U, payload_state.GetUrlSwitchCount());
 }
 
-TEST_F(PayloadStateTest, SetResponseResetsInvalidUrlIndex) {
+TEST(PayloadStateTest, SetResponseResetsInvalidUrlIndex) {
   OmahaResponse response;
   PayloadState payload_state;
+  FakeSystemState fake_system_state;
 
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   SetupPayloadStateWith2Urls(
       "Hash4427", true, false, &payload_state, &response);
 
@@ -596,26 +589,27 @@
   EXPECT_EQ(1U, payload_state.GetUrlSwitchCount());
 
   // Now, simulate a corrupted url index on persisted store which gets
-  // loaded when update_engine restarts.
-  FakeSystemState::Get()->set_prefs(nullptr);
-  auto* prefs = FakeSystemState::Get()->mock_prefs();
-  EXPECT_CALL(*prefs, Exists(_)).WillRepeatedly(Return(true));
-  EXPECT_CALL(*prefs, GetInt64(_, _)).Times(AtLeast(1));
-  EXPECT_CALL(*prefs, GetInt64(kPrefsPayloadAttemptNumber, _))
+  // loaded when update_engine restarts. Using a different prefs object
+  // so as to not bother accounting for the uninteresting calls above.
+  FakeSystemState fake_system_state2;
+  NiceMock<MockPrefs>* prefs2 = fake_system_state2.mock_prefs();
+  EXPECT_CALL(*prefs2, Exists(_)).WillRepeatedly(Return(true));
+  EXPECT_CALL(*prefs2, GetInt64(_, _)).Times(AtLeast(1));
+  EXPECT_CALL(*prefs2, GetInt64(kPrefsPayloadAttemptNumber, _))
       .Times(AtLeast(1));
-  EXPECT_CALL(*prefs, GetInt64(kPrefsFullPayloadAttemptNumber, _))
+  EXPECT_CALL(*prefs2, GetInt64(kPrefsFullPayloadAttemptNumber, _))
       .Times(AtLeast(1));
-  EXPECT_CALL(*prefs, GetInt64(kPrefsCurrentUrlIndex, _))
+  EXPECT_CALL(*prefs2, GetInt64(kPrefsCurrentUrlIndex, _))
       .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(true)));
-  EXPECT_CALL(*prefs, GetInt64(kPrefsCurrentUrlFailureCount, _))
+  EXPECT_CALL(*prefs2, GetInt64(kPrefsCurrentUrlFailureCount, _))
       .Times(AtLeast(1));
-  EXPECT_CALL(*prefs, GetInt64(kPrefsUrlSwitchCount, _)).Times(AtLeast(1));
+  EXPECT_CALL(*prefs2, GetInt64(kPrefsUrlSwitchCount, _)).Times(AtLeast(1));
 
   // Note: This will be a different payload object, but the response should
   // have the same hash as before so as to not trivially reset because the
   // response was different. We want to specifically test that even if the
   // response is same, we should reset the state if we find it corrupted.
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state2));
   SetupPayloadStateWith2Urls(
       "Hash4427", true, false, &payload_state, &response);
 
@@ -628,14 +622,15 @@
   EXPECT_EQ(0U, payload_state.GetUrlSwitchCount());
 }
 
-TEST_F(PayloadStateTest, NoBackoffInteractiveChecks) {
+TEST(PayloadStateTest, NoBackoffInteractiveChecks) {
   OmahaResponse response;
   PayloadState payload_state;
-  OmahaRequestParams params;
-  params.Init("", "", {.interactive = true});
-  FakeSystemState::Get()->set_request_params(&params);
+  FakeSystemState fake_system_state;
+  OmahaRequestParams params(&fake_system_state);
+  params.Init("", "", true);  // interactive = True.
+  fake_system_state.set_request_params(&params);
 
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   SetupPayloadStateWith2Urls(
       "Hash6437", true, false, &payload_state, &response);
 
@@ -650,14 +645,15 @@
   EXPECT_FALSE(payload_state.ShouldBackoffDownload());
 }
 
-TEST_F(PayloadStateTest, NoBackoffForP2PUpdates) {
+TEST(PayloadStateTest, NoBackoffForP2PUpdates) {
   OmahaResponse response;
   PayloadState payload_state;
-  OmahaRequestParams params;
-  params.Init("", "", {});
-  FakeSystemState::Get()->set_request_params(&params);
+  FakeSystemState fake_system_state;
+  OmahaRequestParams params(&fake_system_state);
+  params.Init("", "", false);  // interactive = False.
+  fake_system_state.set_request_params(&params);
 
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   SetupPayloadStateWith2Urls(
       "Hash6437", true, false, &payload_state, &response);
 
@@ -680,11 +676,12 @@
   EXPECT_TRUE(payload_state.ShouldBackoffDownload());
 }
 
-TEST_F(PayloadStateTest, NoBackoffForDeltaPayloads) {
+TEST(PayloadStateTest, NoBackoffForDeltaPayloads) {
   OmahaResponse response;
   PayloadState payload_state;
+  FakeSystemState fake_system_state;
 
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   SetupPayloadStateWith2Urls("Hash6437", true, true, &payload_state, &response);
 
   // Simulate a successful download and see that we're ready to download
@@ -723,11 +720,12 @@
             backoff_expiry_time.ToInternalValue());
 }
 
-TEST_F(PayloadStateTest, BackoffPeriodsAreInCorrectRange) {
+TEST(PayloadStateTest, BackoffPeriodsAreInCorrectRange) {
   OmahaResponse response;
   PayloadState payload_state;
+  FakeSystemState fake_system_state;
 
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   SetupPayloadStateWith2Urls(
       "Hash8939", true, false, &payload_state, &response);
 
@@ -743,12 +741,13 @@
   CheckPayloadBackoffState(&payload_state, 10, TimeDelta::FromDays(16));
 }
 
-TEST_F(PayloadStateTest, BackoffLogicCanBeDisabled) {
+TEST(PayloadStateTest, BackoffLogicCanBeDisabled) {
   OmahaResponse response;
   response.disable_payload_backoff = true;
   PayloadState payload_state;
+  FakeSystemState fake_system_state;
 
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   SetupPayloadStateWith2Urls(
       "Hash8939", true, false, &payload_state, &response);
 
@@ -769,14 +768,15 @@
   EXPECT_FALSE(payload_state.ShouldBackoffDownload());
 }
 
-TEST_F(PayloadStateTest, BytesDownloadedMetricsGetAddedToCorrectSources) {
+TEST(PayloadStateTest, BytesDownloadedMetricsGetAddedToCorrectSources) {
   OmahaResponse response;
   response.disable_payload_backoff = true;
   PayloadState payload_state;
+  FakeSystemState fake_system_state;
   uint64_t https_total = 0;
   uint64_t http_total = 0;
 
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   SetupPayloadStateWith2Urls(
       "Hash3286", true, false, &payload_state, &response);
   EXPECT_EQ(1, payload_state.GetNumResponsesSeen());
@@ -859,7 +859,7 @@
   EXPECT_EQ(p2p_total,
             payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpPeer));
 
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
+  EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
               ReportSuccessfulUpdateMetrics(
                   1, _, kPayloadTypeFull, _, _, 314, _, _, _, 3));
 
@@ -877,11 +877,12 @@
   EXPECT_EQ(0, payload_state.GetNumResponsesSeen());
 }
 
-TEST_F(PayloadStateTest, DownloadSourcesUsedIsCorrect) {
+TEST(PayloadStateTest, DownloadSourcesUsedIsCorrect) {
   OmahaResponse response;
   PayloadState payload_state;
+  FakeSystemState fake_system_state;
 
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   SetupPayloadStateWith2Urls(
       "Hash3286", true, false, &payload_state, &response);
 
@@ -899,7 +900,7 @@
   int64_t total_bytes[kNumDownloadSources] = {};
   total_bytes[kDownloadSourceHttpServer] = num_bytes;
 
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
+  EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
               ReportSuccessfulUpdateMetrics(
                   _,
                   _,
@@ -916,11 +917,12 @@
   payload_state.UpdateSucceeded();
 }
 
-TEST_F(PayloadStateTest, RestartingUpdateResetsMetrics) {
+TEST(PayloadStateTest, RestartingUpdateResetsMetrics) {
   OmahaResponse response;
+  FakeSystemState fake_system_state;
   PayloadState payload_state;
 
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
 
   // Set the first response.
   SetupPayloadStateWith2Urls(
@@ -945,24 +947,25 @@
             payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpServer));
 }
 
-TEST_F(PayloadStateTest, NumRebootsIncrementsCorrectly) {
-  FakeSystemState::Get()->set_prefs(nullptr);
-  auto* prefs = FakeSystemState::Get()->mock_prefs();
+TEST(PayloadStateTest, NumRebootsIncrementsCorrectly) {
+  FakeSystemState fake_system_state;
+  PayloadState payload_state;
+
+  NiceMock<MockPrefs>* prefs = fake_system_state.mock_prefs();
   EXPECT_CALL(*prefs, SetInt64(_, _)).Times(AtLeast(0));
   EXPECT_CALL(*prefs, SetInt64(kPrefsNumReboots, 1)).Times(AtLeast(1));
 
-  PayloadState payload_state;
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
 
   payload_state.UpdateRestarted();
   EXPECT_EQ(0U, payload_state.GetNumReboots());
 
-  FakeSystemState::Get()->set_system_rebooted(true);
+  fake_system_state.set_system_rebooted(true);
   payload_state.UpdateResumed();
   // Num reboots should be incremented because system rebooted detected.
   EXPECT_EQ(1U, payload_state.GetNumReboots());
 
-  FakeSystemState::Get()->set_system_rebooted(false);
+  fake_system_state.set_system_rebooted(false);
   payload_state.UpdateResumed();
   // Num reboots should now be 1 as reboot was not detected.
   EXPECT_EQ(1U, payload_state.GetNumReboots());
@@ -972,12 +975,13 @@
   EXPECT_EQ(0U, payload_state.GetNumReboots());
 }
 
-TEST_F(PayloadStateTest, RollbackHappened) {
-  FakeSystemState::Get()->set_powerwash_safe_prefs(nullptr);
-  auto* mock_powerwash_safe_prefs =
-      FakeSystemState::Get()->mock_powerwash_safe_prefs();
+TEST(PayloadStateTest, RollbackHappened) {
+  FakeSystemState fake_system_state;
   PayloadState payload_state;
-  EXPECT_TRUE(payload_state.Initialize());
+
+  NiceMock<MockPrefs>* mock_powerwash_safe_prefs =
+      fake_system_state.mock_powerwash_safe_prefs();
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
 
   // Verify pre-conditions are good.
   EXPECT_FALSE(payload_state.GetRollbackHappened());
@@ -1002,23 +1006,23 @@
   EXPECT_TRUE(payload_state.GetRollbackHappened());
 }
 
-TEST_F(PayloadStateTest, RollbackVersion) {
-  FakeSystemState::Get()->set_powerwash_safe_prefs(nullptr);
-  auto* mock_powerwash_safe_prefs =
-      FakeSystemState::Get()->mock_powerwash_safe_prefs();
-
-  // Mock out the os version and make sure it's excluded correctly.
-  string rollback_version = "2345.0.0";
-  OmahaRequestParams params;
-  params.Init(rollback_version, "", {});
-  FakeSystemState::Get()->set_request_params(&params);
-
+TEST(PayloadStateTest, RollbackVersion) {
+  FakeSystemState fake_system_state;
   PayloadState payload_state;
-  EXPECT_TRUE(payload_state.Initialize());
+
+  NiceMock<MockPrefs>* mock_powerwash_safe_prefs =
+      fake_system_state.mock_powerwash_safe_prefs();
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
 
   // Verify pre-conditions are good.
   EXPECT_TRUE(payload_state.GetRollbackVersion().empty());
 
+  // Mock out the os version and make sure it's blacklisted correctly.
+  string rollback_version = "2345.0.0";
+  OmahaRequestParams params(&fake_system_state);
+  params.Init(rollback_version, "", false);
+  fake_system_state.set_request_params(&params);
+
   EXPECT_CALL(*mock_powerwash_safe_prefs,
               SetString(kPrefsRollbackVersion, rollback_version));
   payload_state.Rollback();
@@ -1037,33 +1041,37 @@
 
   // Check that we report only UpdateEngine.Rollback.* metrics in
   // UpdateSucceeded().
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
+  EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
               ReportRollbackMetrics(metrics::RollbackResult::kSuccess))
       .Times(1);
 
   payload_state.UpdateSucceeded();
 }
 
-TEST_F(PayloadStateTest, DurationsAreCorrect) {
+TEST(PayloadStateTest, DurationsAreCorrect) {
   OmahaResponse response;
   response.packages.resize(1);
+  PayloadState payload_state;
+  FakeSystemState fake_system_state;
+  FakeClock fake_clock;
+  FakePrefs fake_prefs;
 
   // Set the clock to a well-known time - 1 second on the wall-clock
   // and 2 seconds on the monotonic clock
-  auto* fake_clock = FakeSystemState::Get()->fake_clock();
-  fake_clock->SetWallclockTime(Time::FromInternalValue(1000000));
-  fake_clock->SetMonotonicTime(Time::FromInternalValue(2000000));
+  fake_clock.SetWallclockTime(Time::FromInternalValue(1000000));
+  fake_clock.SetMonotonicTime(Time::FromInternalValue(2000000));
 
-  PayloadState payload_state;
-  EXPECT_TRUE(payload_state.Initialize());
+  fake_system_state.set_clock(&fake_clock);
+  fake_system_state.set_prefs(&fake_prefs);
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
 
   // Check that durations are correct for a successful update where
   // time has advanced 7 seconds on the wall clock and 4 seconds on
   // the monotonic clock.
   SetupPayloadStateWith2Urls(
       "Hash8593", true, false, &payload_state, &response);
-  fake_clock->SetWallclockTime(Time::FromInternalValue(8000000));
-  fake_clock->SetMonotonicTime(Time::FromInternalValue(6000000));
+  fake_clock.SetWallclockTime(Time::FromInternalValue(8000000));
+  fake_clock.SetMonotonicTime(Time::FromInternalValue(6000000));
   payload_state.UpdateSucceeded();
   EXPECT_EQ(payload_state.GetUpdateDuration().InMicroseconds(), 7000000);
   EXPECT_EQ(payload_state.GetUpdateDurationUptime().InMicroseconds(), 4000000);
@@ -1076,8 +1084,8 @@
 
   // Advance time a bit (10 secs), simulate download progress and
   // check that durations are updated.
-  fake_clock->SetWallclockTime(Time::FromInternalValue(18000000));
-  fake_clock->SetMonotonicTime(Time::FromInternalValue(16000000));
+  fake_clock.SetWallclockTime(Time::FromInternalValue(18000000));
+  fake_clock.SetMonotonicTime(Time::FromInternalValue(16000000));
   payload_state.DownloadProgress(10);
   EXPECT_EQ(payload_state.GetUpdateDuration().InMicroseconds(), 10000000);
   EXPECT_EQ(payload_state.GetUpdateDurationUptime().InMicroseconds(), 10000000);
@@ -1085,9 +1093,9 @@
   // Now simulate a reboot by resetting monotonic time (to 5000) and
   // creating a new PayloadState object and check that we load the
   // durations correctly (e.g. they are the same as before).
-  fake_clock->SetMonotonicTime(Time::FromInternalValue(5000));
+  fake_clock.SetMonotonicTime(Time::FromInternalValue(5000));
   PayloadState payload_state2;
-  EXPECT_TRUE(payload_state2.Initialize());
+  EXPECT_TRUE(payload_state2.Initialize(&fake_system_state));
   payload_state2.SetResponse(response);
   EXPECT_EQ(payload_state2.GetUpdateDuration().InMicroseconds(), 10000000);
   EXPECT_EQ(payload_state2.GetUpdateDurationUptime().InMicroseconds(),
@@ -1095,62 +1103,65 @@
 
   // Advance wall-clock by 7 seconds and monotonic clock by 6 seconds
   // and check that the durations are increased accordingly.
-  fake_clock->SetWallclockTime(Time::FromInternalValue(25000000));
-  fake_clock->SetMonotonicTime(Time::FromInternalValue(6005000));
+  fake_clock.SetWallclockTime(Time::FromInternalValue(25000000));
+  fake_clock.SetMonotonicTime(Time::FromInternalValue(6005000));
   payload_state2.UpdateSucceeded();
   EXPECT_EQ(payload_state2.GetUpdateDuration().InMicroseconds(), 17000000);
   EXPECT_EQ(payload_state2.GetUpdateDurationUptime().InMicroseconds(),
             16000000);
 }
 
-TEST_F(PayloadStateTest, RebootAfterSuccessfulUpdateTest) {
+TEST(PayloadStateTest, RebootAfterSuccessfulUpdateTest) {
   OmahaResponse response;
+  PayloadState payload_state;
+  FakeSystemState fake_system_state;
+  FakeClock fake_clock;
+  FakePrefs fake_prefs;
 
   // Set the clock to a well-known time (t = 30 seconds).
-  auto* fake_clock = FakeSystemState::Get()->fake_clock();
-  fake_clock->SetMonotonicTime(
+  fake_clock.SetMonotonicTime(
       Time::FromInternalValue(30 * Time::kMicrosecondsPerSecond));
 
-  PayloadState payload_state;
-  EXPECT_TRUE(payload_state.Initialize());
+  fake_system_state.set_clock(&fake_clock);
+  fake_system_state.set_prefs(&fake_prefs);
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
 
   // Make the update succeed.
   SetupPayloadStateWith2Urls(
       "Hash8593", true, false, &payload_state, &response);
   payload_state.UpdateSucceeded();
 
-  auto* fake_prefs = FakeSystemState::Get()->fake_prefs();
   // Check that the marker was written.
-  EXPECT_TRUE(fake_prefs->Exists(kPrefsSystemUpdatedMarker));
+  EXPECT_TRUE(fake_prefs.Exists(kPrefsSystemUpdatedMarker));
 
   // Now simulate a reboot and set the wallclock time to a later point
   // (t = 500 seconds). We do this by using a new PayloadState object
   // and checking that it emits the right UMA metric with the right
   // value.
-  fake_clock->SetMonotonicTime(
+  fake_clock.SetMonotonicTime(
       Time::FromInternalValue(500 * Time::kMicrosecondsPerSecond));
   PayloadState payload_state2;
-  EXPECT_TRUE(payload_state2.Initialize());
+  EXPECT_TRUE(payload_state2.Initialize(&fake_system_state));
 
   // Expect 500 - 30 seconds = 470 seconds ~= 7 min 50 sec
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
+  EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
               ReportTimeToReboot(7));
-  FakeSystemState::Get()->set_system_rebooted(true);
+  fake_system_state.set_system_rebooted(true);
 
   payload_state2.UpdateEngineStarted();
 
   // Check that the marker was nuked.
-  EXPECT_FALSE(fake_prefs->Exists(kPrefsSystemUpdatedMarker));
+  EXPECT_FALSE(fake_prefs.Exists(kPrefsSystemUpdatedMarker));
 }
 
-TEST_F(PayloadStateTest, RestartAfterCrash) {
+TEST(PayloadStateTest, RestartAfterCrash) {
   PayloadState payload_state;
+  FakeSystemState fake_system_state;
   testing::StrictMock<MockMetricsReporter> mock_metrics_reporter;
-  FakeSystemState::Get()->set_metrics_reporter(&mock_metrics_reporter);
-  FakeSystemState::Get()->set_prefs(nullptr);
-  auto* prefs = FakeSystemState::Get()->mock_prefs();
+  fake_system_state.set_metrics_reporter(&mock_metrics_reporter);
+  NiceMock<MockPrefs>* prefs = fake_system_state.mock_prefs();
 
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
 
   // Only the |kPrefsAttemptInProgress| state variable should be read.
   EXPECT_CALL(*prefs, Exists(_)).Times(0);
@@ -1163,75 +1174,83 @@
   EXPECT_CALL(*prefs, GetBoolean(kPrefsAttemptInProgress, _));
 
   // Simulate an update_engine restart without a reboot.
-  FakeSystemState::Get()->set_system_rebooted(false);
+  fake_system_state.set_system_rebooted(false);
 
   payload_state.UpdateEngineStarted();
 }
 
-TEST_F(PayloadStateTest, AbnormalTerminationAttemptMetricsNoReporting) {
+TEST(PayloadStateTest, AbnormalTerminationAttemptMetricsNoReporting) {
   PayloadState payload_state;
+  FakeSystemState fake_system_state;
 
   // If there's no marker at startup, ensure we don't report a metric.
-  EXPECT_TRUE(payload_state.Initialize());
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+  EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
               ReportAbnormallyTerminatedUpdateAttemptMetrics())
       .Times(0);
   payload_state.UpdateEngineStarted();
 }
 
-TEST_F(PayloadStateTest, AbnormalTerminationAttemptMetricsReported) {
+TEST(PayloadStateTest, AbnormalTerminationAttemptMetricsReported) {
+  PayloadState payload_state;
+  FakeSystemState fake_system_state;
+  FakePrefs fake_prefs;
+
   // If we have a marker at startup, ensure it's reported and the
   // marker is then cleared.
-  auto* fake_prefs = FakeSystemState::Get()->fake_prefs();
-  fake_prefs->SetBoolean(kPrefsAttemptInProgress, true);
+  fake_system_state.set_prefs(&fake_prefs);
+  fake_prefs.SetBoolean(kPrefsAttemptInProgress, true);
 
-  PayloadState payload_state;
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
 
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
+  EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
               ReportAbnormallyTerminatedUpdateAttemptMetrics())
       .Times(1);
   payload_state.UpdateEngineStarted();
 
-  EXPECT_FALSE(fake_prefs->Exists(kPrefsAttemptInProgress));
+  EXPECT_FALSE(fake_prefs.Exists(kPrefsAttemptInProgress));
 }
 
-TEST_F(PayloadStateTest, AbnormalTerminationAttemptMetricsClearedOnSucceess) {
+TEST(PayloadStateTest, AbnormalTerminationAttemptMetricsClearedOnSucceess) {
+  PayloadState payload_state;
+  FakeSystemState fake_system_state;
+  FakePrefs fake_prefs;
+
   // Make sure the marker is written and cleared during an attempt and
   // also that we DO NOT emit the metric (since the attempt didn't end
   // abnormally).
-  PayloadState payload_state;
-  EXPECT_TRUE(payload_state.Initialize());
+  fake_system_state.set_prefs(&fake_prefs);
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   OmahaResponse response;
   response.packages.resize(1);
   payload_state.SetResponse(response);
 
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
+  EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
               ReportAbnormallyTerminatedUpdateAttemptMetrics())
       .Times(0);
 
-  auto* fake_prefs = FakeSystemState::Get()->fake_prefs();
   // Attempt not in progress, should be clear.
-  EXPECT_FALSE(fake_prefs->Exists(kPrefsAttemptInProgress));
+  EXPECT_FALSE(fake_prefs.Exists(kPrefsAttemptInProgress));
 
   payload_state.UpdateRestarted();
 
   // Attempt not in progress, should be set.
-  EXPECT_TRUE(fake_prefs->Exists(kPrefsAttemptInProgress));
+  EXPECT_TRUE(fake_prefs.Exists(kPrefsAttemptInProgress));
 
   payload_state.UpdateSucceeded();
 
   // Attempt not in progress, should be clear.
-  EXPECT_FALSE(fake_prefs->Exists(kPrefsAttemptInProgress));
+  EXPECT_FALSE(fake_prefs.Exists(kPrefsAttemptInProgress));
 }
 
-TEST_F(PayloadStateTest, CandidateUrlsComputedCorrectly) {
+TEST(PayloadStateTest, CandidateUrlsComputedCorrectly) {
   OmahaResponse response;
+  FakeSystemState fake_system_state;
   PayloadState payload_state;
 
   policy::MockDevicePolicy disable_http_policy;
-  FakeSystemState::Get()->set_device_policy(&disable_http_policy);
-  EXPECT_TRUE(payload_state.Initialize());
+  fake_system_state.set_device_policy(&disable_http_policy);
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
 
   // Test with no device policy. Should default to allowing http.
   EXPECT_CALL(disable_http_policy, GetHttpDownloadsEnabled(_))
@@ -1274,7 +1293,7 @@
   // Now, pretend that the HTTP policy is turned on. We want to make sure
   // the new policy is honored.
   policy::MockDevicePolicy enable_http_policy;
-  FakeSystemState::Get()->set_device_policy(&enable_http_policy);
+  fake_system_state.set_device_policy(&enable_http_policy);
   EXPECT_CALL(enable_http_policy, GetHttpDownloadsEnabled(_))
       .WillRepeatedly(DoAll(SetArgPointee<0>(true), Return(true)));
 
@@ -1297,86 +1316,93 @@
   EXPECT_EQ(0U, payload_state.GetUrlFailureCount());
 }
 
-TEST_F(PayloadStateTest, PayloadTypeMetricWhenTypeIsDelta) {
+TEST(PayloadStateTest, PayloadTypeMetricWhenTypeIsDelta) {
   OmahaResponse response;
   PayloadState payload_state;
+  FakeSystemState fake_system_state;
 
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   SetupPayloadStateWith2Urls("Hash6437", true, true, &payload_state, &response);
 
   // Simulate a successful download and update.
   payload_state.DownloadComplete();
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
+  EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
               ReportSuccessfulUpdateMetrics(
                   _, _, kPayloadTypeDelta, _, _, _, _, _, _, _));
   payload_state.UpdateSucceeded();
 
   // Mock the request to a request where the delta was disabled but Omaha sends
   // a delta anyway and test again.
-  OmahaRequestParams params;
+  OmahaRequestParams params(&fake_system_state);
   params.set_delta_okay(false);
-  FakeSystemState::Get()->set_request_params(&params);
+  fake_system_state.set_request_params(&params);
 
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   SetupPayloadStateWith2Urls("Hash6437", true, true, &payload_state, &response);
 
   payload_state.DownloadComplete();
 
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
+  EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
               ReportSuccessfulUpdateMetrics(
                   _, _, kPayloadTypeDelta, _, _, _, _, _, _, _));
   payload_state.UpdateSucceeded();
 }
 
-TEST_F(PayloadStateTest, PayloadTypeMetricWhenTypeIsForcedFull) {
+TEST(PayloadStateTest, PayloadTypeMetricWhenTypeIsForcedFull) {
   OmahaResponse response;
   PayloadState payload_state;
+  FakeSystemState fake_system_state;
 
-  // Mock the request to a request where the delta was disabled.
-  OmahaRequestParams params;
-  params.set_delta_okay(false);
-  FakeSystemState::Get()->set_request_params(&params);
-
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   SetupPayloadStateWith2Urls(
       "Hash6437", true, false, &payload_state, &response);
 
+  // Mock the request to a request where the delta was disabled.
+  OmahaRequestParams params(&fake_system_state);
+  params.set_delta_okay(false);
+  fake_system_state.set_request_params(&params);
+
   // Simulate a successful download and update.
   payload_state.DownloadComplete();
 
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
+  EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
               ReportSuccessfulUpdateMetrics(
                   _, _, kPayloadTypeForcedFull, _, _, _, _, _, _, _));
   payload_state.UpdateSucceeded();
 }
 
-TEST_F(PayloadStateTest, PayloadTypeMetricWhenTypeIsFull) {
+TEST(PayloadStateTest, PayloadTypeMetricWhenTypeIsFull) {
   OmahaResponse response;
   PayloadState payload_state;
+  FakeSystemState fake_system_state;
 
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   SetupPayloadStateWith2Urls(
       "Hash6437", true, false, &payload_state, &response);
 
   // Mock the request to a request where the delta is enabled, although the
   // result is full.
-  OmahaRequestParams params;
+  OmahaRequestParams params(&fake_system_state);
   params.set_delta_okay(true);
-  FakeSystemState::Get()->set_request_params(&params);
+  fake_system_state.set_request_params(&params);
 
   // Simulate a successful download and update.
   payload_state.DownloadComplete();
 
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
+  EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
               ReportSuccessfulUpdateMetrics(
                   _, _, kPayloadTypeFull, _, _, _, _, _, _, _));
   payload_state.UpdateSucceeded();
 }
 
-TEST_F(PayloadStateTest, RebootAfterUpdateFailedMetric) {
+TEST(PayloadStateTest, RebootAfterUpdateFailedMetric) {
+  FakeSystemState fake_system_state;
   OmahaResponse response;
   PayloadState payload_state;
-  EXPECT_TRUE(payload_state.Initialize());
+  FakePrefs fake_prefs;
+  fake_system_state.set_prefs(&fake_prefs);
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   SetupPayloadStateWith2Urls(
       "Hash3141", true, false, &payload_state, &response);
 
@@ -1386,40 +1412,40 @@
   payload_state.ExpectRebootInNewVersion("Version:12345678");
 
   // Reboot into the same environment to get an UMA metric with a value of 1.
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
+  EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
               ReportFailedUpdateCount(1));
   payload_state.ReportFailedBootIfNeeded();
-  Mock::VerifyAndClearExpectations(
-      FakeSystemState::Get()->mock_metrics_reporter());
+  Mock::VerifyAndClearExpectations(fake_system_state.mock_metrics_reporter());
 
   // Simulate a second update and reboot into the same environment, this should
   // send a value of 2.
   payload_state.ExpectRebootInNewVersion("Version:12345678");
 
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
+  EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
               ReportFailedUpdateCount(2));
   payload_state.ReportFailedBootIfNeeded();
-  Mock::VerifyAndClearExpectations(
-      FakeSystemState::Get()->mock_metrics_reporter());
+  Mock::VerifyAndClearExpectations(fake_system_state.mock_metrics_reporter());
 
   // Simulate a third failed reboot to new version, but this time for a
   // different payload. This should send a value of 1 this time.
   payload_state.ExpectRebootInNewVersion("Version:3141592");
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
+  EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
               ReportFailedUpdateCount(1));
   payload_state.ReportFailedBootIfNeeded();
-  Mock::VerifyAndClearExpectations(
-      FakeSystemState::Get()->mock_metrics_reporter());
+  Mock::VerifyAndClearExpectations(fake_system_state.mock_metrics_reporter());
 }
 
-TEST_F(PayloadStateTest, RebootAfterUpdateSucceed) {
+TEST(PayloadStateTest, RebootAfterUpdateSucceed) {
+  FakeSystemState fake_system_state;
   OmahaResponse response;
   PayloadState payload_state;
-  FakeBootControl* fake_boot_control =
-      FakeSystemState::Get()->fake_boot_control();
+  FakePrefs fake_prefs;
+  fake_system_state.set_prefs(&fake_prefs);
+
+  FakeBootControl* fake_boot_control = fake_system_state.fake_boot_control();
   fake_boot_control->SetCurrentSlot(0);
 
-  EXPECT_TRUE(payload_state.Initialize());
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   SetupPayloadStateWith2Urls(
       "Hash3141", true, false, &payload_state, &response);
 
@@ -1431,7 +1457,7 @@
   // Change the BootDevice to a different one, no metric should be sent.
   fake_boot_control->SetCurrentSlot(1);
 
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
+  EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
               ReportFailedUpdateCount(_))
       .Times(0);
   payload_state.ReportFailedBootIfNeeded();
@@ -1442,10 +1468,14 @@
   payload_state.ReportFailedBootIfNeeded();
 }
 
-TEST_F(PayloadStateTest, RebootAfterCanceledUpdate) {
+TEST(PayloadStateTest, RebootAfterCanceledUpdate) {
+  FakeSystemState fake_system_state;
   OmahaResponse response;
   PayloadState payload_state;
-  EXPECT_TRUE(payload_state.Initialize());
+  FakePrefs fake_prefs;
+
+  fake_system_state.set_prefs(&fake_prefs);
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   SetupPayloadStateWith2Urls(
       "Hash3141", true, false, &payload_state, &response);
 
@@ -1454,7 +1484,7 @@
   payload_state.UpdateSucceeded();
   payload_state.ExpectRebootInNewVersion("Version:12345678");
 
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
+  EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
               ReportFailedUpdateCount(_))
       .Times(0);
 
@@ -1465,11 +1495,15 @@
   payload_state.ReportFailedBootIfNeeded();
 }
 
-TEST_F(PayloadStateTest, UpdateSuccessWithWipedPrefs) {
+TEST(PayloadStateTest, UpdateSuccessWithWipedPrefs) {
+  FakeSystemState fake_system_state;
   PayloadState payload_state;
-  EXPECT_TRUE(payload_state.Initialize());
+  FakePrefs fake_prefs;
 
-  EXPECT_CALL(*FakeSystemState::Get()->mock_metrics_reporter(),
+  fake_system_state.set_prefs(&fake_prefs);
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+
+  EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
               ReportFailedUpdateCount(_))
       .Times(0);
 
@@ -1477,10 +1511,14 @@
   payload_state.ReportFailedBootIfNeeded();
 }
 
-TEST_F(PayloadStateTest, DisallowP2PAfterTooManyAttempts) {
+TEST(PayloadStateTest, DisallowP2PAfterTooManyAttempts) {
   OmahaResponse response;
   PayloadState payload_state;
-  EXPECT_TRUE(payload_state.Initialize());
+  FakeSystemState fake_system_state;
+  FakePrefs fake_prefs;
+  fake_system_state.set_prefs(&fake_prefs);
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   SetupPayloadStateWith2Urls(
       "Hash8593", true, false, &payload_state, &response);
 
@@ -1494,17 +1532,22 @@
   EXPECT_FALSE(payload_state.P2PAttemptAllowed());
 }
 
-TEST_F(PayloadStateTest, DisallowP2PAfterDeadline) {
+TEST(PayloadStateTest, DisallowP2PAfterDeadline) {
   OmahaResponse response;
   PayloadState payload_state;
-  EXPECT_TRUE(payload_state.Initialize());
+  FakeSystemState fake_system_state;
+  FakeClock fake_clock;
+  FakePrefs fake_prefs;
+
+  fake_system_state.set_clock(&fake_clock);
+  fake_system_state.set_prefs(&fake_prefs);
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   SetupPayloadStateWith2Urls(
       "Hash8593", true, false, &payload_state, &response);
 
   // Set the clock to 1 second.
   Time epoch = Time::FromInternalValue(1000000);
-  auto* fake_clock = FakeSystemState::Get()->fake_clock();
-  fake_clock->SetWallclockTime(epoch);
+  fake_clock.SetWallclockTime(epoch);
 
   // Do an attempt - this will set the timestamp.
   payload_state.P2PNewAttempt();
@@ -1516,7 +1559,7 @@
   EXPECT_TRUE(payload_state.P2PAttemptAllowed());
 
   // Set clock to half the deadline - this should work.
-  fake_clock->SetWallclockTime(
+  fake_clock.SetWallclockTime(
       epoch + TimeDelta::FromSeconds(kMaxP2PAttemptTimeSeconds) / 2);
   EXPECT_TRUE(payload_state.P2PAttemptAllowed());
 
@@ -1525,20 +1568,24 @@
   EXPECT_EQ(epoch, payload_state.GetP2PFirstAttemptTimestamp());
 
   // Set clock to _just_ before the deadline - this should work.
-  fake_clock->SetWallclockTime(
+  fake_clock.SetWallclockTime(
       epoch + TimeDelta::FromSeconds(kMaxP2PAttemptTimeSeconds - 1));
   EXPECT_TRUE(payload_state.P2PAttemptAllowed());
 
   // Set clock to _just_ after the deadline - this should not work.
-  fake_clock->SetWallclockTime(
+  fake_clock.SetWallclockTime(
       epoch + TimeDelta::FromSeconds(kMaxP2PAttemptTimeSeconds + 1));
   EXPECT_FALSE(payload_state.P2PAttemptAllowed());
 }
 
-TEST_F(PayloadStateTest, P2PStateVarsInitialValue) {
+TEST(PayloadStateTest, P2PStateVarsInitialValue) {
   OmahaResponse response;
   PayloadState payload_state;
-  EXPECT_TRUE(payload_state.Initialize());
+  FakeSystemState fake_system_state;
+  FakePrefs fake_prefs;
+
+  fake_system_state.set_prefs(&fake_prefs);
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   SetupPayloadStateWith2Urls(
       "Hash8593", true, false, &payload_state, &response);
 
@@ -1547,16 +1594,21 @@
   EXPECT_EQ(0, payload_state.GetP2PNumAttempts());
 }
 
-TEST_F(PayloadStateTest, P2PStateVarsArePersisted) {
+TEST(PayloadStateTest, P2PStateVarsArePersisted) {
   OmahaResponse response;
   PayloadState payload_state;
-  EXPECT_TRUE(payload_state.Initialize());
+  FakeSystemState fake_system_state;
+  FakeClock fake_clock;
+  FakePrefs fake_prefs;
+  fake_system_state.set_clock(&fake_clock);
+  fake_system_state.set_prefs(&fake_prefs);
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   SetupPayloadStateWith2Urls(
       "Hash8593", true, false, &payload_state, &response);
 
   // Set the clock to something known.
   Time time = Time::FromInternalValue(12345);
-  FakeSystemState::Get()->fake_clock()->SetWallclockTime(time);
+  fake_clock.SetWallclockTime(time);
 
   // New p2p attempt - as a side-effect this will update the p2p state vars.
   payload_state.P2PNewAttempt();
@@ -1566,21 +1618,27 @@
   // Now create a new PayloadState and check that it loads the state
   // vars correctly.
   PayloadState payload_state2;
-  EXPECT_TRUE(payload_state2.Initialize());
+  EXPECT_TRUE(payload_state2.Initialize(&fake_system_state));
   EXPECT_EQ(1, payload_state2.GetP2PNumAttempts());
   EXPECT_EQ(time, payload_state2.GetP2PFirstAttemptTimestamp());
 }
 
-TEST_F(PayloadStateTest, P2PStateVarsAreClearedOnNewResponse) {
+TEST(PayloadStateTest, P2PStateVarsAreClearedOnNewResponse) {
   OmahaResponse response;
   PayloadState payload_state;
-  EXPECT_TRUE(payload_state.Initialize());
+  FakeSystemState fake_system_state;
+  FakeClock fake_clock;
+  FakePrefs fake_prefs;
+  fake_system_state.set_clock(&fake_clock);
+  fake_system_state.set_prefs(&fake_prefs);
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
   SetupPayloadStateWith2Urls(
       "Hash8593", true, false, &payload_state, &response);
 
   // Set the clock to something known.
   Time time = Time::FromInternalValue(12345);
-  FakeSystemState::Get()->fake_clock()->SetWallclockTime(time);
+  fake_clock.SetWallclockTime(time);
 
   // New p2p attempt - as a side-effect this will update the p2p state vars.
   payload_state.P2PNewAttempt();
@@ -1597,162 +1655,4 @@
   EXPECT_EQ(null_time, payload_state.GetP2PFirstAttemptTimestamp());
 }
 
-TEST_F(PayloadStateTest, NextPayloadResetsUrlIndex) {
-  PayloadState payload_state;
-  StrictMock<MockExcluder> mock_excluder;
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetExcluder())
-      .WillOnce(Return(&mock_excluder));
-  EXPECT_TRUE(payload_state.Initialize());
-
-  OmahaResponse response;
-  response.packages.push_back(
-      {.payload_urls = {"http://test1a", "http://test2a"},
-       .size = 123456789,
-       .metadata_size = 58123,
-       .metadata_signature = "msign",
-       .hash = "hash"});
-  response.packages.push_back({.payload_urls = {"http://test1b"},
-                               .size = 123456789,
-                               .metadata_size = 58123,
-                               .metadata_signature = "msign",
-                               .hash = "hash"});
-  payload_state.SetResponse(response);
-
-  EXPECT_EQ(payload_state.GetCurrentUrl(), "http://test1a");
-  payload_state.IncrementUrlIndex();
-  EXPECT_EQ(payload_state.GetCurrentUrl(), "http://test2a");
-
-  EXPECT_TRUE(payload_state.NextPayload());
-  EXPECT_EQ(payload_state.GetCurrentUrl(), "http://test1b");
-}
-
-TEST_F(PayloadStateTest, ExcludeNoopForNonExcludables) {
-  PayloadState payload_state;
-  StrictMock<MockExcluder> mock_excluder;
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetExcluder())
-      .WillOnce(Return(&mock_excluder));
-  EXPECT_TRUE(payload_state.Initialize());
-
-  OmahaResponse response;
-  response.packages.push_back(
-      {.payload_urls = {"http://test1a", "http://test2a"},
-       .size = 123456789,
-       .metadata_size = 58123,
-       .metadata_signature = "msign",
-       .hash = "hash",
-       .can_exclude = false});
-  payload_state.SetResponse(response);
-
-  EXPECT_CALL(mock_excluder, Exclude(_)).Times(0);
-  payload_state.ExcludeCurrentPayload();
-}
-
-TEST_F(PayloadStateTest, ExcludeOnlyCanExcludables) {
-  PayloadState payload_state;
-  StrictMock<MockExcluder> mock_excluder;
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetExcluder())
-      .WillOnce(Return(&mock_excluder));
-  EXPECT_TRUE(payload_state.Initialize());
-
-  OmahaResponse response;
-  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);
-
-  EXPECT_CALL(mock_excluder, Exclude(utils::GetExclusionName("http://test1a")))
-      .WillOnce(Return(true));
-  payload_state.ExcludeCurrentPayload();
-}
-
-TEST_F(PayloadStateTest, IncrementFailureExclusionTest) {
-  PayloadState payload_state;
-  StrictMock<MockExcluder> mock_excluder;
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetExcluder())
-      .WillOnce(Return(&mock_excluder));
-  EXPECT_TRUE(payload_state.Initialize());
-
-  OmahaResponse response;
-  // Critical package.
-  response.packages.push_back(
-      {.payload_urls = {"http://crit-test1a", "http://crit-test2a"},
-       .size = 123456789,
-       .metadata_size = 58123,
-       .metadata_signature = "msign",
-       .hash = "hash",
-       .can_exclude = false});
-  // 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});
-  response.max_failure_count_per_url = 2;
-  payload_state.SetResponse(response);
-
-  // Critical package won't be excluded.
-  // Increment twice as failure count allowed per URL is set to 2.
-  payload_state.IncrementFailureCount();
-  payload_state.IncrementFailureCount();
-
-  EXPECT_TRUE(payload_state.NextPayload());
-
-  // First increment failure should not exclude.
-  payload_state.IncrementFailureCount();
-
-  // Second increment failure should exclude.
-  EXPECT_CALL(mock_excluder, Exclude(utils::GetExclusionName("http://test1a")))
-      .WillOnce(Return(true));
-  payload_state.IncrementFailureCount();
-}
-
-TEST_F(PayloadStateTest, HaltExclusionPostPayloadExhaustion) {
-  PayloadState payload_state;
-  StrictMock<MockExcluder> mock_excluder;
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetExcluder())
-      .WillOnce(Return(&mock_excluder));
-  EXPECT_TRUE(payload_state.Initialize());
-
-  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_F(PayloadStateTest, NonInfinitePayloadIndexIncrement) {
-  PayloadState payload_state;
-  EXPECT_TRUE(payload_state.Initialize());
-
-  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/network_selector.h b/power_manager_android.cc
similarity index 63%
copy from common/network_selector.h
copy to power_manager_android.cc
index bfc09c5..63a0351 100644
--- a/common/network_selector.h
+++ b/power_manager_android.cc
@@ -14,20 +14,23 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_COMMON_NETWORK_SELECTOR_H_
-#define UPDATE_ENGINE_COMMON_NETWORK_SELECTOR_H_
+#include "update_engine/power_manager_android.h"
 
 #include <memory>
 
-#include "update_engine/common/network_selector_interface.h"
+#include <base/logging.h>
 
 namespace chromeos_update_engine {
-namespace network {
 
-// Creates the NetworkSelectorInterface instance for the given platform.
-std::unique_ptr<NetworkSelectorInterface> CreateNetworkSelector();
+namespace power_manager {
+std::unique_ptr<PowerManagerInterface> CreatePowerManager() {
+  return std::unique_ptr<PowerManagerInterface>(new PowerManagerAndroid());
+}
+}  // namespace power_manager
 
-}  // namespace network
+bool PowerManagerAndroid::RequestReboot() {
+  LOG(WARNING) << "PowerManager not implemented.";
+  return false;
+}
+
 }  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_COMMON_NETWORK_SELECTOR_H_
diff --git a/power_manager_android.h b/power_manager_android.h
new file mode 100644
index 0000000..86399ab
--- /dev/null
+++ b/power_manager_android.h
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_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/cros/power_manager_chromeos.cc b/power_manager_chromeos.cc
similarity index 92%
rename from cros/power_manager_chromeos.cc
rename to power_manager_chromeos.cc
index c1a2859..531d367 100644
--- a/cros/power_manager_chromeos.cc
+++ b/power_manager_chromeos.cc
@@ -14,14 +14,14 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/power_manager_chromeos.h"
+#include "update_engine/power_manager_chromeos.h"
 
 #include <memory>
 
 #include <power_manager/dbus-constants.h>
 #include <power_manager/dbus-proxies.h>
 
-#include "update_engine/cros/dbus_connection.h"
+#include "update_engine/dbus_connection.h"
 
 namespace chromeos_update_engine {
 
diff --git a/cros/power_manager_chromeos.h b/power_manager_chromeos.h
similarity index 83%
rename from cros/power_manager_chromeos.h
rename to power_manager_chromeos.h
index 8930508..eeb14d8 100644
--- a/cros/power_manager_chromeos.h
+++ b/power_manager_chromeos.h
@@ -14,13 +14,13 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_POWER_MANAGER_CHROMEOS_H_
-#define UPDATE_ENGINE_CROS_POWER_MANAGER_CHROMEOS_H_
+#ifndef UPDATE_ENGINE_POWER_MANAGER_CHROMEOS_H_
+#define UPDATE_ENGINE_POWER_MANAGER_CHROMEOS_H_
 
 #include <base/macros.h>
 #include <power_manager/dbus-proxies.h>
 
-#include "update_engine/cros/power_manager_interface.h"
+#include "update_engine/power_manager_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -41,4 +41,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_POWER_MANAGER_CHROMEOS_H_
+#endif  // UPDATE_ENGINE_POWER_MANAGER_CHROMEOS_H_
diff --git a/cros/power_manager_interface.h b/power_manager_interface.h
similarity index 87%
rename from cros/power_manager_interface.h
rename to power_manager_interface.h
index 1f712d2..8f77650 100644
--- a/cros/power_manager_interface.h
+++ b/power_manager_interface.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_POWER_MANAGER_INTERFACE_H_
-#define UPDATE_ENGINE_CROS_POWER_MANAGER_INTERFACE_H_
+#ifndef UPDATE_ENGINE_POWER_MANAGER_INTERFACE_H_
+#define UPDATE_ENGINE_POWER_MANAGER_INTERFACE_H_
 
 #include <memory>
 
@@ -44,4 +44,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_POWER_MANAGER_INTERFACE_H_
+#endif  // UPDATE_ENGINE_POWER_MANAGER_INTERFACE_H_
diff --git a/protobuflint.py b/protobuflint.py
deleted file mode 100755
index 02ccdbe..0000000
--- a/protobuflint.py
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2021 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.
-#
-
-"""CLI script for linting .proto files inside update_engine."""
-
-import sys
-import re
-import subprocess
-
-def check_proto_file(commit_hash, filename):
-  """Check if |filename| is consistnet with our protobuf guidelines
-
-    Args:
-      commit_hash: Hash of the git commit to look
-      filename: A filesystem path to a .proto file
-    Returns:
-      True if this file passes linting check, False otherwise
-  """
-  output = subprocess.check_output(
-      ["git", "diff", commit_hash+"~", commit_hash, "--", filename])
-  output = output.decode()
-  p = re.compile(r"^[+]?\s*required .*$", re.M)
-  m = p.search(output)
-  if m:
-    print("File", filename,
-          "contains 'required' keyword. Usage of required",
-          "is strongly discouraged in protobuf", m.group())
-    return False
-  return True
-
-def main():
-  if len(sys.argv) < 2:
-    print("Usage:", sys.argv[0], "commit_hash", "<file1>", "<file2>", "...")
-    sys.exit(1)
-  commit_hash = sys.argv[1]
-  for filename in sys.argv[2:]:
-    if filename.endswith(".proto"):
-      if not check_proto_file(commit_hash, filename):
-        sys.exit(1)
-
-if __name__ == "__main__":
-  main()
diff --git a/pylintrc b/pylintrc
index a433868..33adec2 100644
--- a/pylintrc
+++ b/pylintrc
@@ -24,7 +24,7 @@
 # Profiled execution.
 profile=no
 
-# Add files or directories to the ignorelist. They should be base names, not
+# Add files or directories to the blacklist. They should be base names, not
 # paths.
 ignore=CVS,.svn,.git,update_metadata_pb2.py
 
diff --git a/cros/real_system_state.cc b/real_system_state.cc
similarity index 69%
rename from cros/real_system_state.cc
rename to real_system_state.cc
index 5f89b27..2f18b4d 100644
--- a/cros/real_system_state.cc
+++ b/real_system_state.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/real_system_state.h"
+#include "update_engine/real_system_state.h"
 
 #include <memory>
 #include <string>
@@ -25,23 +25,37 @@
 #include <base/location.h>
 #include <base/time/time.h>
 #include <brillo/message_loops/message_loop.h>
+#if USE_CHROME_KIOSK_APP
 #include <chromeos/dbus/service_constants.h>
+#endif  // USE_CHROME_KIOSK_APP
 
 #include "update_engine/common/boot_control.h"
 #include "update_engine/common/boot_control_stub.h"
 #include "update_engine/common/constants.h"
-#include "update_engine/common/dlcservice_interface.h"
+#include "update_engine/common/dlcservice.h"
 #include "update_engine/common/hardware.h"
 #include "update_engine/common/utils.h"
-#include "update_engine/cros/metrics_reporter_omaha.h"
+#include "update_engine/metrics_reporter_omaha.h"
+#include "update_engine/update_boot_flags_action.h"
 #if USE_DBUS
-#include "update_engine/cros/dbus_connection.h"
+#include "update_engine/dbus_connection.h"
 #endif  // USE_DBUS
 #include "update_engine/update_manager/state_factory.h"
 
+using brillo::MessageLoop;
+
 namespace chromeos_update_engine {
 
+RealSystemState::~RealSystemState() {
+  // Prevent any DBus communication from UpdateAttempter when shutting down the
+  // daemon.
+  if (update_attempter_)
+    update_attempter_->ClearObservers();
+}
+
 bool RealSystemState::Initialize() {
+  metrics_reporter_.Initialize();
+
   boot_control_ = boot_control::CreateBootControl();
   if (!boot_control_) {
     LOG(WARNING) << "Unable to create BootControl instance, using stub "
@@ -55,13 +69,15 @@
     return false;
   }
 
+#if USE_CHROME_KIOSK_APP
   kiosk_app_proxy_.reset(new org::chromium::KioskAppServiceInterfaceProxy(
       DBusConnection::Get()->GetDBus(), chromeos::kKioskAppServiceName));
+#endif  // USE_CHROME_KIOSK_APP
 
   LOG_IF(INFO, !hardware_->IsNormalBootMode()) << "Booted in dev mode.";
   LOG_IF(INFO, !hardware_->IsOfficialBuild()) << "Booted non-official build.";
 
-  connection_manager_ = connection_manager::CreateConnectionManager();
+  connection_manager_ = connection_manager::CreateConnectionManager(this);
   if (!connection_manager_) {
     LOG(ERROR) << "Error initializing the ConnectionManagerInterface.";
     return false;
@@ -124,7 +140,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("", "", {})) {
+  if (!request_params_.Init("", "", false)) {
     LOG(WARNING) << "Ignoring OmahaRequestParams initialization error. Some "
                     "features might not work properly.";
   }
@@ -133,7 +149,8 @@
       new CertificateChecker(prefs_.get(), &openssl_wrapper_));
   certificate_checker_->Init();
 
-  update_attempter_.reset(new UpdateAttempter(certificate_checker_.get()));
+  update_attempter_.reset(
+      new UpdateAttempter(this, certificate_checker_.get()));
 
   // Initialize the UpdateAttempter before the UpdateManager.
   update_attempter_->Init();
@@ -141,13 +158,19 @@
   // Initialize the Update Manager using the default state factory.
   chromeos_update_manager::State* um_state =
       chromeos_update_manager::DefaultStateFactory(&policy_provider_,
-                                                   kiosk_app_proxy_.get());
+#if USE_CHROME_KIOSK_APP
+                                                   kiosk_app_proxy_.get(),
+#else
+                                                   nullptr,
+#endif  // USE_CHROME_KIOSK_APP
+                                                   this);
 
   if (!um_state) {
     LOG(ERROR) << "Failed to initialize the Update Manager.";
     return false;
   }
   update_manager_.reset(new chromeos_update_manager::UpdateManager(
+      &clock_,
       base::TimeDelta::FromSeconds(5),
       base::TimeDelta::FromHours(12),
       um_state));
@@ -155,23 +178,25 @@
   // The P2P Manager depends on the Update Manager for its initialization.
   p2p_manager_.reset(
       P2PManager::Construct(nullptr,
+                            &clock_,
                             update_manager_.get(),
                             "cros_au",
                             kMaxP2PFilesToKeep,
                             base::TimeDelta::FromDays(kMaxP2PFileAgeDays)));
 
-  if (!payload_state_.Initialize()) {
+  if (!payload_state_.Initialize(this)) {
     LOG(ERROR) << "Failed to initialize the payload state object.";
     return false;
   }
 
-  // For images that are build for debugging purposes like test images
-  // initialize max kernel key version to 0xfffffffe, which is logical infinity.
-  if (!hardware_->IsOfficialBuild()) {
+  // For devices that are not rollback enabled (ie. consumer devices),
+  // initialize max kernel key version to 0xfffffffe, which is logically
+  // infinity.
+  if (policy_provider_.IsConsumerDevice()) {
     if (!hardware()->SetMaxKernelKeyRollforward(
             chromeos_update_manager::kRollforwardInfinity)) {
       LOG(ERROR) << "Failed to set kernel_max_rollforward to infinity for"
-                 << " device with test/dev image.";
+                 << " consumer devices";
     }
   }
 
@@ -179,4 +204,43 @@
   return true;
 }
 
+bool RealSystemState::StartUpdater() {
+  // Initiate update checks.
+  update_attempter_->ScheduleUpdates();
+
+  auto update_boot_flags_action =
+      std::make_unique<UpdateBootFlagsAction>(boot_control_.get());
+  processor_.EnqueueAction(std::move(update_boot_flags_action));
+  // Update boot flags after 45 seconds.
+  MessageLoop::current()->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&ActionProcessor::StartProcessing,
+                 base::Unretained(&processor_)),
+      base::TimeDelta::FromSeconds(45));
+
+  // Broadcast the update engine status on startup to ensure consistent system
+  // state on crashes.
+  MessageLoop::current()->PostTask(
+      FROM_HERE,
+      base::Bind(&UpdateAttempter::BroadcastStatus,
+                 base::Unretained(update_attempter_.get())));
+
+  // Run the UpdateEngineStarted() method on |update_attempter|.
+  MessageLoop::current()->PostTask(
+      FROM_HERE,
+      base::Bind(&UpdateAttempter::UpdateEngineStarted,
+                 base::Unretained(update_attempter_.get())));
+  return true;
+}
+
+void RealSystemState::AddObserver(ServiceObserverInterface* observer) {
+  CHECK(update_attempter_.get());
+  update_attempter_->AddObserver(observer);
+}
+
+void RealSystemState::RemoveObserver(ServiceObserverInterface* observer) {
+  CHECK(update_attempter_.get());
+  update_attempter_->RemoveObserver(observer);
+}
+
 }  // namespace chromeos_update_engine
diff --git a/real_system_state.h b/real_system_state.h
new file mode 100644
index 0000000..4712008
--- /dev/null
+++ b/real_system_state.h
@@ -0,0 +1,202 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_REAL_SYSTEM_STATE_H_
+#define UPDATE_ENGINE_REAL_SYSTEM_STATE_H_
+
+#include "update_engine/system_state.h"
+
+#include <memory>
+#include <set>
+
+#include <policy/device_policy.h>
+
+#if USE_CHROME_KIOSK_APP
+#include <kiosk-app/dbus-proxies.h>
+#endif  // USE_CHROME_KIOSK_APP
+
+#include "update_engine/certificate_checker.h"
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/clock.h"
+#include "update_engine/common/dlcservice_interface.h"
+#include "update_engine/common/hardware_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/update_manager/update_manager.h"
+
+namespace chromeos_update_engine {
+
+// A real implementation of the SystemStateInterface which is
+// used by the actual product code.
+class RealSystemState : public SystemState, public DaemonStateInterface {
+ public:
+  // Constructs all system objects that do not require separate initialization;
+  // see Initialize() below for the remaining ones.
+  RealSystemState() = default;
+  ~RealSystemState() override;
+
+  // Initializes and sets systems objects that require an initialization
+  // separately from construction. Returns |true| on success.
+  bool Initialize();
+
+  // DaemonStateInterface overrides.
+  // Start the periodic update attempts. Must be called at the beginning of the
+  // program to start the periodic update check process.
+  bool StartUpdater() override;
+
+  void AddObserver(ServiceObserverInterface* observer) override;
+  void RemoveObserver(ServiceObserverInterface* observer) override;
+  const std::set<ServiceObserverInterface*>& service_observers() override {
+    CHECK(update_attempter_.get());
+    return update_attempter_->service_observers();
+  }
+
+  // SystemState overrides.
+  inline void set_device_policy(
+      const policy::DevicePolicy* device_policy) override {
+    device_policy_ = device_policy;
+  }
+
+  inline const policy::DevicePolicy* device_policy() override {
+    return device_policy_;
+  }
+
+  inline BootControlInterface* boot_control() override {
+    return boot_control_.get();
+  }
+
+  inline ClockInterface* clock() override { return &clock_; }
+
+  inline ConnectionManagerInterface* connection_manager() override {
+    return connection_manager_.get();
+  }
+
+  inline HardwareInterface* hardware() override { return hardware_.get(); }
+
+  inline MetricsReporterInterface* metrics_reporter() override {
+    return &metrics_reporter_;
+  }
+
+  inline PrefsInterface* prefs() override { return prefs_.get(); }
+
+  inline PrefsInterface* powerwash_safe_prefs() override {
+    return powerwash_safe_prefs_.get();
+  }
+
+  inline PayloadStateInterface* payload_state() override {
+    return &payload_state_;
+  }
+
+  inline UpdateAttempter* update_attempter() override {
+    return update_attempter_.get();
+  }
+
+  inline OmahaRequestParams* request_params() override {
+    return &request_params_;
+  }
+
+  inline P2PManager* p2p_manager() override { return p2p_manager_.get(); }
+
+  inline chromeos_update_manager::UpdateManager* update_manager() override {
+    return update_manager_.get();
+  }
+
+  inline PowerManagerInterface* power_manager() override {
+    return power_manager_.get();
+  }
+
+  inline bool system_rebooted() override { return system_rebooted_; }
+
+  inline DlcServiceInterface* dlcservice() override {
+    return dlcservice_.get();
+  }
+
+ private:
+  // Real DBus proxies using the DBus connection.
+#if USE_CHROME_KIOSK_APP
+  std::unique_ptr<org::chromium::KioskAppServiceInterfaceProxy>
+      kiosk_app_proxy_;
+#endif  // USE_CHROME_KIOSK_APP
+
+  // Interface for the power manager.
+  std::unique_ptr<PowerManagerInterface> power_manager_;
+
+  // Interface for dlcservice.
+  std::unique_ptr<DlcServiceInterface> dlcservice_;
+
+  // Interface for the clock.
+  std::unique_ptr<BootControlInterface> boot_control_;
+
+  // Interface for the clock.
+  Clock clock_;
+
+  // The latest device policy object from the policy provider.
+  const policy::DevicePolicy* device_policy_{nullptr};
+
+  // The connection manager object that makes download decisions depending on
+  // the current type of connection.
+  std::unique_ptr<ConnectionManagerInterface> connection_manager_;
+
+  // Interface for the hardware functions.
+  std::unique_ptr<HardwareInterface> hardware_;
+
+  // The Metrics reporter for reporting UMA stats.
+  MetricsReporterOmaha metrics_reporter_;
+
+  // Interface for persisted store.
+  std::unique_ptr<PrefsInterface> prefs_;
+
+  // Interface for persisted store that persists across powerwashes.
+  std::unique_ptr<PrefsInterface> powerwash_safe_prefs_;
+
+  // All state pertaining to payload state such as response, URL, backoff
+  // states.
+  PayloadState payload_state_;
+
+  // OpenSSLWrapper and CertificateChecker used for checking SSL certificates.
+  OpenSSLWrapper openssl_wrapper_;
+  std::unique_ptr<CertificateChecker> certificate_checker_;
+
+  // Pointer to the update attempter object.
+  std::unique_ptr<UpdateAttempter> update_attempter_;
+
+  // Common parameters for all Omaha requests.
+  OmahaRequestParams request_params_{this};
+
+  std::unique_ptr<P2PManager> p2p_manager_;
+
+  std::unique_ptr<chromeos_update_manager::UpdateManager> update_manager_;
+
+  policy::PolicyProvider policy_provider_;
+
+  // If true, this is the first instance of the update engine since the system
+  // rebooted. Important for tracking whether you are running instance of the
+  // update engine on first boot or due to a crash/restart.
+  bool system_rebooted_{false};
+
+  ActionProcessor processor_;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_REAL_SYSTEM_STATE_H_
diff --git a/sample_images/generate_images.sh b/sample_images/generate_images.sh
index 81a3296..8478682 100755
--- a/sample_images/generate_images.sh
+++ b/sample_images/generate_images.sh
@@ -186,27 +186,14 @@
 exit 0
 EOF
 
-  # An unlabeled postinstall bash program.
-  sudo tee "${mntdir}"/bin/self_check_default_context >/dev/null <<EOF
-#!/etc/../bin/sh
-echo "This is my context:"
-ls -lZ "\$0"
-ls -lZ "\$0" | grep -F ' u:object_r:postinstall_file:s0 ' || exit 5
-exit 0
-EOF
-
   # A postinstall bash program.
   sudo tee "${mntdir}"/bin/self_check_context >/dev/null <<EOF
 #!/etc/../bin/sh
 echo "This is my context:"
-ls -lZ "\$0"
-ls -lZ "\$0" | grep -F ' u:object_r:postinstall_exec:s0 ' || exit 5
+ls -lZ "\$0" | grep -F ' u:object_r:postinstall_file:s0 ' || exit 5
 exit 0
 EOF
 
-  # Give the test function the context we expect the postinstall-executable to have.
-  sudo setfattr -n security.selinux -v u:object_r:postinstall_exec:s0 "${mntdir}"/bin/self_check_context
-
   sudo tee "${mntdir}"/postinst >/dev/null <<EOF
 #!/etc/../bin/sh
 echo "postinst"
@@ -283,7 +270,6 @@
   # Add squashfs sample images.
   generate_image disk_sqfs_empty sqfs empty $((1024 * 4096)) 4096
   generate_image disk_sqfs_default sqfs default $((1024 * 4096)) 4096
-  generate_image disk_sqfs_unittest sqfs unittest $((1024 * 4096)) 4096
 
   # Generate the tarball and delete temporary images.
   echo "Packing tar file sample_images.tar.bz2"
diff --git a/sample_images/generate_payloads.sh b/sample_images/generate_payloads.sh
deleted file mode 100755
index ee64229..0000000
--- a/sample_images/generate_payloads.sh
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/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_images.tar.bz2 b/sample_images/sample_images.tar.bz2
index 7965d8b..6215482 100644
--- a/sample_images/sample_images.tar.bz2
+++ b/sample_images/sample_images.tar.bz2
Binary files differ
diff --git a/sample_images/sample_payloads.tar.xz b/sample_images/sample_payloads.tar.xz
deleted file mode 100644
index eb589ba..0000000
--- a/sample_images/sample_payloads.tar.xz
+++ /dev/null
Binary files differ
diff --git a/scripts/blockdiff.py b/scripts/blockdiff.py
index 95893cf..5793def 100755
--- a/scripts/blockdiff.py
+++ b/scripts/blockdiff.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python2
 #
 # Copyright (C) 2013 The Android Open Source Project
 #
@@ -17,7 +17,6 @@
 
 """Block diff utility."""
 
-from __future__ import absolute_import
 from __future__ import print_function
 
 # pylint: disable=import-error
@@ -47,7 +46,7 @@
 
   """
   if max_length < 0:
-    max_length = sys.maxsize
+    max_length = sys.maxint
   diff_list = []
   num_blocks = extent_start = extent_length = 0
   while max_length or extent_length:
diff --git a/scripts/brillo_update_payload b/scripts/brillo_update_payload
index 746cefb..d9c18ff 100755
--- a/scripts/brillo_update_payload
+++ b/scripts/brillo_update_payload
@@ -89,14 +89,12 @@
   exit 1
 }
 
-# Loads shflags. We first look at the default install location; then our own
-# directory; finally the parent directory.
+# Loads shflags. We first look at the default install location; then look for
+# crosutils (chroot); finally check our own directory.
 load_shflags() {
   local my_dir="$(dirname "$(readlink -f "$0")")"
   local path
-  for path in /usr/share/misc \
-    "${my_dir}"/lib/shflags \
-    "${my_dir}"/../lib/shflags; do
+  for path in /usr/share/misc "${my_dir}"/lib/shflags; do
     if [[ -r "${path}/shflags" ]]; then
       . "${path}/shflags" || die "Could not load ${path}/shflags."
       return
@@ -188,22 +186,9 @@
     "Optional: The maximum unix timestamp of the OS allowed to apply this \
 payload, should be set to a number higher than the build timestamp of the \
 system running on the device, 0 if not specified."
-  DEFINE_string partition_timestamps "" \
-    "Optional: Per-partition maximum unix timestamp of the OS allowed to \
-apply this payload. Should be a comma separated key value pairs. e.x.\
-system:1234,vendor:456"
   DEFINE_string disable_fec_computation "" \
     "Optional: Disables the on device fec data computation for incremental \
 update. This feature is enabled by default."
-  DEFINE_string disable_verity_computation "" \
-    "Optional: Disables the on device verity computation for incremental \
-update. This feature is enabled by default."
-  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"
-  DEFINE_string disable_vabc "" \
-    "Optional: Disables Virtual AB Compression when installing the OTA"
 fi
 if [[ "${COMMAND}" == "hash" || "${COMMAND}" == "sign" ]]; then
   DEFINE_string unsigned_payload "" "Path to the input unsigned payload."
@@ -286,9 +271,6 @@
 # Path to the dynamic partition info file in target image if exists.
 DYNAMIC_PARTITION_INFO_FILE=""
 
-# Path to the META/apex_info.pb found in target build
-APEX_INFO_FILE=""
-
 # read_option_int <file.txt> <option_key> [default_value]
 #
 # Reads the unsigned integer value associated with |option_key| in a key=value
@@ -572,13 +554,6 @@
         "${dynamic_partitions_info}"
       DYNAMIC_PARTITION_INFO_FILE="${dynamic_partitions_info}"
     fi
-    local apex_info=$(create_tempfile "apex_info.XXXXXX")
-    CLEANUP_FILES+=("${apex_info}")
-    if unzip -l "${image}" "META/apex_info.pb" > /dev/null; then
-      extract_file "${image}" "META/apex_info.pb" \
-        "${apex_info}"
-      APEX_INFO_FILE="${apex_info}"
-    fi
   fi
 
   local part
@@ -667,12 +642,7 @@
     fi
     partition_names+="${part}"
     new_partitions+="${DST_PARTITIONS[${part}]}"
-    if [ "${FLAGS_full_boot}" == "true" ] && [ "${part}" == "boot" ]; then
-      # Skip boot partition.
-      old_partitions+=""
-    else
-      old_partitions+="${SRC_PARTITIONS[${part}]:-}"
-    fi
+    old_partitions+="${SRC_PARTITIONS[${part}]:-}"
     new_mapfiles+="${DST_PARTITIONS_MAP[${part}]:-}"
     old_mapfiles+="${SRC_PARTITIONS_MAP[${part}]:-}"
   done
@@ -684,40 +654,19 @@
     --new_mapfiles="${new_mapfiles}"
   )
 
-  if [[ "${FLAGS_is_partial_update}" == "true" ]]; then
-    GENERATOR_ARGS+=( --is_partial_update="true" )
-    # Need at least minor version 7 for partial update, so generate with minor
-    # version 7 if we don't have a source image. Let the delta_generator to fail
-    # the other incompatiable minor versions.
-    if [[ -z "${FORCE_MINOR_VERSION}" ]]; then
-      FORCE_MINOR_VERSION="7"
-    fi
-  fi
-
   if [[ "${payload_type}" == "delta" ]]; then
     # Source image args:
     GENERATOR_ARGS+=(
       --old_partitions="${old_partitions}"
       --old_mapfiles="${old_mapfiles}"
     )
+    if [[ -n "${FORCE_MINOR_VERSION}" ]]; then
+      GENERATOR_ARGS+=( --minor_version="${FORCE_MINOR_VERSION}" )
+    fi
     if [[ -n "${FLAGS_disable_fec_computation}" ]]; then
       GENERATOR_ARGS+=(
         --disable_fec_computation="${FLAGS_disable_fec_computation}" )
     fi
-    if [[ -n "${FLAGS_disable_verity_computation}" ]]; then
-      GENERATOR_ARGS+=(
-        --disable_verity_computation="${FLAGS_disable_verity_computation}" )
-    fi
-  fi
-
-  if [[ -n "${FLAGS_disable_vabc}" ]]; then
-    GENERATOR_ARGS+=(
-      --disable_vabc="${FLAGS_disable_vabc}" )
-  fi
-
-  # minor version is set only for delta or partial payload.
-  if [[ -n "${FORCE_MINOR_VERSION}" ]]; then
-    GENERATOR_ARGS+=( --minor_version="${FORCE_MINOR_VERSION}" )
   fi
 
   if [[ -n "${FORCE_MAJOR_VERSION}" ]]; then
@@ -732,10 +681,6 @@
     GENERATOR_ARGS+=( --max_timestamp="${FLAGS_max_timestamp}" )
   fi
 
-  if [[ -n "${FLAGS_partition_timestamps}" ]]; then
-    GENERATOR_ARGS+=( --partition_timestamps="${FLAGS_partition_timestamps}" )
-  fi
-
   if [[ -n "${POSTINSTALL_CONFIG_FILE}" ]]; then
     GENERATOR_ARGS+=(
       --new_postinstall_config_file="${POSTINSTALL_CONFIG_FILE}"
@@ -747,11 +692,6 @@
       --dynamic_partition_info_file="${DYNAMIC_PARTITION_INFO_FILE}"
     )
   fi
-  if [[ -n "{APEX_INFO_FILE}" ]]; then
-    GENERATOR_ARGS+=(
-      --apex_info_file="${APEX_INFO_FILE}"
-    )
-  fi
 
   echo "Running delta_generator with args: ${GENERATOR_ARGS[@]}"
   "${GENERATOR}" "${GENERATOR_ARGS[@]}"
@@ -941,8 +881,8 @@
   check_update_payload ${PAYCHECK_ARGS[@]} --check
 }
 
-# Check that the real generator exists:
-[[ -x "${GENERATOR}" ]] || GENERATOR="$(which delta_generator || true)"
+# Sanity check that the real generator exists:
+GENERATOR="$(which delta_generator || true)"
 [[ -x "${GENERATOR}" ]] || die "can't find delta_generator"
 
 case "$COMMAND" in
diff --git a/scripts/cow_converter.py b/scripts/cow_converter.py
deleted file mode 100644
index 14e016c..0000000
--- a/scripts/cow_converter.py
+++ /dev/null
@@ -1,87 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2021 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.
-#
-
-"""Command-line tool for converting OTA payloads to VABC style COW images."""
-
-import os
-import sys
-import tempfile
-import zipfile
-import subprocess
-
-
-def IsSparseImage(filepath):
-  """Determine if an image is a sparse image
-  Args:
-    filepath: str, a path to an .img file
-
-  Returns:
-    return true iff the filepath is a sparse image.
-
-  """
-  with open(filepath, 'rb') as fp:
-    # Magic for android sparse image format
-    # https://source.android.com/devices/bootloader/images
-    return fp.read(4) == b'\x3A\xFF\x26\xED'
-
-
-def ConvertCOW(ota_path, target_file_path, tmp_dir, output_dir):
-  """Convert ota payload to COW IMAGE
-  Args:
-    ota_path: str, path to ota.zip
-    target_file_path: str, path to target_file.zip,
-      must be the target build for OTA.
-    tmp_dir: A temp dir as scratch space
-    output_dir: A directory where all converted COW images will be written.
-  """
-  with zipfile.ZipFile(ota_path) as ota_zip:
-    payload_path = ota_zip.extract("payload.bin", output_dir)
-  with zipfile.ZipFile(target_file_path) as zfp:
-    for fileinfo in zfp.infolist():
-      img_name = os.path.basename(fileinfo.filename)
-      if not fileinfo.filename.endswith(".img"):
-        continue
-      if fileinfo.filename.startswith("IMAGES/") or \
-              fileinfo.filename.startswith("RADIO/"):
-        img_path = zfp.extract(fileinfo, tmp_dir)
-        target_img_path = os.path.join(output_dir, img_name)
-        if IsSparseImage(img_path):
-          subprocess.check_call(["simg2img", img_path, target_img_path])
-        else:
-          os.rename(img_path, target_img_path)
-        print("Extracted", fileinfo.filename, "size:", fileinfo.file_size)
-
-  subprocess.call(["cow_converter", payload_path,
-                   output_dir])
-
-
-def main():
-  if len(sys.argv) != 4:
-    print(
-        "Usage:", sys.argv[0], "<your_ota.zip> <target_file.zip> <output dir>")
-    return 1
-  ota_path = sys.argv[1]
-  target_file_path = sys.argv[2]
-  output_dir = sys.argv[3]
-  os.makedirs(output_dir, exist_ok=True)
-  with tempfile.TemporaryDirectory() as tmp_dir:
-    ConvertCOW(ota_path, target_file_path, tmp_dir, output_dir)
-  return 0
-
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/scripts/ota_stress_test.py b/scripts/ota_stress_test.py
deleted file mode 100644
index 55aa4b1..0000000
--- a/scripts/ota_stress_test.py
+++ /dev/null
@@ -1,122 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2021 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.
-#
-
-"""Repeatedly install an A/B update to an Android device over adb."""
-
-import argparse
-import sys
-from pathlib import Path
-import subprocess
-import signal
-
-
-def CleanupLoopDevices():
-  # b/184716804 clean up unused loop devices
-  subprocess.check_call(["adb", "shell", "su", "0", "losetup", '-D'])
-
-
-def CancelOTA():
-  subprocess.call(["adb", "shell", "su", "0",
-                  "update_engine_client", "--cancel"])
-
-
-def PerformOTAThenPause(otafile: Path, update_device_script: Path):
-  python = sys.executable
-  ota_cmd = [python, str(update_device_script), str(otafile),
-             "--no-postinstall", "--no-slot-switch"]
-  p = subprocess.Popen(ota_cmd)
-  pid = p.pid
-  try:
-    ret = p.wait(10)
-    if ret is not None and ret != 0:
-      raise RuntimeError("OTA failed to apply")
-    if ret == 0:
-      print("OTA finished early? Surprise.")
-      return
-  except subprocess.TimeoutExpired:
-    pass
-  print(f"Killing {pid}")
-  subprocess.check_call(["pkill", "-INT", "-P", str(pid)])
-  p.send_signal(signal.SIGINT)
-  p.wait()
-
-
-def PerformTest(otafile: Path, resumes: int, timeout: int):
-  """Install an OTA to device, raising exceptions on failure
-
-  Args:
-    otafile: Path to the ota.zip to install
-
-  Return:
-    None if no error, if there's an error exception will be thrown
-  """
-  assert otafile.exists()
-  print("Applying", otafile)
-  script_dir = Path(__file__).parent.absolute()
-  update_device_script = script_dir / "update_device.py"
-  assert update_device_script.exists()
-  print(update_device_script)
-  python = sys.executable
-
-  for i in range(resumes):
-    print("Pause/Resume for the", i+1, "th time")
-    PerformOTAThenPause(otafile, update_device_script)
-  CancelOTA()
-  CleanupLoopDevices()
-
-  ota_cmd = [python, str(update_device_script),
-             str(otafile), "--no-postinstall"]
-  print("Finishing OTA Update", ota_cmd)
-  output = subprocess.check_output(
-      ota_cmd, stderr=subprocess.STDOUT, timeout=timeout).decode()
-  print(output)
-  if "onPayloadApplicationComplete(ErrorCode::kSuccess" not in output:
-    raise RuntimeError("Failed to finish OTA")
-  subprocess.call(
-      ["adb", "shell", "su", "0", "update_engine_client", "--cancel"])
-  subprocess.check_call(
-      ["adb", "shell", "su", "0", "update_engine_client", "--reset_status"])
-  CleanupLoopDevices()
-
-
-def main():
-  parser = argparse.ArgumentParser(
-      description='Android A/B OTA stress test helper.')
-  parser.add_argument('otafile', metavar='PAYLOAD', type=Path,
-                      help='the OTA package file (a .zip file) or raw payload \
-                      if device uses Omaha.')
-  parser.add_argument('-n', "--iterations", type=int, default=10,
-                      metavar='ITERATIONS',
-                      help='The number of iterations to run the stress test, or\
-                       -1 to keep running until CTRL+C')
-  parser.add_argument('-r', "--resumes", type=int, default=5, metavar='RESUMES',
-                      help='The number of iterations to pause the update when \
-                        installing')
-  parser.add_argument('-t', "--timeout", type=int, default=60*60,
-                      metavar='TIMEOUTS',
-                      help='Timeout, in seconds, when waiting for OTA to \
-                        finish')
-  args = parser.parse_args()
-  print(args)
-  n = args.iterations
-  while n != 0:
-    PerformTest(args.otafile, args.resumes, args.timeout)
-    n -= 1
-
-
-if __name__ == "__main__":
-  main()
diff --git a/scripts/paycheck.py b/scripts/paycheck.py
index cb1713f..9d61778 100755
--- a/scripts/paycheck.py
+++ b/scripts/paycheck.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python2
 #
 # Copyright (C) 2013 The Android Open Source Project
 #
@@ -17,7 +17,6 @@
 
 """Command-line tool for checking and applying Chrome OS update payloads."""
 
-from __future__ import absolute_import
 from __future__ import print_function
 
 # pylint: disable=import-error
@@ -27,43 +26,18 @@
 import sys
 import tempfile
 
-# pylint: disable=redefined-builtin
-from six.moves import zip
+from update_payload import common
 from update_payload import error
 
-
 lib_dir = os.path.join(os.path.dirname(__file__), 'lib')
 if os.path.exists(lib_dir) and os.path.isdir(lib_dir):
   sys.path.insert(1, lib_dir)
-import update_payload  # pylint: disable=wrong-import-position
+import update_payload
 
 
 _TYPE_FULL = 'full'
 _TYPE_DELTA = 'delta'
 
-def CheckApplyPayload(args):
-  """Whether to check the result after applying the payload.
-
-  Args:
-    args: Parsed command arguments (the return value of
-          ArgumentParser.parse_args).
-
-  Returns:
-    Boolean value whether to check.
-  """
-  return args.dst_part_paths is not None
-
-def ApplyPayload(args):
-  """Whether to apply the payload.
-
-  Args:
-    args: Parsed command arguments (the return value of
-          ArgumentParser.parse_args).
-
-  Returns:
-    Boolean value whether to apply the payload.
-  """
-  return CheckApplyPayload(args) or args.out_dst_part_paths is not None
 
 def ParseArguments(argv):
   """Parse and validate command-line arguments.
@@ -75,9 +49,9 @@
     Returns the arguments returned by the argument parser.
   """
   parser = argparse.ArgumentParser(
-      description=('Applies a Chrome OS update PAYLOAD to src_part_paths'
-                   'emitting dst_part_paths, respectively. '
-                   'src_part_paths are only needed for delta payloads. '
+      description=('Applies a Chrome OS update PAYLOAD to src_kern and '
+                   'src_root emitting dst_kern and dst_root, respectively. '
+                   'src_kern and src_root are only needed for delta payloads. '
                    'When no partitions are provided, verifies the payload '
                    'integrity.'),
       epilog=('Note: a payload may verify correctly but fail to apply, and '
@@ -93,6 +67,9 @@
   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',
@@ -116,6 +93,13 @@
   check_args.add_argument('-s', '--metadata-size', metavar='NUM', default=0,
                           help='the metadata size to verify with the one in'
                           ' payload')
+  # TODO(tbrindus): deprecated in favour of --part_sizes
+  check_args.add_argument('-p', '--root-part-size', metavar='NUM',
+                          default=0, type=int,
+                          help='override rootfs partition size auto-inference')
+  check_args.add_argument('-P', '--kern-part-size', metavar='NUM',
+                          default=0, type=int,
+                          help='override kernel partition size auto-inference')
   check_args.add_argument('--part_sizes', metavar='NUM', nargs='+', type=int,
                           help='override partition size auto-inference')
 
@@ -129,6 +113,21 @@
                           help='use the specified bspatch binary')
   apply_args.add_argument('--puffpatch-path', metavar='FILE',
                           help='use the specified puffpatch binary')
+  # TODO(tbrindus): deprecated in favour of --dst_part_paths
+  apply_args.add_argument('--dst_kern', metavar='FILE',
+                          help='destination kernel partition file')
+  apply_args.add_argument('--dst_root', metavar='FILE',
+                          help='destination root partition file')
+  # TODO(tbrindus): deprecated in favour of --src_part_paths
+  apply_args.add_argument('--src_kern', metavar='FILE',
+                          help='source kernel partition file')
+  apply_args.add_argument('--src_root', metavar='FILE',
+                          help='source root partition file')
+  # TODO(tbrindus): deprecated in favour of --out_dst_part_paths
+  apply_args.add_argument('--out_dst_kern', metavar='FILE',
+                          help='created destination kernel partition file')
+  apply_args.add_argument('--out_dst_root', metavar='FILE',
+                          help='created destination root partition file')
 
   apply_args.add_argument('--src_part_paths', metavar='FILE', nargs='+',
                           help='source partitition files')
@@ -144,28 +143,36 @@
   # Parse command-line arguments.
   args = parser.parse_args(argv)
 
+  # TODO(tbrindus): temporary workaround to keep old-style flags from breaking
+  # without having to handle both types in our code. Remove after flag usage is
+  # removed from calling scripts.
+  args.part_names = args.part_names or [common.KERNEL, common.ROOTFS]
+  args.part_sizes = args.part_sizes or [args.kern_part_size,
+                                        args.root_part_size]
+  args.src_part_paths = args.src_part_paths or [args.src_kern, args.src_root]
+  args.dst_part_paths = args.dst_part_paths or [args.dst_kern, args.dst_root]
+  args.out_dst_part_paths = args.out_dst_part_paths or [args.out_dst_kern,
+                                                        args.out_dst_root]
+
+  # Make sure we don't have new dependencies on old flags by deleting them from
+  # the namespace here.
+  for old in ['kern_part_size', 'root_part_size', 'src_kern', 'src_root',
+              'dst_kern', 'dst_root', 'out_dst_kern', 'out_dst_root']:
+    delattr(args, old)
+
   # There are several options that imply --check.
   args.check = (args.check or args.report or args.assert_type or
                 args.block_size or args.allow_unhashed or
                 args.disabled_tests or args.meta_sig or args.key or
-                args.part_sizes is not None or args.metadata_size)
+                any(args.part_sizes) or args.metadata_size)
 
-  # Makes sure the following arguments have the same length as |part_names| if
-  # set.
   for arg in ['part_sizes', 'src_part_paths', 'dst_part_paths',
               'out_dst_part_paths']:
-    if getattr(args, arg) is None:
-      # Parameter is not set.
-      continue
     if len(args.part_names) != len(getattr(args, arg, [])):
       parser.error('partitions in --%s do not match --part_names' % arg)
 
-  def _IsSrcPartPathsProvided(args):
-    return args.src_part_paths is not None
-
-  # Makes sure parameters are coherent with payload type.
-  if ApplyPayload(args):
-    if _IsSrcPartPathsProvided(args):
+  if all(args.dst_part_paths) or all(args.out_dst_part_paths):
+    if all(args.src_part_paths):
       if args.assert_type == _TYPE_FULL:
         parser.error('%s payload does not accept source partition arguments'
                      % _TYPE_FULL)
@@ -201,12 +208,15 @@
   # Parse and validate arguments.
   args = ParseArguments(argv[1:])
 
-  with open(args.payload, 'rb') as payload_file:
+  with open(args.payload) as payload_file:
     payload = update_payload.Payload(payload_file)
     try:
       # Initialize payload.
       payload.Init()
 
+      if args.describe:
+        payload.Describe()
+
       # Perform payload integrity checks.
       if args.check:
         report_file = None
@@ -220,9 +230,8 @@
               report_file = open(args.report, 'w')
               do_close_report_file = True
 
-          part_sizes = (args.part_sizes and
-                        dict(zip(args.part_names, args.part_sizes)))
-          metadata_sig_file = args.meta_sig and open(args.meta_sig, 'rb')
+          part_sizes = dict(zip(args.part_names, args.part_sizes))
+          metadata_sig_file = args.meta_sig and open(args.meta_sig)
           payload.Check(
               pubkey_file_name=args.key,
               metadata_sig_file=metadata_sig_file,
@@ -240,7 +249,7 @@
             report_file.close()
 
       # Apply payload.
-      if ApplyPayload(args):
+      if all(args.dst_part_paths) or all(args.out_dst_part_paths):
         dargs = {'bsdiff_in_place': not args.extract_bsdiff}
         if args.bspatch_path:
           dargs['bspatch_path'] = args.bspatch_path
@@ -251,9 +260,9 @@
 
         out_dst_parts = {}
         file_handles = []
-        if args.out_dst_part_paths is not None:
+        if all(args.out_dst_part_paths):
           for name, path in zip(args.part_names, args.out_dst_part_paths):
-            handle = open(path, 'wb+')
+            handle = open(path, 'w+')
             file_handles.append(handle)
             out_dst_parts[name] = handle.name
         else:
@@ -266,7 +275,7 @@
 
         # If destination kernel and rootfs partitions are not given, then this
         # just becomes an apply operation with no check.
-        if CheckApplyPayload(args):
+        if all(args.dst_part_paths):
           # Prior to comparing, add the unused space past the filesystem
           # boundary in the new target partitions to become the same size as
           # the given partitions. This will truncate to larger size.
@@ -284,7 +293,7 @@
         # files are created as temp files and will be deleted upon close().
         for handle in file_handles:
           handle.close()
-    except error.PayloadError as e:
+    except error.PayloadError, e:
       sys.stderr.write('Error: %s\n' % e)
       return 1
 
diff --git a/scripts/paycheck_unittest.py b/scripts/paycheck_unittest.py
deleted file mode 100755
index a90d269..0000000
--- a/scripts/paycheck_unittest.py
+++ /dev/null
@@ -1,105 +0,0 @@
-#!/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 8343d21..09a7cf7 100755
--- a/scripts/payload_info.py
+++ b/scripts/payload_info.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2
 # -*- coding: utf-8 -*-
 #
 # Copyright (C) 2015 The Android Open Source Project
@@ -18,17 +18,16 @@
 
 """payload_info: Show information about an update payload."""
 
-from __future__ import absolute_import
 from __future__ import print_function
 
 import argparse
+import itertools
 import sys
 import textwrap
 
-from six.moves import range
 import update_payload
 
-
+MAJOR_PAYLOAD_VERSION_CHROMEOS = 1
 MAJOR_PAYLOAD_VERSION_BRILLO = 2
 
 def DisplayValue(key, value):
@@ -42,12 +41,12 @@
 def DisplayHexData(data, indent=0):
   """Print out binary data as a hex values."""
   for off in range(0, len(data), 16):
-    chunk = bytearray(data[off:off + 16])
+    chunk = data[off:off + 16]
     print(' ' * indent +
-          ' '.join('%.2x' % c for c in chunk) +
+          ' '.join('%.2x' % ord(c) for c in chunk) +
           '   ' * (16 - len(chunk)) +
           ' | ' +
-          ''.join(chr(c) if 32 <= c < 127 else '.' for c in chunk))
+          ''.join(c if 32 <= ord(c) < 127 else '.' for c in chunk))
 
 
 class PayloadCommand(object):
@@ -70,16 +69,15 @@
   def _DisplayManifest(self):
     """Show information from the payload manifest."""
     manifest = self.payload.manifest
-    DisplayValue('Number of partitions', len(manifest.partitions))
-    for partition in manifest.partitions:
-      DisplayValue('  Number of "%s" ops' % partition.partition_name,
-                   len(partition.operations))
-    for partition in manifest.partitions:
-      DisplayValue("  Timestamp for " +
-                   partition.partition_name, partition.version)
-    for partition in manifest.partitions:
-      DisplayValue("  COW Size for " +
-                   partition.partition_name, partition.estimate_cow_size)
+    if self.payload.header.version == MAJOR_PAYLOAD_VERSION_BRILLO:
+      DisplayValue('Number of partitions', len(manifest.partitions))
+      for partition in manifest.partitions:
+        DisplayValue('  Number of "%s" ops' % partition.partition_name,
+                     len(partition.operations))
+    else:
+      DisplayValue('Number of operations', len(manifest.install_operations))
+      DisplayValue('Number of kernel ops',
+                   len(manifest.kernel_install_operations))
     DisplayValue('Block size', manifest.block_size)
     DisplayValue('Minor version', manifest.minor_version)
 
@@ -133,8 +131,8 @@
 
     Args:
       name: The name you want displayed above the operation table.
-      operations: The operations object that you want to display information
-                  about.
+      operations: The install_operations object that you want to display
+                  information about.
     """
     def _DisplayExtents(extents, name):
       """Show information about extents."""
@@ -151,7 +149,7 @@
 
     op_dict = update_payload.common.OpType.NAMES
     print('%s:' % name)
-    for op_count, op in enumerate(operations):
+    for op, op_count in itertools.izip(operations, itertools.count()):
       print('  %d: %s' % (op_count, op_dict[op.type]))
       if op.HasField('data_offset'):
         print('    Data offset: %s' % op.data_offset)
@@ -172,9 +170,14 @@
     read_blocks = 0
     written_blocks = 0
     num_write_seeks = 0
-    for partition in manifest.partitions:
+    if self.payload.header.version == MAJOR_PAYLOAD_VERSION_BRILLO:
+      partitions_operations = [part.operations for part in manifest.partitions]
+    else:
+      partitions_operations = [manifest.install_operations,
+                               manifest.kernel_install_operations]
+    for operations in partitions_operations:
       last_ext = None
-      for curr_op in partition.operations:
+      for curr_op in operations:
         read_blocks += sum([ext.num_blocks for ext in curr_op.src_extents])
         written_blocks += sum([ext.num_blocks for ext in curr_op.dst_extents])
         for curr_ext in curr_op.dst_extents:
@@ -184,10 +187,15 @@
             num_write_seeks += 1
           last_ext = curr_ext
 
-      # Old and new partitions are read once during verification.
-      read_blocks += partition.old_partition_info.size // manifest.block_size
-      read_blocks += partition.new_partition_info.size // manifest.block_size
-
+    if manifest.minor_version == 1:
+      # Rootfs and kernel are written during the filesystem copy in version 1.
+      written_blocks += manifest.old_rootfs_info.size / manifest.block_size
+      written_blocks += manifest.old_kernel_info.size / manifest.block_size
+    # Old and new rootfs and kernel are read once during verification
+    read_blocks += manifest.old_rootfs_info.size / manifest.block_size
+    read_blocks += manifest.old_kernel_info.size / manifest.block_size
+    read_blocks += manifest.new_rootfs_info.size / manifest.block_size
+    read_blocks += manifest.new_kernel_info.size / manifest.block_size
     stats = {'read_blocks': read_blocks,
              'written_blocks': written_blocks,
              'num_write_seeks': num_write_seeks}
@@ -211,15 +219,21 @@
       self._DisplayStats(self.payload.manifest)
     if self.options.list_ops:
       print()
-      for partition in self.payload.manifest.partitions:
-        self._DisplayOps('%s install operations' % partition.partition_name,
-                         partition.operations)
+      if self.payload.header.version == MAJOR_PAYLOAD_VERSION_BRILLO:
+        for partition in self.payload.manifest.partitions:
+          self._DisplayOps('%s install operations' % partition.partition_name,
+                           partition.operations)
+      else:
+        self._DisplayOps('Install operations',
+                         self.payload.manifest.install_operations)
+        self._DisplayOps('Kernel install operations',
+                         self.payload.manifest.kernel_install_operations)
 
 
 def main():
   parser = argparse.ArgumentParser(
       description='Show information about an update payload.')
-  parser.add_argument('payload_file', type=argparse.FileType('rb'),
+  parser.add_argument('payload_file', type=file,
                       help='The update payload file.')
   parser.add_argument('--list_ops', default=False, action='store_true',
                       help='List the install operations and their extents.')
@@ -231,6 +245,5 @@
 
   PayloadCommand(args).Run()
 
-
 if __name__ == '__main__':
   sys.exit(main())
diff --git a/scripts/payload_info_unittest.py b/scripts/payload_info_unittest.py
index 07bb679..a4ee9d5 100755
--- a/scripts/payload_info_unittest.py
+++ b/scripts/payload_info_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python2
 #
 # Copyright (C) 2015 The Android Open Source Project
 #
@@ -17,31 +17,24 @@
 
 """Unit testing payload_info.py."""
 
-# Disable check for function names to avoid errors based on old code
-# pylint: disable-msg=invalid-name
-
-from __future__ import absolute_import
 from __future__ import print_function
 
+import StringIO
+import collections
+import mock
 import sys
 import unittest
 
-from contextlib import contextmanager
-
-from six.moves import StringIO
-
-import mock  # pylint: disable=import-error
-
 import payload_info
 import update_payload
 
-from update_payload import update_metadata_pb2
+from contextlib import contextmanager
 
+from update_payload import update_metadata_pb2
 
 class FakePayloadError(Exception):
   """A generic error when using the FakePayload."""
 
-
 class FakeOption(object):
   """Fake options object for testing."""
 
@@ -49,12 +42,11 @@
     self.list_ops = False
     self.stats = False
     self.signatures = False
-    for key, val in kwargs.items():
+    for key, val in kwargs.iteritems():
       setattr(self, key, val)
     if not hasattr(self, 'payload_file'):
       self.payload_file = None
 
-
 class FakeOp(object):
   """Fake manifest operation for testing."""
 
@@ -62,57 +54,48 @@
     self.src_extents = src_extents
     self.dst_extents = dst_extents
     self.type = op_type
-    for key, val in kwargs.items():
+    for key, val in kwargs.iteritems():
       setattr(self, key, val)
 
   def HasField(self, field):
     return hasattr(self, field)
 
-
-class FakeExtent(object):
-  """Fake Extent for testing."""
-  def __init__(self, start_block, num_blocks):
-    self.start_block = start_block
-    self.num_blocks = num_blocks
-
-
-class FakePartitionInfo(object):
-  """Fake PartitionInfo for testing."""
-  def __init__(self, size):
-    self.size = size
-
-
 class FakePartition(object):
   """Fake PartitionUpdate field for testing."""
 
-  def __init__(self, partition_name, operations, old_size, new_size):
+  def __init__(self, partition_name, operations):
     self.partition_name = partition_name
     self.operations = operations
-    self.old_partition_info = FakePartitionInfo(old_size)
-    self.new_partition_info = FakePartitionInfo(new_size)
-
 
 class FakeManifest(object):
   """Fake manifest for testing."""
 
-  def __init__(self):
-    self.partitions = [
-        FakePartition(update_payload.common.ROOTFS,
-                      [FakeOp([], [FakeExtent(1, 1), FakeExtent(2, 2)],
-                              update_payload.common.OpType.REPLACE_BZ,
-                              dst_length=3*4096,
-                              data_offset=1,
-                              data_length=1)
-                      ], 1 * 4096, 3 * 4096),
-        FakePartition(update_payload.common.KERNEL,
-                      [FakeOp([FakeExtent(1, 1)],
-                              [FakeExtent(x, x) for x in range(20)],
-                              update_payload.common.OpType.SOURCE_COPY,
-                              src_length=4096)
-                      ], 2 * 4096, 4 * 4096),
-    ]
+  def __init__(self, major_version):
+    FakeExtent = collections.namedtuple('FakeExtent',
+                                        ['start_block', 'num_blocks'])
+    self.install_operations = [FakeOp([],
+                                      [FakeExtent(1, 1), FakeExtent(2, 2)],
+                                      update_payload.common.OpType.REPLACE_BZ,
+                                      dst_length=3*4096,
+                                      data_offset=1,
+                                      data_length=1)]
+    self.kernel_install_operations = [FakeOp(
+        [FakeExtent(1, 1)],
+        [FakeExtent(x, x) for x in xrange(20)],
+        update_payload.common.OpType.SOURCE_COPY,
+        src_length=4096)]
+    if major_version == payload_info.MAJOR_PAYLOAD_VERSION_BRILLO:
+      self.partitions = [FakePartition('root', self.install_operations),
+                         FakePartition('kernel',
+                                       self.kernel_install_operations)]
+      self.install_operations = self.kernel_install_operations = []
     self.block_size = 4096
     self.minor_version = 4
+    FakePartInfo = collections.namedtuple('FakePartInfo', ['size'])
+    self.old_rootfs_info = FakePartInfo(1 * 4096)
+    self.old_kernel_info = FakePartInfo(2 * 4096)
+    self.new_rootfs_info = FakePartInfo(3 * 4096)
+    self.new_kernel_info = FakePartInfo(4 * 4096)
     self.signatures_offset = None
     self.signatures_size = None
 
@@ -120,27 +103,26 @@
     """Fake HasField method based on the python members."""
     return hasattr(self, field_name) and getattr(self, field_name) is not None
 
-
 class FakeHeader(object):
   """Fake payload header for testing."""
 
-  def __init__(self, manifest_len, metadata_signature_len):
-    self.version = payload_info.MAJOR_PAYLOAD_VERSION_BRILLO
+  def __init__(self, version, manifest_len, metadata_signature_len):
+    self.version = version
     self.manifest_len = manifest_len
     self.metadata_signature_len = metadata_signature_len
 
   @property
   def size(self):
-    return 24
-
+    return (20 if self.version == payload_info.MAJOR_PAYLOAD_VERSION_CHROMEOS
+            else 24)
 
 class FakePayload(object):
   """Fake payload for testing."""
 
-  def __init__(self):
-    self._header = FakeHeader(222, 0)
+  def __init__(self, major_version):
+    self._header = FakeHeader(major_version, 222, 0)
     self.header = None
-    self._manifest = FakeManifest()
+    self._manifest = FakeManifest(major_version)
     self.manifest = None
 
     self._blobs = {}
@@ -170,7 +152,7 @@
   def _AddSignatureToProto(proto, **kwargs):
     """Add a new Signature element to the passed proto."""
     new_signature = proto.signatures.add()
-    for key, val in kwargs.items():
+    for key, val in kwargs.iteritems():
       setattr(new_signature, key, val)
 
   def AddPayloadSignature(self, **kwargs):
@@ -188,7 +170,6 @@
     self._header.metadata_signature_len = len(blob)
     self._blobs[-len(blob)] = blob
 
-
 class PayloadCommandTest(unittest.TestCase):
   """Test class for our PayloadCommand class."""
 
@@ -197,7 +178,7 @@
     """A tool for capturing the sys.stdout"""
     stdout = sys.stdout
     try:
-      sys.stdout = StringIO()
+      sys.stdout = StringIO.StringIO()
       yield sys.stdout
     finally:
       sys.stdout = stdout
@@ -211,33 +192,60 @@
     with mock.patch.object(update_payload, 'Payload', return_value=payload), \
          self.OutputCapturer() as output:
       payload_cmd.Run()
-    self.assertEqual(output.getvalue(), expected_out)
+    self.assertEquals(output.getvalue(), expected_out)
 
   def testDisplayValue(self):
     """Verify that DisplayValue prints what we expect."""
     with self.OutputCapturer() as output:
       payload_info.DisplayValue('key', 'value')
-    self.assertEqual(output.getvalue(), 'key:                         value\n')
+    self.assertEquals(output.getvalue(), 'key:                         value\n')
 
   def testRun(self):
     """Verify that Run parses and displays the payload like we expect."""
     payload_cmd = payload_info.PayloadCommand(FakeOption(action='show'))
-    payload = FakePayload()
-    expected_out = """Payload version:             2
+    payload = FakePayload(payload_info.MAJOR_PAYLOAD_VERSION_CHROMEOS)
+    expected_out = """Payload version:             1
 Manifest length:             222
-Number of partitions:        2
-  Number of "root" ops:      1
-  Number of "kernel" ops:    1
+Number of operations:        1
+Number of kernel ops:        1
 Block size:                  4096
 Minor version:               4
 """
     self.TestCommand(payload_cmd, payload, expected_out)
 
+  def testListOpsOnVersion1(self):
+    """Verify that the --list_ops option gives the correct output."""
+    payload_cmd = payload_info.PayloadCommand(
+        FakeOption(list_ops=True, action='show'))
+    payload = FakePayload(payload_info.MAJOR_PAYLOAD_VERSION_CHROMEOS)
+    expected_out = """Payload version:             1
+Manifest length:             222
+Number of operations:        1
+Number of kernel ops:        1
+Block size:                  4096
+Minor version:               4
+
+Install operations:
+  0: REPLACE_BZ
+    Data offset: 1
+    Data length: 1
+    Destination: 2 extents (3 blocks)
+      (1,1) (2,2)
+Kernel install operations:
+  0: SOURCE_COPY
+    Source: 1 extent (1 block)
+      (1,1)
+    Destination: 20 extents (190 blocks)
+      (0,0) (1,1) (2,2) (3,3) (4,4) (5,5) (6,6) (7,7) (8,8) (9,9) (10,10)
+      (11,11) (12,12) (13,13) (14,14) (15,15) (16,16) (17,17) (18,18) (19,19)
+"""
+    self.TestCommand(payload_cmd, payload, expected_out)
+
   def testListOpsOnVersion2(self):
     """Verify that the --list_ops option gives the correct output."""
     payload_cmd = payload_info.PayloadCommand(
         FakeOption(list_ops=True, action='show'))
-    payload = FakePayload()
+    payload = FakePayload(payload_info.MAJOR_PAYLOAD_VERSION_BRILLO)
     expected_out = """Payload version:             2
 Manifest length:             222
 Number of partitions:        2
@@ -262,11 +270,28 @@
 """
     self.TestCommand(payload_cmd, payload, expected_out)
 
+  def testStatsOnVersion1(self):
+    """Verify that the --stats option works correctly."""
+    payload_cmd = payload_info.PayloadCommand(
+        FakeOption(stats=True, action='show'))
+    payload = FakePayload(payload_info.MAJOR_PAYLOAD_VERSION_CHROMEOS)
+    expected_out = """Payload version:             1
+Manifest length:             222
+Number of operations:        1
+Number of kernel ops:        1
+Block size:                  4096
+Minor version:               4
+Blocks read:                 11
+Blocks written:              193
+Seeks when writing:          18
+"""
+    self.TestCommand(payload_cmd, payload, expected_out)
+
   def testStatsOnVersion2(self):
     """Verify that the --stats option works correctly on version 2."""
     payload_cmd = payload_info.PayloadCommand(
         FakeOption(stats=True, action='show'))
-    payload = FakePayload()
+    payload = FakePayload(payload_info.MAJOR_PAYLOAD_VERSION_BRILLO)
     expected_out = """Payload version:             2
 Manifest length:             222
 Number of partitions:        2
@@ -284,12 +309,11 @@
     """Verify that the --signatures option works with unsigned payloads."""
     payload_cmd = payload_info.PayloadCommand(
         FakeOption(action='show', signatures=True))
-    payload = FakePayload()
-    expected_out = """Payload version:             2
+    payload = FakePayload(payload_info.MAJOR_PAYLOAD_VERSION_CHROMEOS)
+    expected_out = """Payload version:             1
 Manifest length:             222
-Number of partitions:        2
-  Number of "root" ops:      1
-  Number of "kernel" ops:    1
+Number of operations:        1
+Number of kernel ops:        1
 Block size:                  4096
 Minor version:               4
 No metadata signatures stored in the payload
@@ -301,11 +325,11 @@
     """Verify that the --signatures option shows the present signatures."""
     payload_cmd = payload_info.PayloadCommand(
         FakeOption(action='show', signatures=True))
-    payload = FakePayload()
+    payload = FakePayload(payload_info.MAJOR_PAYLOAD_VERSION_BRILLO)
     payload.AddPayloadSignature(version=1,
-                                data=b'12345678abcdefgh\x00\x01\x02\x03')
-    payload.AddPayloadSignature(data=b'I am a signature so access is yes.')
-    payload.AddMetadataSignature(data=b'\x00\x0a\x0c')
+                                data='12345678abcdefgh\x00\x01\x02\x03')
+    payload.AddPayloadSignature(data='I am a signature so access is yes.')
+    payload.AddMetadataSignature(data='\x00\x0a\x0c')
     expected_out = """Payload version:             2
 Manifest length:             222
 Number of partitions:        2
@@ -329,6 +353,5 @@
 """
     self.TestCommand(payload_cmd, payload, expected_out)
 
-
 if __name__ == '__main__':
   unittest.main()
diff --git a/scripts/run_unittests b/scripts/run_unittests
index db5ed73..0d301ba 100755
--- a/scripts/run_unittests
+++ b/scripts/run_unittests
@@ -26,6 +26,5 @@
 done
 
 ./payload_info_unittest.py
-./paycheck_unittest.py
 
 exit 0
diff --git a/scripts/simulate_ota.py b/scripts/simulate_ota.py
deleted file mode 100644
index 6349979..0000000
--- a/scripts/simulate_ota.py
+++ /dev/null
@@ -1,147 +0,0 @@
-#
-# 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.
-#
-
-"""Tools for running host side simulation of an OTA update."""
-
-
-from __future__ import print_function
-
-import argparse
-import filecmp
-import os
-import shutil
-import subprocess
-import sys
-import tempfile
-import zipfile
-
-import update_payload
-
-
-def extract_file(zip_file_path, entry_name, target_file_path):
-  """Extract a file from zip archive into |target_file_path|"""
-  with open(target_file_path, 'wb') as out_fp:
-    if isinstance(zip_file_path, zipfile.ZipFile):
-      with zip_file_path.open(entry_name) as fp:
-        shutil.copyfileobj(fp, out_fp)
-    elif os.path.isdir(zip_file_path):
-      with open(os.path.join(zip_file_path, entry_name), "rb") as fp:
-        shutil.copyfileobj(fp, out_fp)
-
-def is_sparse_image(filepath):
-  with open(filepath, 'rb') as fp:
-    # Magic for android sparse image format
-    # https://source.android.com/devices/bootloader/images
-    return fp.read(4) == b'\x3A\xFF\x26\xED'
-
-def extract_img(zip_archive, img_name, output_path):
-  entry_name = "IMAGES/" + img_name + ".img"
-  extract_file(zip_archive, entry_name, output_path)
-  if is_sparse_image(output_path):
-    raw_img_path = output_path + ".raw"
-    subprocess.check_output(["simg2img", output_path, raw_img_path])
-    os.rename(raw_img_path, output_path)
-
-def run_ota(source, target, payload_path, tempdir):
-  """Run an OTA on host side"""
-  payload = update_payload.Payload(payload_path)
-  payload.Init()
-  if zipfile.is_zipfile(source):
-    source = zipfile.ZipFile(source)
-  if zipfile.is_zipfile(target):
-    target = zipfile.ZipFile(target)
-
-  old_partitions = []
-  new_partitions = []
-  expected_new_partitions = []
-  for part in payload.manifest.partitions:
-    name = part.partition_name
-    old_image = os.path.join(tempdir, "source_" + name + ".img")
-    new_image = os.path.join(tempdir, "target_" + name + ".img")
-    print("Extracting source image for", name)
-    extract_img(source, name, old_image)
-    print("Extracting target image for", name)
-    extract_img(target, name, new_image)
-
-    old_partitions.append(old_image)
-    scratch_image_name = new_image + ".actual"
-    new_partitions.append(scratch_image_name)
-    with open(scratch_image_name, "wb") as fp:
-      fp.truncate(part.new_partition_info.size)
-    expected_new_partitions.append(new_image)
-
-  delta_generator_args = ["delta_generator", "--in_file=" + payload_path]
-  partition_names = [
-      part.partition_name for part in payload.manifest.partitions
-  ]
-  delta_generator_args.append("--partition_names=" + ":".join(partition_names))
-  delta_generator_args.append("--old_partitions=" + ":".join(old_partitions))
-  delta_generator_args.append("--new_partitions=" + ":".join(new_partitions))
-
-  subprocess.check_output(delta_generator_args)
-
-  valid = True
-  for (expected_part, actual_part, part_name) in \
-      zip(expected_new_partitions, new_partitions, partition_names):
-    if filecmp.cmp(expected_part, actual_part):
-      print("Partition `{}` is valid".format(part_name))
-    else:
-      valid = False
-      print(
-          "Partition `{}` is INVALID expected image: {} actual image: {}"
-          .format(part_name, expected_part, actual_part))
-
-  if not valid and sys.stdout.isatty():
-    input("Paused to investigate invalid partitions, press any key to exit.")
-
-
-def main():
-  parser = argparse.ArgumentParser(
-      description="Run host side simulation of OTA package")
-  parser.add_argument(
-      "--source",
-      help="Target file zip for the source build",
-      required=True, nargs=1)
-  parser.add_argument(
-      "--target",
-      help="Target file zip for the target build",
-      required=True, nargs=1)
-  parser.add_argument(
-      "payload",
-      help="payload.bin for the OTA package, or a zip of OTA package itself",
-      nargs=1)
-  args = parser.parse_args()
-  print(args)
-
-  assert os.path.exists(args.source[0]), \
-    "source target file must point to a valid zipfile or directory"
-  assert os.path.exists(args.target[0]), \
-    "target path must point to a valid zipfile or directory"
-
-  # pylint: disable=no-member
-  with tempfile.TemporaryDirectory() as tempdir:
-    payload_path = args.payload[0]
-    if zipfile.is_zipfile(payload_path):
-      with zipfile.ZipFile(payload_path, "r") as zfp:
-        payload_entry_name = 'payload.bin'
-        zfp.extract(payload_entry_name, tempdir)
-        payload_path = os.path.join(tempdir, payload_entry_name)
-
-    run_ota(args.source[0], args.target[0], payload_path, tempdir)
-
-
-if __name__ == '__main__':
-  main()
diff --git a/scripts/test_paycheck.sh b/scripts/test_paycheck.sh
new file mode 100755
index 0000000..239b984
--- /dev/null
+++ b/scripts/test_paycheck.sh
@@ -0,0 +1,169 @@
+#!/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 f672cda..49f766d 100755
--- a/scripts/update_device.py
+++ b/scripts/update_device.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/python2
 #
 # Copyright (C) 2017 The Android Open Source Project
 #
@@ -17,25 +17,18 @@
 
 """Send an A/B update to an Android device over adb."""
 
-from __future__ import print_function
-from __future__ import absolute_import
-
 import argparse
-import binascii
+import BaseHTTPServer
 import hashlib
 import logging
 import os
 import socket
 import subprocess
 import sys
-import struct
-import tempfile
 import threading
 import xml.etree.ElementTree
 import zipfile
 
-from six.moves import BaseHTTPServer
-
 import update_payload.payload
 
 
@@ -48,7 +41,6 @@
 # The port on the device that update_engine should connect to.
 DEVICE_PORT = 1234
 
-
 def CopyFileObjLength(fsrc, fdst, buffer_size=128 * 1024, copy_length=None):
   """Copy from a file object to another.
 
@@ -93,7 +85,6 @@
   OTA_PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
   SECONDARY_OTA_PAYLOAD_BIN = 'secondary/payload.bin'
   SECONDARY_OTA_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
-  PAYLOAD_MAGIC_HEADER = b'CrAU'
 
   def __init__(self, otafilename, secondary_payload=False):
     self.otafilename = otafilename
@@ -102,34 +93,10 @@
     payload_entry = (self.SECONDARY_OTA_PAYLOAD_BIN if secondary_payload else
                      self.OTA_PAYLOAD_BIN)
     payload_info = otazip.getinfo(payload_entry)
-
-    if payload_info.compress_type != 0:
-      logging.error(
-          "Expected payload to be uncompressed, got compression method %d",
-          payload_info.compress_type)
-    # Don't use len(payload_info.extra). Because that returns size of extra
-    # fields in central directory. We need to look at local file directory,
-    # as these two might have different sizes.
-    with open(otafilename, "rb") as fp:
-      fp.seek(payload_info.header_offset)
-      data = fp.read(zipfile.sizeFileHeader)
-      fheader = struct.unpack(zipfile.structFileHeader, data)
-      # Last two fields of local file header are filename length and
-      # extra length
-      filename_len = fheader[-2]
-      extra_len = fheader[-1]
-      self.offset = payload_info.header_offset
-      self.offset += zipfile.sizeFileHeader
-      self.offset += filename_len + extra_len
-      self.size = payload_info.file_size
-      fp.seek(self.offset)
-      payload_header = fp.read(4)
-      if payload_header != self.PAYLOAD_MAGIC_HEADER:
-        logging.warning(
-            "Invalid header, expected %s, got %s."
-            "Either the offset is not correct, or payload is corrupted",
-            binascii.hexlify(self.PAYLOAD_MAGIC_HEADER),
-            binascii.hexlify(payload_header))
+    self.offset = payload_info.header_offset
+    self.offset += zipfile.sizeFileHeader
+    self.offset += len(payload_info.extra) + len(payload_info.filename)
+    self.size = payload_info.file_size
 
     property_entry = (self.SECONDARY_OTA_PAYLOAD_PROPERTIES_TXT if
                       secondary_payload else self.OTA_PAYLOAD_PROPERTIES_TXT)
@@ -170,6 +137,7 @@
           start_range = file_size - int(e)
     return start_range, end_range
 
+
   def do_GET(self):  # pylint: disable=invalid-name
     """Reply with the requested payload file."""
     if self.path != '/payload':
@@ -212,6 +180,7 @@
     f.seek(serving_start + start_range)
     CopyFileObjLength(f, self.wfile, copy_length=end_range - start_range)
 
+
   def do_POST(self):  # pylint: disable=invalid-name
     """Reply with the omaha response xml."""
     if self.path != '/update':
@@ -307,7 +276,6 @@
     logging.info('Server Terminated')
 
   def StopServer(self):
-    self._httpd.shutdown()
     self._httpd.socket.close()
 
 
@@ -321,13 +289,13 @@
   """Return the command to run to start the update in the Android device."""
   ota = AndroidOTAPackage(ota_filename, secondary)
   headers = ota.properties
-  headers += b'USER_AGENT=Dalvik (something, something)\n'
-  headers += b'NETWORK_ID=0\n'
-  headers += extra_headers.encode()
+  headers += 'USER_AGENT=Dalvik (something, something)\n'
+  headers += 'NETWORK_ID=0\n'
+  headers += extra_headers
 
   return ['update_engine_client', '--update', '--follow',
           '--payload=%s' % payload_url, '--offset=%d' % ota.offset,
-          '--size=%d' % ota.size, '--headers="%s"' % headers.decode()]
+          '--size=%d' % ota.size, '--headers="%s"' % headers]
 
 
 def OmahaUpdateCommand(omaha_url):
@@ -350,7 +318,7 @@
     if self._device_serial:
       self._command_prefix += ['-s', self._device_serial]
 
-  def adb(self, command, timeout_seconds: float = None):
+  def adb(self, command):
     """Run an ADB command like "adb push".
 
     Args:
@@ -365,7 +333,7 @@
     command = self._command_prefix + command
     logging.info('Running: %s', ' '.join(str(x) for x in command))
     p = subprocess.Popen(command, universal_newlines=True)
-    p.wait(timeout_seconds)
+    p.wait()
     return p.returncode
 
   def adb_output(self, command):
@@ -385,28 +353,6 @@
     return subprocess.check_output(command, universal_newlines=True)
 
 
-def PushMetadata(dut, otafile, metadata_path):
-  payload = update_payload.Payload(otafile)
-  payload.Init()
-  with tempfile.TemporaryDirectory() as tmpdir:
-    with zipfile.ZipFile(otafile, "r") as zfp:
-      extracted_path = os.path.join(tmpdir, "payload.bin")
-      with zfp.open("payload.bin") as payload_fp, \
-              open(extracted_path, "wb") as output_fp:
-          # Only extract the first |data_offset| bytes from the payload.
-          # This is because allocateSpaceForPayload only needs to see
-          # the manifest, not the entire payload.
-          # Extracting the entire payload works, but is slow for full
-          # OTA.
-        output_fp.write(payload_fp.read(payload.data_offset))
-
-      return dut.adb([
-          "push",
-          extracted_path,
-          metadata_path
-      ]) == 0
-
-
 def main():
   parser = argparse.ArgumentParser(description='Android A/B OTA helper.')
   parser.add_argument('otafile', metavar='PAYLOAD', type=str,
@@ -426,17 +372,6 @@
                       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.')
-  parser.add_argument('--no-postinstall', action='store_true',
-                      help='Do not execute postinstall scripts after the update.')
-  parser.add_argument('--allocate-only', action='store_true',
-                      help='Allocate space for this OTA, instead of actually \
-                        applying the OTA.')
-  parser.add_argument('--verify-only', action='store_true',
-                      help='Verify metadata then exit, instead of applying the OTA.')
-  parser.add_argument('--no-care-map', action='store_true',
-                      help='Do not push care_map.pb to device.')
   args = parser.parse_args()
   logging.basicConfig(
       level=logging.WARNING if args.no_verbose else logging.INFO)
@@ -454,40 +389,6 @@
   help_cmd = ['shell', 'su', '0', 'update_engine_client', '--help']
   use_omaha = 'omaha' in dut.adb_output(help_cmd)
 
-  metadata_path = "/data/ota_package/metadata"
-  if args.allocate_only:
-    if PushMetadata(dut, args.otafile, metadata_path):
-      dut.adb([
-          "shell", "update_engine_client", "--allocate",
-          "--metadata={}".format(metadata_path)])
-    # Return 0, as we are executing ADB commands here, no work needed after
-    # this point
-    return 0
-  if args.verify_only:
-    if PushMetadata(dut, args.otafile, metadata_path):
-      dut.adb([
-          "shell", "update_engine_client", "--verify",
-          "--metadata={}".format(metadata_path)])
-    # Return 0, as we are executing ADB commands here, no work needed after
-    # this point
-    return 0
-
-  if args.no_slot_switch:
-    args.extra_headers += "\nSWITCH_SLOT_ON_REBOOT=0"
-  if args.no_postinstall:
-    args.extra_headers += "\nRUN_POST_INSTALL=0"
-
-  with zipfile.ZipFile(args.otafile) as zfp:
-    CARE_MAP_ENTRY_NAME = "care_map.pb"
-    if CARE_MAP_ENTRY_NAME in zfp.namelist() and not args.no_care_map:
-      # Need root permission to push to /data
-      dut.adb(["root"])
-      with tempfile.NamedTemporaryFile() as care_map_fp:
-        care_map_fp.write(zfp.read(CARE_MAP_ENTRY_NAME))
-        care_map_fp.flush()
-        dut.adb(["push", care_map_fp.name,
-                "/data/ota_package/" + CARE_MAP_ENTRY_NAME])
-
   if args.file:
     # Update via pushing a file to /data.
     device_ota_file = os.path.join(OTA_PACKAGE_PATH, 'debug.zip')
@@ -546,10 +447,9 @@
     if server_thread:
       server_thread.StopServer()
     for cmd in finalize_cmds:
-      dut.adb(cmd, 5)
+      dut.adb(cmd)
 
   return 0
 
-
 if __name__ == '__main__':
   sys.exit(main())
diff --git a/scripts/update_payload/__init__.py b/scripts/update_payload/__init__.py
index 6e77678..8ee95e2 100644
--- a/scripts/update_payload/__init__.py
+++ b/scripts/update_payload/__init__.py
@@ -17,8 +17,6 @@
 """Library for processing, verifying and applying Chrome OS update payloads."""
 
 # Just raise the interface classes to the root namespace.
-from __future__ import absolute_import
-
 from update_payload.checker import CHECKS_TO_DISABLE
 from update_payload.error import PayloadError
 from update_payload.payload import Payload
diff --git a/scripts/update_payload/applier.py b/scripts/update_payload/applier.py
index 29ccb8e..21d8e87 100644
--- a/scripts/update_payload/applier.py
+++ b/scripts/update_payload/applier.py
@@ -24,12 +24,12 @@
 
 """
 
-from __future__ import absolute_import
 from __future__ import print_function
 
 import array
 import bz2
 import hashlib
+import itertools
 # Not everywhere we can have the lzma library so we ignore it if we didn't have
 # it because it is not going to be used. For example, 'cros flash' uses
 # devserver code which eventually loads this file, but the lzma library is not
@@ -45,6 +45,7 @@
   except ImportError:
     pass
 import os
+import shutil
 import subprocess
 import sys
 import tempfile
@@ -52,6 +53,7 @@
 from update_payload import common
 from update_payload.error import PayloadError
 
+
 #
 # Helper functions.
 #
@@ -70,7 +72,7 @@
   """
   hasher = hashlib.sha256()
   block_length = 1024 * 1024
-  max_length = length if length >= 0 else sys.maxsize
+  max_length = length if length >= 0 else sys.maxint
 
   while max_length > 0:
     read_length = min(max_length, block_length)
@@ -106,16 +108,20 @@
   Returns:
     A character array containing the concatenated read data.
   """
-  data = array.array('B')
+  data = array.array('c')
   if max_length < 0:
-    max_length = sys.maxsize
+    max_length = sys.maxint
   for ex in extents:
     if max_length == 0:
       break
     read_length = min(max_length, ex.num_blocks * block_size)
 
-    file_obj.seek(ex.start_block * block_size)
-    data.fromfile(file_obj, read_length)
+    # Fill with zeros or read from file, depending on the type of extent.
+    if ex.start_block == common.PSEUDO_EXTENT_MARKER:
+      data.extend(itertools.repeat('\0', read_length))
+    else:
+      file_obj.seek(ex.start_block * block_size)
+      data.fromfile(file_obj, read_length)
 
     max_length -= read_length
 
@@ -143,8 +149,12 @@
     if not data_length:
       raise PayloadError('%s: more write extents than data' % ex_name)
     write_length = min(data_length, ex.num_blocks * block_size)
-    file_obj.seek(ex.start_block * block_size)
-    file_obj.write(data[data_offset:(data_offset + write_length)])
+
+    # Only do actual writing if this is not a pseudo-extent.
+    if ex.start_block != common.PSEUDO_EXTENT_MARKER:
+      file_obj.seek(ex.start_block * block_size)
+      data_view = buffer(data, data_offset, write_length)
+      file_obj.write(data_view)
 
     data_offset += write_length
     data_length -= write_length
@@ -174,17 +184,20 @@
   arg = ''
   pad_off = pad_len = 0
   if data_length < 0:
-    data_length = sys.maxsize
+    data_length = sys.maxint
   for ex, ex_name in common.ExtentIter(extents, base_name):
     if not data_length:
       raise PayloadError('%s: more extents than total data length' % ex_name)
 
-    start_byte = ex.start_block * block_size
+    is_pseudo = ex.start_block == common.PSEUDO_EXTENT_MARKER
+    start_byte = -1 if is_pseudo else ex.start_block * block_size
     num_bytes = ex.num_blocks * block_size
     if data_length < num_bytes:
       # We're only padding a real extent.
-      pad_off = start_byte + data_length
-      pad_len = num_bytes - data_length
+      if not is_pseudo:
+        pad_off = start_byte + data_length
+        pad_len = num_bytes - data_length
+
       num_bytes = data_length
 
     arg += '%s%d:%d' % (arg and ',', start_byte, num_bytes)
@@ -261,28 +274,30 @@
       num_blocks = ex.num_blocks
       count = num_blocks * block_size
 
-      data_end = data_start + count
+      # Make sure it's not a fake (signature) operation.
+      if start_block != common.PSEUDO_EXTENT_MARKER:
+        data_end = data_start + count
 
-      # Make sure we're not running past partition boundary.
-      if (start_block + num_blocks) * block_size > part_size:
-        raise PayloadError(
-            '%s: extent (%s) exceeds partition size (%d)' %
-            (ex_name, common.FormatExtent(ex, block_size),
-             part_size))
+        # Make sure we're not running past partition boundary.
+        if (start_block + num_blocks) * block_size > part_size:
+          raise PayloadError(
+              '%s: extent (%s) exceeds partition size (%d)' %
+              (ex_name, common.FormatExtent(ex, block_size),
+               part_size))
 
-      # Make sure that we have enough data to write.
-      if data_end >= data_length + block_size:
-        raise PayloadError(
-            '%s: more dst blocks than data (even with padding)')
+        # Make sure that we have enough data to write.
+        if data_end >= data_length + block_size:
+          raise PayloadError(
+              '%s: more dst blocks than data (even with padding)')
 
-      # Pad with zeros if necessary.
-      if data_end > data_length:
-        padding = data_end - data_length
-        out_data += b'\0' * padding
+        # Pad with zeros if necessary.
+        if data_end > data_length:
+          padding = data_end - data_length
+          out_data += '\0' * padding
 
-      self.payload.payload_file.seek(start_block * block_size)
-      part_file.seek(start_block * block_size)
-      part_file.write(out_data[data_start:data_end])
+        self.payload.payload_file.seek(start_block * block_size)
+        part_file.seek(start_block * block_size)
+        part_file.write(out_data[data_start:data_end])
 
       data_start += count
 
@@ -291,6 +306,30 @@
       raise PayloadError('%s: wrote fewer bytes (%d) than expected (%d)' %
                          (op_name, data_start, data_length))
 
+  def _ApplyMoveOperation(self, op, op_name, part_file):
+    """Applies a MOVE operation.
+
+    Note that this operation must read the whole block data from the input and
+    only then dump it, due to our in-place update semantics; otherwise, it
+    might clobber data midway through.
+
+    Args:
+      op: the operation object
+      op_name: name string for error reporting
+      part_file: the partition file object
+
+    Raises:
+      PayloadError if something goes wrong.
+    """
+    block_size = self.block_size
+
+    # Gather input raw data from src extents.
+    in_data = _ReadExtents(part_file, op.src_extents, block_size)
+
+    # Dump extracted data to dst extents.
+    _WriteExtents(part_file, in_data, op.dst_extents, block_size,
+                  '%s.dst_extents' % op_name)
+
   def _ApplyZeroOperation(self, op, op_name, part_file):
     """Applies a ZERO operation.
 
@@ -308,8 +347,10 @@
     # Iterate over the extents and write zero.
     # pylint: disable=unused-variable
     for ex, ex_name in common.ExtentIter(op.dst_extents, base_name):
-      part_file.seek(ex.start_block * block_size)
-      part_file.write(b'\0' * (ex.num_blocks * block_size))
+      # Only do actual writing if this is not a pseudo-extent.
+      if ex.start_block != common.PSEUDO_EXTENT_MARKER:
+        part_file.seek(ex.start_block * block_size)
+        part_file.write('\0' * (ex.num_blocks * block_size))
 
   def _ApplySourceCopyOperation(self, op, op_name, old_part_file,
                                 new_part_file):
@@ -398,19 +439,12 @@
       # Diff from source partition.
       old_file_name = '/dev/fd/%d' % old_part_file.fileno()
 
-      # In python3, file descriptors(fd) are not passed to child processes by
-      # default. To pass the fds to the child processes, we need to set the flag
-      # 'inheritable' in the fds and make the subprocess calls with the argument
-      # close_fds set to False.
-      if sys.version_info.major >= 3:
-        os.set_inheritable(new_part_file.fileno(), True)
-        os.set_inheritable(old_part_file.fileno(), True)
-
-      if op.type in (common.OpType.SOURCE_BSDIFF, common.OpType.BROTLI_BSDIFF):
+      if op.type in (common.OpType.BSDIFF, common.OpType.SOURCE_BSDIFF,
+                     common.OpType.BROTLI_BSDIFF):
         # Invoke bspatch on partition file with extents args.
         bspatch_cmd = [self.bspatch_path, old_file_name, new_file_name,
                        patch_file_name, in_extents_arg, out_extents_arg]
-        subprocess.check_call(bspatch_cmd, close_fds=False)
+        subprocess.check_call(bspatch_cmd)
       elif op.type == common.OpType.PUFFDIFF:
         # Invoke puffpatch on partition file with extents args.
         puffpatch_cmd = [self.puffpatch_path,
@@ -420,14 +454,14 @@
                          "--patch_file=%s" % patch_file_name,
                          "--src_extents=%s" % in_extents_arg,
                          "--dst_extents=%s" % out_extents_arg]
-        subprocess.check_call(puffpatch_cmd, close_fds=False)
+        subprocess.check_call(puffpatch_cmd)
       else:
-        raise PayloadError("Unknown operation %s" % op.type)
+        raise PayloadError("Unknown operation %s", op.type)
 
       # Pad with zeros past the total output length.
       if pad_len:
         new_part_file.seek(pad_off)
-        new_part_file.write(b'\0' * pad_len)
+        new_part_file.write('\0' * pad_len)
     else:
       # Gather input raw data and write to a temp file.
       input_part_file = old_part_file if old_part_file else new_part_file
@@ -443,7 +477,8 @@
       with tempfile.NamedTemporaryFile(delete=False) as out_file:
         out_file_name = out_file.name
 
-      if op.type in (common.OpType.SOURCE_BSDIFF, common.OpType.BROTLI_BSDIFF):
+      if op.type in (common.OpType.BSDIFF, common.OpType.SOURCE_BSDIFF,
+                     common.OpType.BROTLI_BSDIFF):
         # Invoke bspatch.
         bspatch_cmd = [self.bspatch_path, in_file_name, out_file_name,
                        patch_file_name]
@@ -457,7 +492,7 @@
                          "--patch_file=%s" % patch_file_name]
         subprocess.check_call(puffpatch_cmd)
       else:
-        raise PayloadError("Unknown operation %s" % op.type)
+        raise PayloadError("Unknown operation %s", op.type)
 
       # Read output.
       with open(out_file_name, 'rb') as out_file:
@@ -470,7 +505,7 @@
       # Write output back to partition, with padding.
       unaligned_out_len = len(out_data) % block_size
       if unaligned_out_len:
-        out_data += b'\0' * (block_size - unaligned_out_len)
+        out_data += '\0' * (block_size - unaligned_out_len)
       _WriteExtents(new_part_file, out_data, op.dst_extents, block_size,
                     '%s.dst_extents' % op_name)
 
@@ -485,6 +520,10 @@
                        new_part_file, part_size):
     """Applies a sequence of update operations to a partition.
 
+    This assumes an in-place update semantics for MOVE and BSDIFF, namely all
+    reads are performed first, then the data is processed and written back to
+    the same file.
+
     Args:
       operations: the sequence of operations
       base_name: the name of the operation sequence
@@ -502,8 +541,13 @@
       if op.type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ,
                      common.OpType.REPLACE_XZ):
         self._ApplyReplaceOperation(op, op_name, data, new_part_file, part_size)
+      elif op.type == common.OpType.MOVE:
+        self._ApplyMoveOperation(op, op_name, new_part_file)
       elif op.type == common.OpType.ZERO:
         self._ApplyZeroOperation(op, op_name, new_part_file)
+      elif op.type == common.OpType.BSDIFF:
+        self._ApplyDiffOperation(op, op_name, data, new_part_file,
+                                 new_part_file)
       elif op.type == common.OpType.SOURCE_COPY:
         self._ApplySourceCopyOperation(op, op_name, old_part_file,
                                        new_part_file)
@@ -539,8 +583,15 @@
         _VerifySha256(old_part_file, old_part_info.hash,
                       'old ' + part_name, length=old_part_info.size)
       new_part_file_mode = 'r+b'
-      open(new_part_file_name, 'w').close()
-
+      if self.minor_version == common.INPLACE_MINOR_PAYLOAD_VERSION:
+        # Copy the src partition to the dst one; make sure we don't truncate it.
+        shutil.copyfile(old_part_file_name, new_part_file_name)
+      elif self.minor_version >= common.SOURCE_MINOR_PAYLOAD_VERSION:
+        # In minor version >= 2, we don't want to copy the partitions, so
+        # instead just make the new partition file.
+        open(new_part_file_name, 'w').close()
+      else:
+        raise PayloadError("Unknown minor version: %d" % self.minor_version)
     else:
       # We need to create/truncate the dst partition file.
       new_part_file_mode = 'w+b'
@@ -588,11 +639,20 @@
     install_operations = []
 
     manifest = self.payload.manifest
-    for part in manifest.partitions:
-      name = part.partition_name
-      new_part_info[name] = part.new_partition_info
-      old_part_info[name] = part.old_partition_info
-      install_operations.append((name, part.operations))
+    if self.payload.header.version == 1:
+      for real_name, proto_name in common.CROS_PARTITIONS:
+        new_part_info[real_name] = getattr(manifest, 'new_%s_info' % proto_name)
+        old_part_info[real_name] = getattr(manifest, 'old_%s_info' % proto_name)
+
+      install_operations.append((common.ROOTFS, manifest.install_operations))
+      install_operations.append((common.KERNEL,
+                                 manifest.kernel_install_operations))
+    else:
+      for part in manifest.partitions:
+        name = part.partition_name
+        new_part_info[name] = part.new_partition_info
+        old_part_info[name] = part.old_partition_info
+        install_operations.append((name, part.operations))
 
     part_names = set(new_part_info.keys())  # Equivalently, old_part_info.keys()
 
diff --git a/scripts/update_payload/checker.py b/scripts/update_payload/checker.py
index 56a9370..6d17fbe 100644
--- a/scripts/update_payload/checker.py
+++ b/scripts/update_payload/checker.py
@@ -24,7 +24,6 @@
   checker.Run(...)
 """
 
-from __future__ import absolute_import
 from __future__ import print_function
 
 import array
@@ -35,22 +34,22 @@
 import os
 import subprocess
 
-# pylint: disable=redefined-builtin
-from six.moves import range
-
 from update_payload import common
 from update_payload import error
 from update_payload import format_utils
 from update_payload import histogram
 from update_payload import update_metadata_pb2
 
+
 #
 # Constants.
 #
 
+_CHECK_DST_PSEUDO_EXTENTS = 'dst-pseudo-extents'
 _CHECK_MOVE_SAME_SRC_DST_BLOCK = 'move-same-src-dst-block'
 _CHECK_PAYLOAD_SIG = 'payload-sig'
 CHECKS_TO_DISABLE = (
+    _CHECK_DST_PSEUDO_EXTENTS,
     _CHECK_MOVE_SAME_SRC_DST_BLOCK,
     _CHECK_PAYLOAD_SIG,
 )
@@ -67,14 +66,15 @@
 # Supported minor version map to payload types allowed to be using them.
 _SUPPORTED_MINOR_VERSIONS = {
     0: (_TYPE_FULL,),
+    1: (_TYPE_DELTA,),
     2: (_TYPE_DELTA,),
     3: (_TYPE_DELTA,),
     4: (_TYPE_DELTA,),
     5: (_TYPE_DELTA,),
     6: (_TYPE_DELTA,),
-    7: (_TYPE_DELTA,),
 }
 
+_OLD_DELTA_USABLE_PART_SIZE = 2 * 1024 * 1024 * 1024
 
 #
 # Helper functions.
@@ -323,6 +323,8 @@
     self.allow_unhashed = allow_unhashed
 
     # Disable specific tests.
+    self.check_dst_pseudo_extents = (
+        _CHECK_DST_PSEUDO_EXTENTS not in disabled_tests)
     self.check_move_same_src_dst_block = (
         _CHECK_MOVE_SAME_SRC_DST_BLOCK not in disabled_tests)
     self.check_payload_sig = _CHECK_PAYLOAD_SIG not in disabled_tests
@@ -607,7 +609,7 @@
     """
     self.major_version = self.payload.header.version
 
-    part_sizes = part_sizes or collections.defaultdict(int)
+    part_sizes = collections.defaultdict(int, part_sizes)
     manifest = self.payload.manifest
     report.AddSection('manifest')
 
@@ -626,23 +628,35 @@
     self._CheckPresentIff(self.sigs_offset, self.sigs_size,
                           'signatures_offset', 'signatures_size', 'manifest')
 
-    for part in manifest.partitions:
-      name = part.partition_name
-      self.old_part_info[name] = self._CheckOptionalSubMsg(
-          part, 'old_partition_info', report)
-      self.new_part_info[name] = self._CheckMandatorySubMsg(
-          part, 'new_partition_info', report, 'manifest.partitions')
+    if self.major_version == common.CHROMEOS_MAJOR_PAYLOAD_VERSION:
+      for real_name, proto_name in common.CROS_PARTITIONS:
+        self.old_part_info[real_name] = self._CheckOptionalSubMsg(
+            manifest, 'old_%s_info' % proto_name, report)
+        self.new_part_info[real_name] = self._CheckMandatorySubMsg(
+            manifest, 'new_%s_info' % proto_name, report, 'manifest')
 
-    # Check: Old-style partition infos should not be specified.
-    for _, part in common.CROS_PARTITIONS:
-      self._CheckElemNotPresent(manifest, 'old_%s_info' % part, 'manifest')
-      self._CheckElemNotPresent(manifest, 'new_%s_info' % part, 'manifest')
+      # Check: old_kernel_info <==> old_rootfs_info.
+      self._CheckPresentIff(self.old_part_info[common.KERNEL].msg,
+                            self.old_part_info[common.ROOTFS].msg,
+                            'old_kernel_info', 'old_rootfs_info', 'manifest')
+    else:
+      for part in manifest.partitions:
+        name = part.partition_name
+        self.old_part_info[name] = self._CheckOptionalSubMsg(
+            part, 'old_partition_info', report)
+        self.new_part_info[name] = self._CheckMandatorySubMsg(
+            part, 'new_partition_info', report, 'manifest.partitions')
 
-    # Check: If old_partition_info is specified anywhere, it must be
-    # specified everywhere.
-    old_part_msgs = [part.msg for part in self.old_part_info.values() if part]
-    self._CheckPresentIffMany(old_part_msgs, 'old_partition_info',
-                              'manifest.partitions')
+      # Check: Old-style partition infos should not be specified.
+      for _, part in common.CROS_PARTITIONS:
+        self._CheckElemNotPresent(manifest, 'old_%s_info' % part, 'manifest')
+        self._CheckElemNotPresent(manifest, 'new_%s_info' % part, 'manifest')
+
+      # Check: If old_partition_info is specified anywhere, it must be
+      # specified everywhere.
+      old_part_msgs = [part.msg for part in self.old_part_info.values() if part]
+      self._CheckPresentIffMany(old_part_msgs, 'old_partition_info',
+                                'manifest.partitions')
 
     is_delta = any(part and part.msg for part in self.old_part_info.values())
     if is_delta:
@@ -652,7 +666,7 @@
             'Apparent full payload contains old_{kernel,rootfs}_info.')
       self.payload_type = _TYPE_DELTA
 
-      for part, (msg, part_report) in self.old_part_info.items():
+      for part, (msg, part_report) in self.old_part_info.iteritems():
         # Check: {size, hash} present in old_{kernel,rootfs}_info.
         field = 'old_%s_info' % part
         self.old_fs_sizes[part] = self._CheckMandatoryField(msg, 'size',
@@ -673,7 +687,7 @@
       self.payload_type = _TYPE_FULL
 
     # Check: new_{kernel,rootfs}_info present; contains {size, hash}.
-    for part, (msg, part_report) in self.new_part_info.items():
+    for part, (msg, part_report) in self.new_part_info.iteritems():
       field = 'new_%s_info' % part
       self.new_fs_sizes[part] = self._CheckMandatoryField(msg, 'size',
                                                           part_report, field)
@@ -710,7 +724,8 @@
     self._CheckBlocksFitLength(length, total_blocks, self.block_size,
                                '%s: %s' % (op_name, length_name))
 
-  def _CheckExtents(self, extents, usable_size, block_counters, name):
+  def _CheckExtents(self, extents, usable_size, block_counters, name,
+                    allow_pseudo=False, allow_signature=False):
     """Checks a sequence of extents.
 
     Args:
@@ -718,6 +733,8 @@
       usable_size: The usable size of the partition to which the extents apply.
       block_counters: Array of counters corresponding to the number of blocks.
       name: The name of the extent block.
+      allow_pseudo: Whether or not pseudo block numbers are allowed.
+      allow_signature: Whether or not the extents are used for a signature.
 
     Returns:
       The total number of blocks in the extents.
@@ -738,15 +755,20 @@
       if num_blocks == 0:
         raise error.PayloadError('%s: extent length is zero.' % ex_name)
 
-      # Check: Make sure we're within the partition limit.
-      if usable_size and end_block * self.block_size > usable_size:
-        raise error.PayloadError(
-            '%s: extent (%s) exceeds usable partition size (%d).' %
-            (ex_name, common.FormatExtent(ex, self.block_size), usable_size))
+      if start_block != common.PSEUDO_EXTENT_MARKER:
+        # Check: Make sure we're within the partition limit.
+        if usable_size and end_block * self.block_size > usable_size:
+          raise error.PayloadError(
+              '%s: extent (%s) exceeds usable partition size (%d).' %
+              (ex_name, common.FormatExtent(ex, self.block_size), usable_size))
 
-      # Record block usage.
-      for i in range(start_block, end_block):
-        block_counters[i] += 1
+        # Record block usage.
+        for i in xrange(start_block, end_block):
+          block_counters[i] += 1
+      elif not (allow_pseudo or (allow_signature and len(extents) == 1)):
+        # Pseudo-extents must be allowed explicitly, or otherwise be part of a
+        # signature operation (in which case there has to be exactly one).
+        raise error.PayloadError('%s: unexpected pseudo-extent.' % ex_name)
 
       total_num_blocks += num_blocks
 
@@ -764,11 +786,6 @@
     Raises:
       error.PayloadError if any check fails.
     """
-    # Check: total_dst_blocks is not a floating point.
-    if isinstance(total_dst_blocks, float):
-      raise error.PayloadError('%s: contains invalid data type of '
-                               'total_dst_blocks.' % op_name)
-
     # Check: Does not contain src extents.
     if op.src_extents:
       raise error.PayloadError('%s: contains src_extents.' % op_name)
@@ -789,6 +806,89 @@
             'space (%d * %d).' %
             (op_name, data_length, total_dst_blocks, self.block_size))
 
+  def _CheckMoveOperation(self, op, data_offset, total_src_blocks,
+                          total_dst_blocks, op_name):
+    """Specific checks for MOVE operations.
+
+    Args:
+      op: The operation object from the manifest.
+      data_offset: The offset of a data blob for the operation.
+      total_src_blocks: Total number of blocks in src_extents.
+      total_dst_blocks: Total number of blocks in dst_extents.
+      op_name: Operation name for error reporting.
+
+    Raises:
+      error.PayloadError if any check fails.
+    """
+    # Check: No data_{offset,length}.
+    if data_offset is not None:
+      raise error.PayloadError('%s: contains data_{offset,length}.' % op_name)
+
+    # Check: total_src_blocks == total_dst_blocks.
+    if total_src_blocks != total_dst_blocks:
+      raise error.PayloadError(
+          '%s: total src blocks (%d) != total dst blocks (%d).' %
+          (op_name, total_src_blocks, total_dst_blocks))
+
+    # Check: For all i, i-th src block index != i-th dst block index.
+    i = 0
+    src_extent_iter = iter(op.src_extents)
+    dst_extent_iter = iter(op.dst_extents)
+    src_extent = dst_extent = None
+    src_idx = src_num = dst_idx = dst_num = 0
+    while i < total_src_blocks:
+      # Get the next source extent, if needed.
+      if not src_extent:
+        try:
+          src_extent = src_extent_iter.next()
+        except StopIteration:
+          raise error.PayloadError('%s: ran out of src extents (%d/%d).' %
+                                   (op_name, i, total_src_blocks))
+        src_idx = src_extent.start_block
+        src_num = src_extent.num_blocks
+
+      # Get the next dest extent, if needed.
+      if not dst_extent:
+        try:
+          dst_extent = dst_extent_iter.next()
+        except StopIteration:
+          raise error.PayloadError('%s: ran out of dst extents (%d/%d).' %
+                                   (op_name, i, total_dst_blocks))
+        dst_idx = dst_extent.start_block
+        dst_num = dst_extent.num_blocks
+
+      # Check: start block is not 0. See crbug/480751; there are still versions
+      # of update_engine which fail when seeking to 0 in PReadAll and PWriteAll,
+      # so we need to fail payloads that try to MOVE to/from block 0.
+      if src_idx == 0 or dst_idx == 0:
+        raise error.PayloadError(
+            '%s: MOVE operation cannot have extent with start block 0' %
+            op_name)
+
+      if self.check_move_same_src_dst_block and src_idx == dst_idx:
+        raise error.PayloadError(
+            '%s: src/dst block number %d is the same (%d).' %
+            (op_name, i, src_idx))
+
+      advance = min(src_num, dst_num)
+      i += advance
+
+      src_idx += advance
+      src_num -= advance
+      if src_num == 0:
+        src_extent = None
+
+      dst_idx += advance
+      dst_num -= advance
+      if dst_num == 0:
+        dst_extent = None
+
+    # Make sure we've exhausted all src/dst extents.
+    if src_extent:
+      raise error.PayloadError('%s: excess src blocks.' % op_name)
+    if dst_extent:
+      raise error.PayloadError('%s: excess dst blocks.' % op_name)
+
   def _CheckZeroOperation(self, op, op_name):
     """Specific checks for ZERO operations.
 
@@ -808,7 +908,7 @@
       raise error.PayloadError('%s: contains data_offset.' % op_name)
 
   def _CheckAnyDiffOperation(self, op, data_length, total_dst_blocks, op_name):
-    """Specific checks for SOURCE_BSDIFF, PUFFDIFF and BROTLI_BSDIFF
+    """Specific checks for BSDIFF, SOURCE_BSDIFF, PUFFDIFF and BROTLI_BSDIFF
        operations.
 
     Args:
@@ -833,7 +933,8 @@
            total_dst_blocks * self.block_size))
 
     # Check the existence of src_length and dst_length for legacy bsdiffs.
-    if op.type == common.OpType.SOURCE_BSDIFF and self.minor_version <= 3:
+    if (op.type == common.OpType.BSDIFF or
+        (op.type == common.OpType.SOURCE_BSDIFF and self.minor_version <= 3)):
       if not op.HasField('src_length') or not op.HasField('dst_length'):
         raise error.PayloadError('%s: require {src,dst}_length.' % op_name)
     else:
@@ -882,19 +983,21 @@
     if self.minor_version >= 3 and op.src_sha256_hash is None:
       raise error.PayloadError('%s: source hash missing.' % op_name)
 
-  def _CheckOperation(self, op, op_name, old_block_counters, new_block_counters,
-                      old_usable_size, new_usable_size, prev_data_offset,
-                      blob_hash_counts):
+  def _CheckOperation(self, op, op_name, is_last, old_block_counters,
+                      new_block_counters, old_usable_size, new_usable_size,
+                      prev_data_offset, allow_signature, blob_hash_counts):
     """Checks a single update operation.
 
     Args:
       op: The operation object.
       op_name: Operation name string for error reporting.
+      is_last: Whether this is the last operation in the sequence.
       old_block_counters: Arrays of block read counters.
       new_block_counters: Arrays of block write counters.
       old_usable_size: The overall usable size for src data in bytes.
       new_usable_size: The overall usable size for dst data in bytes.
       prev_data_offset: Offset of last used data bytes.
+      allow_signature: Whether this may be a signature operation.
       blob_hash_counts: Counters for hashed/unhashed blobs.
 
     Returns:
@@ -906,10 +1009,14 @@
     # Check extents.
     total_src_blocks = self._CheckExtents(
         op.src_extents, old_usable_size, old_block_counters,
-        op_name + '.src_extents')
+        op_name + '.src_extents', allow_pseudo=True)
+    allow_signature_in_extents = (allow_signature and is_last and
+                                  op.type == common.OpType.REPLACE)
     total_dst_blocks = self._CheckExtents(
         op.dst_extents, new_usable_size, new_block_counters,
-        op_name + '.dst_extents')
+        op_name + '.dst_extents',
+        allow_pseudo=(not self.check_dst_pseudo_extents),
+        allow_signature=allow_signature_in_extents)
 
     # Check: data_offset present <==> data_length present.
     data_offset = self._CheckOptionalField(op, 'data_offset', None)
@@ -945,7 +1052,9 @@
             (op_name, common.FormatSha256(op.data_sha256_hash),
              common.FormatSha256(actual_hash.digest())))
     elif data_offset is not None:
-      if self.allow_unhashed:
+      if allow_signature_in_extents:
+        blob_hash_counts['signature'] += 1
+      elif self.allow_unhashed:
         blob_hash_counts['unhashed'] += 1
       else:
         raise error.PayloadError('%s: unhashed operation not allowed.' %
@@ -959,11 +1068,19 @@
             (op_name, data_offset, prev_data_offset))
 
     # Type-specific checks.
-    if op.type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ,
-                   common.OpType.REPLACE_XZ):
+    if op.type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ):
       self._CheckReplaceOperation(op, data_length, total_dst_blocks, op_name)
+    elif (op.type == common.OpType.REPLACE_XZ and
+          (self.minor_version >= 3 or
+           self.major_version >= common.BRILLO_MAJOR_PAYLOAD_VERSION)):
+      self._CheckReplaceOperation(op, data_length, total_dst_blocks, op_name)
+    elif op.type == common.OpType.MOVE and self.minor_version == 1:
+      self._CheckMoveOperation(op, data_offset, total_src_blocks,
+                               total_dst_blocks, op_name)
     elif op.type == common.OpType.ZERO and self.minor_version >= 4:
       self._CheckZeroOperation(op, op_name)
+    elif op.type == common.OpType.BSDIFF and self.minor_version == 1:
+      self._CheckAnyDiffOperation(op, data_length, total_dst_blocks, op_name)
     elif op.type == common.OpType.SOURCE_COPY and self.minor_version >= 2:
       self._CheckSourceCopyOperation(data_offset, total_src_blocks,
                                      total_dst_blocks, op_name)
@@ -985,7 +1102,7 @@
 
   def _SizeToNumBlocks(self, size):
     """Returns the number of blocks needed to contain a given byte size."""
-    return (size + self.block_size - 1) // self.block_size
+    return (size + self.block_size - 1) / self.block_size
 
   def _AllocBlockCounters(self, total_size):
     """Returns a freshly initialized array of block counters.
@@ -1005,7 +1122,7 @@
 
   def _CheckOperations(self, operations, report, base_name, old_fs_size,
                        new_fs_size, old_usable_size, new_usable_size,
-                       prev_data_offset):
+                       prev_data_offset, allow_signature):
     """Checks a sequence of update operations.
 
     Args:
@@ -1017,6 +1134,7 @@
       old_usable_size: The overall usable size of the old partition in bytes.
       new_usable_size: The overall usable size of the new partition in bytes.
       prev_data_offset: Offset of last used data bytes.
+      allow_signature: Whether this sequence may contain signature operations.
 
     Returns:
       The total data blob size used.
@@ -1031,7 +1149,9 @@
         common.OpType.REPLACE: 0,
         common.OpType.REPLACE_BZ: 0,
         common.OpType.REPLACE_XZ: 0,
+        common.OpType.MOVE: 0,
         common.OpType.ZERO: 0,
+        common.OpType.BSDIFF: 0,
         common.OpType.SOURCE_COPY: 0,
         common.OpType.SOURCE_BSDIFF: 0,
         common.OpType.PUFFDIFF: 0,
@@ -1042,6 +1162,8 @@
         common.OpType.REPLACE: 0,
         common.OpType.REPLACE_BZ: 0,
         common.OpType.REPLACE_XZ: 0,
+        # MOVE operations don't have blobs.
+        common.OpType.BSDIFF: 0,
         # SOURCE_COPY operations don't have blobs.
         common.OpType.SOURCE_BSDIFF: 0,
         common.OpType.PUFFDIFF: 0,
@@ -1052,6 +1174,8 @@
         'hashed': 0,
         'unhashed': 0,
     }
+    if allow_signature:
+      blob_hash_counts['signature'] = 0
 
     # Allocate old and new block counters.
     old_block_counters = (self._AllocBlockCounters(old_usable_size)
@@ -1064,14 +1188,16 @@
       op_num += 1
 
       # Check: Type is valid.
-      if op.type not in op_counts:
+      if op.type not in op_counts.keys():
         raise error.PayloadError('%s: invalid type (%d).' % (op_name, op.type))
       op_counts[op.type] += 1
 
+      is_last = op_num == len(operations)
       curr_data_used = self._CheckOperation(
-          op, op_name, old_block_counters, new_block_counters,
+          op, op_name, is_last, old_block_counters, new_block_counters,
           old_usable_size, new_usable_size,
-          prev_data_offset + total_data_used, blob_hash_counts)
+          prev_data_offset + total_data_used, allow_signature,
+          blob_hash_counts)
       if curr_data_used:
         op_blob_totals[op.type] += curr_data_used
         total_data_used += curr_data_used
@@ -1125,17 +1251,21 @@
     if not sigs.signatures:
       raise error.PayloadError('Signature block is empty.')
 
-    # Check that we don't have the signature operation blob at the end (used to
-    # be for major version 1).
-    last_partition = self.payload.manifest.partitions[-1]
-    if last_partition.operations:
-      last_op = last_partition.operations[-1]
+    last_ops_section = (self.payload.manifest.kernel_install_operations or
+                        self.payload.manifest.install_operations)
+
+    # Only major version 1 has the fake signature OP at the end.
+    if self.major_version == common.CHROMEOS_MAJOR_PAYLOAD_VERSION:
+      fake_sig_op = last_ops_section[-1]
       # Check: signatures_{offset,size} must match the last (fake) operation.
-      if (last_op.type == common.OpType.REPLACE and
-          last_op.data_offset == self.sigs_offset and
-          last_op.data_length == self.sigs_size):
-        raise error.PayloadError('It seems like the last operation is the '
-                                 'signature blob. This is an invalid payload.')
+      if not (fake_sig_op.type == common.OpType.REPLACE and
+              self.sigs_offset == fake_sig_op.data_offset and
+              self.sigs_size == fake_sig_op.data_length):
+        raise error.PayloadError('Signatures_{offset,size} (%d+%d) does not'
+                                 ' match last operation (%d+%d).' %
+                                 (self.sigs_offset, self.sigs_size,
+                                  fake_sig_op.data_offset,
+                                  fake_sig_op.data_length))
 
     # Compute the checksum of all data up to signature blob.
     # TODO(garnold) we're re-reading the whole data section into a string
@@ -1150,13 +1280,17 @@
       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.data:
+      if sig.version == 1:
         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):
@@ -1210,38 +1344,62 @@
       self._CheckManifest(report, part_sizes)
       assert self.payload_type, 'payload type should be known by now'
 
-      # Make sure deprecated values are not present in the payload.
-      for field in ('install_operations', 'kernel_install_operations'):
-        self._CheckRepeatedElemNotPresent(self.payload.manifest, field,
+      manifest = self.payload.manifest
+
+      # Part 3: Examine partition operations.
+      install_operations = []
+      if self.major_version == common.CHROMEOS_MAJOR_PAYLOAD_VERSION:
+        # partitions field should not ever exist in major version 1 payloads
+        self._CheckRepeatedElemNotPresent(manifest, 'partitions', 'manifest')
+
+        install_operations.append((common.ROOTFS, manifest.install_operations))
+        install_operations.append((common.KERNEL,
+                                   manifest.kernel_install_operations))
+
+      else:
+        self._CheckRepeatedElemNotPresent(manifest, 'install_operations',
                                           'manifest')
-      for field in ('old_kernel_info', 'old_rootfs_info',
-                    'new_kernel_info', 'new_rootfs_info'):
-        self._CheckElemNotPresent(self.payload.manifest, field, 'manifest')
+        self._CheckRepeatedElemNotPresent(manifest, 'kernel_install_operations',
+                                          'manifest')
+
+        for update in manifest.partitions:
+          install_operations.append((update.partition_name, update.operations))
 
       total_blob_size = 0
-      for part, operations in ((p.partition_name, p.operations)
-                               for p in self.payload.manifest.partitions):
+      for part, operations in install_operations:
         report.AddSection('%s operations' % part)
 
         new_fs_usable_size = self.new_fs_sizes[part]
         old_fs_usable_size = self.old_fs_sizes[part]
 
-        if part_sizes is not None and part_sizes.get(part, None):
+        if part_sizes.get(part, None):
           new_fs_usable_size = old_fs_usable_size = part_sizes[part]
+        # Infer the usable partition size when validating rootfs operations:
+        # - If rootfs partition size was provided, use that.
+        # - Otherwise, if this is an older delta (minor version < 2), stick with
+        #   a known constant size. This is necessary because older deltas may
+        #   exceed the filesystem size when moving data blocks around.
+        # - Otherwise, use the encoded filesystem size.
+        elif self.payload_type == _TYPE_DELTA and part == common.ROOTFS and \
+            self.minor_version in (None, 1):
+          new_fs_usable_size = old_fs_usable_size = _OLD_DELTA_USABLE_PART_SIZE
 
-        # TODO(chromium:243559) only default to the filesystem size if no
-        # explicit size provided *and* the partition size is not embedded in the
-        # payload; see issue for more details.
+        # TODO(garnold)(chromium:243559) only default to the filesystem size if
+        # no explicit size provided *and* the partition size is not embedded in
+        # the payload; see issue for more details.
         total_blob_size += self._CheckOperations(
             operations, report, '%s_install_operations' % part,
             self.old_fs_sizes[part], self.new_fs_sizes[part],
-            old_fs_usable_size, new_fs_usable_size, total_blob_size)
+            old_fs_usable_size, new_fs_usable_size, total_blob_size,
+            (self.major_version == common.CHROMEOS_MAJOR_PAYLOAD_VERSION
+             and part == common.KERNEL))
 
       # Check: Operations data reach the end of the payload file.
       used_payload_size = self.payload.data_offset + total_blob_size
       # Major versions 2 and higher have a signature at the end, so it should be
       # considered in the total size of the image.
-      if self.sigs_size:
+      if (self.major_version >= common.BRILLO_MAJOR_PAYLOAD_VERSION and
+          self.sigs_size):
         used_payload_size += self.sigs_size
 
       if used_payload_size != payload_file_size:
diff --git a/scripts/update_payload/checker_unittest.py b/scripts/update_payload/checker_unittest.py
index 993b785..7e52233 100755
--- a/scripts/update_payload/checker_unittest.py
+++ b/scripts/update_payload/checker_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python2
 #
 # Copyright (C) 2013 The Android Open Source Project
 #
@@ -17,36 +17,35 @@
 
 """Unit testing checker.py."""
 
-# Disable check for function names to avoid errors based on old code
-# pylint: disable-msg=invalid-name
-
-from __future__ import absolute_import
+from __future__ import print_function
 
 import array
 import collections
+import cStringIO
 import hashlib
-import io
 import itertools
 import os
 import unittest
 
-from six.moves import zip
-
-import mock  # pylint: disable=import-error
+# pylint cannot find mox.
+# pylint: disable=F0401
+import mox
 
 from update_payload import checker
 from update_payload import common
 from update_payload import test_utils
 from update_payload import update_metadata_pb2
 from update_payload.error import PayloadError
-from update_payload.payload import Payload  # Avoid name conflicts later.
+from update_payload.payload import Payload # Avoid name conflicts later.
 
 
 def _OpTypeByName(op_name):
-  """Returns the type of an operation from its name."""
+  """Returns the type of an operation from itsname."""
   op_name_to_type = {
       'REPLACE': common.OpType.REPLACE,
       'REPLACE_BZ': common.OpType.REPLACE_BZ,
+      'MOVE': common.OpType.MOVE,
+      'BSDIFF': common.OpType.BSDIFF,
       'SOURCE_COPY': common.OpType.SOURCE_COPY,
       'SOURCE_BSDIFF': common.OpType.SOURCE_BSDIFF,
       'ZERO': common.OpType.ZERO,
@@ -66,7 +65,7 @@
   if checker_init_dargs is None:
     checker_init_dargs = {}
 
-  payload_file = io.BytesIO()
+  payload_file = cStringIO.StringIO()
   payload_gen_write_to_file_func(payload_file, **payload_gen_dargs)
   payload_file.seek(0)
   payload = Payload(payload_file)
@@ -76,7 +75,7 @@
 
 def _GetPayloadCheckerWithData(payload_gen):
   """Returns a payload checker from a given payload generator."""
-  payload_file = io.BytesIO()
+  payload_file = cStringIO.StringIO()
   payload_gen.WriteToFile(payload_file)
   payload_file.seek(0)
   payload = Payload(payload_file)
@@ -90,7 +89,7 @@
 # pylint: disable=W0212
 # Don't bark about missing members of classes you cannot import.
 # pylint: disable=E1101
-class PayloadCheckerTest(unittest.TestCase):
+class PayloadCheckerTest(mox.MoxTestBase):
   """Tests the PayloadChecker class.
 
   In addition to ordinary testFoo() methods, which are automatically invoked by
@@ -103,42 +102,11 @@
   all such tests is done in AddAllParametricTests().
   """
 
-  def setUp(self):
-    """setUp function for unittest testcase"""
-    self.mock_checks = []
-
-  def tearDown(self):
-    """tearDown function for unittest testcase"""
-    # Verify that all mock functions were called.
-    for check in self.mock_checks:
-      check.mock_fn.assert_called_once_with(*check.exp_args, **check.exp_kwargs)
-
-  class MockChecksAtTearDown(object):
-    """Mock data storage.
-
-    This class stores the mock functions and its arguments to be checked at a
-    later point.
-    """
-    def __init__(self, mock_fn, *args, **kwargs):
-      self.mock_fn = mock_fn
-      self.exp_args = args
-      self.exp_kwargs = kwargs
-
-  def addPostCheckForMockFunction(self, mock_fn, *args, **kwargs):
-    """Store a mock function and its arguments to self.mock_checks
-
-    Args:
-      mock_fn: mock function object
-      args: expected positional arguments for the mock_fn
-      kwargs: expected named arguments for the mock_fn
-    """
-    self.mock_checks.append(self.MockChecksAtTearDown(mock_fn, *args, **kwargs))
-
   def MockPayload(self):
     """Create a mock payload object, complete with a mock manifest."""
-    payload = mock.create_autospec(Payload)
+    payload = self.mox.CreateMock(Payload)
     payload.is_init = True
-    payload.manifest = mock.create_autospec(
+    payload.manifest = self.mox.CreateMock(
         update_metadata_pb2.DeltaArchiveManifest)
     return payload
 
@@ -207,20 +175,19 @@
     subreport = 'fake subreport'
 
     # Create a mock message.
-    msg = mock.create_autospec(update_metadata_pb2._message.Message)
-    self.addPostCheckForMockFunction(msg.HasField, name)
-    msg.HasField.return_value = is_present
+    msg = self.mox.CreateMock(update_metadata_pb2._message.Message)
+    msg.HasField(name).AndReturn(is_present)
     setattr(msg, name, val)
+
     # Create a mock report.
-    report = mock.create_autospec(checker._PayloadReport)
+    report = self.mox.CreateMock(checker._PayloadReport)
     if is_present:
       if is_submsg:
-        self.addPostCheckForMockFunction(report.AddSubReport, name)
-        report.AddSubReport.return_value = subreport
+        report.AddSubReport(name).AndReturn(subreport)
       else:
-        self.addPostCheckForMockFunction(report.AddField, name, convert(val),
-                                         linebreak=linebreak, indent=indent)
+        report.AddField(name, convert(val), linebreak=linebreak, indent=indent)
 
+    self.mox.ReplayAll()
     return (msg, report, subreport, name, val)
 
   def DoAddElemTest(self, is_present, is_mandatory, is_submsg, convert,
@@ -246,9 +213,9 @@
     else:
       ret_val, ret_subreport = checker.PayloadChecker._CheckElem(*args,
                                                                  **kwargs)
-      self.assertEqual(val if is_present else None, ret_val)
-      self.assertEqual(subreport if is_present and is_submsg else None,
-                       ret_subreport)
+      self.assertEquals(val if is_present else None, ret_val)
+      self.assertEquals(subreport if is_present and is_submsg else None,
+                        ret_subreport)
 
   def DoAddFieldTest(self, is_mandatory, is_present, convert, linebreak,
                      indent):
@@ -278,7 +245,7 @@
       self.assertRaises(PayloadError, tested_func, *args, **kwargs)
     else:
       ret_val = tested_func(*args, **kwargs)
-      self.assertEqual(val if is_present else None, ret_val)
+      self.assertEquals(val if is_present else None, ret_val)
 
   def DoAddSubMsgTest(self, is_mandatory, is_present):
     """Parametrized testing of _Check{Mandatory,Optional}SubMsg().
@@ -302,8 +269,8 @@
       self.assertRaises(PayloadError, tested_func, *args)
     else:
       ret_val, ret_subreport = tested_func(*args)
-      self.assertEqual(val if is_present else None, ret_val)
-      self.assertEqual(subreport if is_present else None, ret_subreport)
+      self.assertEquals(val if is_present else None, ret_val)
+      self.assertEquals(subreport if is_present else None, ret_subreport)
 
   def testCheckPresentIff(self):
     """Tests _CheckPresentIff()."""
@@ -329,14 +296,15 @@
       returned_signed_hash: The signed hash data retuned by openssl.
       expected_signed_hash: The signed hash data to compare against.
     """
-    # Stub out the subprocess invocation.
-    with mock.patch.object(checker.PayloadChecker, '_Run') \
-         as mock_payload_checker:
+    try:
+      # Stub out the subprocess invocation.
+      self.mox.StubOutWithMock(checker.PayloadChecker, '_Run')
       if expect_subprocess_call:
-        mock_payload_checker([], send_data=sig_data)
-        mock_payload_checker.return_value = (
-            sig_asn1_header + returned_signed_hash, None)
+        checker.PayloadChecker._Run(
+            mox.IsA(list), send_data=sig_data).AndReturn(
+                (sig_asn1_header + returned_signed_hash, None))
 
+      self.mox.ReplayAll()
       if expect_pass:
         self.assertIsNone(checker.PayloadChecker._CheckSha256Signature(
             sig_data, 'foo', expected_signed_hash, 'bar'))
@@ -344,11 +312,13 @@
         self.assertRaises(PayloadError,
                           checker.PayloadChecker._CheckSha256Signature,
                           sig_data, 'foo', expected_signed_hash, 'bar')
+    finally:
+      self.mox.UnsetStubs()
 
   def testCheckSha256Signature_Pass(self):
     """Tests _CheckSha256Signature(); pass case."""
     sig_data = 'fake-signature'.ljust(256)
-    signed_hash = hashlib.sha256(b'fake-data').digest()
+    signed_hash = hashlib.sha256('fake-data').digest()
     self.DoCheckSha256SignatureTest(True, True, sig_data,
                                     common.SIG_ASN1_HEADER, signed_hash,
                                     signed_hash)
@@ -356,7 +326,7 @@
   def testCheckSha256Signature_FailBadSignature(self):
     """Tests _CheckSha256Signature(); fails due to malformed signature."""
     sig_data = 'fake-signature'  # Malformed (not 256 bytes in length).
-    signed_hash = hashlib.sha256(b'fake-data').digest()
+    signed_hash = hashlib.sha256('fake-data').digest()
     self.DoCheckSha256SignatureTest(False, False, sig_data,
                                     common.SIG_ASN1_HEADER, signed_hash,
                                     signed_hash)
@@ -364,7 +334,7 @@
   def testCheckSha256Signature_FailBadOutputLength(self):
     """Tests _CheckSha256Signature(); fails due to unexpected output length."""
     sig_data = 'fake-signature'.ljust(256)
-    signed_hash = b'fake-hash'  # Malformed (not 32 bytes in length).
+    signed_hash = 'fake-hash'  # Malformed (not 32 bytes in length).
     self.DoCheckSha256SignatureTest(False, True, sig_data,
                                     common.SIG_ASN1_HEADER, signed_hash,
                                     signed_hash)
@@ -372,16 +342,16 @@
   def testCheckSha256Signature_FailBadAsnHeader(self):
     """Tests _CheckSha256Signature(); fails due to bad ASN1 header."""
     sig_data = 'fake-signature'.ljust(256)
-    signed_hash = hashlib.sha256(b'fake-data').digest()
-    bad_asn1_header = b'bad-asn-header'.ljust(len(common.SIG_ASN1_HEADER))
+    signed_hash = hashlib.sha256('fake-data').digest()
+    bad_asn1_header = 'bad-asn-header'.ljust(len(common.SIG_ASN1_HEADER))
     self.DoCheckSha256SignatureTest(False, True, sig_data, bad_asn1_header,
                                     signed_hash, signed_hash)
 
   def testCheckSha256Signature_FailBadHash(self):
     """Tests _CheckSha256Signature(); fails due to bad hash returned."""
     sig_data = 'fake-signature'.ljust(256)
-    expected_signed_hash = hashlib.sha256(b'fake-data').digest()
-    returned_signed_hash = hashlib.sha256(b'bad-fake-data').digest()
+    expected_signed_hash = hashlib.sha256('fake-data').digest()
+    returned_signed_hash = hashlib.sha256('bad-fake-data').digest()
     self.DoCheckSha256SignatureTest(False, True, sig_data,
                                     common.SIG_ASN1_HEADER,
                                     expected_signed_hash, returned_signed_hash)
@@ -459,10 +429,10 @@
       payload_gen.SetBlockSize(test_utils.KiB(4))
 
     # Add some operations.
-    payload_gen.AddOperation(common.ROOTFS, common.OpType.SOURCE_COPY,
+    payload_gen.AddOperation(False, common.OpType.MOVE,
                              src_extents=[(0, 16), (16, 497)],
                              dst_extents=[(16, 496), (0, 16)])
-    payload_gen.AddOperation(common.KERNEL, common.OpType.SOURCE_COPY,
+    payload_gen.AddOperation(True, common.OpType.MOVE,
                              src_extents=[(0, 8), (8, 8)],
                              dst_extents=[(8, 8), (0, 8)])
 
@@ -487,23 +457,21 @@
     # Add old kernel/rootfs partition info, as required.
     if fail_mismatched_oki_ori or fail_old_kernel_fs_size or fail_bad_oki:
       oki_hash = (None if fail_bad_oki
-                  else hashlib.sha256(b'fake-oki-content').digest())
-      payload_gen.SetPartInfo(common.KERNEL, False, old_kernel_fs_size,
-                              oki_hash)
+                  else hashlib.sha256('fake-oki-content').digest())
+      payload_gen.SetPartInfo(True, False, old_kernel_fs_size, oki_hash)
     if not fail_mismatched_oki_ori and (fail_old_rootfs_fs_size or
                                         fail_bad_ori):
       ori_hash = (None if fail_bad_ori
-                  else hashlib.sha256(b'fake-ori-content').digest())
-      payload_gen.SetPartInfo(common.ROOTFS, False, old_rootfs_fs_size,
-                              ori_hash)
+                  else hashlib.sha256('fake-ori-content').digest())
+      payload_gen.SetPartInfo(False, False, old_rootfs_fs_size, ori_hash)
 
     # Add new kernel/rootfs partition info.
     payload_gen.SetPartInfo(
-        common.KERNEL, True, new_kernel_fs_size,
-        None if fail_bad_nki else hashlib.sha256(b'fake-nki-content').digest())
+        True, True, new_kernel_fs_size,
+        None if fail_bad_nki else hashlib.sha256('fake-nki-content').digest())
     payload_gen.SetPartInfo(
-        common.ROOTFS, True, new_rootfs_fs_size,
-        None if fail_bad_nri else hashlib.sha256(b'fake-nri-content').digest())
+        False, True, new_rootfs_fs_size,
+        None if fail_bad_nri else hashlib.sha256('fake-nri-content').digest())
 
     # Set the minor version.
     payload_gen.SetMinorVersion(0)
@@ -550,11 +518,28 @@
 
     # Passes w/ all real extents.
     extents = self.NewExtentList((0, 4), (8, 3), (1024, 16))
-    self.assertEqual(
+    self.assertEquals(
         23,
         payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
                                       collections.defaultdict(int), 'foo'))
 
+    # Passes w/ pseudo-extents (aka sparse holes).
+    extents = self.NewExtentList((0, 4), (common.PSEUDO_EXTENT_MARKER, 5),
+                                 (8, 3))
+    self.assertEquals(
+        12,
+        payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
+                                      collections.defaultdict(int), 'foo',
+                                      allow_pseudo=True))
+
+    # Passes w/ pseudo-extent due to a signature.
+    extents = self.NewExtentList((common.PSEUDO_EXTENT_MARKER, 2))
+    self.assertEquals(
+        2,
+        payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
+                                      collections.defaultdict(int), 'foo',
+                                      allow_signature=True))
+
     # Fails, extent missing a start block.
     extents = self.NewExtentList((-1, 4), (8, 3), (1024, 16))
     self.assertRaises(
@@ -585,34 +570,34 @@
     block_size = payload_checker.block_size
     data_length = 10000
 
-    op = mock.create_autospec(update_metadata_pb2.InstallOperation)
+    op = self.mox.CreateMock(
+        update_metadata_pb2.InstallOperation)
     op.type = common.OpType.REPLACE
 
     # Pass.
     op.src_extents = []
     self.assertIsNone(
         payload_checker._CheckReplaceOperation(
-            op, data_length, (data_length + block_size - 1) // block_size,
+            op, data_length, (data_length + block_size - 1) / block_size,
             'foo'))
 
     # Fail, src extents founds.
     op.src_extents = ['bar']
     self.assertRaises(
         PayloadError, payload_checker._CheckReplaceOperation,
-        op, data_length, (data_length + block_size - 1) // block_size, 'foo')
+        op, data_length, (data_length + block_size - 1) / block_size, 'foo')
 
     # Fail, missing data.
     op.src_extents = []
     self.assertRaises(
         PayloadError, payload_checker._CheckReplaceOperation,
-        op, None, (data_length + block_size - 1) // block_size, 'foo')
+        op, None, (data_length + block_size - 1) / block_size, 'foo')
 
     # Fail, length / block number mismatch.
     op.src_extents = ['bar']
     self.assertRaises(
         PayloadError, payload_checker._CheckReplaceOperation,
-        op, data_length, (data_length + block_size - 1) // block_size + 1,
-        'foo')
+        op, data_length, (data_length + block_size - 1) / block_size + 1, 'foo')
 
   def testCheckReplaceBzOperation(self):
     """Tests _CheckReplaceOperation() where op.type == REPLACE_BZ."""
@@ -620,7 +605,7 @@
     block_size = payload_checker.block_size
     data_length = block_size * 3
 
-    op = mock.create_autospec(
+    op = self.mox.CreateMock(
         update_metadata_pb2.InstallOperation)
     op.type = common.OpType.REPLACE_BZ
 
@@ -628,32 +613,25 @@
     op.src_extents = []
     self.assertIsNone(
         payload_checker._CheckReplaceOperation(
-            op, data_length, (data_length + block_size - 1) // block_size + 5,
+            op, data_length, (data_length + block_size - 1) / block_size + 5,
             'foo'))
 
     # Fail, src extents founds.
     op.src_extents = ['bar']
     self.assertRaises(
         PayloadError, payload_checker._CheckReplaceOperation,
-        op, data_length, (data_length + block_size - 1) // block_size + 5,
-        'foo')
+        op, data_length, (data_length + block_size - 1) / block_size + 5, 'foo')
 
     # Fail, missing data.
     op.src_extents = []
     self.assertRaises(
         PayloadError, payload_checker._CheckReplaceOperation,
-        op, None, (data_length + block_size - 1) // block_size, 'foo')
+        op, None, (data_length + block_size - 1) / block_size, 'foo')
 
     # Fail, too few blocks to justify BZ.
     op.src_extents = []
     self.assertRaises(
         PayloadError, payload_checker._CheckReplaceOperation,
-        op, data_length, (data_length + block_size - 1) // block_size, 'foo')
-
-    # Fail, total_dst_blocks is a floating point value.
-    op.src_extents = []
-    self.assertRaises(
-        PayloadError, payload_checker._CheckReplaceOperation,
         op, data_length, (data_length + block_size - 1) / block_size, 'foo')
 
   def testCheckReplaceXzOperation(self):
@@ -662,7 +640,7 @@
     block_size = payload_checker.block_size
     data_length = block_size * 3
 
-    op = mock.create_autospec(
+    op = self.mox.CreateMock(
         update_metadata_pb2.InstallOperation)
     op.type = common.OpType.REPLACE_XZ
 
@@ -670,34 +648,153 @@
     op.src_extents = []
     self.assertIsNone(
         payload_checker._CheckReplaceOperation(
-            op, data_length, (data_length + block_size - 1) // block_size + 5,
+            op, data_length, (data_length + block_size - 1) / block_size + 5,
             'foo'))
 
     # Fail, src extents founds.
     op.src_extents = ['bar']
     self.assertRaises(
         PayloadError, payload_checker._CheckReplaceOperation,
-        op, data_length, (data_length + block_size - 1) // block_size + 5,
-        'foo')
+        op, data_length, (data_length + block_size - 1) / block_size + 5, 'foo')
 
     # Fail, missing data.
     op.src_extents = []
     self.assertRaises(
         PayloadError, payload_checker._CheckReplaceOperation,
-        op, None, (data_length + block_size - 1) // block_size, 'foo')
+        op, None, (data_length + block_size - 1) / block_size, 'foo')
 
     # Fail, too few blocks to justify XZ.
     op.src_extents = []
     self.assertRaises(
         PayloadError, payload_checker._CheckReplaceOperation,
-        op, data_length, (data_length + block_size - 1) // block_size, 'foo')
-
-    # Fail, total_dst_blocks is a floating point value.
-    op.src_extents = []
-    self.assertRaises(
-        PayloadError, payload_checker._CheckReplaceOperation,
         op, data_length, (data_length + block_size - 1) / block_size, 'foo')
 
+  def testCheckMoveOperation_Pass(self):
+    """Tests _CheckMoveOperation(); pass case."""
+    payload_checker = checker.PayloadChecker(self.MockPayload())
+    op = update_metadata_pb2.InstallOperation()
+    op.type = common.OpType.MOVE
+
+    self.AddToMessage(op.src_extents,
+                      self.NewExtentList((1, 4), (12, 2), (1024, 128)))
+    self.AddToMessage(op.dst_extents,
+                      self.NewExtentList((16, 128), (512, 6)))
+    self.assertIsNone(
+        payload_checker._CheckMoveOperation(op, None, 134, 134, 'foo'))
+
+  def testCheckMoveOperation_FailContainsData(self):
+    """Tests _CheckMoveOperation(); fails, message contains data."""
+    payload_checker = checker.PayloadChecker(self.MockPayload())
+    op = update_metadata_pb2.InstallOperation()
+    op.type = common.OpType.MOVE
+
+    self.AddToMessage(op.src_extents,
+                      self.NewExtentList((1, 4), (12, 2), (1024, 128)))
+    self.AddToMessage(op.dst_extents,
+                      self.NewExtentList((16, 128), (512, 6)))
+    self.assertRaises(
+        PayloadError, payload_checker._CheckMoveOperation,
+        op, 1024, 134, 134, 'foo')
+
+  def testCheckMoveOperation_FailInsufficientSrcBlocks(self):
+    """Tests _CheckMoveOperation(); fails, not enough actual src blocks."""
+    payload_checker = checker.PayloadChecker(self.MockPayload())
+    op = update_metadata_pb2.InstallOperation()
+    op.type = common.OpType.MOVE
+
+    self.AddToMessage(op.src_extents,
+                      self.NewExtentList((1, 4), (12, 2), (1024, 127)))
+    self.AddToMessage(op.dst_extents,
+                      self.NewExtentList((16, 128), (512, 6)))
+    self.assertRaises(
+        PayloadError, payload_checker._CheckMoveOperation,
+        op, None, 134, 134, 'foo')
+
+  def testCheckMoveOperation_FailInsufficientDstBlocks(self):
+    """Tests _CheckMoveOperation(); fails, not enough actual dst blocks."""
+    payload_checker = checker.PayloadChecker(self.MockPayload())
+    op = update_metadata_pb2.InstallOperation()
+    op.type = common.OpType.MOVE
+
+    self.AddToMessage(op.src_extents,
+                      self.NewExtentList((1, 4), (12, 2), (1024, 128)))
+    self.AddToMessage(op.dst_extents,
+                      self.NewExtentList((16, 128), (512, 5)))
+    self.assertRaises(
+        PayloadError, payload_checker._CheckMoveOperation,
+        op, None, 134, 134, 'foo')
+
+  def testCheckMoveOperation_FailExcessSrcBlocks(self):
+    """Tests _CheckMoveOperation(); fails, too many actual src blocks."""
+    payload_checker = checker.PayloadChecker(self.MockPayload())
+    op = update_metadata_pb2.InstallOperation()
+    op.type = common.OpType.MOVE
+
+    self.AddToMessage(op.src_extents,
+                      self.NewExtentList((1, 4), (12, 2), (1024, 128)))
+    self.AddToMessage(op.dst_extents,
+                      self.NewExtentList((16, 128), (512, 5)))
+    self.assertRaises(
+        PayloadError, payload_checker._CheckMoveOperation,
+        op, None, 134, 134, 'foo')
+    self.AddToMessage(op.src_extents,
+                      self.NewExtentList((1, 4), (12, 2), (1024, 129)))
+    self.AddToMessage(op.dst_extents,
+                      self.NewExtentList((16, 128), (512, 6)))
+    self.assertRaises(
+        PayloadError, payload_checker._CheckMoveOperation,
+        op, None, 134, 134, 'foo')
+
+  def testCheckMoveOperation_FailExcessDstBlocks(self):
+    """Tests _CheckMoveOperation(); fails, too many actual dst blocks."""
+    payload_checker = checker.PayloadChecker(self.MockPayload())
+    op = update_metadata_pb2.InstallOperation()
+    op.type = common.OpType.MOVE
+
+    self.AddToMessage(op.src_extents,
+                      self.NewExtentList((1, 4), (12, 2), (1024, 128)))
+    self.AddToMessage(op.dst_extents,
+                      self.NewExtentList((16, 128), (512, 7)))
+    self.assertRaises(
+        PayloadError, payload_checker._CheckMoveOperation,
+        op, None, 134, 134, 'foo')
+
+  def testCheckMoveOperation_FailStagnantBlocks(self):
+    """Tests _CheckMoveOperation(); fails, there are blocks that do not move."""
+    payload_checker = checker.PayloadChecker(self.MockPayload())
+    op = update_metadata_pb2.InstallOperation()
+    op.type = common.OpType.MOVE
+
+    self.AddToMessage(op.src_extents,
+                      self.NewExtentList((1, 4), (12, 2), (1024, 128)))
+    self.AddToMessage(op.dst_extents,
+                      self.NewExtentList((8, 128), (512, 6)))
+    self.assertRaises(
+        PayloadError, payload_checker._CheckMoveOperation,
+        op, None, 134, 134, 'foo')
+
+  def testCheckMoveOperation_FailZeroStartBlock(self):
+    """Tests _CheckMoveOperation(); fails, has extent with start block 0."""
+    payload_checker = checker.PayloadChecker(self.MockPayload())
+    op = update_metadata_pb2.InstallOperation()
+    op.type = common.OpType.MOVE
+
+    self.AddToMessage(op.src_extents,
+                      self.NewExtentList((0, 4), (12, 2), (1024, 128)))
+    self.AddToMessage(op.dst_extents,
+                      self.NewExtentList((8, 128), (512, 6)))
+    self.assertRaises(
+        PayloadError, payload_checker._CheckMoveOperation,
+        op, None, 134, 134, 'foo')
+
+    self.AddToMessage(op.src_extents,
+                      self.NewExtentList((1, 4), (12, 2), (1024, 128)))
+    self.AddToMessage(op.dst_extents,
+                      self.NewExtentList((0, 128), (512, 6)))
+    self.assertRaises(
+        PayloadError, payload_checker._CheckMoveOperation,
+        op, None, 134, 134, 'foo')
+
   def testCheckAnyDiff(self):
     """Tests _CheckAnyDiffOperation()."""
     payload_checker = checker.PayloadChecker(self.MockPayload())
@@ -735,8 +832,8 @@
     self.assertRaises(PayloadError, payload_checker._CheckSourceCopyOperation,
                       None, 0, 1, 'foo')
 
-  def DoCheckOperationTest(self, op_type_name, allow_unhashed,
-                           fail_src_extents, fail_dst_extents,
+  def DoCheckOperationTest(self, op_type_name, is_last, allow_signature,
+                           allow_unhashed, fail_src_extents, fail_dst_extents,
                            fail_mismatched_data_offset_length,
                            fail_missing_dst_extents, fail_src_length,
                            fail_dst_length, fail_data_hash,
@@ -744,8 +841,10 @@
     """Parametric testing of _CheckOperation().
 
     Args:
-      op_type_name: 'REPLACE', 'REPLACE_BZ', 'REPLACE_XZ',
+      op_type_name: 'REPLACE', 'REPLACE_BZ', 'REPLACE_XZ', 'MOVE', 'BSDIFF',
         'SOURCE_COPY', 'SOURCE_BSDIFF', BROTLI_BSDIFF or 'PUFFDIFF'.
+      is_last: Whether we're testing the last operation in a sequence.
+      allow_signature: Whether we're testing a signature-capable operation.
       allow_unhashed: Whether we're allowing to not hash the data.
       fail_src_extents: Tamper with src extents.
       fail_dst_extents: Tamper with dst extents.
@@ -770,9 +869,9 @@
     old_part_size = test_utils.MiB(4)
     new_part_size = test_utils.MiB(8)
     old_block_counters = array.array(
-        'B', [0] * ((old_part_size + block_size - 1) // block_size))
+        'B', [0] * ((old_part_size + block_size - 1) / block_size))
     new_block_counters = array.array(
-        'B', [0] * ((new_part_size + block_size - 1) // block_size))
+        'B', [0] * ((new_part_size + block_size - 1) / block_size))
     prev_data_offset = 1876
     blob_hash_counts = collections.defaultdict(int)
 
@@ -781,7 +880,8 @@
     op.type = op_type
 
     total_src_blocks = 0
-    if op_type in (common.OpType.SOURCE_COPY, common.OpType.SOURCE_BSDIFF,
+    if op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
+                   common.OpType.SOURCE_COPY, common.OpType.SOURCE_BSDIFF,
                    common.OpType.PUFFDIFF, common.OpType.BROTLI_BSDIFF):
       if fail_src_extents:
         self.AddToMessage(op.src_extents,
@@ -791,9 +891,12 @@
                           self.NewExtentList((1, 16)))
         total_src_blocks = 16
 
-    payload_checker.major_version = common.BRILLO_MAJOR_PAYLOAD_VERSION
+    # TODO(tbrindus): add major version 2 tests.
+    payload_checker.major_version = common.CHROMEOS_MAJOR_PAYLOAD_VERSION
     if op_type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ):
       payload_checker.minor_version = 0
+    elif op_type in (common.OpType.MOVE, common.OpType.BSDIFF):
+      payload_checker.minor_version = 2 if fail_bad_minor_version else 1
     elif op_type in (common.OpType.SOURCE_COPY, common.OpType.SOURCE_BSDIFF):
       payload_checker.minor_version = 1 if fail_bad_minor_version else 2
     if op_type == common.OpType.REPLACE_XZ:
@@ -804,7 +907,7 @@
     elif op_type == common.OpType.PUFFDIFF:
       payload_checker.minor_version = 4 if fail_bad_minor_version else 5
 
-    if op_type != common.OpType.SOURCE_COPY:
+    if op_type not in (common.OpType.MOVE, common.OpType.SOURCE_COPY):
       if not fail_mismatched_data_offset_length:
         op.data_length = 16 * block_size - 8
       if fail_prev_data_offset:
@@ -813,16 +916,20 @@
         op.data_offset = prev_data_offset
 
       fake_data = 'fake-data'.ljust(op.data_length)
-      if not allow_unhashed and not fail_data_hash:
-        # Create a valid data blob hash.
-        op.data_sha256_hash = hashlib.sha256(fake_data.encode('utf-8')).digest()
-        payload.ReadDataBlob.return_value = fake_data.encode('utf-8')
+      if not (allow_unhashed or (is_last and allow_signature and
+                                 op_type == common.OpType.REPLACE)):
+        if not fail_data_hash:
+          # Create a valid data blob hash.
+          op.data_sha256_hash = hashlib.sha256(fake_data).digest()
+          payload.ReadDataBlob(op.data_offset, op.data_length).AndReturn(
+              fake_data)
 
       elif fail_data_hash:
         # Create an invalid data blob hash.
         op.data_sha256_hash = hashlib.sha256(
-            fake_data.replace(' ', '-').encode('utf-8')).digest()
-        payload.ReadDataBlob.return_value = fake_data.encode('utf-8')
+            fake_data.replace(' ', '-')).digest()
+        payload.ReadDataBlob(op.data_offset, op.data_length).AndReturn(
+            fake_data)
 
     total_dst_blocks = 0
     if not fail_missing_dst_extents:
@@ -837,7 +944,8 @@
     if total_src_blocks:
       if fail_src_length:
         op.src_length = total_src_blocks * block_size + 8
-      elif (op_type == common.OpType.SOURCE_BSDIFF and
+      elif (op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
+                        common.OpType.SOURCE_BSDIFF) and
             payload_checker.minor_version <= 3):
         op.src_length = total_src_blocks * block_size
     elif fail_src_length:
@@ -847,17 +955,19 @@
     if total_dst_blocks:
       if fail_dst_length:
         op.dst_length = total_dst_blocks * block_size + 8
-      elif (op_type == common.OpType.SOURCE_BSDIFF and
+      elif (op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
+                        common.OpType.SOURCE_BSDIFF) and
             payload_checker.minor_version <= 3):
         op.dst_length = total_dst_blocks * block_size
 
+    self.mox.ReplayAll()
     should_fail = (fail_src_extents or fail_dst_extents or
                    fail_mismatched_data_offset_length or
                    fail_missing_dst_extents or fail_src_length or
                    fail_dst_length or fail_data_hash or fail_prev_data_offset or
                    fail_bad_minor_version)
-    args = (op, 'foo', old_block_counters, new_block_counters,
-            old_part_size, new_part_size, prev_data_offset,
+    args = (op, 'foo', is_last, old_block_counters, new_block_counters,
+            old_part_size, new_part_size, prev_data_offset, allow_signature,
             blob_hash_counts)
     if should_fail:
       self.assertRaises(PayloadError, payload_checker._CheckOperation, *args)
@@ -899,9 +1009,8 @@
     if fail_nonexhaustive_full_update:
       rootfs_data_length -= block_size
 
-    payload_gen.AddOperation(common.ROOTFS, rootfs_op_type,
-                             dst_extents=
-                             [(0, rootfs_data_length // block_size)],
+    payload_gen.AddOperation(False, rootfs_op_type,
+                             dst_extents=[(0, rootfs_data_length / block_size)],
                              data_offset=0,
                              data_length=rootfs_data_length)
 
@@ -911,17 +1020,17 @@
                                              'allow_unhashed': True})
     payload_checker.payload_type = checker._TYPE_FULL
     report = checker._PayloadReport()
-    partition = next((p for p in payload_checker.payload.manifest.partitions
-                      if p.partition_name == common.ROOTFS), None)
-    args = (partition.operations, report, 'foo',
-            0, rootfs_part_size, rootfs_part_size, rootfs_part_size, 0)
+
+    args = (payload_checker.payload.manifest.install_operations, report, 'foo',
+            0, rootfs_part_size, rootfs_part_size, rootfs_part_size, 0, False)
     if fail_nonexhaustive_full_update:
       self.assertRaises(PayloadError, payload_checker._CheckOperations, *args)
     else:
       self.assertEqual(rootfs_data_length,
                        payload_checker._CheckOperations(*args))
 
-  def DoCheckSignaturesTest(self, fail_empty_sigs_blob, fail_sig_missing_fields,
+  def DoCheckSignaturesTest(self, fail_empty_sigs_blob, fail_missing_pseudo_op,
+                            fail_mismatched_pseudo_op, fail_sig_missing_fields,
                             fail_unknown_sig_version, fail_incorrect_sig):
     """Tests _CheckSignatures()."""
     # Generate a test payload. For this test, we only care about the signature
@@ -932,18 +1041,20 @@
     payload_gen.SetBlockSize(block_size)
     rootfs_part_size = test_utils.MiB(2)
     kernel_part_size = test_utils.KiB(16)
-    payload_gen.SetPartInfo(common.ROOTFS, True, rootfs_part_size,
-                            hashlib.sha256(b'fake-new-rootfs-content').digest())
-    payload_gen.SetPartInfo(common.KERNEL, True, kernel_part_size,
-                            hashlib.sha256(b'fake-new-kernel-content').digest())
+    payload_gen.SetPartInfo(False, True, rootfs_part_size,
+                            hashlib.sha256('fake-new-rootfs-content').digest())
+    payload_gen.SetPartInfo(True, True, kernel_part_size,
+                            hashlib.sha256('fake-new-kernel-content').digest())
     payload_gen.SetMinorVersion(0)
     payload_gen.AddOperationWithData(
-        common.ROOTFS, common.OpType.REPLACE,
-        dst_extents=[(0, rootfs_part_size // block_size)],
+        False, common.OpType.REPLACE,
+        dst_extents=[(0, rootfs_part_size / block_size)],
         data_blob=os.urandom(rootfs_part_size))
 
-    do_forge_sigs_data = (fail_empty_sigs_blob or fail_sig_missing_fields or
-                          fail_unknown_sig_version or fail_incorrect_sig)
+    do_forge_pseudo_op = (fail_missing_pseudo_op or fail_mismatched_pseudo_op)
+    do_forge_sigs_data = (do_forge_pseudo_op or fail_empty_sigs_blob or
+                          fail_sig_missing_fields or fail_unknown_sig_version
+                          or fail_incorrect_sig)
 
     sigs_data = None
     if do_forge_sigs_data:
@@ -952,19 +1063,29 @@
         if fail_sig_missing_fields:
           sig_data = None
         else:
-          sig_data = test_utils.SignSha256(b'fake-payload-content',
+          sig_data = test_utils.SignSha256('fake-payload-content',
                                            test_utils._PRIVKEY_FILE_NAME)
         sigs_gen.AddSig(5 if fail_unknown_sig_version else 1, sig_data)
 
       sigs_data = sigs_gen.ToBinary()
       payload_gen.SetSignatures(payload_gen.curr_offset, len(sigs_data))
 
+    if do_forge_pseudo_op:
+      assert sigs_data is not None, 'should have forged signatures blob by now'
+      sigs_len = len(sigs_data)
+      payload_gen.AddOperation(
+          False, common.OpType.REPLACE,
+          data_offset=payload_gen.curr_offset / 2,
+          data_length=sigs_len / 2,
+          dst_extents=[(0, (sigs_len / 2 + block_size - 1) / block_size)])
+
     # Generate payload (complete w/ signature) and create the test object.
     payload_checker = _GetPayloadChecker(
         payload_gen.WriteToFileWithData,
         payload_gen_dargs={
             'sigs_data': sigs_data,
-            'privkey_file_name': test_utils._PRIVKEY_FILE_NAME})
+            'privkey_file_name': test_utils._PRIVKEY_FILE_NAME,
+            'do_add_pseudo_operation': not do_forge_pseudo_op})
     payload_checker.payload_type = checker._TYPE_FULL
     report = checker._PayloadReport()
 
@@ -974,7 +1095,8 @@
         common.KERNEL: kernel_part_size
     })
 
-    should_fail = (fail_empty_sigs_blob or fail_sig_missing_fields or
+    should_fail = (fail_empty_sigs_blob or fail_missing_pseudo_op or
+                   fail_mismatched_pseudo_op or fail_sig_missing_fields or
                    fail_unknown_sig_version or fail_incorrect_sig)
     args = (report, test_utils._PUBKEY_FILE_NAME)
     if should_fail:
@@ -998,6 +1120,7 @@
 
     should_succeed = (
         (minor_version == 0 and payload_type == checker._TYPE_FULL) or
+        (minor_version == 1 and payload_type == checker._TYPE_DELTA) or
         (minor_version == 2 and payload_type == checker._TYPE_DELTA) or
         (minor_version == 3 and payload_type == checker._TYPE_DELTA) or
         (minor_version == 4 and payload_type == checker._TYPE_DELTA) or
@@ -1027,10 +1150,10 @@
     payload_gen.SetBlockSize(block_size)
     kernel_filesystem_size = test_utils.KiB(16)
     rootfs_filesystem_size = test_utils.MiB(2)
-    payload_gen.SetPartInfo(common.ROOTFS, True, rootfs_filesystem_size,
-                            hashlib.sha256(b'fake-new-rootfs-content').digest())
-    payload_gen.SetPartInfo(common.KERNEL, True, kernel_filesystem_size,
-                            hashlib.sha256(b'fake-new-kernel-content').digest())
+    payload_gen.SetPartInfo(False, True, rootfs_filesystem_size,
+                            hashlib.sha256('fake-new-rootfs-content').digest())
+    payload_gen.SetPartInfo(True, True, kernel_filesystem_size,
+                            hashlib.sha256('fake-new-kernel-content').digest())
     payload_gen.SetMinorVersion(0)
 
     rootfs_part_size = 0
@@ -1040,8 +1163,8 @@
     if fail_rootfs_part_size_exceeded:
       rootfs_op_size += block_size
     payload_gen.AddOperationWithData(
-        common.ROOTFS, common.OpType.REPLACE,
-        dst_extents=[(0, rootfs_op_size // block_size)],
+        False, common.OpType.REPLACE,
+        dst_extents=[(0, rootfs_op_size / block_size)],
         data_blob=os.urandom(rootfs_op_size))
 
     kernel_part_size = 0
@@ -1051,8 +1174,8 @@
     if fail_kernel_part_size_exceeded:
       kernel_op_size += block_size
     payload_gen.AddOperationWithData(
-        common.KERNEL, common.OpType.REPLACE,
-        dst_extents=[(0, kernel_op_size // block_size)],
+        True, common.OpType.REPLACE,
+        dst_extents=[(0, kernel_op_size / block_size)],
         data_blob=os.urandom(kernel_op_size))
 
     # Generate payload (complete w/ signature) and create the test object.
@@ -1063,14 +1186,16 @@
     else:
       use_block_size = block_size
 
-    # For the unittests 237 is the value that generated for the payload.
-    metadata_size = 237
+    # For the unittests 246 is the value that generated for the payload.
+    metadata_size = 246
     if fail_mismatched_metadata_size:
       metadata_size += 1
 
     kwargs = {
         'payload_gen_dargs': {
             'privkey_file_name': test_utils._PRIVKEY_FILE_NAME,
+            'do_add_pseudo_operation': True,
+            'is_pseudo_in_kernel': True,
             'padding': os.urandom(1024) if fail_excess_data else None},
         'checker_init_dargs': {
             'assert_type': 'delta' if fail_wrong_payload_type else 'full',
@@ -1082,7 +1207,7 @@
       payload_checker = _GetPayloadChecker(payload_gen.WriteToFileWithData,
                                            **kwargs)
 
-      kwargs2 = {
+      kwargs = {
           'pubkey_file_name': test_utils._PUBKEY_FILE_NAME,
           'metadata_size': metadata_size,
           'part_sizes': {
@@ -1094,15 +1219,15 @@
                      fail_rootfs_part_size_exceeded or
                      fail_kernel_part_size_exceeded)
       if should_fail:
-        self.assertRaises(PayloadError, payload_checker.Run, **kwargs2)
+        self.assertRaises(PayloadError, payload_checker.Run, **kwargs)
       else:
-        self.assertIsNone(payload_checker.Run(**kwargs2))
-
+        self.assertIsNone(payload_checker.Run(**kwargs))
 
 # This implements a generic API, hence the occasional unused args.
 # pylint: disable=W0613
-def ValidateCheckOperationTest(op_type_name, allow_unhashed,
-                               fail_src_extents, fail_dst_extents,
+def ValidateCheckOperationTest(op_type_name, is_last, allow_signature,
+                               allow_unhashed, fail_src_extents,
+                               fail_dst_extents,
                                fail_mismatched_data_offset_length,
                                fail_missing_dst_extents, fail_src_length,
                                fail_dst_length, fail_data_hash,
@@ -1119,8 +1244,8 @@
                                                  fail_bad_minor_version)):
     return False
 
-  # SOURCE_COPY operation does not carry data.
-  if (op_type == common.OpType.SOURCE_COPY and (
+  # MOVE and SOURCE_COPY operations don't carry data.
+  if (op_type in (common.OpType.MOVE, common.OpType.SOURCE_COPY) and (
       fail_mismatched_data_offset_length or fail_data_hash or
       fail_prev_data_offset)):
     return False
@@ -1149,14 +1274,14 @@
                (values) associated with them.
     validate_func: A function used for validating test argument combinations.
   """
-  for value_tuple in itertools.product(*iter(arg_space.values())):
-    run_dargs = dict(zip(iter(arg_space.keys()), value_tuple))
+  for value_tuple in itertools.product(*arg_space.itervalues()):
+    run_dargs = dict(zip(arg_space.iterkeys(), value_tuple))
     if validate_func and not validate_func(**run_dargs):
       continue
     run_method_name = 'Do%sTest' % tested_method_name
     test_method_name = 'test%s' % tested_method_name
-    for arg_key, arg_val in run_dargs.items():
-      if arg_val or isinstance(arg_val, int):
+    for arg_key, arg_val in run_dargs.iteritems():
+      if arg_val or type(arg_val) is int:
         test_method_name += '__%s=%s' % (arg_key, arg_val)
     setattr(PayloadCheckerTest, test_method_name,
             TestMethodBody(run_method_name, run_dargs))
@@ -1203,8 +1328,11 @@
   # Add all _CheckOperation() test cases.
   AddParametricTests('CheckOperation',
                      {'op_type_name': ('REPLACE', 'REPLACE_BZ', 'REPLACE_XZ',
-                                       'SOURCE_COPY', 'SOURCE_BSDIFF',
-                                       'PUFFDIFF', 'BROTLI_BSDIFF'),
+                                       'MOVE', 'BSDIFF', 'SOURCE_COPY',
+                                       'SOURCE_BSDIFF', 'PUFFDIFF',
+                                       'BROTLI_BSDIFF'),
+                      'is_last': (True, False),
+                      'allow_signature': (True, False),
                       'allow_unhashed': (True, False),
                       'fail_src_extents': (True, False),
                       'fail_dst_extents': (True, False),
@@ -1224,13 +1352,15 @@
   # Add all _CheckOperations() test cases.
   AddParametricTests('CheckSignatures',
                      {'fail_empty_sigs_blob': (True, False),
+                      'fail_missing_pseudo_op': (True, False),
+                      'fail_mismatched_pseudo_op': (True, False),
                       'fail_sig_missing_fields': (True, False),
                       'fail_unknown_sig_version': (True, False),
                       'fail_incorrect_sig': (True, False)})
 
   # Add all _CheckManifestMinorVersion() test cases.
   AddParametricTests('CheckManifestMinorVersion',
-                     {'minor_version': (None, 0, 2, 3, 4, 5, 555),
+                     {'minor_version': (None, 0, 1, 2, 3, 4, 5, 555),
                       'payload_type': (checker._TYPE_FULL,
                                        checker._TYPE_DELTA)})
 
diff --git a/scripts/update_payload/common.py b/scripts/update_payload/common.py
index b934cf8..9061a75 100644
--- a/scripts/update_payload/common.py
+++ b/scripts/update_payload/common.py
@@ -16,11 +16,8 @@
 
 """Utilities for update payload processing."""
 
-from __future__ import absolute_import
 from __future__ import print_function
 
-import base64
-
 from update_payload import update_metadata_pb2
 from update_payload.error import PayloadError
 
@@ -28,14 +25,18 @@
 #
 # Constants.
 #
+PSEUDO_EXTENT_MARKER = (1L << 64) - 1  # UINT64_MAX
+
 SIG_ASN1_HEADER = (
-    b'\x30\x31\x30\x0d\x06\x09\x60\x86'
-    b'\x48\x01\x65\x03\x04\x02\x01\x05'
-    b'\x00\x04\x20'
+    '\x30\x31\x30\x0d\x06\x09\x60\x86'
+    '\x48\x01\x65\x03\x04\x02\x01\x05'
+    '\x00\x04\x20'
 )
 
+CHROMEOS_MAJOR_PAYLOAD_VERSION = 1
 BRILLO_MAJOR_PAYLOAD_VERSION = 2
 
+INPLACE_MINOR_PAYLOAD_VERSION = 1
 SOURCE_MINOR_PAYLOAD_VERSION = 2
 OPSRCHASH_MINOR_PAYLOAD_VERSION = 3
 BROTLI_BSDIFF_MINOR_PAYLOAD_VERSION = 4
@@ -46,7 +47,6 @@
 # Tuple of (name in system, name in protobuf).
 CROS_PARTITIONS = ((KERNEL, KERNEL), (ROOTFS, 'rootfs'))
 
-
 #
 # Payload operation types.
 #
@@ -55,6 +55,8 @@
   _CLASS = update_metadata_pb2.InstallOperation
   REPLACE = _CLASS.REPLACE
   REPLACE_BZ = _CLASS.REPLACE_BZ
+  MOVE = _CLASS.MOVE
+  BSDIFF = _CLASS.BSDIFF
   SOURCE_COPY = _CLASS.SOURCE_COPY
   SOURCE_BSDIFF = _CLASS.SOURCE_BSDIFF
   ZERO = _CLASS.ZERO
@@ -62,11 +64,13 @@
   REPLACE_XZ = _CLASS.REPLACE_XZ
   PUFFDIFF = _CLASS.PUFFDIFF
   BROTLI_BSDIFF = _CLASS.BROTLI_BSDIFF
-  ALL = (REPLACE, REPLACE_BZ, SOURCE_COPY, SOURCE_BSDIFF, ZERO,
+  ALL = (REPLACE, REPLACE_BZ, MOVE, BSDIFF, SOURCE_COPY, SOURCE_BSDIFF, ZERO,
          DISCARD, REPLACE_XZ, PUFFDIFF, BROTLI_BSDIFF)
   NAMES = {
       REPLACE: 'REPLACE',
       REPLACE_BZ: 'REPLACE_BZ',
+      MOVE: 'MOVE',
+      BSDIFF: 'BSDIFF',
       SOURCE_COPY: 'SOURCE_COPY',
       SOURCE_BSDIFF: 'SOURCE_BSDIFF',
       ZERO: 'ZERO',
@@ -142,7 +146,7 @@
 
   try:
     data = file_obj.read(length)
-  except IOError as e:
+  except IOError, e:
     raise PayloadError('error reading from file (%s): %s' % (file_obj.name, e))
 
   if len(data) != length:
@@ -163,12 +167,13 @@
   end_block = ex.start_block + ex.num_blocks
   if block_size:
     return '%d->%d * %d' % (ex.start_block, end_block, block_size)
-  return '%d->%d' % (ex.start_block, end_block)
+  else:
+    return '%d->%d' % (ex.start_block, end_block)
 
 
 def FormatSha256(digest):
   """Returns a canonical string representation of a SHA256 digest."""
-  return base64.b64encode(digest).decode('utf-8')
+  return digest.encode('base64').strip()
 
 
 #
diff --git a/scripts/update_payload/format_utils.py b/scripts/update_payload/format_utils.py
index e73badf..6248ba9 100644
--- a/scripts/update_payload/format_utils.py
+++ b/scripts/update_payload/format_utils.py
@@ -16,8 +16,6 @@
 
 """Various formatting functions."""
 
-from __future__ import division
-
 
 def NumToPercent(num, total, min_precision=1, max_precision=5):
   """Returns the percentage (string) of |num| out of |total|.
@@ -52,7 +50,7 @@
   precision = min(min_precision, max_precision)
   factor = 10 ** precision
   while precision <= max_precision:
-    percent = num * 100 * factor // total
+    percent = num * 100 * factor / total
     if percent:
       break
     factor *= 10
@@ -104,8 +102,8 @@
     magnitude = next_magnitude
 
   if exp != 0:
-    whole = size // magnitude
-    frac = (size % magnitude) * (10 ** precision) // magnitude
+    whole = size / magnitude
+    frac = (size % magnitude) * (10 ** precision) / magnitude
     while frac and not frac % 10:
       frac /= 10
     return '%d%s %s' % (whole, '.%d' % frac if frac else '', suffixes[exp - 1])
diff --git a/scripts/update_payload/format_utils_unittest.py b/scripts/update_payload/format_utils_unittest.py
index 4dcd652..42ea621 100755
--- a/scripts/update_payload/format_utils_unittest.py
+++ b/scripts/update_payload/format_utils_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python2
 #
 # Copyright (C) 2013 The Android Open Source Project
 #
@@ -17,11 +17,6 @@
 
 """Unit tests for format_utils.py."""
 
-# Disable check for function names to avoid errors based on old code
-# pylint: disable-msg=invalid-name
-
-from __future__ import absolute_import
-
 import unittest
 
 from update_payload import format_utils
diff --git a/scripts/update_payload/histogram.py b/scripts/update_payload/histogram.py
index bad2dc3..1ac2ab5 100644
--- a/scripts/update_payload/histogram.py
+++ b/scripts/update_payload/histogram.py
@@ -16,9 +16,6 @@
 
 """Histogram generation tools."""
 
-from __future__ import absolute_import
-from __future__ import division
-
 from collections import defaultdict
 
 from update_payload import format_utils
@@ -113,7 +110,7 @@
     hist_bar = '|'
     for key, count in self.data:
       if self.total:
-        bar_len = count * self.scale // self.total
+        bar_len = count * self.scale / self.total
         hist_bar = '|%s|' % ('#' * bar_len).ljust(self.scale)
 
       line = '%s %s %s' % (
diff --git a/scripts/update_payload/histogram_unittest.py b/scripts/update_payload/histogram_unittest.py
index ccde2bb..e757dd0 100755
--- a/scripts/update_payload/histogram_unittest.py
+++ b/scripts/update_payload/histogram_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python2
 #
 # Copyright (C) 2013 The Android Open Source Project
 #
@@ -17,11 +17,6 @@
 
 """Unit tests for histogram.py."""
 
-# Disable check for function names to avoid errors based on old code
-# pylint: disable-msg=invalid-name
-
-from __future__ import absolute_import
-
 import unittest
 
 from update_payload import format_utils
diff --git a/scripts/update_payload/payload.py b/scripts/update_payload/payload.py
index 998703a..2a0cb58 100644
--- a/scripts/update_payload/payload.py
+++ b/scripts/update_payload/payload.py
@@ -16,14 +16,10 @@
 
 """Tools for reading, verifying and applying Chrome OS update payloads."""
 
-from __future__ import absolute_import
 from __future__ import print_function
 
 import hashlib
-import io
-import mmap
 import struct
-import zipfile
 
 from update_payload import applier
 from update_payload import checker
@@ -68,7 +64,7 @@
     """Update payload header struct."""
 
     # Header constants; sizes are in bytes.
-    _MAGIC = b'CrAU'
+    _MAGIC = 'CrAU'
     _VERSION_SIZE = 8
     _MANIFEST_LEN_SIZE = 8
     _METADATA_SIGNATURE_LEN_SIZE = 4
@@ -115,6 +111,7 @@
             payload_file, self._METADATA_SIGNATURE_LEN_SIZE, True,
             hasher=hasher)
 
+
   def __init__(self, payload_file, payload_file_offset=0):
     """Initialize the payload object.
 
@@ -122,15 +119,7 @@
       payload_file: update payload file object open for reading
       payload_file_offset: the offset of the actual payload
     """
-    if zipfile.is_zipfile(payload_file):
-      with zipfile.ZipFile(payload_file) as zfp:
-        self.payload_file = zfp.open("payload.bin", "r")
-    elif isinstance(payload_file, str):
-      payload_fp = open(payload_file, "rb")
-      payload_bytes = mmap.mmap(payload_fp.fileno(), 0, access=mmap.ACCESS_READ)
-      self.payload_file = io.BytesIO(payload_bytes)
-    else:
-      self.payload_file = payload_file
+    self.payload_file = payload_file
     self.payload_file_offset = payload_file_offset
     self.manifest_hasher = None
     self.is_init = False
@@ -237,6 +226,31 @@
 
     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:
@@ -249,7 +263,9 @@
   def IsDelta(self):
     """Returns True iff the payload appears to be a delta."""
     self._AssertInit()
-    return (any(partition.HasField('old_partition_info')
+    return (self.manifest.HasField('old_kernel_info') or
+            self.manifest.HasField('old_rootfs_info') or
+            any(partition.HasField('old_partition_info')
                 for partition in self.manifest.partitions))
 
   def IsFull(self):
diff --git a/scripts/update_payload/test_utils.py b/scripts/update_payload/test_utils.py
index e153669..1e2259d 100644
--- a/scripts/update_payload/test_utils.py
+++ b/scripts/update_payload/test_utils.py
@@ -16,10 +16,9 @@
 
 """Utilities for unit testing."""
 
-from __future__ import absolute_import
 from __future__ import print_function
 
-import io
+import cStringIO
 import hashlib
 import os
 import struct
@@ -71,7 +70,7 @@
   """
   try:
     file_obj.write(struct.pack(common.IntPackingFmtStr(size, is_unsigned), val))
-  except IOError as e:
+  except IOError, e:
     raise payload.PayloadError('error writing to file (%s): %s' %
                                (file_obj.name, e))
 
@@ -174,37 +173,31 @@
     self.block_size = block_size
     _SetMsgField(self.manifest, 'block_size', block_size)
 
-  def SetPartInfo(self, part_name, is_new, part_size, part_hash):
+  def SetPartInfo(self, is_kernel, is_new, part_size, part_hash):
     """Set the partition info entry.
 
     Args:
-      part_name: The name of the partition.
-      is_new: Whether to set old (False) or new (True) info.
-      part_size: The partition size (in fact, filesystem size).
-      part_hash: The partition hash.
+      is_kernel: whether this is kernel partition info
+      is_new: whether to set old (False) or new (True) info
+      part_size: the partition size (in fact, filesystem size)
+      part_hash: the partition hash
     """
-    partition = next((x for x in self.manifest.partitions
-                      if x.partition_name == part_name), None)
-    if partition is None:
-      partition = self.manifest.partitions.add()
-      partition.partition_name = part_name
-
-    part_info = (partition.new_partition_info if is_new
-                 else partition.old_partition_info)
+    if is_kernel:
+      part_info = (self.manifest.new_kernel_info if is_new
+                   else self.manifest.old_kernel_info)
+    else:
+      part_info = (self.manifest.new_rootfs_info if is_new
+                   else self.manifest.old_rootfs_info)
     _SetMsgField(part_info, 'size', part_size)
     _SetMsgField(part_info, 'hash', part_hash)
 
-  def AddOperation(self, part_name, op_type, data_offset=None,
+  def AddOperation(self, is_kernel, op_type, data_offset=None,
                    data_length=None, src_extents=None, src_length=None,
                    dst_extents=None, dst_length=None, data_sha256_hash=None):
     """Adds an InstallOperation entry."""
-    partition = next((x for x in self.manifest.partitions
-                      if x.partition_name == part_name), None)
-    if partition is None:
-      partition = self.manifest.partitions.add()
-      partition.partition_name = part_name
+    operations = (self.manifest.kernel_install_operations if is_kernel
+                  else self.manifest.install_operations)
 
-    operations = partition.operations
     op = operations.add()
     op.type = op_type
 
@@ -284,7 +277,7 @@
     self.data_blobs.append(data_blob)
     return data_length, data_offset
 
-  def AddOperationWithData(self, part_name, op_type, src_extents=None,
+  def AddOperationWithData(self, is_kernel, op_type, src_extents=None,
                            src_length=None, dst_extents=None, dst_length=None,
                            data_blob=None, do_hash_data_blob=True):
     """Adds an install operation and associated data blob.
@@ -294,12 +287,12 @@
     necessary offset/length accounting.
 
     Args:
-      part_name: The name of the partition (e.g. kernel or root).
-      op_type: one of REPLACE, REPLACE_BZ, REPLACE_XZ.
+      is_kernel: whether this is a kernel (True) or rootfs (False) operation
+      op_type: one of REPLACE, REPLACE_BZ, REPLACE_XZ, MOVE or BSDIFF
       src_extents: list of (start, length) pairs indicating src block ranges
-      src_length: size of the src data in bytes (needed for diff operations)
+      src_length: size of the src data in bytes (needed for BSDIFF)
       dst_extents: list of (start, length) pairs indicating dst block ranges
-      dst_length: size of the dst data in bytes (needed for diff operations)
+      dst_length: size of the dst data in bytes (needed for BSDIFF)
       data_blob: a data blob associated with this operation
       do_hash_data_blob: whether or not to compute and add a data blob hash
     """
@@ -309,13 +302,15 @@
         data_sha256_hash = hashlib.sha256(data_blob).digest()
       data_length, data_offset = self.AddData(data_blob)
 
-    self.AddOperation(part_name, op_type, data_offset=data_offset,
+    self.AddOperation(is_kernel, op_type, data_offset=data_offset,
                       data_length=data_length, src_extents=src_extents,
                       src_length=src_length, dst_extents=dst_extents,
                       dst_length=dst_length, data_sha256_hash=data_sha256_hash)
 
   def WriteToFileWithData(self, file_obj, sigs_data=None,
-                          privkey_file_name=None, padding=None):
+                          privkey_file_name=None,
+                          do_add_pseudo_operation=False,
+                          is_pseudo_in_kernel=False, padding=None):
     """Writes the payload content to a file, optionally signing the content.
 
     Args:
@@ -324,6 +319,10 @@
                  payload signature fields assumed to be preset by the caller)
       privkey_file_name: key used for signing the payload (optional; used only
                          if explicit signatures blob not provided)
+      do_add_pseudo_operation: whether a pseudo-operation should be added to
+                               account for the signature blob
+      is_pseudo_in_kernel: whether the pseudo-operation should be added to
+                           kernel (True) or rootfs (False) operations
       padding: stuff to dump past the normal data blobs provided (optional)
 
     Raises:
@@ -336,7 +335,7 @@
 
     if do_generate_sigs_data:
       # First, sign some arbitrary data to obtain the size of a signature blob.
-      fake_sig = SignSha256(b'fake-payload-data', privkey_file_name)
+      fake_sig = SignSha256('fake-payload-data', privkey_file_name)
       fake_sigs_gen = SignaturesGenerator()
       fake_sigs_gen.AddSig(1, fake_sig)
       sigs_len = len(fake_sigs_gen.ToBinary())
@@ -344,9 +343,20 @@
       # Update the payload with proper signature attributes.
       self.SetSignatures(self.curr_offset, sigs_len)
 
+    # Add a pseudo-operation to account for the signature blob, if requested.
+    if do_add_pseudo_operation:
+      if not self.block_size:
+        raise TestError('cannot add pseudo-operation without knowing the '
+                        'payload block size')
+      self.AddOperation(
+          is_pseudo_in_kernel, common.OpType.REPLACE,
+          data_offset=self.curr_offset, data_length=sigs_len,
+          dst_extents=[(common.PSEUDO_EXTENT_MARKER,
+                        (sigs_len + self.block_size - 1) / self.block_size)])
+
     if do_generate_sigs_data:
       # Once all payload fields are updated, dump and sign it.
-      temp_payload_file = io.BytesIO()
+      temp_payload_file = cStringIO.StringIO()
       self.WriteToFile(temp_payload_file, data_blobs=self.data_blobs)
       sig = SignSha256(temp_payload_file.getvalue(), privkey_file_name)
       sigs_gen = SignaturesGenerator()
diff --git a/scripts/update_payload/update_metadata_pb2.py b/scripts/update_payload/update_metadata_pb2.py
index 9aef9f2..cb8f4c2 100644
--- a/scripts/update_payload/update_metadata_pb2.py
+++ b/scripts/update_payload/update_metadata_pb2.py
@@ -20,7 +20,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\"\x8f\x01\n\tImageInfo\x12\x11\n\x05\x62oard\x18\x01 \x01(\tB\x02\x18\x01\x12\x0f\n\x03key\x18\x02 \x01(\tB\x02\x18\x01\x12\x13\n\x07\x63hannel\x18\x03 \x01(\tB\x02\x18\x01\x12\x13\n\x07version\x18\x04 \x01(\tB\x02\x18\x01\x12\x19\n\rbuild_channel\x18\x05 \x01(\tB\x02\x18\x01\x12\x19\n\rbuild_version\x18\x06 \x01(\tB\x02\x18\x01\"\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\"\xa9\x01\n\x18\x44ynamicPartitionMetadata\x12=\n\x06groups\x18\x01 \x03(\x0b\x32-.chromeos_update_engine.DynamicPartitionGroup\x12\x18\n\x10snapshot_enabled\x18\x02 \x01(\x08\x12\x14\n\x0cvabc_enabled\x18\x03 \x01(\x08\x12\x1e\n\x16vabc_compression_param\x18\x04 \x01(\t\"c\n\x08\x41pexInfo\x12\x14\n\x0cpackage_name\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\x03\x12\x15\n\ris_compressed\x18\x03 \x01(\x08\x12\x19\n\x11\x64\x65\x63ompressed_size\x18\x04 \x01(\x03\"C\n\x0c\x41pexMetadata\x12\x33\n\tapex_info\x18\x01 \x03(\x0b\x32 .chromeos_update_engine.ApexInfo\"\x9e\x07\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=\n\x0eold_image_info\x18\n \x01(\x0b\x32!.chromeos_update_engine.ImageInfoB\x02\x18\x01\x12=\n\x0enew_image_info\x18\x0b \x01(\x0b\x32!.chromeos_update_engine.ImageInfoB\x02\x18\x01\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\x12\x33\n\tapex_info\x18\x11 \x03(\x0b\x32 .chromeos_update_engine.ApexInfoB\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\"z\n\nSignatures\x12@\n\nsignatures\x18\x01 \x03(\x0b\x32,.chromeos_update_engine.Signatures.Signature\x1a*\n\tSignature\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"+\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\"\xd7\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\"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\"\xb1\x06\n\x14\x44\x65ltaArchiveManifest\x12\x44\n\x12install_operations\x18\x01 \x03(\x0b\x32(.chromeos_update_engine.InstallOperation\x12K\n\x19kernel_install_operations\x18\x02 \x03(\x0b\x32(.chromeos_update_engine.InstallOperation\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>\n\x0fold_kernel_info\x18\x06 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12>\n\x0fnew_kernel_info\x18\x07 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12>\n\x0fold_rootfs_info\x18\x08 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12>\n\x0fnew_rootfs_info\x18\t \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\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.DynamicPartitionMetadataB\x02H\x03')
 )
 
 
@@ -78,29 +78,11 @@
   ],
   containing_type=None,
   serialized_options=None,
-  serialized_start=775,
-  serialized_end=948,
+  serialized_start=712,
+  serialized_end=885,
 )
 _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=1138,
-  serialized_end=1158,
-)
-_sym_db.RegisterEnumDescriptor(_COWMERGEOPERATION_TYPE)
-
 
 _EXTENT = _descriptor.Descriptor(
   name='Extent',
@@ -153,7 +135,7 @@
       has_default_value=False, default_value=0,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=_b('\030\001'), file=DESCRIPTOR),
+      serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='data', full_name='chromeos_update_engine.Signatures.Signature.data', index=1,
       number=2, type=12, cpp_type=9, label=1,
@@ -161,13 +143,6 @@
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='unpadded_signature_size', full_name='chromeos_update_engine.Signatures.Signature.unpadded_signature_size', index=2,
-      number=3, type=7, cpp_type=3, 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=[
   ],
@@ -180,8 +155,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=181,
-  serialized_end=260,
+  serialized_start=180,
+  serialized_end=222,
 )
 
 _SIGNATURES = _descriptor.Descriptor(
@@ -210,8 +185,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=101,
-  serialized_end=260,
+  serialized_start=100,
+  serialized_end=222,
 )
 
 
@@ -248,8 +223,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=262,
-  serialized_end=305,
+  serialized_start=224,
+  serialized_end=267,
 )
 
 
@@ -266,42 +241,42 @@
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=_b('\030\001'), file=DESCRIPTOR),
+      serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='key', full_name='chromeos_update_engine.ImageInfo.key', index=1,
       number=2, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=_b('\030\001'), file=DESCRIPTOR),
+      serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='channel', full_name='chromeos_update_engine.ImageInfo.channel', index=2,
       number=3, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=_b('\030\001'), file=DESCRIPTOR),
+      serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='version', full_name='chromeos_update_engine.ImageInfo.version', index=3,
       number=4, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=_b('\030\001'), file=DESCRIPTOR),
+      serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='build_channel', full_name='chromeos_update_engine.ImageInfo.build_channel', index=4,
       number=5, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=_b('\030\001'), file=DESCRIPTOR),
+      serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='build_version', full_name='chromeos_update_engine.ImageInfo.build_version', index=5,
       number=6, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=_b('\030\001'), file=DESCRIPTOR),
+      serialized_options=None, file=DESCRIPTOR),
   ],
   extensions=[
   ],
@@ -314,8 +289,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=308,
-  serialized_end=451,
+  serialized_start=269,
+  serialized_end=388,
 )
 
 
@@ -402,54 +377,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=454,
-  serialized_end=948,
-)
-
-
-_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=951,
-  serialized_end=1158,
+  serialized_start=391,
+  serialized_end=885,
 )
 
 
@@ -572,27 +501,6 @@
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='version', full_name='chromeos_update_engine.PartitionUpdate.version', index=16,
-      number=17, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      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=[
   ],
@@ -605,8 +513,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1161,
-  serialized_end=2001,
+  serialized_start=888,
+  serialized_end=1615,
 )
 
 
@@ -650,8 +558,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2003,
-  serialized_end=2079,
+  serialized_start=1617,
+  serialized_end=1693,
 )
 
 
@@ -676,20 +584,6 @@
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='vabc_enabled', full_name='chromeos_update_engine.DynamicPartitionMetadata.vabc_enabled', index=2,
-      number=3, type=8, cpp_type=7, label=1,
-      has_default_value=False, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='vabc_compression_param', full_name='chromeos_update_engine.DynamicPartitionMetadata.vabc_compression_param', index=3,
-      number=4, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
   ],
   extensions=[
   ],
@@ -702,91 +596,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2082,
-  serialized_end=2251,
-)
-
-
-_APEXINFO = _descriptor.Descriptor(
-  name='ApexInfo',
-  full_name='chromeos_update_engine.ApexInfo',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='package_name', full_name='chromeos_update_engine.ApexInfo.package_name', index=0,
-      number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='version', full_name='chromeos_update_engine.ApexInfo.version', index=1,
-      number=2, type=3, cpp_type=2, 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='is_compressed', full_name='chromeos_update_engine.ApexInfo.is_compressed', index=2,
-      number=3, type=8, cpp_type=7, label=1,
-      has_default_value=False, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='decompressed_size', full_name='chromeos_update_engine.ApexInfo.decompressed_size', index=3,
-      number=4, type=3, cpp_type=2, 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=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=2253,
-  serialized_end=2352,
-)
-
-
-_APEXMETADATA = _descriptor.Descriptor(
-  name='ApexMetadata',
-  full_name='chromeos_update_engine.ApexMetadata',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='apex_info', full_name='chromeos_update_engine.ApexMetadata.apex_info', index=0,
-      number=1, 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),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=2354,
-  serialized_end=2421,
+  serialized_start=1695,
+  serialized_end=1810,
 )
 
 
@@ -803,14 +614,14 @@
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=_b('\030\001'), file=DESCRIPTOR),
+      serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='kernel_install_operations', full_name='chromeos_update_engine.DeltaArchiveManifest.kernel_install_operations', index=1,
       number=2, 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=_b('\030\001'), file=DESCRIPTOR),
+      serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='block_size', full_name='chromeos_update_engine.DeltaArchiveManifest.block_size', index=2,
       number=3, type=13, cpp_type=3, label=1,
@@ -838,42 +649,42 @@
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=_b('\030\001'), file=DESCRIPTOR),
+      serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='new_kernel_info', full_name='chromeos_update_engine.DeltaArchiveManifest.new_kernel_info', index=6,
       number=7, 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=_b('\030\001'), file=DESCRIPTOR),
+      serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='old_rootfs_info', full_name='chromeos_update_engine.DeltaArchiveManifest.old_rootfs_info', index=7,
       number=8, 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=_b('\030\001'), file=DESCRIPTOR),
+      serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='new_rootfs_info', full_name='chromeos_update_engine.DeltaArchiveManifest.new_rootfs_info', index=8,
       number=9, 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=_b('\030\001'), file=DESCRIPTOR),
+      serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='old_image_info', full_name='chromeos_update_engine.DeltaArchiveManifest.old_image_info', index=9,
       number=10, 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=_b('\030\001'), file=DESCRIPTOR),
+      serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='new_image_info', full_name='chromeos_update_engine.DeltaArchiveManifest.new_image_info', index=10,
       number=11, 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=_b('\030\001'), file=DESCRIPTOR),
+      serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='minor_version', full_name='chromeos_update_engine.DeltaArchiveManifest.minor_version', index=11,
       number=12, type=13, cpp_type=3, label=1,
@@ -902,20 +713,6 @@
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='partial_update', full_name='chromeos_update_engine.DeltaArchiveManifest.partial_update', index=15,
-      number=16, type=8, cpp_type=7, label=1,
-      has_default_value=False, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='apex_info', full_name='chromeos_update_engine.DeltaArchiveManifest.apex_info', index=16,
-      number=17, 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),
   ],
   extensions=[
   ],
@@ -928,8 +725,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2424,
-  serialized_end=3350,
+  serialized_start=1813,
+  serialized_end=2630,
 )
 
 _SIGNATURES_SIGNATURE.containing_type = _SIGNATURES
@@ -938,10 +735,6 @@
 _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
@@ -950,9 +743,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
-_APEXMETADATA.fields_by_name['apex_info'].message_type = _APEXINFO
 _DELTAARCHIVEMANIFEST.fields_by_name['install_operations'].message_type = _INSTALLOPERATION
 _DELTAARCHIVEMANIFEST.fields_by_name['kernel_install_operations'].message_type = _INSTALLOPERATION
 _DELTAARCHIVEMANIFEST.fields_by_name['old_kernel_info'].message_type = _PARTITIONINFO
@@ -963,18 +754,14 @@
 _DELTAARCHIVEMANIFEST.fields_by_name['new_image_info'].message_type = _IMAGEINFO
 _DELTAARCHIVEMANIFEST.fields_by_name['partitions'].message_type = _PARTITIONUPDATE
 _DELTAARCHIVEMANIFEST.fields_by_name['dynamic_partition_metadata'].message_type = _DYNAMICPARTITIONMETADATA
-_DELTAARCHIVEMANIFEST.fields_by_name['apex_info'].message_type = _APEXINFO
 DESCRIPTOR.message_types_by_name['Extent'] = _EXTENT
 DESCRIPTOR.message_types_by_name['Signatures'] = _SIGNATURES
 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
-DESCRIPTOR.message_types_by_name['ApexInfo'] = _APEXINFO
-DESCRIPTOR.message_types_by_name['ApexMetadata'] = _APEXMETADATA
 DESCRIPTOR.message_types_by_name['DeltaArchiveManifest'] = _DELTAARCHIVEMANIFEST
 _sym_db.RegisterFileDescriptor(DESCRIPTOR)
 
@@ -1021,13 +808,6 @@
   })
 _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'
@@ -1049,20 +829,6 @@
   })
 _sym_db.RegisterMessage(DynamicPartitionMetadata)
 
-ApexInfo = _reflection.GeneratedProtocolMessageType('ApexInfo', (_message.Message,), {
-  'DESCRIPTOR' : _APEXINFO,
-  '__module__' : 'update_metadata_pb2'
-  # @@protoc_insertion_point(class_scope:chromeos_update_engine.ApexInfo)
-  })
-_sym_db.RegisterMessage(ApexInfo)
-
-ApexMetadata = _reflection.GeneratedProtocolMessageType('ApexMetadata', (_message.Message,), {
-  'DESCRIPTOR' : _APEXMETADATA,
-  '__module__' : 'update_metadata_pb2'
-  # @@protoc_insertion_point(class_scope:chromeos_update_engine.ApexMetadata)
-  })
-_sym_db.RegisterMessage(ApexMetadata)
-
 DeltaArchiveManifest = _reflection.GeneratedProtocolMessageType('DeltaArchiveManifest', (_message.Message,), {
   'DESCRIPTOR' : _DELTAARCHIVEMANIFEST,
   '__module__' : 'update_metadata_pb2'
@@ -1072,21 +838,6 @@
 
 
 DESCRIPTOR._options = None
-_SIGNATURES_SIGNATURE.fields_by_name['version']._options = None
-_IMAGEINFO.fields_by_name['board']._options = None
-_IMAGEINFO.fields_by_name['key']._options = None
-_IMAGEINFO.fields_by_name['channel']._options = None
-_IMAGEINFO.fields_by_name['version']._options = None
-_IMAGEINFO.fields_by_name['build_channel']._options = None
-_IMAGEINFO.fields_by_name['build_version']._options = None
 _INSTALLOPERATION_TYPE.values_by_name["MOVE"]._options = None
 _INSTALLOPERATION_TYPE.values_by_name["BSDIFF"]._options = None
-_DELTAARCHIVEMANIFEST.fields_by_name['install_operations']._options = None
-_DELTAARCHIVEMANIFEST.fields_by_name['kernel_install_operations']._options = None
-_DELTAARCHIVEMANIFEST.fields_by_name['old_kernel_info']._options = None
-_DELTAARCHIVEMANIFEST.fields_by_name['new_kernel_info']._options = None
-_DELTAARCHIVEMANIFEST.fields_by_name['old_rootfs_info']._options = None
-_DELTAARCHIVEMANIFEST.fields_by_name['new_rootfs_info']._options = None
-_DELTAARCHIVEMANIFEST.fields_by_name['old_image_info']._options = None
-_DELTAARCHIVEMANIFEST.fields_by_name['new_image_info']._options = None
 # @@protoc_insertion_point(module_scope)
diff --git a/aosp/service_delegate_android_interface.h b/service_delegate_android_interface.h
similarity index 95%
rename from aosp/service_delegate_android_interface.h
rename to service_delegate_android_interface.h
index 3c28794..34a9712 100644
--- a/aosp/service_delegate_android_interface.h
+++ b/service_delegate_android_interface.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_AOSP_SERVICE_DELEGATE_ANDROID_INTERFACE_H_
-#define UPDATE_ENGINE_AOSP_SERVICE_DELEGATE_ANDROID_INTERFACE_H_
+#ifndef UPDATE_ENGINE_SERVICE_DELEGATE_ANDROID_INTERFACE_H_
+#define UPDATE_ENGINE_SERVICE_DELEGATE_ANDROID_INTERFACE_H_
 
 #include <inttypes.h>
 
@@ -124,4 +124,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_AOSP_SERVICE_DELEGATE_ANDROID_INTERFACE_H_
+#endif  // UPDATE_ENGINE_SERVICE_DELEGATE_ANDROID_INTERFACE_H_
diff --git a/common/service_observer_interface.h b/service_observer_interface.h
similarity index 88%
rename from common/service_observer_interface.h
rename to service_observer_interface.h
index c471231..4edb0ac 100644
--- a/common/service_observer_interface.h
+++ b/service_observer_interface.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_COMMON_SERVICE_OBSERVER_INTERFACE_H_
-#define UPDATE_ENGINE_COMMON_SERVICE_OBSERVER_INTERFACE_H_
+#ifndef UPDATE_ENGINE_SERVICE_OBSERVER_INTERFACE_H_
+#define UPDATE_ENGINE_SERVICE_OBSERVER_INTERFACE_H_
 
 #include <memory>
 #include <string>
@@ -43,4 +43,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_COMMON_SERVICE_OBSERVER_INTERFACE_H_
+#endif  // UPDATE_ENGINE_SERVICE_OBSERVER_INTERFACE_H_
diff --git a/cros/shill_proxy.cc b/shill_proxy.cc
similarity index 93%
rename from cros/shill_proxy.cc
rename to shill_proxy.cc
index a3c8543..d398bba 100644
--- a/cros/shill_proxy.cc
+++ b/shill_proxy.cc
@@ -14,9 +14,9 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/shill_proxy.h"
+#include "update_engine/shill_proxy.h"
 
-#include "update_engine/cros/dbus_connection.h"
+#include "update_engine/dbus_connection.h"
 
 using org::chromium::flimflam::ManagerProxy;
 using org::chromium::flimflam::ManagerProxyInterface;
diff --git a/cros/shill_proxy.h b/shill_proxy.h
similarity index 88%
rename from cros/shill_proxy.h
rename to shill_proxy.h
index aff428a..4b466c9 100644
--- a/cros/shill_proxy.h
+++ b/shill_proxy.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_SHILL_PROXY_H_
-#define UPDATE_ENGINE_CROS_SHILL_PROXY_H_
+#ifndef UPDATE_ENGINE_SHILL_PROXY_H_
+#define UPDATE_ENGINE_SHILL_PROXY_H_
 
 #include <memory>
 #include <string>
@@ -25,7 +25,7 @@
 #include <dbus/object_path.h>
 #include <shill/dbus-proxies.h>
 
-#include "update_engine/cros/shill_proxy_interface.h"
+#include "update_engine/shill_proxy_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -51,4 +51,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_SHILL_PROXY_H_
+#endif  // UPDATE_ENGINE_SHILL_PROXY_H_
diff --git a/cros/shill_proxy_interface.h b/shill_proxy_interface.h
similarity index 91%
rename from cros/shill_proxy_interface.h
rename to shill_proxy_interface.h
index 19e81f3..5f6b44e 100644
--- a/cros/shill_proxy_interface.h
+++ b/shill_proxy_interface.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_SHILL_PROXY_INTERFACE_H_
-#define UPDATE_ENGINE_CROS_SHILL_PROXY_INTERFACE_H_
+#ifndef UPDATE_ENGINE_SHILL_PROXY_INTERFACE_H_
+#define UPDATE_ENGINE_SHILL_PROXY_INTERFACE_H_
 
 #include <memory>
 #include <string>
@@ -53,4 +53,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_SHILL_PROXY_INTERFACE_H_
+#endif  // UPDATE_ENGINE_SHILL_PROXY_INTERFACE_H_
diff --git a/aosp/sideload_main.cc b/sideload_main.cc
similarity index 94%
rename from aosp/sideload_main.cc
rename to sideload_main.cc
index bf015c9..27967cd 100644
--- a/aosp/sideload_main.cc
+++ b/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;
@@ -154,11 +154,8 @@
     return false;
   }
 
-  UpdateAttempterAndroid update_attempter(&sideload_daemon_state,
-                                          &prefs,
-                                          boot_control.get(),
-                                          hardware.get(),
-                                          nullptr);
+  UpdateAttempterAndroid update_attempter(
+      &sideload_daemon_state, &prefs, boot_control.get(), hardware.get());
   update_attempter.Init();
 
   TEST_AND_RETURN_FALSE(update_attempter.ApplyPayload(
diff --git a/stable/Android.bp b/stable/Android.bp
deleted file mode 100644
index 1573ebd..0000000
--- a/stable/Android.bp
+++ /dev/null
@@ -1,79 +0,0 @@
-//
-// 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.
-//
-
-// Stable AIDL interface between update_engine and other APEXes
-// ========================================================
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "system_update_engine_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["system_update_engine_license"],
-}
-
-aidl_interface {
-    name: "libupdate_engine_stable",
-
-    // This header library is available to core and product modules.
-    product_available: true,
-
-    srcs: [
-        "android/os/IUpdateEngineStable.aidl",
-        "android/os/IUpdateEngineStableCallback.aidl",
-    ],
-    backend: {
-        cpp: {
-            enabled: true,
-        },
-        java: {
-            enabled: false,
-        },
-        ndk: {
-            enabled: true,
-            apex_available: [
-                "com.android.gki.*",
-            ],
-        },
-    },
-    versions: ["1"],
-}
-
-// update_engine_stable_client (type: executable)
-// ========================================================
-// update_engine console client installed to APEXes.
-cc_binary {
-    name: "update_engine_stable_client",
-    product_specific: true,
-    header_libs: [
-        "libupdate_engine_headers",
-    ],
-    shared_libs: [
-        "libbinder_ndk",
-        "libbase",
-        "liblog",
-    ],
-    static_libs: [
-        "libgflags",
-        "libupdate_engine_stable-V1-ndk_platform",
-    ],
-    srcs: [
-        "update_engine_stable_client.cc",
-    ],
-    apex_available: [
-        "com.android.gki.*",
-    ],
-}
diff --git a/stable/aidl_api/libupdate_engine_stable/1/.hash b/stable/aidl_api/libupdate_engine_stable/1/.hash
deleted file mode 100644
index f21562a..0000000
--- a/stable/aidl_api/libupdate_engine_stable/1/.hash
+++ /dev/null
@@ -1 +0,0 @@
-526043ea6cb098d53a9c3e778420e64c4e864d8c
diff --git a/stable/aidl_api/libupdate_engine_stable/1/android/os/IUpdateEngineStable.aidl b/stable/aidl_api/libupdate_engine_stable/1/android/os/IUpdateEngineStable.aidl
deleted file mode 100644
index 67db18e..0000000
--- a/stable/aidl_api/libupdate_engine_stable/1/android/os/IUpdateEngineStable.aidl
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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 FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a snapshot of an AIDL file. Do not edit it manually. There are
-// two cases:
-// 1). this is a frozen version file - do not edit this in any case.
-// 2). this is a 'current' file. If you make a backwards compatible change to
-//     the interface (from the latest frozen version), the build system will
-//     prompt you to update this file with `m <name>-update-api`.
-//
-// You must not make a backward incompatible change to any AIDL file built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.os;
-interface IUpdateEngineStable {
-  void applyPayloadFd(in ParcelFileDescriptor pfd, in long payload_offset, in long payload_size, in String[] headerKeyValuePairs);
-  boolean bind(android.os.IUpdateEngineStableCallback callback);
-  boolean unbind(android.os.IUpdateEngineStableCallback callback);
-}
diff --git a/stable/aidl_api/libupdate_engine_stable/1/android/os/IUpdateEngineStableCallback.aidl b/stable/aidl_api/libupdate_engine_stable/1/android/os/IUpdateEngineStableCallback.aidl
deleted file mode 100644
index dbca127..0000000
--- a/stable/aidl_api/libupdate_engine_stable/1/android/os/IUpdateEngineStableCallback.aidl
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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 FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a snapshot of an AIDL file. Do not edit it manually. There are
-// two cases:
-// 1). this is a frozen version file - do not edit this in any case.
-// 2). this is a 'current' file. If you make a backwards compatible change to
-//     the interface (from the latest frozen version), the build system will
-//     prompt you to update this file with `m <name>-update-api`.
-//
-// You must not make a backward incompatible change to any AIDL file built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.os;
-interface IUpdateEngineStableCallback {
-  oneway void onStatusUpdate(int status_code, float percentage);
-  oneway void onPayloadApplicationComplete(int error_code);
-}
diff --git a/stable/aidl_api/libupdate_engine_stable/current/android/os/IUpdateEngineStable.aidl b/stable/aidl_api/libupdate_engine_stable/current/android/os/IUpdateEngineStable.aidl
deleted file mode 100644
index 82c3ca5..0000000
--- a/stable/aidl_api/libupdate_engine_stable/current/android/os/IUpdateEngineStable.aidl
+++ /dev/null
@@ -1,23 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.os;
-interface IUpdateEngineStable {
-  void applyPayloadFd(in ParcelFileDescriptor pfd, in long payload_offset, in long payload_size, in String[] headerKeyValuePairs);
-  boolean bind(android.os.IUpdateEngineStableCallback callback);
-  boolean unbind(android.os.IUpdateEngineStableCallback callback);
-}
diff --git a/stable/aidl_api/libupdate_engine_stable/current/android/os/IUpdateEngineStableCallback.aidl b/stable/aidl_api/libupdate_engine_stable/current/android/os/IUpdateEngineStableCallback.aidl
deleted file mode 100644
index 4c72b49..0000000
--- a/stable/aidl_api/libupdate_engine_stable/current/android/os/IUpdateEngineStableCallback.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.os;
-interface IUpdateEngineStableCallback {
-  oneway void onStatusUpdate(int status_code, float percentage);
-  oneway void onPayloadApplicationComplete(int error_code);
-}
diff --git a/stable/android/os/IUpdateEngineStable.aidl b/stable/android/os/IUpdateEngineStable.aidl
deleted file mode 100644
index b3b6674..0000000
--- a/stable/android/os/IUpdateEngineStable.aidl
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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.
- */
-
-package android.os;
-
-import android.os.IUpdateEngineStableCallback;
-import android.os.ParcelFileDescriptor;
-
-/**
- * The stable interface exposed by the update engine daemon.
- */
-interface IUpdateEngineStable {
-  /**
-   * Apply the given payload as provided in the given file descriptor.
-   *
-   * See {@link #bind(IUpdateEngineCallback)} for status updates.
-   *
-   * @param pfd The file descriptor opened at the payload file. Note that the daemon must have
-   *   enough permission to operate on the file descriptor.
-   * @param payload_offset offset into pfd where the payload binary starts.
-   * @param payload_size length after payload_offset to read from pfd. If 0, it will be auto
-   *   detected.
-   * @param headerKeyValuePairs additional header key value pairs, in the format of "key=value".
-   * @see android.os.UpdateEngine#applyPayload(android.content.res.AssetFileDescriptor, String[])
-   */
-  void applyPayloadFd(in ParcelFileDescriptor pfd,
-                      in long payload_offset,
-                      in long payload_size,
-                      in String[] headerKeyValuePairs);
-
-  /**
-   * Bind a callback for status updates on payload application.
-   *
-   * At any given time, only one callback can be bound. If a callback is already bound,
-   * subsequent binding will fail and return false until the bound callback is unbound. That is,
-   * binding is first-come, first-serve.
-   *
-   * A bound callback may be unbound explicitly by calling
-   * {@link #unbind(IUpdateEngineStableCallback)}, or
-   * implicitly when the process implementing the callback dies.
-   *
-   * @param callback See {@link IUpdateEngineStableCallback}
-   * @return true if binding is successful, false otherwise.
-   * @see android.os.UpdateEngine#bind(android.os.UpdateEngineCallback)
-   */
-  boolean bind(IUpdateEngineStableCallback callback);
-
-  /**
-   * Unbind a possibly bound callback.
-   *
-   * If the provided callback does not match the previously bound callback, unbinding fails.
-   *
-   * Note that a callback may also be unbound when the process implementing the callback dies.
-   * Hence, a client usually does not need to explicitly unbind a callback unless it wants to change
-   * the bound callback.
-   *
-   * @param callback The callback to be unbound. See {@link IUpdateEngineStableCallback}.
-   * @return true if unbinding is successful, false otherwise.
-   * @see android.os.UpdateEngine#unbind(android.os.UpdateEngineCallback)
-   */
-  boolean unbind(IUpdateEngineStableCallback callback);
-}
diff --git a/stable/android/os/IUpdateEngineStableCallback.aidl b/stable/android/os/IUpdateEngineStableCallback.aidl
deleted file mode 100644
index d8fc333..0000000
--- a/stable/android/os/IUpdateEngineStableCallback.aidl
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.
- */
-
-package android.os;
-
-/**
- * The stable Callback interface for IUpdateEngineStable.
- */
-oneway interface IUpdateEngineStableCallback {
-  /**
-   * Invoked when a payload is being applied and there is a status update.
-   *
-   * @param status_code see {@link android.os.UpdateEngine.UpdateStatusConstants}.
-   * @param percentage percentage of progress of the current stage.
-   * @see android.os.UpdateEngineCallback#onStatusUpdate(int, float)
-   */
-  void onStatusUpdate(int status_code, float percentage);
-
-  /**
-   * Invoked when a payload has finished being applied.
-   *
-   * @param error_code see {@link android.os.UpdateEngine.ErrorCodeConstants}
-   * @see android.os.UpdateEngineCallback#onPayloadApplicationComplete(int)
-   */
-  void onPayloadApplicationComplete(int error_code);
-}
diff --git a/stable/update_engine_stable_client.cc b/stable/update_engine_stable_client.cc
deleted file mode 100644
index 17f66b6..0000000
--- a/stable/update_engine_stable_client.cc
+++ /dev/null
@@ -1,187 +0,0 @@
-//
-// 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.
-//
-
-// update_engine console client installed to APEXes for scripts to invoke
-// directly. Uses the stable API.
-
-#include <fcntl.h>
-#include <sysexits.h>
-#include <unistd.h>
-
-#include <vector>
-
-#include <aidl/android/os/BnUpdateEngineStableCallback.h>
-#include <aidl/android/os/IUpdateEngineStable.h>
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-#include <android/binder_manager.h>
-#include <android/binder_process.h>
-#include <android/binder_ibinder.h>
-#include <common/error_code.h>
-#include <gflags/gflags.h>
-
-namespace chromeos_update_engine::internal {
-
-DEFINE_string(payload,
-              "file:///path/to/payload.bin",
-              "The file URI to the update payload to use, or path to the file");
-DEFINE_int64(offset,
-             0,
-             "The offset in the payload where the CrAU update starts.");
-DEFINE_int64(size,
-             0,
-             "The size of the CrAU part of the payload. If 0 is passed, it "
-             "will be autodetected.");
-DEFINE_string(headers,
-              "",
-              "A list of key-value pairs, one element of the list per line.");
-
-[[noreturn]] int Exit(int return_code) {
-  LOG(INFO) << "Exit: " << return_code;
-  exit(return_code);
-}
-// Called whenever the UpdateEngine daemon dies.
-void UpdateEngineServiceDied(void*) {
-  LOG(ERROR) << "UpdateEngineService died.";
-  Exit(EX_SOFTWARE);
-}
-
-class UpdateEngineClientAndroid {
- public:
-  UpdateEngineClientAndroid() = default;
-  int Run();
-
- private:
-  class UECallback : public aidl::android::os::BnUpdateEngineStableCallback {
-   public:
-    UECallback() = default;
-
-    // android::os::BnUpdateEngineStableCallback overrides.
-    ndk::ScopedAStatus onStatusUpdate(int status_code, float progress) override;
-    ndk::ScopedAStatus onPayloadApplicationComplete(int error_code) override;
-  };
-
-  static std::vector<std::string> ParseHeaders(const std::string& arg);
-
-  const ndk::ScopedAIBinder_DeathRecipient death_recipient_{
-      AIBinder_DeathRecipient_new(&UpdateEngineServiceDied)};
-  std::shared_ptr<aidl::android::os::IUpdateEngineStable> service_;
-  std::shared_ptr<aidl::android::os::BnUpdateEngineStableCallback> callback_;
-};
-
-ndk::ScopedAStatus UpdateEngineClientAndroid::UECallback::onStatusUpdate(
-    int status_code, float progress) {
-  LOG(INFO) << "onStatusUpdate(" << status_code << ", " << progress << ")";
-  return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus
-UpdateEngineClientAndroid::UECallback::onPayloadApplicationComplete(
-    int error_code) {
-  LOG(INFO) << "onPayloadApplicationComplete(" << error_code << ")";
-  auto code = static_cast<ErrorCode>(error_code);
-  Exit((code == ErrorCode::kSuccess || code == ErrorCode::kUpdatedButNotActive)
-           ? EX_OK
-           : EX_SOFTWARE);
-}
-
-int UpdateEngineClientAndroid::Run() {
-  service_ = aidl::android::os::IUpdateEngineStable::fromBinder(ndk::SpAIBinder(
-      AServiceManager_getService("android.os.UpdateEngineStableService")));
-  if (service_ == nullptr) {
-    LOG(ERROR)
-        << "Failed to get IUpdateEngineStable binder from service manager.";
-    return EX_SOFTWARE;
-  }
-
-  // Register a callback object with the service.
-  callback_ = ndk::SharedRefBase::make<UECallback>();
-  bool bound;
-  if (!service_->bind(callback_, &bound).isOk() || !bound) {
-    LOG(ERROR) << "Failed to bind() the UpdateEngine daemon.";
-    return EX_SOFTWARE;
-  }
-
-  auto headers = ParseHeaders(FLAGS_headers);
-  ndk::ScopedAStatus status;
-  const char* payload_path;
-  std::string file_prefix = "file://";
-  if (android::base::StartsWith(FLAGS_payload, file_prefix)) {
-    payload_path = FLAGS_payload.data() + file_prefix.length();
-  } else {
-    payload_path = FLAGS_payload.data();
-  }
-  ndk::ScopedFileDescriptor ufd(
-      TEMP_FAILURE_RETRY(open(payload_path, O_RDONLY)));
-  if (ufd.get() < 0) {
-    PLOG(ERROR) << "Can't open " << payload_path;
-    return EX_SOFTWARE;
-  }
-  status = service_->applyPayloadFd(ufd, FLAGS_offset, FLAGS_size, headers);
-  if (!status.isOk()) {
-    LOG(ERROR) << "Cannot apply payload: " << status.getDescription();
-    return EX_SOFTWARE;
-  }
-
-  // When following updates status changes, exit if the update_engine daemon
-  // dies.
-  if (AIBinder_linkToDeath(service_->asBinder().get(),
-                           death_recipient_.get(),
-                           nullptr) != STATUS_OK) {
-    return EX_SOFTWARE;
-  }
-
-  return EX_OK;
-}
-
-std::vector<std::string> UpdateEngineClientAndroid::ParseHeaders(
-    const std::string& arg) {
-  std::vector<std::string> lines = android::base::Split(arg, "\n");
-  std::vector<std::string> headers;
-  for (const auto& line : lines) {
-    auto header = android::base::Trim(line);
-    if (!header.empty()) {
-      headers.push_back(header);
-    }
-  }
-  return headers;
-}
-
-}  // namespace chromeos_update_engine::internal
-
-int main(int argc, char** argv) {
-  android::base::InitLogging(argv);
-  gflags::ParseCommandLineFlags(&argc, &argv, true);
-
-  // Unlike other update_engine* processes that uses message loops,
-  // update_engine_stable_client uses a thread pool model. However, number of
-  // threads is limited to 1; that is, 0 additional threads should be spawned.
-  // This avoids some race conditions.
-  if (!ABinderProcess_setThreadPoolMaxThreadCount(0)) {
-    LOG(ERROR) << "Cannot set thread pool max thread count";
-    return EX_SOFTWARE;
-  }
-  ABinderProcess_startThreadPool();
-
-  chromeos_update_engine::internal::UpdateEngineClientAndroid client{};
-  int code = client.Run();
-  if (code != EX_OK)
-    return code;
-
-  ABinderProcess_joinThreadPool();
-  LOG(ERROR) << "Exited from joinThreadPool.";
-  return EX_SOFTWARE;
-}
diff --git a/common/system_state.h b/system_state.h
similarity index 88%
rename from common/system_state.h
rename to system_state.h
index 8a9c865..f46cbcf 100644
--- a/common/system_state.h
+++ b/system_state.h
@@ -14,15 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_COMMON_SYSTEM_STATE_H_
-#define UPDATE_ENGINE_COMMON_SYSTEM_STATE_H_
-
-#include <memory>
-
-#include <base/logging.h>
-
-#include "update_engine/common/clock_interface.h"
-#include "update_engine/common/prefs_interface.h"
+#ifndef UPDATE_ENGINE_SYSTEM_STATE_H_
+#define UPDATE_ENGINE_SYSTEM_STATE_H_
 
 namespace chromeos_update_manager {
 
@@ -42,6 +35,7 @@
 // any circular references in header file inclusion. Hence forward-declaring
 // the required classes.
 class BootControlInterface;
+class ClockInterface;
 class ConnectionManagerInterface;
 class DlcServiceInterface;
 class HardwareInterface;
@@ -50,20 +44,20 @@
 class P2PManager;
 class PayloadStateInterface;
 class PowerManagerInterface;
+class PrefsInterface;
 class UpdateAttempter;
 
 // An interface to global system context, including platform resources,
 // the current state of the system, high-level objects whose lifetime is same
 // as main, system interfaces, etc.
 // Carved out separately so it can be mocked for unit tests.
+// Currently it has only one method, but we should start migrating other
+// methods to use this as and when needed to unit test them.
+// TODO(jaysri): Consider renaming this to something like GlobalContext.
 class SystemState {
  public:
-  virtual ~SystemState() = default;
-
-  static SystemState* Get() {
-    CHECK(g_pointer_ != nullptr);
-    return g_pointer_;
-  }
+  // Destructs this object.
+  virtual ~SystemState() {}
 
   // Sets or gets the latest device policy.
   virtual void set_device_policy(const policy::DevicePolicy* device_policy) = 0;
@@ -119,11 +113,8 @@
 
   // Returns a pointer to the DlcServiceInterface singleton.
   virtual DlcServiceInterface* dlcservice() = 0;
-
- protected:
-  static SystemState* g_pointer_;
 };
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_COMMON_SYSTEM_STATE_H_
+#endif  // UPDATE_ENGINE_SYSTEM_STATE_H_
diff --git a/tar_bunzip2.gni b/tar_bunzip2.gni
deleted file mode 100644
index 5d90167..0000000
--- a/tar_bunzip2.gni
+++ /dev/null
@@ -1,31 +0,0 @@
-#
-# Copyright (C) 2019 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-template("tar_bunzip2") {
-  forward_variables_from(invoker, [ "image_out_dir" ])
-  out_dir = "${root_gen_dir}/${image_out_dir}"
-
-  action_foreach(target_name) {
-    sources = invoker.sources
-    script = "//common-mk/file_generator_wrapper.py"
-    outputs = [ "${out_dir}/{{source_name_part}}.flag" ]
-    args = [
-      "sh",
-      "-c",
-      "tar -xvf \"{{source}}\" -C \"${out_dir}\" && touch ${out_dir}/{{source_name_part}}.flag",
-    ]
-  }
-}
diff --git a/tar_bunzip2.gypi b/tar_bunzip2.gypi
new file mode 100644
index 0000000..4d1be28
--- /dev/null
+++ b/tar_bunzip2.gypi
@@ -0,0 +1,39 @@
+#
+# 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.
+#
+{
+  'variables': {
+    'out_dir': '<(SHARED_INTERMEDIATE_DIR)/<(image_out_dir)',
+  },
+  'rules': [
+    {
+      'rule_name': 'tar-bunzip2',
+      'extension': 'bz2',
+      'outputs': [
+        # The .flag file is used to mark the timestamp of the file extraction
+        # and re-run this action if a new .bz2 file is generated.
+        '<(out_dir)/<(RULE_INPUT_ROOT).flag',
+      ],
+      'action': [
+        'sh',
+        '-c',
+        'tar -xvf "<(RULE_INPUT_PATH)" -C "<(out_dir)" && touch <(out_dir)/<(RULE_INPUT_ROOT).flag',
+      ],
+      'msvs_cygwin_shell': 0,
+      'process_outputs_as_sources': 1,
+      'message': 'Unpacking file <(RULE_INPUT_PATH)',
+    },
+  ],
+}
diff --git a/test_config.xml b/test_config.xml
index fe3cbfd..2639e7f 100644
--- a/test_config.xml
+++ b/test_config.xml
@@ -16,14 +16,13 @@
 <configuration description="Config to run update_engine_unittests on device">
     <option name="test-suite-tag" value="apct" />
     <option name="test-suite-tag" value="apct-native" />
-    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
         <option name="cleanup" value="true" />
-        <option name="push" value="update_engine_unittests->/data/nativetest/update_engine_unittests" />
+        <option name="push" value="update_engine_unittests->/data/local/tmp/update_engine_unittests" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.GTest" >
-        <option name="native-test-device-path" value="/data/nativetest" />
+        <option name="native-test-device-path" value="/data/local/tmp" />
         <!-- The following rules avoid test runner from calling the following helper executables
              directly as gtests. -->
         <option name="file-exclusion-filter-regex" value=".*/delta_generator$" />
diff --git a/test_http_server.cc b/test_http_server.cc
index a2f1e05..4536f37 100644
--- a/test_http_server.cc
+++ b/test_http_server.cc
@@ -189,8 +189,7 @@
   ret = WriteString(fd,
                     string("HTTP/1.1 ") + Itoa(return_code) + " " +
                         GetHttpResponseDescription(return_code) +
-                        EOL "Content-Type: application/octet-stream" EOL
-                            "Connection: close" EOL);
+                        EOL "Content-Type: application/octet-stream" EOL);
   if (ret < 0)
     return -1;
   written += ret;
@@ -407,7 +406,6 @@
   if ((ret = WriteString(fd, "HTTP/1.1 " + Itoa(code) + " " + status + EOL)) <
       0)
     return;
-  WriteString(fd, "Connection: close" EOL);
   WriteString(fd, "Location: " + url + EOL);
 }
 
@@ -660,4 +658,5 @@
       LOG(FATAL) << "ERROR on accept";
     HandleConnection(client_fd);
   }
+  return 0;
 }
diff --git a/cros/update_attempter.cc b/update_attempter.cc
similarity index 69%
rename from cros/update_attempter.cc
rename to update_attempter.cc
index e039480..ee571db 100644
--- a/cros/update_attempter.cc
+++ b/update_attempter.cc
@@ -14,24 +14,20 @@
 // limitations under the License.
 //
 
-#include "update_engine/cros/update_attempter.h"
+#include "update_engine/update_attempter.h"
 
 #include <stdint.h>
 
 #include <algorithm>
-#include <map>
 #include <memory>
 #include <string>
-#include <unordered_set>
 #include <utility>
 #include <vector>
 
 #include <base/bind.h>
-#include <base/compiler_specific.h>
 #include <base/files/file_util.h>
 #include <base/logging.h>
 #include <base/rand_util.h>
-#include <base/strings/string_number_conversions.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
 #include <base/time/time.h>
@@ -44,29 +40,26 @@
 
 #include "update_engine/certificate_checker.h"
 #include "update_engine/common/boot_control_interface.h"
+#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/download_action_chromeos.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/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"
@@ -75,7 +68,6 @@
 
 using base::Bind;
 using base::Callback;
-using base::FilePath;
 using base::Time;
 using base::TimeDelta;
 using base::TimeTicks;
@@ -85,7 +77,6 @@
 using chromeos_update_manager::Policy;
 using chromeos_update_manager::StagingCase;
 using chromeos_update_manager::UpdateCheckParams;
-using std::map;
 using std::string;
 using std::vector;
 using update_engine::UpdateAttemptFlags;
@@ -126,16 +117,14 @@
   return code;
 }
 
-UpdateAttempter::UpdateAttempter(CertificateChecker* cert_checker)
+UpdateAttempter::UpdateAttempter(SystemState* system_state,
+                                 CertificateChecker* cert_checker)
     : processor_(new ActionProcessor()),
+      system_state_(system_state),
       cert_checker_(cert_checker),
       is_install_(false) {}
 
 UpdateAttempter::~UpdateAttempter() {
-  // Prevent any DBus communication from UpdateAttempter when shutting down the
-  // daemon.
-  ClearObservers();
-
   // CertificateChecker might not be initialized in unittests.
   if (cert_checker_)
     cert_checker_->SetObserver(nullptr);
@@ -148,78 +137,48 @@
   // Pulling from the SystemState can only be done after construction, since
   // this is an aggregate of various objects (such as the UpdateAttempter),
   // which requires them all to be constructed prior to it being used.
-  prefs_ = SystemState::Get()->prefs();
-  omaha_request_params_ = SystemState::Get()->request_params();
+  prefs_ = system_state_->prefs();
+  omaha_request_params_ = system_state_->request_params();
 
   if (cert_checker_)
     cert_checker_->SetObserver(this);
 
   // In case of update_engine restart without a reboot we need to restore the
   // reboot needed state.
-  if (GetBootTimeAtUpdate(nullptr)) {
+  if (GetBootTimeAtUpdate(nullptr))
     status_ = UpdateStatus::UPDATED_NEED_REBOOT;
-  } else {
+  else
     status_ = UpdateStatus::IDLE;
-    prefs_->Delete(kPrefsLastFp, {kDlcPrefsSubDir});
-  }
 }
 
 bool UpdateAttempter::ScheduleUpdates() {
-  if (IsBusyOrUpdateScheduled())
+  if (IsUpdateRunningOrScheduled())
     return false;
 
   chromeos_update_manager::UpdateManager* const update_manager =
-      SystemState::Get()->update_manager();
+      system_state_->update_manager();
   CHECK(update_manager);
   Callback<void(EvalStatus, const UpdateCheckParams&)> callback =
       Bind(&UpdateAttempter::OnUpdateScheduled, base::Unretained(this));
   // We limit the async policy request to a reasonably short time, to avoid a
   // starvation due to a transient bug.
-  update_manager->AsyncPolicyRequestUpdateCheckAllowed(
-      callback, &Policy::UpdateCheckAllowed);
+  update_manager->AsyncPolicyRequest(callback, &Policy::UpdateCheckAllowed);
   waiting_for_scheduled_check_ = true;
   return true;
 }
 
-bool UpdateAttempter::StartUpdater() {
-  // Initiate update checks.
-  ScheduleUpdates();
-
-  auto update_boot_flags_action = std::make_unique<UpdateBootFlagsAction>(
-      SystemState::Get()->boot_control());
-  aux_processor_.EnqueueAction(std::move(update_boot_flags_action));
-  // Update boot flags after 45 seconds.
-  MessageLoop::current()->PostDelayedTask(
-      FROM_HERE,
-      base::Bind(&ActionProcessor::StartProcessing,
-                 base::Unretained(&aux_processor_)),
-      base::TimeDelta::FromSeconds(45));
-
-  // Broadcast the update engine status on startup to ensure consistent system
-  // state on crashes.
-  MessageLoop::current()->PostTask(
-      FROM_HERE,
-      base::Bind(&UpdateAttempter::BroadcastStatus, base::Unretained(this)));
-
-  MessageLoop::current()->PostTask(
-      FROM_HERE,
-      base::Bind(&UpdateAttempter::UpdateEngineStarted,
-                 base::Unretained(this)));
-  return true;
-}
-
 void UpdateAttempter::CertificateChecked(ServerToCheck server_to_check,
                                          CertificateCheckResult result) {
-  SystemState::Get()->metrics_reporter()->ReportCertificateCheckMetrics(
+  system_state_->metrics_reporter()->ReportCertificateCheckMetrics(
       server_to_check, result);
 }
 
 bool UpdateAttempter::CheckAndReportDailyMetrics() {
   int64_t stored_value;
-  Time now = SystemState::Get()->clock()->GetWallclockTime();
-  if (SystemState::Get()->prefs()->Exists(kPrefsDailyMetricsLastReportedAt) &&
-      SystemState::Get()->prefs()->GetInt64(kPrefsDailyMetricsLastReportedAt,
-                                            &stored_value)) {
+  Time now = system_state_->clock()->GetWallclockTime();
+  if (system_state_->prefs()->Exists(kPrefsDailyMetricsLastReportedAt) &&
+      system_state_->prefs()->GetInt64(kPrefsDailyMetricsLastReportedAt,
+                                       &stored_value)) {
     Time last_reported_at = Time::FromInternalValue(stored_value);
     TimeDelta time_reported_since = now - last_reported_at;
     if (time_reported_since.InSeconds() < 0) {
@@ -242,8 +201,8 @@
   }
 
   LOG(INFO) << "Reporting daily metrics.";
-  SystemState::Get()->prefs()->SetInt64(kPrefsDailyMetricsLastReportedAt,
-                                        now.ToInternalValue());
+  system_state_->prefs()->SetInt64(kPrefsDailyMetricsLastReportedAt,
+                                   now.ToInternalValue());
 
   ReportOSAge();
 
@@ -252,6 +211,10 @@
 
 void UpdateAttempter::ReportOSAge() {
   struct stat sb;
+
+  if (system_state_ == nullptr)
+    return;
+
   if (stat("/etc/lsb-release", &sb) != 0) {
     PLOG(ERROR) << "Error getting file status for /etc/lsb-release "
                 << "(Note: this may happen in some unit tests)";
@@ -259,7 +222,7 @@
   }
 
   Time lsb_release_timestamp = Time::FromTimeSpec(sb.st_ctim);
-  Time now = SystemState::Get()->clock()->GetWallclockTime();
+  Time now = system_state_->clock()->GetWallclockTime();
   TimeDelta age = now - lsb_release_timestamp;
   if (age.InSeconds() < 0) {
     LOG(ERROR) << "The OS age (" << utils::FormatTimeDelta(age)
@@ -268,10 +231,16 @@
     return;
   }
 
-  SystemState::Get()->metrics_reporter()->ReportDailyMetrics(age);
+  system_state_->metrics_reporter()->ReportDailyMetrics(age);
 }
 
-void UpdateAttempter::Update(const UpdateCheckParams& params) {
+void UpdateAttempter::Update(const string& app_version,
+                             const string& omaha_url,
+                             const string& target_channel,
+                             const string& target_version_prefix,
+                             bool rollback_allowed,
+                             bool obey_proxies,
+                             bool interactive) {
   // 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)
@@ -287,7 +256,8 @@
     // not performing an update check because of this.
     LOG(INFO) << "Not updating b/c we already updated and we're waiting for "
               << "reboot, we'll ping Omaha instead";
-    SystemState::Get()->metrics_reporter()->ReportUpdateCheckMetrics(
+    system_state_->metrics_reporter()->ReportUpdateCheckMetrics(
+        system_state_,
         metrics::CheckResult::kRebootPending,
         metrics::CheckReaction::kUnset,
         metrics::DownloadErrorCode::kUnset);
@@ -299,11 +269,17 @@
     return;
   }
 
-  if (!CalculateUpdateParams(params)) {
+  if (!CalculateUpdateParams(app_version,
+                             omaha_url,
+                             target_channel,
+                             target_version_prefix,
+                             rollback_allowed,
+                             obey_proxies,
+                             interactive)) {
     return;
   }
 
-  BuildUpdateActions(params.interactive);
+  BuildUpdateActions(interactive);
 
   SetStatusAndNotify(UpdateStatus::CHECKING_FOR_UPDATE);
 
@@ -330,8 +306,8 @@
   else
     LOG(INFO) << "No device policies/settings present.";
 
-  SystemState::Get()->set_device_policy(device_policy);
-  SystemState::Get()->p2p_manager()->SetDevicePolicy(device_policy);
+  system_state_->set_device_policy(device_policy);
+  system_state_->p2p_manager()->SetDevicePolicy(device_policy);
 }
 
 void UpdateAttempter::CalculateP2PParams(bool interactive) {
@@ -344,31 +320,37 @@
   // (Why would a developer want to opt in? If they are working on the
   // update_engine or p2p codebases so they can actually test their code.)
 
-  if (!SystemState::Get()->p2p_manager()->IsP2PEnabled()) {
-    LOG(INFO) << "p2p is not enabled - disallowing p2p for both"
-              << " downloading and sharing.";
-  } else {
-    // Allow p2p for sharing, even in interactive checks.
-    use_p2p_for_sharing = true;
-    if (!interactive) {
-      LOG(INFO) << "Non-interactive check - allowing p2p for downloading";
-      use_p2p_for_downloading = true;
+  if (system_state_ != nullptr) {
+    if (!system_state_->p2p_manager()->IsP2PEnabled()) {
+      LOG(INFO) << "p2p is not enabled - disallowing p2p for both"
+                << " downloading and sharing.";
     } else {
-      LOG(INFO) << "Forcibly disabling use of p2p for downloading "
-                << "since this update attempt is interactive.";
+      // Allow p2p for sharing, even in interactive checks.
+      use_p2p_for_sharing = true;
+      if (!interactive) {
+        LOG(INFO) << "Non-interactive check - allowing p2p for downloading";
+        use_p2p_for_downloading = true;
+      } else {
+        LOG(INFO) << "Forcibly disabling use of p2p for downloading "
+                  << "since this update attempt is interactive.";
+      }
     }
   }
 
-  PayloadStateInterface* const payload_state =
-      SystemState::Get()->payload_state();
+  PayloadStateInterface* const payload_state = system_state_->payload_state();
   payload_state->SetUsingP2PForDownloading(use_p2p_for_downloading);
   payload_state->SetUsingP2PForSharing(use_p2p_for_sharing);
 }
 
-bool UpdateAttempter::CalculateUpdateParams(const UpdateCheckParams& params) {
+bool UpdateAttempter::CalculateUpdateParams(const string& app_version,
+                                            const string& omaha_url,
+                                            const string& target_channel,
+                                            const string& target_version_prefix,
+                                            bool rollback_allowed,
+                                            bool obey_proxies,
+                                            bool interactive) {
   http_response_code_ = 0;
-  PayloadStateInterface* const payload_state =
-      SystemState::Get()->payload_state();
+  PayloadStateInterface* const payload_state = system_state_->payload_state();
 
   // Refresh the policy before computing all the update parameters.
   RefreshDevicePolicy();
@@ -377,13 +359,19 @@
   // policy is available again.
   UpdateRollbackHappened();
 
-  CalculateStagingParams(params.interactive);
+  // 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);
+
+  CalculateStagingParams(interactive);
   // If staging_wait_time_ wasn't set, staging is off, use scattering instead.
   if (staging_wait_time_.InSeconds() == 0) {
-    CalculateScatteringParams(params.interactive);
+    CalculateScatteringParams(interactive);
   }
 
-  CalculateP2PParams(params.interactive);
+  CalculateP2PParams(interactive);
   if (payload_state->GetUsingP2PForDownloading() ||
       payload_state->GetUsingP2PForSharing()) {
     // OK, p2p is to be used - start it and perform housekeeping.
@@ -396,18 +384,34 @@
     }
   }
 
-  if (!omaha_request_params_->Init(
-          forced_app_version_, forced_omaha_url_, params)) {
+  if (!omaha_request_params_->Init(app_version, omaha_url, interactive)) {
     LOG(ERROR) << "Unable to initialize Omaha request params.";
     return false;
   }
 
-  // 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_|,
-  // |image_props_.product_id| and |image_props_.canary_product_id| from
-  // |omaha_request_params_| shall be made below this line.
-  CalculateDlcParams();
+  // 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();
+  }
+  // Set the DLC module ID list.
+  omaha_request_params_->set_dlc_module_ids(dlc_module_ids_);
+  omaha_request_params_->set_is_install(is_install_);
 
   LOG(INFO) << "target_version_prefix = "
             << omaha_request_params_->target_version_prefix()
@@ -430,7 +434,7 @@
             << payload_state->GetUsingP2PForSharing();
 
   obeying_proxies_ = true;
-  if (proxy_manual_checks_ == 0) {
+  if (obey_proxies || 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_++;
@@ -454,8 +458,7 @@
   // Take a copy of the old scatter value before we update it, as
   // we need to update the waiting period if this value changes.
   TimeDelta old_scatter_factor = scatter_factor_;
-  const policy::DevicePolicy* device_policy =
-      SystemState::Get()->device_policy();
+  const policy::DevicePolicy* device_policy = system_state_->device_policy();
   if (device_policy) {
     int64_t new_scatter_factor_in_secs = 0;
     device_policy->GetScatterFactorInSeconds(&new_scatter_factor_in_secs);
@@ -469,8 +472,8 @@
     LOG(INFO) << "Scattering disabled since scatter factor is set to 0";
   } else if (interactive) {
     LOG(INFO) << "Scattering disabled as this is an interactive update check";
-  } else if (SystemState::Get()->hardware()->IsOOBEEnabled() &&
-             !SystemState::Get()->hardware()->IsOOBEComplete(nullptr)) {
+  } else if (system_state_->hardware()->IsOOBEEnabled() &&
+             !system_state_->hardware()->IsOOBEComplete(nullptr)) {
     LOG(INFO) << "Scattering disabled since OOBE is enabled but not complete "
                  "yet";
   } else {
@@ -572,19 +575,19 @@
   // fails, we'll still be able to scatter based on our in-memory value.
   // The persistence only helps in ensuring a good overall distribution
   // across multiple devices if they tend to reboot too often.
-  SystemState::Get()->payload_state()->SetScatteringWaitPeriod(
+  system_state_->payload_state()->SetScatteringWaitPeriod(
       omaha_request_params_->waiting_period());
 }
 
 void UpdateAttempter::CalculateStagingParams(bool interactive) {
-  bool oobe_complete = SystemState::Get()->hardware()->IsOOBEEnabled() &&
-                       SystemState::Get()->hardware()->IsOOBEComplete(nullptr);
-  auto device_policy = SystemState::Get()->device_policy();
+  bool oobe_complete = system_state_->hardware()->IsOOBEEnabled() &&
+                       system_state_->hardware()->IsOOBEComplete(nullptr);
+  auto device_policy = system_state_->device_policy();
   StagingCase staging_case = StagingCase::kOff;
   if (device_policy && !interactive && oobe_complete) {
     staging_wait_time_ = omaha_request_params_->waiting_period();
     staging_case = CalculateStagingCase(
-        device_policy, &staging_wait_time_, &staging_schedule_);
+        device_policy, prefs_, &staging_wait_time_, &staging_schedule_);
   }
   switch (staging_case) {
     case StagingCase::kOff:
@@ -599,10 +602,8 @@
     case StagingCase::kNoSavedValue:
       prefs_->SetInt64(kPrefsWallClockStagingWaitPeriod,
                        staging_wait_time_.InDays());
-      FALLTHROUGH;
     case StagingCase::kSetStagingFromPref:
       omaha_request_params_->set_waiting_period(staging_wait_time_);
-      FALLTHROUGH;
     case StagingCase::kNoAction:
       // Staging is on, enable wallclock based wait so that its values get used.
       omaha_request_params_->set_wall_clock_based_wait_enabled(true);
@@ -617,180 +618,61 @@
   }
 }
 
-bool UpdateAttempter::ResetDlcPrefs(const string& dlc_id) {
-  vector<string> failures;
-  for (auto& sub_key :
-       {kPrefsPingActive, kPrefsPingLastActive, kPrefsPingLastRollcall}) {
-    auto key = prefs_->CreateSubKey({kDlcPrefsSubDir, dlc_id, sub_key});
-    if (!prefs_->Delete(key))
-      failures.emplace_back(sub_key);
-  }
-  if (failures.size() != 0)
-    PLOG(ERROR) << "Failed to delete prefs (" << base::JoinString(failures, ",")
-                << " for DLC (" << dlc_id << ").";
-
-  return failures.size() == 0;
-}
-
-void UpdateAttempter::SetPref(const string& pref_key,
-                              const string& pref_value,
-                              const string& payload_id) {
-  string dlc_id;
-  if (!omaha_request_params_->GetDlcId(payload_id, &dlc_id)) {
-    // Not a DLC ID, set fingerprint in perf for platform ID.
-    prefs_->SetString(pref_key, pref_value);
-  } else {
-    // Set fingerprint in pref for DLC ID.
-    auto key = prefs_->CreateSubKey({kDlcPrefsSubDir, dlc_id, pref_key});
-    prefs_->SetString(key, pref_value);
-  }
-}
-
-bool UpdateAttempter::SetDlcActiveValue(bool is_active, const string& dlc_id) {
-  if (dlc_id.empty()) {
-    LOG(ERROR) << "Empty DLC ID passed.";
-    return false;
-  }
-  LOG(INFO) << "Set DLC (" << dlc_id << ") to "
-            << (is_active ? "Active" : "Inactive");
-  if (is_active) {
-    auto ping_active_key =
-        prefs_->CreateSubKey({kDlcPrefsSubDir, dlc_id, kPrefsPingActive});
-    if (!prefs_->SetInt64(ping_active_key, kPingActiveValue)) {
-      LOG(ERROR) << "Failed to set the value of ping metadata '"
-                 << kPrefsPingActive << "'.";
-      return false;
-    }
-  } else {
-    return ResetDlcPrefs(dlc_id);
-  }
-  return true;
-}
-
-int64_t UpdateAttempter::GetPingMetadata(const string& metadata_key) const {
-  // The first time a ping is sent, the metadata files containing the values
-  // sent back by the server still don't exist. A value of -1 is used to
-  // indicate this.
-  if (!SystemState::Get()->prefs()->Exists(metadata_key))
-    return kPingNeverPinged;
-
-  int64_t value;
-  if (SystemState::Get()->prefs()->GetInt64(metadata_key, &value))
-    return value;
-
-  // Return -2 when the file exists and there is a problem reading from it, or
-  // the value cannot be converted to an integer.
-  return kPingUnknownValue;
-}
-
-void UpdateAttempter::CalculateDlcParams() {
-  // Set the |dlc_ids_| only for an update. This is required to get the
-  // currently installed DLC(s).
-  if (!is_install_ &&
-      !SystemState::Get()->dlcservice()->GetDlcsToUpdate(&dlc_ids_)) {
-    LOG(INFO) << "Failed to retrieve DLC module IDs from dlcservice. Check the "
-                 "state of dlcservice, will not update DLC modules.";
-  }
-  map<string, OmahaRequestParams::AppParams> dlc_apps_params;
-  for (const auto& dlc_id : dlc_ids_) {
-    OmahaRequestParams::AppParams dlc_params{
-        .active_counting_type = OmahaRequestParams::kDateBased,
-        .name = dlc_id,
-        .send_ping = false};
-    if (is_install_) {
-      // In some cases, |SetDlcActiveValue| might fail to reset the DLC prefs
-      // when a DLC is uninstalled. To avoid having stale values from that
-      // scenario, we reset the metadata values on a new install request.
-      // Ignore failure to delete stale prefs.
-      ResetDlcPrefs(dlc_id);
-      SetDlcActiveValue(true, dlc_id);
-    } else {
-      // Only send the ping when the request is to update DLCs. When installing
-      // DLCs, we don't want to send the ping yet, since the DLCs might fail to
-      // install or might not really be active yet.
-      dlc_params.ping_active = kPingActiveValue;
-      auto ping_active_key =
-          prefs_->CreateSubKey({kDlcPrefsSubDir, dlc_id, kPrefsPingActive});
-      if (!prefs_->GetInt64(ping_active_key, &dlc_params.ping_active) ||
-          dlc_params.ping_active != kPingActiveValue) {
-        dlc_params.ping_active = kPingInactiveValue;
-      }
-      auto ping_last_active_key =
-          prefs_->CreateSubKey({kDlcPrefsSubDir, dlc_id, kPrefsPingLastActive});
-      dlc_params.ping_date_last_active = GetPingMetadata(ping_last_active_key);
-
-      auto ping_last_rollcall_key = prefs_->CreateSubKey(
-          {kDlcPrefsSubDir, dlc_id, kPrefsPingLastRollcall});
-      dlc_params.ping_date_last_rollcall =
-          GetPingMetadata(ping_last_rollcall_key);
-
-      dlc_params.send_ping = true;
-    }
-    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) {
   CHECK(!processor_->IsRunning());
   processor_->set_delegate(this);
 
-  // The session ID needs to be kept throughout the update flow. The value
-  // of the session ID will reset/update only when it is a new update flow.
-  session_id_ = base::GenerateGUID();
-
   // Actions:
   auto update_check_fetcher = std::make_unique<LibcurlHttpFetcher>(
-      GetProxyResolver(), SystemState::Get()->hardware());
+      GetProxyResolver(), system_state_->hardware());
   update_check_fetcher->set_server_to_check(ServerToCheck::kUpdate);
   // Try harder to connect to the network, esp when not interactive.
   // See comment in libcurl_http_fetcher.cc.
   update_check_fetcher->set_no_network_max_retries(interactive ? 1 : 3);
-  update_check_fetcher->set_is_update_check(true);
   auto update_check_action = std::make_unique<OmahaRequestAction>(
-      nullptr, std::move(update_check_fetcher), false, session_id_);
-  auto response_handler_action = std::make_unique<OmahaResponseHandlerAction>();
-  auto update_boot_flags_action = std::make_unique<UpdateBootFlagsAction>(
-      SystemState::Get()->boot_control());
+      system_state_, nullptr, std::move(update_check_fetcher), false);
+  auto response_handler_action =
+      std::make_unique<OmahaResponseHandlerAction>(system_state_);
+  auto update_boot_flags_action =
+      std::make_unique<UpdateBootFlagsAction>(system_state_->boot_control());
   auto download_started_action = std::make_unique<OmahaRequestAction>(
+      system_state_,
       new OmahaEvent(OmahaEvent::kTypeUpdateDownloadStarted),
       std::make_unique<LibcurlHttpFetcher>(GetProxyResolver(),
-                                           SystemState::Get()->hardware()),
-      false,
-      session_id_);
+                                           system_state_->hardware()),
+      false);
 
-  LibcurlHttpFetcher* download_fetcher = new LibcurlHttpFetcher(
-      GetProxyResolver(), SystemState::Get()->hardware());
+  LibcurlHttpFetcher* download_fetcher =
+      new LibcurlHttpFetcher(GetProxyResolver(), system_state_->hardware());
   download_fetcher->set_server_to_check(ServerToCheck::kDownload);
   if (interactive)
     download_fetcher->set_max_retry_count(kDownloadMaxRetryCountInteractive);
-  download_fetcher->SetHeader(kXGoogleUpdateSessionId, session_id_);
-  auto download_action = std::make_unique<DownloadActionChromeos>(
-      prefs_,
-      SystemState::Get()->boot_control(),
-      SystemState::Get()->hardware(),
-      download_fetcher,  // passes ownership
-      interactive);
+  auto download_action =
+      std::make_unique<DownloadAction>(prefs_,
+                                       system_state_->boot_control(),
+                                       system_state_->hardware(),
+                                       system_state_,
+                                       download_fetcher,  // passes ownership
+                                       interactive);
   download_action->set_delegate(this);
 
   auto download_finished_action = std::make_unique<OmahaRequestAction>(
+      system_state_,
       new OmahaEvent(OmahaEvent::kTypeUpdateDownloadFinished),
       std::make_unique<LibcurlHttpFetcher>(GetProxyResolver(),
-                                           SystemState::Get()->hardware()),
-      false,
-      session_id_);
-  auto filesystem_verifier_action = std::make_unique<FilesystemVerifierAction>(
-      SystemState::Get()->boot_control()->GetDynamicPartitionControl());
+                                           system_state_->hardware()),
+      false);
+  auto filesystem_verifier_action =
+      std::make_unique<FilesystemVerifierAction>();
   auto update_complete_action = std::make_unique<OmahaRequestAction>(
+      system_state_,
       new OmahaEvent(OmahaEvent::kTypeUpdateComplete),
       std::make_unique<LibcurlHttpFetcher>(GetProxyResolver(),
-                                           SystemState::Get()->hardware()),
-      false,
-      session_id_);
+                                           system_state_->hardware()),
+      false);
 
   auto postinstall_runner_action = std::make_unique<PostinstallRunnerAction>(
-      SystemState::Get()->boot_control(), SystemState::Get()->hardware());
+      system_state_->boot_control(), system_state_->hardware());
   postinstall_runner_action->set_delegate(this);
 
   // Bond them together. We have to use the leaf-types when calling
@@ -824,8 +706,7 @@
     // Enterprise-enrolled devices have an empty owner in their device policy.
     string owner;
     RefreshDevicePolicy();
-    const policy::DevicePolicy* device_policy =
-        SystemState::Get()->device_policy();
+    const policy::DevicePolicy* device_policy = system_state_->device_policy();
     if (device_policy && (!device_policy->GetOwner(&owner) || owner.empty())) {
       LOG(ERROR) << "Enterprise device detected. "
                  << "Cannot perform a powerwash for enterprise devices.";
@@ -836,7 +717,7 @@
   processor_->set_delegate(this);
 
   // Initialize the default request params.
-  if (!omaha_request_params_->Init("", "", {.interactive = true})) {
+  if (!omaha_request_params_->Init("", "", true)) {
     LOG(ERROR) << "Unable to initialize Omaha request params.";
     return false;
   }
@@ -844,26 +725,26 @@
   LOG(INFO) << "Setting rollback options.";
   install_plan_.reset(new InstallPlan());
   install_plan_->target_slot = GetRollbackSlot();
-  install_plan_->source_slot =
-      SystemState::Get()->boot_control()->GetCurrentSlot();
+  install_plan_->source_slot = system_state_->boot_control()->GetCurrentSlot();
 
-  TEST_AND_RETURN_FALSE(install_plan_->LoadPartitionsFromSlots(
-      SystemState::Get()->boot_control()));
+  TEST_AND_RETURN_FALSE(
+      install_plan_->LoadPartitionsFromSlots(system_state_->boot_control()));
   install_plan_->powerwash_required = powerwash;
 
+  LOG(INFO) << "Using this install plan:";
   install_plan_->Dump();
 
   auto install_plan_action =
       std::make_unique<InstallPlanAction>(*install_plan_);
   auto postinstall_runner_action = std::make_unique<PostinstallRunnerAction>(
-      SystemState::Get()->boot_control(), SystemState::Get()->hardware());
+      system_state_->boot_control(), system_state_->hardware());
   postinstall_runner_action->set_delegate(this);
   BondActions(install_plan_action.get(), postinstall_runner_action.get());
   processor_->EnqueueAction(std::move(install_plan_action));
   processor_->EnqueueAction(std::move(postinstall_runner_action));
 
   // Update the payload state for Rollback.
-  SystemState::Get()->payload_state()->Rollback();
+  system_state_->payload_state()->Rollback();
 
   SetStatusAndNotify(UpdateStatus::ATTEMPTING_ROLLBACK);
 
@@ -880,10 +761,9 @@
 
 BootControlInterface::Slot UpdateAttempter::GetRollbackSlot() const {
   LOG(INFO) << "UpdateAttempter::GetRollbackSlot";
-  const unsigned int num_slots =
-      SystemState::Get()->boot_control()->GetNumSlots();
+  const unsigned int num_slots = system_state_->boot_control()->GetNumSlots();
   const BootControlInterface::Slot current_slot =
-      SystemState::Get()->boot_control()->GetCurrentSlot();
+      system_state_->boot_control()->GetCurrentSlot();
 
   LOG(INFO) << "  Installed slots: " << num_slots;
   LOG(INFO) << "  Booted from slot: "
@@ -897,7 +777,7 @@
   vector<BootControlInterface::Slot> bootable_slots;
   for (BootControlInterface::Slot slot = 0; slot < num_slots; slot++) {
     if (slot != current_slot &&
-        SystemState::Get()->boot_control()->IsSlotBootable(slot)) {
+        system_state_->boot_control()->IsSlotBootable(slot)) {
       LOG(INFO) << "Found bootable slot "
                 << BootControlInterface::SlotName(slot);
       return slot;
@@ -910,16 +790,19 @@
 bool UpdateAttempter::CheckForUpdate(const string& app_version,
                                      const string& omaha_url,
                                      UpdateAttemptFlags flags) {
-  if (status_ != UpdateStatus::IDLE) {
-    LOG(INFO) << "Refusing to do an update as there is an "
-              << (is_install_ ? "install" : "update")
-              << " already in progress.";
+  dlc_module_ids_.clear();
+  is_install_ = false;
+  bool interactive = !(flags & UpdateAttemptFlags::kFlagNonInteractive);
+
+  if (interactive && status_ != UpdateStatus::IDLE) {
+    // An update check is either in-progress, or an update has completed and the
+    // system is in UPDATED_NEED_REBOOT.  Either way, don't do an interactive
+    // update at this time
+    LOG(INFO) << "Refusing to do an interactive update with an update already "
+                 "in progress";
     return false;
   }
 
-  bool interactive = !(flags & UpdateAttemptFlags::kFlagNonInteractive);
-  is_install_ = false;
-
   LOG(INFO) << "Forced update check requested.";
   forced_app_version_.clear();
   forced_omaha_url_.clear();
@@ -946,33 +829,25 @@
     // of the previously set ones.
     current_update_attempt_flags_ = flags;
     // Note: The caching for non-interactive update checks happens in
-    // |OnUpdateScheduled()|.
+    // OnUpdateScheduled().
   }
 
-  // |forced_update_pending_callback_| should always be set, but even in the
-  // case that it is not, we still return true indicating success because the
-  // scheduled periodic check will pick up these changes.
   if (forced_update_pending_callback_.get()) {
-    // Always call |ScheduleUpdates()| before forcing an update. This is because
-    // we need an update to be scheduled for the
-    // |forced_update_pending_callback_| to have an effect. Here we don't need
-    // to care about the return value from |ScheduleUpdate()|.
+    if (!system_state_->dlcservice()->GetInstalled(&dlc_module_ids_)) {
+      dlc_module_ids_.clear();
+    }
+    // Make sure that a scheduling request is made prior to calling the forced
+    // update pending callback.
     ScheduleUpdates();
     forced_update_pending_callback_->Run(true, interactive);
   }
+
   return true;
 }
 
-bool UpdateAttempter::CheckForInstall(const vector<string>& dlc_ids,
+bool UpdateAttempter::CheckForInstall(const vector<string>& dlc_module_ids,
                                       const string& omaha_url) {
-  if (status_ != UpdateStatus::IDLE) {
-    LOG(INFO) << "Refusing to do an install as there is an "
-              << (is_install_ ? "install" : "update")
-              << " already in progress.";
-    return false;
-  }
-
-  dlc_ids_ = dlc_ids;
+  dlc_module_ids_ = dlc_module_ids;
   is_install_ = true;
   forced_omaha_url_.clear();
 
@@ -982,28 +857,35 @@
   if (IsAnyUpdateSourceAllowed()) {
     forced_omaha_url_ = omaha_url;
   }
-
-  if (omaha_url == kScheduledAUTestURLRequest ||
-      omaha_url == kAUTestURLRequest) {
+  if (omaha_url == kScheduledAUTestURLRequest) {
+    forced_omaha_url_ = constants::kOmahaDefaultAUTestURL;
+  } else if (omaha_url == kAUTestURLRequest) {
     forced_omaha_url_ = constants::kOmahaDefaultAUTestURL;
   }
 
-  // |forced_update_pending_callback_| should always be set, but even in the
-  // case that it is not, we still return true indicating success because the
-  // scheduled periodic check will pick up these changes.
-  if (forced_update_pending_callback_.get()) {
-    // Always call |ScheduleUpdates()| before forcing an update. This is because
-    // we need an update to be scheduled for the
-    // |forced_update_pending_callback_| to have an effect. Here we don't need
-    // to care about the return value from |ScheduleUpdate()|.
-    ScheduleUpdates();
-    forced_update_pending_callback_->Run(true, true);
+  if (!ScheduleUpdates()) {
+    if (forced_update_pending_callback_.get()) {
+      // Make sure that a scheduling request is made prior to calling the forced
+      // update pending callback.
+      ScheduleUpdates();
+      forced_update_pending_callback_->Run(true, true);
+      return true;
+    }
+    return false;
   }
   return true;
 }
 
 bool UpdateAttempter::RebootIfNeeded() {
-  if (SystemState::Get()->power_manager()->RequestReboot())
+#ifdef __ANDROID__
+  if (status_ != UpdateStatus::UPDATED_NEED_REBOOT) {
+    LOG(INFO) << "Reboot requested, but status is "
+              << UpdateStatusToString(status_) << ", so not rebooting.";
+    return false;
+  }
+#endif  // __ANDROID__
+
+  if (system_state_->power_manager()->RequestReboot())
     return true;
 
   return RebootDirectly();
@@ -1015,14 +897,18 @@
     return;
   prefs_->SetString(kPrefsUpdateCompletedOnBootId, boot_id);
 
-  int64_t value = SystemState::Get()->clock()->GetBootTime().ToInternalValue();
+  int64_t value = system_state_->clock()->GetBootTime().ToInternalValue();
   prefs_->SetInt64(kPrefsUpdateCompletedBootTime, value);
 }
 
 bool UpdateAttempter::RebootDirectly() {
-  vector<string> command = {"/sbin/shutdown", "-r", "now"};
+  vector<string> command;
+  command.push_back("/sbin/shutdown");
+  command.push_back("-r");
+  command.push_back("now");
+  LOG(INFO) << "Running \"" << base::JoinString(command, " ") << "\"";
   int rc = 0;
-  Subprocess::SynchronousExec(command, &rc, nullptr, nullptr);
+  Subprocess::SynchronousExec(command, &rc, nullptr);
   return rc == 0;
 }
 
@@ -1055,7 +941,13 @@
     LOG(INFO) << "Update attempt flags in use = 0x" << std::hex
               << current_update_attempt_flags_;
 
-    Update(params);
+    Update(forced_app_version_,
+           forced_omaha_url_,
+           params.target_channel,
+           params.target_version_prefix,
+           params.rollback_allowed,
+           /*obey_proxies=*/false,
+           params.interactive);
     // Always clear the forced app_version and omaha_url after an update attempt
     // so the next update uses the defaults.
     forced_app_version_.clear();
@@ -1071,37 +963,45 @@
   // a bug that will most likely prevent further automatic update checks. It
   // seems better to crash in such cases and restart the update_engine daemon
   // into, hopefully, a known good state.
-  CHECK(IsBusyOrUpdateScheduled());
+  CHECK(IsUpdateRunningOrScheduled());
 }
 
 void UpdateAttempter::UpdateLastCheckedTime() {
-  last_checked_time_ =
-      SystemState::Get()->clock()->GetWallclockTime().ToTimeT();
+  last_checked_time_ = system_state_->clock()->GetWallclockTime().ToTimeT();
 }
 
 void UpdateAttempter::UpdateRollbackHappened() {
-  DCHECK(SystemState::Get()->payload_state());
+  DCHECK(system_state_);
+  DCHECK(system_state_->payload_state());
   DCHECK(policy_provider_);
-  if (SystemState::Get()->payload_state()->GetRollbackHappened() &&
+  if (system_state_->payload_state()->GetRollbackHappened() &&
       (policy_provider_->device_policy_is_loaded() ||
        policy_provider_->IsConsumerDevice())) {
     // Rollback happened, but we already went through OOBE and policy is
     // present or it's a consumer device.
-    SystemState::Get()->payload_state()->SetRollbackHappened(false);
+    system_state_->payload_state()->SetRollbackHappened(false);
   }
 }
 
-void UpdateAttempter::ProcessingDoneInternal(const ActionProcessor* processor,
-                                             ErrorCode code) {
+// Delegate methods:
+void UpdateAttempter::ProcessingDone(const ActionProcessor* processor,
+                                     ErrorCode code) {
+  LOG(INFO) << "Processing Done.";
+
   // Reset cpu shares back to normal.
   cpu_limiter_.StopLimiter();
 
-  ResetInteractivityFlags();
+  // reset the state that's only valid for a single update pass
+  current_update_attempt_flags_ = UpdateAttemptFlags::kNone;
+
+  if (forced_update_pending_callback_.get())
+    // Clear prior interactive requests once the processor is done.
+    forced_update_pending_callback_->Run(false, false);
 
   if (status_ == UpdateStatus::REPORTING_ERROR_EVENT) {
     LOG(INFO) << "Error event sent.";
 
-    // Inform scheduler of new status.
+    // Inform scheduler of new status;
     SetStatusAndNotify(UpdateStatus::IDLE);
     ScheduleUpdates();
 
@@ -1114,126 +1014,93 @@
 
   attempt_error_code_ = utils::GetBaseErrorCode(code);
 
-  if (code != ErrorCode::kSuccess) {
-    if (ScheduleErrorEventAction()) {
+  if (code == ErrorCode::kSuccess) {
+    // For install operation, we do not mark update complete since we do not
+    // need reboot.
+    if (!is_install_)
+      WriteUpdateCompletedMarker();
+    ReportTimeToUpdateAppliedMetric();
+
+    prefs_->SetInt64(kPrefsDeltaUpdateFailures, 0);
+    prefs_->SetString(kPrefsPreviousVersion,
+                      omaha_request_params_->app_version());
+    DeltaPerformer::ResetUpdateProgress(prefs_, false);
+
+    system_state_->payload_state()->UpdateSucceeded();
+
+    // Since we're done with scattering fully at this point, this is the
+    // safest point delete the state files, as we're sure that the status is
+    // set to reboot (which means no more updates will be applied until reboot)
+    // This deletion is required for correctness as we want the next update
+    // check to re-create a new random number for the update check count.
+    // Similarly, we also delete the wall-clock-wait period that was persisted
+    // so that we start with a new random value for the next update check
+    // after reboot so that the same device is not favored or punished in any
+    // way.
+    prefs_->Delete(kPrefsUpdateCheckCount);
+    system_state_->payload_state()->SetScatteringWaitPeriod(TimeDelta());
+    system_state_->payload_state()->SetStagingWaitPeriod(TimeDelta());
+    prefs_->Delete(kPrefsUpdateFirstSeenAt);
+
+    if (is_install_) {
+      LOG(INFO) << "DLC successfully installed, no reboot needed.";
+      SetStatusAndNotify(UpdateStatus::IDLE);
+      ScheduleUpdates();
       return;
     }
-    LOG(INFO) << "No update.";
-    SetStatusAndNotify(UpdateStatus::IDLE);
+
+    SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT);
     ScheduleUpdates();
+    LOG(INFO) << "Update successfully applied, waiting to reboot.";
+
+    // |install_plan_| is null during rollback operations, and the stats don't
+    // make much sense then anyway.
+    if (install_plan_) {
+      // Generate an unique payload identifier.
+      string target_version_uid;
+      for (const auto& payload : install_plan_->payloads) {
+        target_version_uid +=
+            brillo::data_encoding::Base64Encode(payload.hash) + ":" +
+            payload.metadata_signature + ":";
+      }
+
+      // If we just downloaded a rollback image, we should preserve this fact
+      // over the following powerwash.
+      if (install_plan_->is_rollback) {
+        system_state_->payload_state()->SetRollbackHappened(true);
+        system_state_->metrics_reporter()->ReportEnterpriseRollbackMetrics(
+            /*success=*/true, install_plan_->version);
+      }
+
+      // Expect to reboot into the new version to send the proper metric during
+      // next boot.
+      system_state_->payload_state()->ExpectRebootInNewVersion(
+          target_version_uid);
+    } else {
+      // If we just finished a rollback, then we expect to have no Omaha
+      // response. Otherwise, it's an error.
+      if (system_state_->payload_state()->GetRollbackVersion().empty()) {
+        LOG(ERROR) << "Can't send metrics because there was no Omaha response";
+      }
+    }
     return;
   }
 
-  ReportTimeToUpdateAppliedMetric();
-  prefs_->SetInt64(kPrefsDeltaUpdateFailures, 0);
-  prefs_->SetString(kPrefsPreviousVersion,
-                    omaha_request_params_->app_version());
-  DeltaPerformer::ResetUpdateProgress(prefs_, false);
-
-  SystemState::Get()->payload_state()->UpdateSucceeded();
-
-  // Since we're done with scattering fully at this point, this is the
-  // safest point delete the state files, as we're sure that the status is
-  // set to reboot (which means no more updates will be applied until reboot)
-  // This deletion is required for correctness as we want the next update
-  // check to re-create a new random number for the update check count.
-  // Similarly, we also delete the wall-clock-wait period that was persisted
-  // so that we start with a new random value for the next update check
-  // after reboot so that the same device is not favored or punished in any
-  // way.
-  prefs_->Delete(kPrefsUpdateCheckCount);
-  SystemState::Get()->payload_state()->SetScatteringWaitPeriod(TimeDelta());
-  SystemState::Get()->payload_state()->SetStagingWaitPeriod(TimeDelta());
-  prefs_->Delete(kPrefsUpdateFirstSeenAt);
-
-  // Note: below this comment should only be on |ErrorCode::kSuccess|.
-  if (is_install_) {
-    ProcessingDoneInstall(processor, code);
-  } else {
-    ProcessingDoneUpdate(processor, code);
+  if (ScheduleErrorEventAction()) {
+    return;
   }
-}
-
-vector<string> UpdateAttempter::GetSuccessfulDlcIds() {
-  vector<string> dlc_ids;
-  for (const auto& pr : omaha_request_params_->dlc_apps_params())
-    if (pr.second.updated)
-      dlc_ids.push_back(pr.second.name);
-  return dlc_ids;
-}
-
-void UpdateAttempter::ProcessingDoneInstall(const ActionProcessor* processor,
-                                            ErrorCode code) {
-  if (!SystemState::Get()->dlcservice()->InstallCompleted(
-          GetSuccessfulDlcIds()))
-    LOG(WARNING) << "dlcservice didn't successfully handle install completion.";
+  LOG(INFO) << "No update.";
   SetStatusAndNotify(UpdateStatus::IDLE);
   ScheduleUpdates();
-  LOG(INFO) << "DLC successfully installed, no reboot needed.";
-}
-
-void UpdateAttempter::ProcessingDoneUpdate(const ActionProcessor* processor,
-                                           ErrorCode code) {
-  WriteUpdateCompletedMarker();
-
-  if (!SystemState::Get()->dlcservice()->UpdateCompleted(GetSuccessfulDlcIds()))
-    LOG(WARNING) << "dlcservice didn't successfully handle update completion.";
-  SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT);
-  ScheduleUpdates();
-  LOG(INFO) << "Update successfully applied, waiting to reboot.";
-
-  // |install_plan_| is null during rollback operations, and the stats don't
-  // make much sense then anyway.
-  if (install_plan_) {
-    // Generate an unique payload identifier.
-    string target_version_uid;
-    for (const auto& payload : install_plan_->payloads) {
-      target_version_uid += brillo::data_encoding::Base64Encode(payload.hash) +
-                            ":" + payload.metadata_signature + ":";
-      // Set fingerprint value for updates only.
-      if (!is_install_)
-        SetPref(kPrefsLastFp, payload.fp, payload.app_id);
-    }
-
-    // If we just downloaded a rollback image, we should preserve this fact
-    // over the following powerwash.
-    if (install_plan_->is_rollback) {
-      SystemState::Get()->payload_state()->SetRollbackHappened(true);
-      SystemState::Get()->metrics_reporter()->ReportEnterpriseRollbackMetrics(
-          /*success=*/true, install_plan_->version);
-    }
-
-    // Expect to reboot into the new version to send the proper metric during
-    // next boot.
-    SystemState::Get()->payload_state()->ExpectRebootInNewVersion(
-        target_version_uid);
-  } else {
-    // If we just finished a rollback, then we expect to have no Omaha
-    // response. Otherwise, it's an error.
-    if (SystemState::Get()->payload_state()->GetRollbackVersion().empty()) {
-      LOG(ERROR) << "Can't send metrics because there was no Omaha response";
-    }
-  }
-}
-
-// Delegate methods:
-void UpdateAttempter::ProcessingDone(const ActionProcessor* processor,
-                                     ErrorCode code) {
-  LOG(INFO) << "Processing Done.";
-  ProcessingDoneInternal(processor, code);
-
-  // Note: do cleanups here for any variables that need to be reset after a
-  // failure, error, update, or install.
-  is_install_ = false;
 }
 
 void UpdateAttempter::ProcessingStopped(const ActionProcessor* processor) {
   // Reset cpu shares back to normal.
   cpu_limiter_.StopLimiter();
   download_progress_ = 0.0;
-
-  ResetInteractivityFlags();
-
+  if (forced_update_pending_callback_.get())
+    // Clear prior interactive requests once the processor is done.
+    forced_update_pending_callback_->Run(false, false);
   SetStatusAndNotify(UpdateStatus::IDLE);
   ScheduleUpdates();
   error_event_.reset(nullptr);
@@ -1249,10 +1116,9 @@
   // actions (update download as well as the initial update check
   // actions).
   const string type = action->Type();
-  if (type == DownloadActionChromeos::StaticType()) {
+  if (type == DownloadAction::StaticType()) {
     download_progress_ = 0.0;
-    DownloadActionChromeos* download_action =
-        static_cast<DownloadActionChromeos*>(action);
+    DownloadAction* download_action = static_cast<DownloadAction*>(action);
     http_response_code_ = download_action->GetHTTPResponseCode();
   } else if (type == OmahaRequestAction::StaticType()) {
     OmahaRequestAction* omaha_request_action =
@@ -1302,6 +1168,7 @@
           new InstallPlan(omaha_response_handler_action->install_plan()));
       UpdateLastCheckedTime();
       new_version_ = install_plan_->version;
+      new_system_version_ = install_plan_->system_version;
       new_payload_size_ = 0;
       for (const auto& payload : install_plan_->payloads)
         new_payload_size_ += payload.size;
@@ -1328,7 +1195,6 @@
         case UpdateStatus::REPORTING_ERROR_EVENT:
         case UpdateStatus::ATTEMPTING_ROLLBACK:
         case UpdateStatus::DISABLED:
-        case UpdateStatus::CLEANUP_PREVIOUS_UPDATE:
           MarkDeltaUpdateFailure();
           break;
       }
@@ -1359,7 +1225,7 @@
                                     uint64_t total) {
   // The PayloadState keeps track of how many bytes were actually downloaded
   // from a given URL for the URL skipping logic.
-  SystemState::Get()->payload_state()->DownloadProgress(bytes_progressed);
+  system_state_->payload_state()->DownloadProgress(bytes_progressed);
 
   double progress = 0;
   if (total)
@@ -1373,7 +1239,7 @@
 }
 
 void UpdateAttempter::DownloadComplete() {
-  SystemState::Get()->payload_state()->DownloadComplete();
+  system_state_->payload_state()->DownloadComplete();
 }
 
 void UpdateAttempter::ProgressUpdate(double progress) {
@@ -1388,15 +1254,6 @@
   }
 }
 
-void UpdateAttempter::ResetInteractivityFlags() {
-  // Reset the state that's only valid for a single update pass.
-  current_update_attempt_flags_ = UpdateAttemptFlags::kNone;
-
-  if (forced_update_pending_callback_.get())
-    // Clear prior interactive requests once the processor is done.
-    forced_update_pending_callback_->Run(false, false);
-}
-
 bool UpdateAttempter::ResetStatus() {
   LOG(INFO) << "Attempting to reset state from "
             << UpdateStatusToString(status_) << " to UpdateStatus::IDLE";
@@ -1415,10 +1272,9 @@
       // UpdateStatus::UPDATED_NEED_REBOOT state.
       ret_value = prefs_->Delete(kPrefsUpdateCompletedOnBootId) && ret_value;
       ret_value = prefs_->Delete(kPrefsUpdateCompletedBootTime) && ret_value;
-      ret_value = prefs_->Delete(kPrefsLastFp, {kDlcPrefsSubDir}) && ret_value;
 
       // Update the boot flags so the current slot has higher priority.
-      BootControlInterface* boot_control = SystemState::Get()->boot_control();
+      BootControlInterface* boot_control = system_state_->boot_control();
       if (!boot_control->SetActiveBootSlot(boot_control->GetCurrentSlot()))
         ret_value = false;
 
@@ -1429,7 +1285,7 @@
         ret_value = false;
 
       // Notify the PayloadState that the successful payload was canceled.
-      SystemState::Get()->payload_state()->ResetUpdateStatus();
+      system_state_->payload_state()->ResetUpdateStatus();
 
       // The previous version is used to report back to omaha after reboot that
       // we actually rebooted into the new version from this "prev-version". We
@@ -1451,26 +1307,11 @@
   out_status->last_checked_time = last_checked_time_;
   out_status->status = status_;
   out_status->current_version = omaha_request_params_->app_version();
+  out_status->current_system_version = omaha_request_params_->system_version();
   out_status->progress = download_progress_;
   out_status->new_size_bytes = new_payload_size_;
   out_status->new_version = new_version_;
-  out_status->is_enterprise_rollback =
-      install_plan_ && install_plan_->is_rollback;
-  out_status->is_install = is_install_;
-
-  string str_eol_date;
-  if (SystemState::Get()->prefs()->Exists(kPrefsOmahaEolDate) &&
-      !SystemState::Get()->prefs()->GetString(kPrefsOmahaEolDate,
-                                              &str_eol_date))
-    LOG(ERROR) << "Failed to retrieve kPrefsOmahaEolDate pref.";
-  out_status->eol_date = StringToEolDate(str_eol_date);
-
-  // A powerwash will take place either if the install plan says it is required
-  // or if an enterprise rollback is happening.
-  out_status->will_powerwash_after_reboot =
-      install_plan_ &&
-      (install_plan_->powerwash_required || install_plan_->is_rollback);
-
+  out_status->new_system_version = new_system_version_;
   return true;
 }
 
@@ -1488,13 +1329,13 @@
 uint32_t UpdateAttempter::GetErrorCodeFlags() {
   uint32_t flags = 0;
 
-  if (!SystemState::Get()->hardware()->IsNormalBootMode())
+  if (!system_state_->hardware()->IsNormalBootMode())
     flags |= static_cast<uint32_t>(ErrorCode::kDevModeFlag);
 
   if (install_plan_ && install_plan_->is_resume)
     flags |= static_cast<uint32_t>(ErrorCode::kResumedFlag);
 
-  if (!SystemState::Get()->hardware()->IsOfficialBuild())
+  if (!system_state_->hardware()->IsOfficialBuild())
     flags |= static_cast<uint32_t>(ErrorCode::kTestImageFlag);
 
   if (!omaha_request_params_->IsUpdateUrlOfficial()) {
@@ -1507,7 +1348,7 @@
 bool UpdateAttempter::ShouldCancel(ErrorCode* cancel_reason) {
   // Check if the channel we're attempting to update to is the same as the
   // target channel currently chosen by the user.
-  OmahaRequestParams* params = SystemState::Get()->request_params();
+  OmahaRequestParams* params = system_state_->request_params();
   if (params->download_channel() != params->target_channel()) {
     LOG(ERROR) << "Aborting download as target channel: "
                << params->target_channel()
@@ -1565,22 +1406,22 @@
     return false;
 
   LOG(ERROR) << "Update failed.";
-  SystemState::Get()->payload_state()->UpdateFailed(error_event_->error_code);
+  system_state_->payload_state()->UpdateFailed(error_event_->error_code);
 
   // Send metrics if it was a rollback.
   if (install_plan_ && install_plan_->is_rollback) {
-    SystemState::Get()->metrics_reporter()->ReportEnterpriseRollbackMetrics(
+    system_state_->metrics_reporter()->ReportEnterpriseRollbackMetrics(
         /*success=*/false, install_plan_->version);
   }
 
   // Send it to Omaha.
   LOG(INFO) << "Reporting the error event";
   auto error_event_action = std::make_unique<OmahaRequestAction>(
+      system_state_,
       error_event_.release(),  // Pass ownership.
       std::make_unique<LibcurlHttpFetcher>(GetProxyResolver(),
-                                           SystemState::Get()->hardware()),
-      false,
-      session_id_);
+                                           system_state_->hardware()),
+      false);
   processor_->EnqueueAction(std::move(error_event_action));
   SetStatusAndNotify(UpdateStatus::REPORTING_ERROR_EVENT);
   processor_->StartProcessing();
@@ -1618,14 +1459,12 @@
 
 void UpdateAttempter::PingOmaha() {
   if (!processor_->IsRunning()) {
-    ResetInteractivityFlags();
-
     auto ping_action = std::make_unique<OmahaRequestAction>(
+        system_state_,
         nullptr,
         std::make_unique<LibcurlHttpFetcher>(GetProxyResolver(),
-                                             SystemState::Get()->hardware()),
-        true,
-        "" /* session_id */);
+                                             system_state_->hardware()),
+        true);
     processor_->set_delegate(nullptr);
     processor_->EnqueueAction(std::move(ping_action));
     // Call StartProcessing() synchronously here to avoid any race conditions
@@ -1706,9 +1545,9 @@
   // in case we rebooted because of a crash of the old version, so we
   // can do a proper crash report with correct information.
   // This must be done before calling
-  // SystemState::Get()->payload_state()->UpdateEngineStarted() since it will
+  // system_state_->payload_state()->UpdateEngineStarted() since it will
   // delete SystemUpdated marker file.
-  if (SystemState::Get()->system_rebooted() &&
+  if (system_state_->system_rebooted() &&
       prefs_->Exists(kPrefsSystemUpdatedMarker)) {
     if (!prefs_->GetString(kPrefsPreviousVersion, &prev_version_)) {
       // If we fail to get the version string, make sure it stays empty.
@@ -1716,38 +1555,18 @@
     }
   }
 
-  MoveToPrefs({kPrefsLastRollCallPingDay, kPrefsLastActivePingDay});
-
-  SystemState::Get()->payload_state()->UpdateEngineStarted();
+  system_state_->payload_state()->UpdateEngineStarted();
   StartP2PAtStartup();
-
-  excluder_ = CreateExcluder();
-}
-
-void UpdateAttempter::MoveToPrefs(const vector<string>& keys) {
-  auto* powerwash_safe_prefs = SystemState::Get()->powerwash_safe_prefs();
-  for (const auto& key : keys) {
-    // Do not overwrite existing pref key with powerwash prefs.
-    if (!prefs_->Exists(key) && powerwash_safe_prefs->Exists(key)) {
-      string value;
-      if (!powerwash_safe_prefs->GetString(key, &value) ||
-          !prefs_->SetString(key, value)) {
-        PLOG(ERROR) << "Unable to add powerwash safe key " << key
-                    << " to prefs. Powerwash safe key will be deleted.";
-      }
-    }
-    // Delete keys regardless of operation success to preserve privacy.
-    powerwash_safe_prefs->Delete(key);
-  }
 }
 
 bool UpdateAttempter::StartP2PAtStartup() {
-  if (!SystemState::Get()->p2p_manager()->IsP2PEnabled()) {
+  if (system_state_ == nullptr ||
+      !system_state_->p2p_manager()->IsP2PEnabled()) {
     LOG(INFO) << "Not starting p2p at startup since it's not enabled.";
     return false;
   }
 
-  if (SystemState::Get()->p2p_manager()->CountSharedFiles() < 1) {
+  if (system_state_->p2p_manager()->CountSharedFiles() < 1) {
     LOG(INFO) << "Not starting p2p at startup since our application "
               << "is not sharing any files.";
     return false;
@@ -1757,19 +1576,22 @@
 }
 
 bool UpdateAttempter::StartP2PAndPerformHousekeeping() {
-  if (!SystemState::Get()->p2p_manager()->IsP2PEnabled()) {
+  if (system_state_ == nullptr)
+    return false;
+
+  if (!system_state_->p2p_manager()->IsP2PEnabled()) {
     LOG(INFO) << "Not starting p2p since it's not enabled.";
     return false;
   }
 
   LOG(INFO) << "Ensuring that p2p is running.";
-  if (!SystemState::Get()->p2p_manager()->EnsureP2PRunning()) {
+  if (!system_state_->p2p_manager()->EnsureP2PRunning()) {
     LOG(ERROR) << "Error starting p2p.";
     return false;
   }
 
   LOG(INFO) << "Performing p2p housekeeping.";
-  if (!SystemState::Get()->p2p_manager()->PerformHousekeeping()) {
+  if (!system_state_->p2p_manager()->PerformHousekeeping()) {
     LOG(ERROR) << "Error performing housekeeping for p2p.";
     return false;
   }
@@ -1804,7 +1626,7 @@
   return true;
 }
 
-bool UpdateAttempter::IsBusyOrUpdateScheduled() {
+bool UpdateAttempter::IsUpdateRunningOrScheduled() {
   return ((status_ != UpdateStatus::IDLE &&
            status_ != UpdateStatus::UPDATED_NEED_REBOOT) ||
           waiting_for_scheduled_check_);
@@ -1816,12 +1638,12 @@
   //  * The debugd dev features are accessible (i.e. in devmode with no owner).
   // This protects users running a base image, while still allowing a specific
   // window (gated by the debug dev features) where `cros flash` is usable.
-  if (!SystemState::Get()->hardware()->IsOfficialBuild()) {
+  if (!system_state_->hardware()->IsOfficialBuild()) {
     LOG(INFO) << "Non-official build; allowing any update source.";
     return true;
   }
 
-  if (SystemState::Get()->hardware()->AreDevFeaturesEnabled()) {
+  if (system_state_->hardware()->AreDevFeaturesEnabled()) {
     LOG(INFO) << "Developer features enabled; allowing custom update sources.";
     return true;
   }
@@ -1832,22 +1654,20 @@
 }
 
 void UpdateAttempter::ReportTimeToUpdateAppliedMetric() {
-  const policy::DevicePolicy* device_policy =
-      SystemState::Get()->device_policy();
+  const policy::DevicePolicy* device_policy = system_state_->device_policy();
   if (device_policy && device_policy->IsEnterpriseEnrolled()) {
     vector<policy::DevicePolicy::WeeklyTimeInterval> parsed_intervals;
     bool has_time_restrictions =
         device_policy->GetDisallowedTimeIntervals(&parsed_intervals);
 
     int64_t update_first_seen_at_int;
-    if (SystemState::Get()->prefs()->Exists(kPrefsUpdateFirstSeenAt)) {
-      if (SystemState::Get()->prefs()->GetInt64(kPrefsUpdateFirstSeenAt,
-                                                &update_first_seen_at_int)) {
+    if (system_state_->prefs()->Exists(kPrefsUpdateFirstSeenAt)) {
+      if (system_state_->prefs()->GetInt64(kPrefsUpdateFirstSeenAt,
+                                           &update_first_seen_at_int)) {
         TimeDelta update_delay =
-            SystemState::Get()->clock()->GetWallclockTime() -
+            system_state_->clock()->GetWallclockTime() -
             Time::FromInternalValue(update_first_seen_at_int);
-        SystemState::Get()
-            ->metrics_reporter()
+        system_state_->metrics_reporter()
             ->ReportEnterpriseUpdateSeenToDownloadDays(has_time_restrictions,
                                                        update_delay.InDays());
       }
diff --git a/cros/update_attempter.h b/update_attempter.h
similarity index 79%
rename from cros/update_attempter.h
rename to update_attempter.h
index 6010484..c27f8a4 100644
--- a/cros/update_attempter.h
+++ b/update_attempter.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_CROS_UPDATE_ATTEMPTER_H_
-#define UPDATE_ENGINE_CROS_UPDATE_ATTEMPTER_H_
+#ifndef UPDATE_ENGINE_UPDATE_ATTEMPTER_H_
+#define UPDATE_ENGINE_UPDATE_ATTEMPTER_H_
 
 #include <time.h>
 
@@ -26,25 +26,23 @@
 #include <vector>
 
 #include <base/bind.h>
-#include <base/guid.h>
 #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/daemon_state_interface.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/common/service_observer_interface.h"
-#include "update_engine/common/system_state.h"
-#include "update_engine/cros/chrome_browser_proxy_resolver.h"
-#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/omaha_request_params.h"
+#include "update_engine/omaha_response_handler_action.h"
+#include "update_engine/payload_consumer/download_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"
@@ -58,14 +56,13 @@
 class UpdateAttempter : public ActionProcessorDelegate,
                         public DownloadActionDelegate,
                         public CertificateChecker::Observer,
-                        public PostinstallRunnerAction::DelegateInterface,
-                        public DaemonStateInterface {
+                        public PostinstallRunnerAction::DelegateInterface {
  public:
   using UpdateStatus = update_engine::UpdateStatus;
   using UpdateAttemptFlags = update_engine::UpdateAttemptFlags;
   static const int kMaxDeltaUpdateFailures;
 
-  explicit UpdateAttempter(CertificateChecker* cert_checker);
+  UpdateAttempter(SystemState* system_state, CertificateChecker* cert_checker);
   ~UpdateAttempter() override;
 
   // Further initialization to be done post construction.
@@ -76,8 +73,19 @@
   virtual bool ScheduleUpdates();
 
   // Checks for update and, if a newer version is available, attempts to update
-  // the system.
-  virtual void Update(const chromeos_update_manager::UpdateCheckParams& params);
+  // 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 obey_proxies,
+                      bool interactive);
 
   // ActionProcessorDelegate methods:
   void ProcessingDone(const ActionProcessor* processor,
@@ -129,7 +137,7 @@
                               UpdateAttemptFlags flags);
 
   // This is the version of CheckForUpdate called by AttemptInstall API.
-  virtual bool CheckForInstall(const std::vector<std::string>& dlc_ids,
+  virtual bool CheckForInstall(const std::vector<std::string>& dlc_module_ids,
                                const std::string& omaha_url);
 
   // This is the internal entry point for going through a rollback. This will
@@ -150,9 +158,6 @@
   // UPDATED_NEED_REBOOT. Returns true on success, false otherwise.
   bool RebootIfNeeded();
 
-  // 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:
   void BytesReceived(uint64_t bytes_progressed,
                      uint64_t bytes_received,
@@ -172,9 +177,6 @@
   // Called at update_engine startup to do various house-keeping.
   void UpdateEngineStarted();
 
-  // Returns the |Excluder| that is currently held onto.
-  virtual ExcluderInterface* GetExcluder() const { return excluder_.get(); }
-
   // Reloads the device policy from libbrillo. Note: This method doesn't
   // cause a real-time policy fetch from the policy server. It just reloads the
   // latest value that libbrillo has cached. libbrillo fetches the policies
@@ -219,15 +221,15 @@
   // 'cros flash' to function properly).
   bool IsAnyUpdateSourceAllowed() const;
 
-  // |DaemonStateInterface| overrides.
-  bool StartUpdater() override;
-  void AddObserver(ServiceObserverInterface* observer) override {
+  // Add and remove a service observer.
+  void AddObserver(ServiceObserverInterface* observer) {
     service_observers_.insert(observer);
   }
-  void RemoveObserver(ServiceObserverInterface* observer) override {
+  void RemoveObserver(ServiceObserverInterface* observer) {
     service_observers_.erase(observer);
   }
-  const std::set<ServiceObserverInterface*>& service_observers() override {
+
+  const std::set<ServiceObserverInterface*>& service_observers() {
     return service_observers_;
   }
 
@@ -243,64 +245,38 @@
   FRIEND_TEST(UpdateAttempterTest, ActionCompletedOmahaRequestTest);
   FRIEND_TEST(UpdateAttempterTest, BootTimeInUpdateMarkerFile);
   FRIEND_TEST(UpdateAttempterTest, BroadcastCompleteDownloadTest);
-  FRIEND_TEST(UpdateAttempterTest, CalculateDlcParamsInstallTest);
-  FRIEND_TEST(UpdateAttempterTest, CalculateDlcParamsNoPrefFilesTest);
-  FRIEND_TEST(UpdateAttempterTest, CalculateDlcParamsNonParseableValuesTest);
-  FRIEND_TEST(UpdateAttempterTest, CalculateDlcParamsValidValuesTest);
-  FRIEND_TEST(UpdateAttempterTest, CalculateDlcParamsRemoveStaleMetadata);
   FRIEND_TEST(UpdateAttempterTest, ChangeToDownloadingOnReceivedBytesTest);
-  FRIEND_TEST(UpdateAttempterTest, CheckForInstallNotIdleFails);
   FRIEND_TEST(UpdateAttempterTest, CheckForUpdateAUDlcTest);
   FRIEND_TEST(UpdateAttempterTest, CreatePendingErrorEventTest);
   FRIEND_TEST(UpdateAttempterTest, CreatePendingErrorEventResumedTest);
   FRIEND_TEST(UpdateAttempterTest, DisableDeltaUpdateIfNeededTest);
   FRIEND_TEST(UpdateAttempterTest, DownloadProgressAccumulationTest);
   FRIEND_TEST(UpdateAttempterTest, InstallSetsStatusIdle);
-  FRIEND_TEST(UpdateAttempterTest, IsEnterpriseRollbackInGetStatusTrue);
-  FRIEND_TEST(UpdateAttempterTest, IsEnterpriseRollbackInGetStatusFalse);
-  FRIEND_TEST(UpdateAttempterTest,
-              PowerwashInGetStatusTrueBecausePowerwashRequired);
-  FRIEND_TEST(UpdateAttempterTest, PowerwashInGetStatusTrueBecauseRollback);
   FRIEND_TEST(UpdateAttempterTest, MarkDeltaUpdateFailureTest);
   FRIEND_TEST(UpdateAttempterTest, PingOmahaTest);
-  FRIEND_TEST(UpdateAttempterTest, ProcessingDoneInstallError);
-  FRIEND_TEST(UpdateAttempterTest, ProcessingDoneUpdateError);
   FRIEND_TEST(UpdateAttempterTest, ReportDailyMetrics);
   FRIEND_TEST(UpdateAttempterTest, RollbackNotAllowed);
   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);
   FRIEND_TEST(UpdateAttempterTest, RollbackMetricsRollbackSuccess);
   FRIEND_TEST(UpdateAttempterTest, ScheduleErrorEventActionNoEventTest);
   FRIEND_TEST(UpdateAttempterTest, ScheduleErrorEventActionTest);
-  FRIEND_TEST(UpdateAttempterTest, SessionIdTestEnforceEmptyStrPingOmaha);
-  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);
   FRIEND_TEST(UpdateAttempterTest, UpdateDeferredByPolicyTest);
   FRIEND_TEST(UpdateAttempterTest, UpdateIsNotRunningWhenUpdateAvailable);
-  FRIEND_TEST(UpdateAttempterTest, GetSuccessfulDlcIds);
-  FRIEND_TEST(UpdateAttempterTest, QuickFixTokenWhenDeviceIsEnterpriseEnrolled);
-  FRIEND_TEST(UpdateAttempterTest, MoveToPrefs);
 
   // Returns the special flags to be added to ErrorCode values based on the
   // parameters used in the current update attempt.
   uint32_t GetErrorCodeFlags();
 
-  // ActionProcessorDelegate methods |ProcessingDone()| internal helpers.
-  void ProcessingDoneInternal(const ActionProcessor* processor, ErrorCode code);
-  void ProcessingDoneUpdate(const ActionProcessor* processor, ErrorCode code);
-  void ProcessingDoneInstall(const ActionProcessor* processor, ErrorCode code);
-
   // CertificateChecker::Observer method.
   // Report metrics about the certificate being checked.
   void CertificateChecked(ServerToCheck server_to_check,
@@ -341,8 +317,10 @@
   void MarkDeltaUpdateFailure();
 
   ProxyResolver* GetProxyResolver() {
+#if USE_CHROME_NETWORK_PROXY
     if (obeying_proxies_)
       return &chrome_proxy_resolver_;
+#endif  // USE_CHROME_NETWORK_PROXY
     return &direct_proxy_resolver_;
   }
 
@@ -356,13 +334,18 @@
   // 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 chromeos_update_manager::UpdateCheckParams& params);
+  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 obey_proxies,
+                             bool interactive);
 
   // Calculates all the scattering related parameters (such as waiting period,
   // which type of scattering is enabled, etc.) and also updates/deletes
   // the corresponding prefs file used in scattering. Should be called
-  // only after the device policy has been loaded and set in the system state.
+  // only after the device policy has been loaded and set in the system_state_.
   void CalculateScatteringParams(bool interactive);
 
   // Sets a random value for the waiting period to wait for before downloading
@@ -388,10 +371,6 @@
   // on the |omaha_request_params_| object.
   void CalculateP2PParams(bool interactive);
 
-  // For each key, reads value from powerwash safe prefs and adds it to prefs
-  // if key doesnt already exist. Then deletes the powerwash safe keys.
-  void MoveToPrefs(const std::vector<std::string>& keys);
-
   // Starts P2P if it's enabled and there are files to actually share.
   // Called only at program startup. Returns true only if p2p was
   // started and housekeeping was performed.
@@ -419,8 +398,8 @@
   // policy is available again.
   void UpdateRollbackHappened();
 
-  // Returns if an update is: running, applied and needs reboot, or scheduled.
-  bool IsBusyOrUpdateScheduled();
+  // Returns whether an update is currently running or scheduled.
+  bool IsUpdateRunningOrScheduled();
 
   void CalculateStagingParams(bool interactive);
 
@@ -429,32 +408,6 @@
   // will only be reported for enterprise enrolled devices.
   void ReportTimeToUpdateAppliedMetric();
 
-  // Resets interactivity and forced update flags.
-  void ResetInteractivityFlags();
-
-  // Resets all the DLC prefs.
-  bool ResetDlcPrefs(const std::string& dlc_id);
-
-  // Sets given pref key for DLC and platform.
-  void SetPref(const std::string& pref_key,
-               const std::string& pref_value,
-               const std::string& payload_id);
-
-  // Get the integer values from the DLC metadata for |kPrefsPingLastActive|
-  // or |kPrefsPingLastRollcall|.
-  // The value is equal to -2 when the value cannot be read or is not numeric.
-  // The value is equal to -1 the first time it is being sent, which is
-  // when the metadata file doesn't exist.
-  int64_t GetPingMetadata(const std::string& metadata_key) const;
-
-  // Calculates the update parameters for DLCs. Sets the |dlc_ids_|
-  // parameter on the |omaha_request_params_| object.
-  void CalculateDlcParams();
-
-  // Returns the list of DLC IDs that were installed/updated, excluding the ones
-  // which had "noupdate" in the Omaha response.
-  std::vector<std::string> GetSuccessfulDlcIds();
-
   // Last status notification timestamp used for throttling. Use monotonic
   // TimeTicks to ensure that notifications are sent even if the system clock is
   // set back in the middle of an update.
@@ -462,11 +415,15 @@
 
   // Our two proxy resolvers
   DirectProxyResolver direct_proxy_resolver_;
+#if USE_CHROME_NETWORK_PROXY
   ChromeBrowserProxyResolver chrome_proxy_resolver_;
+#endif  // USE_CHROME_NETWORK_PROXY
 
   std::unique_ptr<ActionProcessor> processor_;
 
-  ActionProcessor aux_processor_;
+  // External state of the system outside the update_engine process
+  // carved out separately to mock out easily in unit tests.
+  SystemState* system_state_;
 
   // Pointer to the certificate checker instance to use.
   CertificateChecker* cert_checker_;
@@ -478,7 +435,7 @@
   std::unique_ptr<InstallPlan> install_plan_;
 
   // Pointer to the preferences store interface. This is just a cached
-  // copy of SystemState::Get()->prefs() because it's used in many methods and
+  // copy of system_state->prefs() because it's used in many methods and
   // is convenient this way.
   PrefsInterface* prefs_ = nullptr;
 
@@ -503,6 +460,7 @@
   int64_t last_checked_time_ = 0;
   std::string prev_version_;
   std::string new_version_ = "0.0.0.0";
+  std::string new_system_version_;
   uint64_t new_payload_size_ = 0;
   // Flags influencing all periodic update checks
   UpdateAttemptFlags update_attempt_flags_ = UpdateAttemptFlags::kNone;
@@ -551,7 +509,7 @@
   std::string forced_omaha_url_;
 
   // A list of DLC module IDs.
-  std::vector<std::string> dlc_ids_;
+  std::vector<std::string> dlc_module_ids_;
   // Whether the operation is install (write to the current slot not the
   // inactive slot).
   bool is_install_;
@@ -560,12 +518,6 @@
   base::TimeDelta staging_wait_time_;
   chromeos_update_manager::StagingSchedule staging_schedule_;
 
-  // This is the session ID used to track update flow to Omaha.
-  std::string session_id_;
-
-  // Interface for excluder.
-  std::unique_ptr<ExcluderInterface> excluder_;
-
   DISALLOW_COPY_AND_ASSIGN(UpdateAttempter);
 };
 
@@ -578,4 +530,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_CROS_UPDATE_ATTEMPTER_H_
+#endif  // UPDATE_ENGINE_UPDATE_ATTEMPTER_H_
diff --git a/aosp/update_attempter_android.cc b/update_attempter_android.cc
similarity index 90%
rename from aosp/update_attempter_android.cc
rename to update_attempter_android.cc
index 4636c43..b7d119f 100644
--- a/aosp/update_attempter_android.cc
+++ b/update_attempter_android.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/aosp/update_attempter_android.h"
+#include "update_engine/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/aosp/cleanup_previous_update_action.h"
+#include "update_engine/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"
@@ -64,6 +64,7 @@
 using base::Time;
 using base::TimeDelta;
 using base::TimeTicks;
+using std::shared_ptr;
 using std::string;
 using std::vector;
 using update_engine::UpdateEngineStatus;
@@ -132,17 +133,14 @@
     DaemonStateInterface* daemon_state,
     PrefsInterface* prefs,
     BootControlInterface* boot_control,
-    HardwareInterface* hardware,
-    std::unique_ptr<ApexHandlerInterface> apex_handler)
+    HardwareInterface* hardware)
     : daemon_state_(daemon_state),
       prefs_(prefs),
       boot_control_(boot_control),
       hardware_(hardware),
-      apex_handler_android_(std::move(apex_handler)),
       processor_(new ActionProcessor()),
       clock_(new Clock()) {
-  metrics_reporter_ = metrics::CreateMetricsReporter(
-      boot_control_->GetDynamicPartitionControl(), &install_plan_);
+  metrics_reporter_ = metrics::CreateMetricsReporter();
   network_selector_ = network::CreateNetworkSelector();
 }
 
@@ -183,7 +181,7 @@
     return LogAndSetError(
         error, FROM_HERE, "Already processing an update, cancel it first.");
   }
-  DCHECK_EQ(status_, UpdateStatus::IDLE);
+  DCHECK(status_ == UpdateStatus::IDLE);
 
   std::map<string, string> headers;
   if (!ParseKeyValuePairHeaders(key_value_pair_headers, &headers, error)) {
@@ -319,19 +317,6 @@
     int64_t payload_size,
     const vector<string>& key_value_pair_headers,
     brillo::ErrorPtr* error) {
-  // update_engine state must be checked before modifying payload_fd_ otherwise
-  // already running update will be terminated (existing file descriptor will be
-  // closed)
-  if (status_ == UpdateStatus::UPDATED_NEED_REBOOT) {
-    return LogAndSetError(
-        error, FROM_HERE, "An update already applied, waiting for reboot");
-  }
-  if (processor_->IsRunning()) {
-    return LogAndSetError(
-        error, FROM_HERE, "Already processing an update, cancel it first.");
-  }
-  DCHECK_EQ(status_, UpdateStatus::IDLE);
-
   payload_fd_.reset(dup(fd));
   const string payload_url = "fd://" + std::to_string(payload_fd_.get());
 
@@ -364,12 +349,6 @@
   LOG(INFO) << "Attempting to reset state from "
             << UpdateStatusToString(status_) << " to UpdateStatus::IDLE";
 
-  if (apex_handler_android_ != nullptr) {
-    LOG(INFO) << "Cleaning up reserved space for compressed APEX (if any)";
-    std::vector<ApexInfo> apex_infos_blank;
-    apex_handler_android_->AllocateSpace(apex_infos_blank);
-  }
-
   switch (status_) {
     case UpdateStatus::IDLE: {
       if (!boot_control_->GetDynamicPartitionControl()->ResetUpdate(prefs_)) {
@@ -395,9 +374,6 @@
       // Resets the warm reset property since we won't switch the slot.
       hardware_->SetWarmReset(false);
 
-      // Resets the vbmeta digest.
-      hardware_->SetVbmetaDigestForInactiveSlot(true /* reset */);
-
       // Remove update progress for DeltaPerformer and remove snapshots.
       if (!boot_control_->GetDynamicPartitionControl()->ResetUpdate(prefs_))
         ret_value = false;
@@ -531,7 +507,7 @@
         return LogAndSetError(
             error, FROM_HERE, "Failed to hash " + partition_path);
       }
-      if (!PartitionWriter::ValidateSourceHash(
+      if (!DeltaPerformer::ValidateSourceHash(
               source_hash, operation, fd, &errorcode)) {
         return false;
       }
@@ -600,9 +576,9 @@
     cleanup_previous_update_code_ = code;
     NotifyCleanupPreviousUpdateCallbacksAndClear();
   }
-  // download_progress_ is actually used by other actions, such as
-  // filesystem_verify_action. Therefore we always clear it.
-  download_progress_ = 0;
+  if (type == DownloadAction::StaticType()) {
+    download_progress_ = 0;
+  }
   if (type == PostinstallRunnerAction::StaticType()) {
     bool succeeded =
         code == ErrorCode::kSuccess || code == ErrorCode::kUpdatedButNotActive;
@@ -616,11 +592,8 @@
     SetStatusAndNotify(UpdateStatus::CLEANUP_PREVIOUS_UPDATE);
   }
   if (type == DownloadAction::StaticType()) {
-    auto download_action = static_cast<DownloadAction*>(action);
-    install_plan_ = *download_action->install_plan();
-    SetStatusAndNotify(UpdateStatus::VERIFYING);
-  } else if (type == FilesystemVerifierAction::StaticType()) {
     SetStatusAndNotify(UpdateStatus::FINALIZING);
+  } else if (type == FilesystemVerifierAction::StaticType()) {
     prefs_->SetBoolean(kPrefsVerityWritten, true);
   }
 }
@@ -671,11 +644,6 @@
   }
 }
 
-void UpdateAttempterAndroid::OnVerifyProgressUpdate(double progress) {
-  assert(status_ == UpdateStatus::VERIFYING);
-  ProgressUpdate(progress);
-}
-
 void UpdateAttempterAndroid::ScheduleProcessingStart() {
   LOG(INFO) << "Scheduling an action processor start.";
   brillo::MessageLoop::current()->PostTask(
@@ -757,15 +725,15 @@
       std::make_unique<DownloadAction>(prefs_,
                                        boot_control_,
                                        hardware_,
+                                       nullptr,  // system_state, not used.
                                        fetcher,  // passes ownership
                                        true /* interactive */);
   download_action->set_delegate(this);
   download_action->set_base_offset(base_offset_);
-  auto filesystem_verifier_action = std::make_unique<FilesystemVerifierAction>(
-      boot_control_->GetDynamicPartitionControl());
+  auto filesystem_verifier_action =
+      std::make_unique<FilesystemVerifierAction>();
   auto postinstall_runner_action =
       std::make_unique<PostinstallRunnerAction>(boot_control_, hardware_);
-  filesystem_verifier_action->set_delegate(this);
   postinstall_runner_action->set_delegate(this);
 
   // Bond them together. We have to use the leaf-types when calling
@@ -827,6 +795,7 @@
   TimeDelta duration_uptime = clock_->GetMonotonicTime() - monotonic_time_start;
 
   metrics_reporter_->ReportUpdateAttemptMetrics(
+      nullptr,  // system_state
       static_cast<int>(attempt_number),
       payload_type,
       duration,
@@ -890,25 +859,20 @@
   string current_version =
       android::base::GetProperty("ro.build.version.incremental", "");
   TEST_AND_RETURN(!current_version.empty());
-  const auto current_slot = boot_control_->GetCurrentSlot();
 
   // If there's no record of previous version (e.g. due to a data wipe), we
   // save the info of current boot and skip the metrics report.
   if (!prefs_->Exists(kPrefsPreviousVersion)) {
     prefs_->SetString(kPrefsBootId, current_boot_id);
     prefs_->SetString(kPrefsPreviousVersion, current_version);
-    prefs_->SetInt64(std::string{kPrefsPreviousSlot},
-                     boot_control_->GetCurrentSlot());
     ClearMetricsPrefs();
     return;
   }
-  int64_t previous_slot = -1;
-  prefs_->GetInt64(kPrefsPreviousSlot, &previous_slot);
   string previous_version;
-  // update_engine restarted under the same build and same slot.
+  // update_engine restarted under the same build.
   // TODO(xunchang) identify and report rollback by checking UpdateMarker.
   if (prefs_->GetString(kPrefsPreviousVersion, &previous_version) &&
-      previous_version == current_version && previous_slot == current_slot) {
+      previous_version == current_version) {
     string last_boot_id;
     bool is_reboot = prefs_->Exists(kPrefsBootId) &&
                      (prefs_->GetString(kPrefsBootId, &last_boot_id) &&
@@ -928,8 +892,6 @@
   // TODO(xunchang) check the build version is larger than the previous one.
   prefs_->SetString(kPrefsBootId, current_boot_id);
   prefs_->SetString(kPrefsPreviousVersion, current_version);
-  prefs_->SetInt64(std::string{kPrefsPreviousSlot},
-                   boot_control_->GetCurrentSlot());
 
   bool previous_attempt_exists = prefs_->Exists(kPrefsPayloadAttemptNumber);
   // |kPrefsPayloadAttemptNumber| should be cleared upon successful update.
@@ -992,20 +954,6 @@
     return 0;
   }
 
-  std::vector<ApexInfo> apex_infos(manifest.apex_info().begin(),
-                                   manifest.apex_info().end());
-  uint64_t apex_size_required = 0;
-  if (apex_handler_android_ != nullptr) {
-    auto result = apex_handler_android_->CalculateSize(apex_infos);
-    if (!result.ok()) {
-      LogAndSetError(error,
-                     FROM_HERE,
-                     "Failed to calculate size required for compressed APEX");
-      return 0;
-    }
-    apex_size_required = *result;
-  }
-
   string payload_id = GetPayloadId(headers);
   uint64_t required_size = 0;
   if (!DeltaPerformer::PreparePartitionsForUpdate(prefs_,
@@ -1019,19 +967,11 @@
       return 0;
     } else {
       LOG(ERROR) << "Insufficient space for payload: " << required_size
-                 << " bytes, apex decompression: " << apex_size_required
                  << " bytes";
-      return required_size + apex_size_required;
+      return required_size;
     }
   }
 
-  if (apex_size_required > 0 && apex_handler_android_ != nullptr &&
-      !apex_handler_android_->AllocateSpace(apex_infos)) {
-    LOG(ERROR) << "Insufficient space for apex decompression: "
-               << apex_size_required << " bytes";
-    return apex_size_required;
-  }
-
   LOG(INFO) << "Successfully allocated space for payload.";
   return 0;
 }
diff --git a/aosp/update_attempter_android.h b/update_attempter_android.h
similarity index 89%
rename from aosp/update_attempter_android.h
rename to update_attempter_android.h
index 70938bc..f8c78de 100644
--- a/aosp/update_attempter_android.h
+++ b/update_attempter_android.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-#ifndef UPDATE_ENGINE_AOSP_UPDATE_ATTEMPTER_ANDROID_H_
-#define UPDATE_ENGINE_AOSP_UPDATE_ATTEMPTER_ANDROID_H_
+#ifndef UPDATE_ENGINE_UPDATE_ATTEMPTER_ANDROID_H_
+#define UPDATE_ENGINE_UPDATE_ATTEMPTER_ANDROID_H_
 
 #include <stdint.h>
 
@@ -26,22 +26,20 @@
 #include <android-base/unique_fd.h>
 #include <base/time/time.h>
 
-#include "update_engine/aosp/apex_handler_interface.h"
-#include "update_engine/aosp/service_delegate_android_interface.h"
 #include "update_engine/client_library/include/update_engine/update_status.h"
 #include "update_engine/common/action_processor.h"
 #include "update_engine/common/boot_control_interface.h"
 #include "update_engine/common/clock.h"
-#include "update_engine/common/daemon_state_interface.h"
-#include "update_engine/common/download_action.h"
 #include "update_engine/common/hardware_interface.h"
-#include "update_engine/common/metrics_reporter_interface.h"
-#include "update_engine/common/network_selector_interface.h"
 #include "update_engine/common/prefs_interface.h"
-#include "update_engine/common/service_observer_interface.h"
+#include "update_engine/daemon_state_interface.h"
+#include "update_engine/metrics_reporter_interface.h"
 #include "update_engine/metrics_utils.h"
-#include "update_engine/payload_consumer/filesystem_verifier_action.h"
+#include "update_engine/network_selector_interface.h"
+#include "update_engine/payload_consumer/download_action.h"
 #include "update_engine/payload_consumer/postinstall_runner_action.h"
+#include "update_engine/service_delegate_android_interface.h"
+#include "update_engine/service_observer_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -49,7 +47,6 @@
     : public ServiceDelegateAndroidInterface,
       public ActionProcessorDelegate,
       public DownloadActionDelegate,
-      public FilesystemVerifyDelegate,
       public PostinstallRunnerAction::DelegateInterface,
       public CleanupPreviousUpdateActionDelegateInterface {
  public:
@@ -58,8 +55,7 @@
   UpdateAttempterAndroid(DaemonStateInterface* daemon_state,
                          PrefsInterface* prefs,
                          BootControlInterface* boot_control_,
-                         HardwareInterface* hardware_,
-                         std::unique_ptr<ApexHandlerInterface> apex_handler);
+                         HardwareInterface* hardware_);
   ~UpdateAttempterAndroid() override;
 
   // Further initialization to be done post construction.
@@ -105,9 +101,6 @@
   bool ShouldCancel(ErrorCode* cancel_reason) override;
   void DownloadComplete() override;
 
-  // FilesystemVerifyDelegate overrides
-  void OnVerifyProgressUpdate(double progress) override;
-
   // PostinstallRunnerAction::DelegateInterface
   void ProgressUpdate(double progress) override;
 
@@ -207,8 +200,6 @@
   BootControlInterface* boot_control_;
   HardwareInterface* hardware_;
 
-  std::unique_ptr<ApexHandlerInterface> apex_handler_android_;
-
   // Last status notification timestamp used for throttling. Use monotonic
   // TimeTicks to ensure that notifications are sent even if the system clock is
   // set back in the middle of an update.
@@ -250,4 +241,4 @@
 
 }  // namespace chromeos_update_engine
 
-#endif  // UPDATE_ENGINE_AOSP_UPDATE_ATTEMPTER_ANDROID_H_
+#endif  // UPDATE_ENGINE_UPDATE_ATTEMPTER_ANDROID_H_
diff --git a/aosp/update_attempter_android_unittest.cc b/update_attempter_android_unittest.cc
similarity index 91%
rename from aosp/update_attempter_android_unittest.cc
rename to update_attempter_android_unittest.cc
index f73df16..721b735 100644
--- a/aosp/update_attempter_android_unittest.cc
+++ b/update_attempter_android_unittest.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "update_engine/aosp/update_attempter_android.h"
+#include "update_engine/update_attempter_android.h"
 
 #include <memory>
 #include <string>
@@ -24,16 +24,15 @@
 #include <base/time/time.h>
 #include <gtest/gtest.h>
 
-#include "common/constants.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;
@@ -64,14 +63,14 @@
         std::move(payload));
   }
 
+  UpdateAttempterAndroid update_attempter_android_{
+      &daemon_state_, &prefs_, &boot_control_, &hardware_};
+
   DaemonStateAndroid daemon_state_;
   FakePrefs prefs_;
   FakeBootControl boot_control_;
   FakeHardware hardware_;
 
-  UpdateAttempterAndroid update_attempter_android_{
-      &daemon_state_, &prefs_, &boot_control_, &hardware_, nullptr};
-
   FakeClock* clock_;
   testing::NiceMock<MockMetricsReporter>* metrics_reporter_;
 };
@@ -82,8 +81,6 @@
   prefs_.SetString(kPrefsPreviousVersion, build_version);
   prefs_.SetString(kPrefsBootId, "oldboot");
   prefs_.SetInt64(kPrefsNumReboots, 1);
-  prefs_.SetInt64(kPrefsPreviousSlot, 1);
-  boot_control_.SetCurrentSlot(1);
 
   EXPECT_CALL(*metrics_reporter_, ReportTimeToReboot(_)).Times(0);
   update_attempter_android_.Init();
@@ -91,15 +88,15 @@
   // Check that the boot_id and reboot_count are updated.
   std::string boot_id;
   utils::GetBootId(&boot_id);
-  ASSERT_TRUE(prefs_.Exists(kPrefsBootId));
+  EXPECT_TRUE(prefs_.Exists(kPrefsBootId));
   std::string prefs_boot_id;
-  ASSERT_TRUE(prefs_.GetString(kPrefsBootId, &prefs_boot_id));
-  ASSERT_EQ(boot_id, prefs_boot_id);
+  EXPECT_TRUE(prefs_.GetString(kPrefsBootId, &prefs_boot_id));
+  EXPECT_EQ(boot_id, prefs_boot_id);
 
-  ASSERT_TRUE(prefs_.Exists(kPrefsNumReboots));
+  EXPECT_TRUE(prefs_.Exists(kPrefsNumReboots));
   int64_t reboot_count;
-  ASSERT_TRUE(prefs_.GetInt64(kPrefsNumReboots, &reboot_count));
-  ASSERT_EQ(2, reboot_count);
+  EXPECT_TRUE(prefs_.GetInt64(kPrefsNumReboots, &reboot_count));
+  EXPECT_EQ(2, reboot_count);
 }
 
 TEST_F(UpdateAttempterAndroidTest, UpdatePrefsBuildVersionChangeOnInit) {
@@ -141,7 +138,8 @@
   TimeDelta duration_uptime = up_time - Time::FromInternalValue(12345);
   EXPECT_CALL(
       *metrics_reporter_,
-      ReportUpdateAttemptMetrics(2,
+      ReportUpdateAttemptMetrics(_,
+                                 2,
                                  _,
                                  duration,
                                  duration_uptime,
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
new file mode 100644
index 0000000..579c736
--- /dev/null
+++ b/update_attempter_unittest.cc
@@ -0,0 +1,1569 @@
+//
+// Copyright (C) 2012 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_attempter.h"
+
+#include <stdint.h>
+
+#include <memory>
+
+#include <base/files/file_util.h>
+#include <base/message_loop/message_loop.h>
+#include <brillo/message_loops/base_message_loop.h>
+#include <brillo/message_loops/message_loop.h>
+#include <brillo/message_loops/message_loop_utils.h>
+#include <gtest/gtest.h>
+#include <policy/libpolicy.h>
+#include <policy/mock_device_policy.h>
+#include <policy/mock_libpolicy.h>
+
+#include "update_engine/common/dlcservice_interface.h"
+#include "update_engine/common/fake_clock.h"
+#include "update_engine/common/fake_prefs.h"
+#include "update_engine/common/mock_action.h"
+#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/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/mock_p2p_manager.h"
+#include "update_engine/mock_payload_state.h"
+#include "update_engine/mock_service_observer.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"
+#include "update_engine/payload_consumer/postinstall_runner_action.h"
+#include "update_engine/update_boot_flags_action.h"
+
+using base::Time;
+using base::TimeDelta;
+using chromeos_update_manager::EvalStatus;
+using chromeos_update_manager::StagingSchedule;
+using chromeos_update_manager::UpdateCheckParams;
+using policy::DevicePolicy;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+using testing::_;
+using testing::DoAll;
+using testing::Field;
+using testing::InSequence;
+using testing::Ne;
+using testing::NiceMock;
+using testing::Pointee;
+using testing::Property;
+using testing::Return;
+using testing::ReturnPointee;
+using testing::ReturnRef;
+using testing::SaveArg;
+using testing::SetArgPointee;
+using update_engine::UpdateAttemptFlags;
+using update_engine::UpdateEngineStatus;
+using update_engine::UpdateStatus;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+class MockDlcService : public DlcServiceInterface {
+ public:
+  MOCK_METHOD1(GetInstalled, bool(vector<string>*));
+};
+
+}  // namespace
+
+const char kRollbackVersion[] = "10575.39.2";
+
+// Test a subclass rather than the main class directly so that we can mock out
+// methods within the class. There're explicit unit tests for the mocked out
+// methods.
+class UpdateAttempterUnderTest : public UpdateAttempter {
+ public:
+  explicit UpdateAttempterUnderTest(SystemState* system_state)
+      : UpdateAttempter(system_state, nullptr) {}
+
+  // Wrap the update scheduling method, allowing us to opt out of scheduled
+  // updates for testing purposes.
+  bool ScheduleUpdates() override {
+    schedule_updates_called_ = true;
+    if (do_schedule_updates_) {
+      UpdateAttempter::ScheduleUpdates();
+    } else {
+      LOG(INFO) << "[TEST] Update scheduling disabled.";
+    }
+    return true;
+  }
+  void EnableScheduleUpdates() { do_schedule_updates_ = true; }
+  void DisableScheduleUpdates() { do_schedule_updates_ = false; }
+
+  // Indicates whether ScheduleUpdates() was called.
+  bool schedule_updates_called() const { return schedule_updates_called_; }
+
+  // Need to expose forced_omaha_url_ so we can test it.
+  const string& forced_omaha_url() const { return forced_omaha_url_; }
+
+ private:
+  bool schedule_updates_called_ = false;
+  bool do_schedule_updates_ = true;
+};
+
+class UpdateAttempterTest : public ::testing::Test {
+ protected:
+  UpdateAttempterTest()
+      : certificate_checker_(fake_system_state_.mock_prefs(),
+                             &openssl_wrapper_) {
+    // Override system state members.
+    fake_system_state_.set_connection_manager(&mock_connection_manager);
+    fake_system_state_.set_update_attempter(&attempter_);
+    fake_system_state_.set_dlcservice(&mock_dlcservice_);
+    loop_.SetAsCurrent();
+
+    certificate_checker_.Init();
+
+    attempter_.set_forced_update_pending_callback(
+        new base::Callback<void(bool, bool)>(base::Bind([](bool, bool) {})));
+    // Finish initializing the attempter.
+    attempter_.Init();
+  }
+
+  void SetUp() override {
+    EXPECT_NE(nullptr, attempter_.system_state_);
+    EXPECT_EQ(0, attempter_.http_response_code_);
+    EXPECT_EQ(UpdateStatus::IDLE, attempter_.status_);
+    EXPECT_EQ(0.0, attempter_.download_progress_);
+    EXPECT_EQ(0, attempter_.last_checked_time_);
+    EXPECT_EQ("0.0.0.0", attempter_.new_version_);
+    EXPECT_EQ(0ULL, attempter_.new_payload_size_);
+    processor_ = new NiceMock<MockActionProcessor>();
+    attempter_.processor_.reset(processor_);  // Transfers ownership.
+    prefs_ = fake_system_state_.mock_prefs();
+
+    // Set up store/load semantics of P2P properties via the mock PayloadState.
+    actual_using_p2p_for_downloading_ = false;
+    EXPECT_CALL(*fake_system_state_.mock_payload_state(),
+                SetUsingP2PForDownloading(_))
+        .WillRepeatedly(SaveArg<0>(&actual_using_p2p_for_downloading_));
+    EXPECT_CALL(*fake_system_state_.mock_payload_state(),
+                GetUsingP2PForDownloading())
+        .WillRepeatedly(ReturnPointee(&actual_using_p2p_for_downloading_));
+    actual_using_p2p_for_sharing_ = false;
+    EXPECT_CALL(*fake_system_state_.mock_payload_state(),
+                SetUsingP2PForSharing(_))
+        .WillRepeatedly(SaveArg<0>(&actual_using_p2p_for_sharing_));
+    EXPECT_CALL(*fake_system_state_.mock_payload_state(),
+                GetUsingP2PForDownloading())
+        .WillRepeatedly(ReturnPointee(&actual_using_p2p_for_sharing_));
+  }
+
+ public:
+  void ScheduleQuitMainLoop();
+
+  // Callbacks to run the different tests from the main loop.
+  void UpdateTestStart();
+  void UpdateTestVerify();
+  void RollbackTestStart(bool enterprise_rollback, bool valid_slot);
+  void RollbackTestVerify();
+  void PingOmahaTestStart();
+  void ReadScatterFactorFromPolicyTestStart();
+  void DecrementUpdateCheckCountTestStart();
+  void NoScatteringDoneDuringManualUpdateTestStart();
+  void P2PNotEnabledStart();
+  void P2PEnabledStart();
+  void P2PEnabledInteractiveStart();
+  void P2PEnabledStartingFailsStart();
+  void P2PEnabledHousekeepingFailsStart();
+  void ResetRollbackHappenedStart(bool is_consumer,
+                                  bool is_policy_available,
+                                  bool expected_reset);
+  // Staging related callbacks.
+  void SetUpStagingTest(const StagingSchedule& schedule, FakePrefs* prefs);
+  void CheckStagingOff();
+  void StagingSetsPrefsAndTurnsOffScatteringStart();
+  void StagingOffIfInteractiveStart();
+  void StagingOffIfOobeStart();
+
+  bool actual_using_p2p_for_downloading() {
+    return actual_using_p2p_for_downloading_;
+  }
+  bool actual_using_p2p_for_sharing() { return actual_using_p2p_for_sharing_; }
+
+  base::MessageLoopForIO base_loop_;
+  brillo::BaseMessageLoop loop_{&base_loop_};
+
+  FakeSystemState fake_system_state_;
+  UpdateAttempterUnderTest attempter_{&fake_system_state_};
+  OpenSSLWrapper openssl_wrapper_;
+  CertificateChecker certificate_checker_;
+  MockDlcService mock_dlcservice_;
+
+  NiceMock<MockActionProcessor>* processor_;
+  NiceMock<MockPrefs>* prefs_;  // Shortcut to fake_system_state_->mock_prefs().
+  NiceMock<MockConnectionManager> mock_connection_manager;
+
+  bool actual_using_p2p_for_downloading_;
+  bool actual_using_p2p_for_sharing_;
+};
+
+void UpdateAttempterTest::ScheduleQuitMainLoop() {
+  loop_.PostTask(
+      FROM_HERE,
+      base::Bind([](brillo::BaseMessageLoop* loop) { loop->BreakLoop(); },
+                 base::Unretained(&loop_)));
+}
+
+TEST_F(UpdateAttempterTest, ActionCompletedDownloadTest) {
+  unique_ptr<MockHttpFetcher> fetcher(new MockHttpFetcher("", 0, nullptr));
+  fetcher->FailTransfer(503);  // Sets the HTTP response code.
+  DownloadAction action(prefs_,
+                        nullptr,
+                        nullptr,
+                        nullptr,
+                        fetcher.release(),
+                        false /* interactive */);
+  EXPECT_CALL(*prefs_, GetInt64(kPrefsDeltaUpdateFailures, _)).Times(0);
+  attempter_.ActionCompleted(nullptr, &action, ErrorCode::kSuccess);
+  EXPECT_EQ(UpdateStatus::FINALIZING, attempter_.status());
+  EXPECT_EQ(0.0, attempter_.download_progress_);
+  ASSERT_EQ(nullptr, attempter_.error_event_.get());
+}
+
+TEST_F(UpdateAttempterTest, ActionCompletedErrorTest) {
+  MockAction action;
+  EXPECT_CALL(action, Type()).WillRepeatedly(Return("MockAction"));
+  attempter_.status_ = UpdateStatus::DOWNLOADING;
+  EXPECT_CALL(*prefs_, GetInt64(kPrefsDeltaUpdateFailures, _))
+      .WillOnce(Return(false));
+  attempter_.ActionCompleted(nullptr, &action, ErrorCode::kError);
+  ASSERT_NE(nullptr, attempter_.error_event_.get());
+}
+
+TEST_F(UpdateAttempterTest, DownloadProgressAccumulationTest) {
+  // Simple test case, where all the values match (nothing was skipped)
+  uint64_t bytes_progressed_1 = 1024 * 1024;  // 1MB
+  uint64_t bytes_progressed_2 = 1024 * 1024;  // 1MB
+  uint64_t bytes_received_1 = bytes_progressed_1;
+  uint64_t bytes_received_2 = bytes_received_1 + bytes_progressed_2;
+  uint64_t bytes_total = 20 * 1024 * 1024;  // 20MB
+
+  double progress_1 =
+      static_cast<double>(bytes_received_1) / static_cast<double>(bytes_total);
+  double progress_2 =
+      static_cast<double>(bytes_received_2) / static_cast<double>(bytes_total);
+
+  EXPECT_EQ(0.0, attempter_.download_progress_);
+  // This is set via inspecting the InstallPlan payloads when the
+  // OmahaResponseAction is completed
+  attempter_.new_payload_size_ = bytes_total;
+  NiceMock<MockServiceObserver> observer;
+  EXPECT_CALL(observer,
+              SendStatusUpdate(AllOf(
+                  Field(&UpdateEngineStatus::progress, progress_1),
+                  Field(&UpdateEngineStatus::status, UpdateStatus::DOWNLOADING),
+                  Field(&UpdateEngineStatus::new_size_bytes, bytes_total))));
+  EXPECT_CALL(observer,
+              SendStatusUpdate(AllOf(
+                  Field(&UpdateEngineStatus::progress, progress_2),
+                  Field(&UpdateEngineStatus::status, UpdateStatus::DOWNLOADING),
+                  Field(&UpdateEngineStatus::new_size_bytes, bytes_total))));
+  attempter_.AddObserver(&observer);
+  attempter_.BytesReceived(bytes_progressed_1, bytes_received_1, bytes_total);
+  EXPECT_EQ(progress_1, attempter_.download_progress_);
+  // This iteration validates that a later set of updates to the variables are
+  // properly handled (so that |getStatus()| will return the same progress info
+  // as the callback is receiving.
+  attempter_.BytesReceived(bytes_progressed_2, bytes_received_2, bytes_total);
+  EXPECT_EQ(progress_2, attempter_.download_progress_);
+}
+
+TEST_F(UpdateAttempterTest, ChangeToDownloadingOnReceivedBytesTest) {
+  // The transition into UpdateStatus::DOWNLOADING happens when the
+  // first bytes are received.
+  uint64_t bytes_progressed = 1024 * 1024;    // 1MB
+  uint64_t bytes_received = 2 * 1024 * 1024;  // 2MB
+  uint64_t bytes_total = 20 * 1024 * 1024;    // 300MB
+  attempter_.status_ = UpdateStatus::CHECKING_FOR_UPDATE;
+  // This is set via inspecting the InstallPlan payloads when the
+  // OmahaResponseAction is completed
+  attempter_.new_payload_size_ = bytes_total;
+  EXPECT_EQ(0.0, attempter_.download_progress_);
+  NiceMock<MockServiceObserver> observer;
+  EXPECT_CALL(observer,
+              SendStatusUpdate(AllOf(
+                  Field(&UpdateEngineStatus::status, UpdateStatus::DOWNLOADING),
+                  Field(&UpdateEngineStatus::new_size_bytes, bytes_total))));
+  attempter_.AddObserver(&observer);
+  attempter_.BytesReceived(bytes_progressed, bytes_received, bytes_total);
+  EXPECT_EQ(UpdateStatus::DOWNLOADING, attempter_.status_);
+}
+
+TEST_F(UpdateAttempterTest, BroadcastCompleteDownloadTest) {
+  // There is a special case to ensure that at 100% downloaded,
+  // download_progress_ is updated and that value broadcast. This test confirms
+  // that.
+  uint64_t bytes_progressed = 0;              // ignored
+  uint64_t bytes_received = 5 * 1024 * 1024;  // ignored
+  uint64_t bytes_total = 5 * 1024 * 1024;     // 300MB
+  attempter_.status_ = UpdateStatus::DOWNLOADING;
+  attempter_.new_payload_size_ = bytes_total;
+  EXPECT_EQ(0.0, attempter_.download_progress_);
+  NiceMock<MockServiceObserver> observer;
+  EXPECT_CALL(observer,
+              SendStatusUpdate(AllOf(
+                  Field(&UpdateEngineStatus::progress, 1.0),
+                  Field(&UpdateEngineStatus::status, UpdateStatus::DOWNLOADING),
+                  Field(&UpdateEngineStatus::new_size_bytes, bytes_total))));
+  attempter_.AddObserver(&observer);
+  attempter_.BytesReceived(bytes_progressed, bytes_received, bytes_total);
+  EXPECT_EQ(1.0, attempter_.download_progress_);
+}
+
+TEST_F(UpdateAttempterTest, ActionCompletedOmahaRequestTest) {
+  unique_ptr<MockHttpFetcher> fetcher(new MockHttpFetcher("", 0, nullptr));
+  fetcher->FailTransfer(500);  // Sets the HTTP response code.
+  OmahaRequestAction action(
+      &fake_system_state_, nullptr, std::move(fetcher), false);
+  ObjectCollectorAction<OmahaResponse> collector_action;
+  BondActions(&action, &collector_action);
+  OmahaResponse response;
+  response.poll_interval = 234;
+  action.SetOutputObject(response);
+  EXPECT_CALL(*prefs_, GetInt64(kPrefsDeltaUpdateFailures, _)).Times(0);
+  attempter_.ActionCompleted(nullptr, &action, ErrorCode::kSuccess);
+  EXPECT_EQ(500, attempter_.http_response_code());
+  EXPECT_EQ(UpdateStatus::IDLE, attempter_.status());
+  EXPECT_EQ(234U, attempter_.server_dictated_poll_interval_);
+  ASSERT_TRUE(attempter_.error_event_.get() == nullptr);
+}
+
+TEST_F(UpdateAttempterTest, ConstructWithUpdatedMarkerTest) {
+  FakePrefs fake_prefs;
+  string boot_id;
+  EXPECT_TRUE(utils::GetBootId(&boot_id));
+  fake_prefs.SetString(kPrefsUpdateCompletedOnBootId, boot_id);
+  fake_system_state_.set_prefs(&fake_prefs);
+  attempter_.Init();
+  EXPECT_EQ(UpdateStatus::UPDATED_NEED_REBOOT, attempter_.status());
+}
+
+TEST_F(UpdateAttempterTest, GetErrorCodeForActionTest) {
+  EXPECT_EQ(ErrorCode::kSuccess,
+            GetErrorCodeForAction(nullptr, ErrorCode::kSuccess));
+
+  FakeSystemState fake_system_state;
+  OmahaRequestAction omaha_request_action(
+      &fake_system_state, nullptr, nullptr, false);
+  EXPECT_EQ(ErrorCode::kOmahaRequestError,
+            GetErrorCodeForAction(&omaha_request_action, ErrorCode::kError));
+  OmahaResponseHandlerAction omaha_response_handler_action(&fake_system_state_);
+  EXPECT_EQ(
+      ErrorCode::kOmahaResponseHandlerError,
+      GetErrorCodeForAction(&omaha_response_handler_action, ErrorCode::kError));
+  FilesystemVerifierAction filesystem_verifier_action;
+  EXPECT_EQ(
+      ErrorCode::kFilesystemVerifierError,
+      GetErrorCodeForAction(&filesystem_verifier_action, ErrorCode::kError));
+  PostinstallRunnerAction postinstall_runner_action(
+      fake_system_state.fake_boot_control(), fake_system_state.fake_hardware());
+  EXPECT_EQ(
+      ErrorCode::kPostinstallRunnerError,
+      GetErrorCodeForAction(&postinstall_runner_action, ErrorCode::kError));
+  MockAction action_mock;
+  EXPECT_CALL(action_mock, Type()).WillOnce(Return("MockAction"));
+  EXPECT_EQ(ErrorCode::kError,
+            GetErrorCodeForAction(&action_mock, ErrorCode::kError));
+}
+
+TEST_F(UpdateAttempterTest, DisableDeltaUpdateIfNeededTest) {
+  attempter_.omaha_request_params_->set_delta_okay(true);
+  EXPECT_CALL(*prefs_, GetInt64(kPrefsDeltaUpdateFailures, _))
+      .WillOnce(Return(false));
+  attempter_.DisableDeltaUpdateIfNeeded();
+  EXPECT_TRUE(attempter_.omaha_request_params_->delta_okay());
+  EXPECT_CALL(*prefs_, GetInt64(kPrefsDeltaUpdateFailures, _))
+      .WillOnce(
+          DoAll(SetArgPointee<1>(UpdateAttempter::kMaxDeltaUpdateFailures - 1),
+                Return(true)));
+  attempter_.DisableDeltaUpdateIfNeeded();
+  EXPECT_TRUE(attempter_.omaha_request_params_->delta_okay());
+  EXPECT_CALL(*prefs_, GetInt64(kPrefsDeltaUpdateFailures, _))
+      .WillOnce(
+          DoAll(SetArgPointee<1>(UpdateAttempter::kMaxDeltaUpdateFailures),
+                Return(true)));
+  attempter_.DisableDeltaUpdateIfNeeded();
+  EXPECT_FALSE(attempter_.omaha_request_params_->delta_okay());
+  EXPECT_CALL(*prefs_, GetInt64(_, _)).Times(0);
+  attempter_.DisableDeltaUpdateIfNeeded();
+  EXPECT_FALSE(attempter_.omaha_request_params_->delta_okay());
+}
+
+TEST_F(UpdateAttempterTest, MarkDeltaUpdateFailureTest) {
+  EXPECT_CALL(*prefs_, GetInt64(kPrefsDeltaUpdateFailures, _))
+      .WillOnce(Return(false))
+      .WillOnce(DoAll(SetArgPointee<1>(-1), Return(true)))
+      .WillOnce(DoAll(SetArgPointee<1>(1), Return(true)))
+      .WillOnce(
+          DoAll(SetArgPointee<1>(UpdateAttempter::kMaxDeltaUpdateFailures),
+                Return(true)));
+  EXPECT_CALL(*prefs_, SetInt64(Ne(kPrefsDeltaUpdateFailures), _))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(*prefs_, SetInt64(kPrefsDeltaUpdateFailures, 1)).Times(2);
+  EXPECT_CALL(*prefs_, SetInt64(kPrefsDeltaUpdateFailures, 2));
+  EXPECT_CALL(*prefs_,
+              SetInt64(kPrefsDeltaUpdateFailures,
+                       UpdateAttempter::kMaxDeltaUpdateFailures + 1));
+  for (int i = 0; i < 4; i++)
+    attempter_.MarkDeltaUpdateFailure();
+}
+
+TEST_F(UpdateAttempterTest, ScheduleErrorEventActionNoEventTest) {
+  EXPECT_CALL(*processor_, EnqueueAction(_)).Times(0);
+  EXPECT_CALL(*processor_, StartProcessing()).Times(0);
+  EXPECT_CALL(*fake_system_state_.mock_payload_state(), UpdateFailed(_))
+      .Times(0);
+  OmahaResponse response;
+  string url1 = "http://url1";
+  response.packages.push_back({.payload_urls = {url1, "https://url"}});
+  EXPECT_CALL(*(fake_system_state_.mock_payload_state()), GetCurrentUrl())
+      .WillRepeatedly(Return(url1));
+  fake_system_state_.mock_payload_state()->SetResponse(response);
+  attempter_.ScheduleErrorEventAction();
+  EXPECT_EQ(url1, fake_system_state_.mock_payload_state()->GetCurrentUrl());
+}
+
+TEST_F(UpdateAttempterTest, ScheduleErrorEventActionTest) {
+  EXPECT_CALL(*processor_,
+              EnqueueAction(Pointee(Property(
+                  &AbstractAction::Type, OmahaRequestAction::StaticType()))));
+  EXPECT_CALL(*processor_, StartProcessing());
+  ErrorCode err = ErrorCode::kError;
+  EXPECT_CALL(*fake_system_state_.mock_payload_state(), UpdateFailed(err));
+  attempter_.error_event_.reset(new OmahaEvent(
+      OmahaEvent::kTypeUpdateComplete, OmahaEvent::kResultError, err));
+  attempter_.ScheduleErrorEventAction();
+  EXPECT_EQ(UpdateStatus::REPORTING_ERROR_EVENT, attempter_.status());
+}
+
+namespace {
+// Actions that will be built as part of an update check.
+const string kUpdateActionTypes[] = {  // NOLINT(runtime/string)
+    OmahaRequestAction::StaticType(),
+    OmahaResponseHandlerAction::StaticType(),
+    UpdateBootFlagsAction::StaticType(),
+    OmahaRequestAction::StaticType(),
+    DownloadAction::StaticType(),
+    OmahaRequestAction::StaticType(),
+    FilesystemVerifierAction::StaticType(),
+    PostinstallRunnerAction::StaticType(),
+    OmahaRequestAction::StaticType()};
+
+// Actions that will be built as part of a user-initiated rollback.
+const string kRollbackActionTypes[] = {
+    // NOLINT(runtime/string)
+    InstallPlanAction::StaticType(),
+    PostinstallRunnerAction::StaticType(),
+};
+
+const StagingSchedule kValidStagingSchedule = {
+    {4, 10}, {10, 40}, {19, 70}, {26, 100}};
+
+}  // namespace
+
+void UpdateAttempterTest::UpdateTestStart() {
+  attempter_.set_http_response_code(200);
+
+  // Expect that the device policy is loaded by the UpdateAttempter at some
+  // point by calling RefreshDevicePolicy.
+  auto device_policy = std::make_unique<policy::MockDevicePolicy>();
+  EXPECT_CALL(*device_policy, LoadPolicy())
+      .Times(testing::AtLeast(1))
+      .WillRepeatedly(Return(true));
+  attempter_.policy_provider_.reset(
+      new policy::PolicyProvider(std::move(device_policy)));
+
+  {
+    InSequence s;
+    for (size_t i = 0; i < arraysize(kUpdateActionTypes); ++i) {
+      EXPECT_CALL(*processor_,
+                  EnqueueAction(Pointee(
+                      Property(&AbstractAction::Type, kUpdateActionTypes[i]))));
+    }
+    EXPECT_CALL(*processor_, StartProcessing());
+  }
+
+  attempter_.Update("", "", "", "", false, false, false);
+  loop_.PostTask(FROM_HERE,
+                 base::Bind(&UpdateAttempterTest::UpdateTestVerify,
+                            base::Unretained(this)));
+}
+
+void UpdateAttempterTest::UpdateTestVerify() {
+  EXPECT_EQ(0, attempter_.http_response_code());
+  EXPECT_EQ(&attempter_, processor_->delegate());
+  EXPECT_EQ(UpdateStatus::CHECKING_FOR_UPDATE, attempter_.status());
+  loop_.BreakLoop();
+}
+
+void UpdateAttempterTest::RollbackTestStart(bool enterprise_rollback,
+                                            bool valid_slot) {
+  // Create a device policy so that we can change settings.
+  auto device_policy = std::make_unique<policy::MockDevicePolicy>();
+  EXPECT_CALL(*device_policy, LoadPolicy()).WillRepeatedly(Return(true));
+  fake_system_state_.set_device_policy(device_policy.get());
+  if (enterprise_rollback) {
+    // We return an empty owner as this is an enterprise.
+    EXPECT_CALL(*device_policy, GetOwner(_))
+        .WillRepeatedly(DoAll(SetArgPointee<0>(string("")), Return(true)));
+  } else {
+    // We return a fake owner as this is an owned consumer device.
+    EXPECT_CALL(*device_policy, GetOwner(_))
+        .WillRepeatedly(DoAll(SetArgPointee<0>(string("fake.mail@fake.com")),
+                              Return(true)));
+  }
+
+  attempter_.policy_provider_.reset(
+      new policy::PolicyProvider(std::move(device_policy)));
+
+  if (valid_slot) {
+    BootControlInterface::Slot rollback_slot = 1;
+    LOG(INFO) << "Test Mark Bootable: "
+              << BootControlInterface::SlotName(rollback_slot);
+    fake_system_state_.fake_boot_control()->SetSlotBootable(rollback_slot,
+                                                            true);
+  }
+
+  bool is_rollback_allowed = false;
+
+  // We only allow rollback on devices that are not enterprise enrolled and
+  // which have a valid slot to rollback to.
+  if (!enterprise_rollback && valid_slot) {
+    is_rollback_allowed = true;
+  }
+
+  if (is_rollback_allowed) {
+    InSequence s;
+    for (size_t i = 0; i < arraysize(kRollbackActionTypes); ++i) {
+      EXPECT_CALL(*processor_,
+                  EnqueueAction(Pointee(Property(&AbstractAction::Type,
+                                                 kRollbackActionTypes[i]))));
+    }
+    EXPECT_CALL(*processor_, StartProcessing());
+
+    EXPECT_TRUE(attempter_.Rollback(true));
+    loop_.PostTask(FROM_HERE,
+                   base::Bind(&UpdateAttempterTest::RollbackTestVerify,
+                              base::Unretained(this)));
+  } else {
+    EXPECT_FALSE(attempter_.Rollback(true));
+    loop_.BreakLoop();
+  }
+}
+
+void UpdateAttempterTest::RollbackTestVerify() {
+  // Verifies the actions that were enqueued.
+  EXPECT_EQ(&attempter_, processor_->delegate());
+  EXPECT_EQ(UpdateStatus::ATTEMPTING_ROLLBACK, attempter_.status());
+  EXPECT_EQ(0U, attempter_.install_plan_->partitions.size());
+  EXPECT_EQ(attempter_.install_plan_->powerwash_required, true);
+  loop_.BreakLoop();
+}
+
+TEST_F(UpdateAttempterTest, UpdateTest) {
+  UpdateTestStart();
+  loop_.Run();
+}
+
+TEST_F(UpdateAttempterTest, RollbackTest) {
+  loop_.PostTask(FROM_HERE,
+                 base::Bind(&UpdateAttempterTest::RollbackTestStart,
+                            base::Unretained(this),
+                            false,
+                            true));
+  loop_.Run();
+}
+
+TEST_F(UpdateAttempterTest, InvalidSlotRollbackTest) {
+  loop_.PostTask(FROM_HERE,
+                 base::Bind(&UpdateAttempterTest::RollbackTestStart,
+                            base::Unretained(this),
+                            false,
+                            false));
+  loop_.Run();
+}
+
+TEST_F(UpdateAttempterTest, EnterpriseRollbackTest) {
+  loop_.PostTask(FROM_HERE,
+                 base::Bind(&UpdateAttempterTest::RollbackTestStart,
+                            base::Unretained(this),
+                            true,
+                            true));
+  loop_.Run();
+}
+
+void UpdateAttempterTest::PingOmahaTestStart() {
+  EXPECT_CALL(*processor_,
+              EnqueueAction(Pointee(Property(
+                  &AbstractAction::Type, OmahaRequestAction::StaticType()))));
+  EXPECT_CALL(*processor_, StartProcessing());
+  attempter_.PingOmaha();
+  ScheduleQuitMainLoop();
+}
+
+TEST_F(UpdateAttempterTest, PingOmahaTest) {
+  EXPECT_FALSE(attempter_.waiting_for_scheduled_check_);
+  EXPECT_FALSE(attempter_.schedule_updates_called());
+  // Disable scheduling of subsequnet checks; we're using the DefaultPolicy in
+  // testing, which is more permissive than we want to handle here.
+  attempter_.DisableScheduleUpdates();
+  loop_.PostTask(FROM_HERE,
+                 base::Bind(&UpdateAttempterTest::PingOmahaTestStart,
+                            base::Unretained(this)));
+  brillo::MessageLoopRunMaxIterations(&loop_, 100);
+  EXPECT_EQ(UpdateStatus::UPDATED_NEED_REBOOT, attempter_.status());
+  EXPECT_TRUE(attempter_.schedule_updates_called());
+}
+
+TEST_F(UpdateAttempterTest, CreatePendingErrorEventTest) {
+  MockAction action;
+  const ErrorCode kCode = ErrorCode::kDownloadTransferError;
+  attempter_.CreatePendingErrorEvent(&action, kCode);
+  ASSERT_NE(nullptr, attempter_.error_event_.get());
+  EXPECT_EQ(OmahaEvent::kTypeUpdateComplete, attempter_.error_event_->type);
+  EXPECT_EQ(OmahaEvent::kResultError, attempter_.error_event_->result);
+  EXPECT_EQ(
+      static_cast<ErrorCode>(static_cast<int>(kCode) |
+                             static_cast<int>(ErrorCode::kTestOmahaUrlFlag)),
+      attempter_.error_event_->error_code);
+}
+
+TEST_F(UpdateAttempterTest, CreatePendingErrorEventResumedTest) {
+  attempter_.install_plan_.reset(new InstallPlan);
+  attempter_.install_plan_->is_resume = true;
+  MockAction action;
+  const ErrorCode kCode = ErrorCode::kInstallDeviceOpenError;
+  attempter_.CreatePendingErrorEvent(&action, kCode);
+  ASSERT_NE(nullptr, attempter_.error_event_.get());
+  EXPECT_EQ(OmahaEvent::kTypeUpdateComplete, attempter_.error_event_->type);
+  EXPECT_EQ(OmahaEvent::kResultError, attempter_.error_event_->result);
+  EXPECT_EQ(
+      static_cast<ErrorCode>(static_cast<int>(kCode) |
+                             static_cast<int>(ErrorCode::kResumedFlag) |
+                             static_cast<int>(ErrorCode::kTestOmahaUrlFlag)),
+      attempter_.error_event_->error_code);
+}
+
+TEST_F(UpdateAttempterTest, P2PNotStartedAtStartupWhenNotEnabled) {
+  MockP2PManager mock_p2p_manager;
+  fake_system_state_.set_p2p_manager(&mock_p2p_manager);
+  mock_p2p_manager.fake().SetP2PEnabled(false);
+  EXPECT_CALL(mock_p2p_manager, EnsureP2PRunning()).Times(0);
+  attempter_.UpdateEngineStarted();
+}
+
+TEST_F(UpdateAttempterTest, P2PNotStartedAtStartupWhenEnabledButNotSharing) {
+  MockP2PManager mock_p2p_manager;
+  fake_system_state_.set_p2p_manager(&mock_p2p_manager);
+  mock_p2p_manager.fake().SetP2PEnabled(true);
+  EXPECT_CALL(mock_p2p_manager, EnsureP2PRunning()).Times(0);
+  attempter_.UpdateEngineStarted();
+}
+
+TEST_F(UpdateAttempterTest, P2PStartedAtStartupWhenEnabledAndSharing) {
+  MockP2PManager mock_p2p_manager;
+  fake_system_state_.set_p2p_manager(&mock_p2p_manager);
+  mock_p2p_manager.fake().SetP2PEnabled(true);
+  mock_p2p_manager.fake().SetCountSharedFilesResult(1);
+  EXPECT_CALL(mock_p2p_manager, EnsureP2PRunning());
+  attempter_.UpdateEngineStarted();
+}
+
+TEST_F(UpdateAttempterTest, P2PNotEnabled) {
+  loop_.PostTask(FROM_HERE,
+                 base::Bind(&UpdateAttempterTest::P2PNotEnabledStart,
+                            base::Unretained(this)));
+  loop_.Run();
+}
+
+void UpdateAttempterTest::P2PNotEnabledStart() {
+  // If P2P is not enabled, check that we do not attempt housekeeping
+  // and do not convey that p2p is to be used.
+  MockP2PManager mock_p2p_manager;
+  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, false);
+  EXPECT_FALSE(actual_using_p2p_for_downloading_);
+  EXPECT_FALSE(actual_using_p2p_for_sharing());
+  ScheduleQuitMainLoop();
+}
+
+TEST_F(UpdateAttempterTest, P2PEnabledStartingFails) {
+  loop_.PostTask(FROM_HERE,
+                 base::Bind(&UpdateAttempterTest::P2PEnabledStartingFailsStart,
+                            base::Unretained(this)));
+  loop_.Run();
+}
+
+void UpdateAttempterTest::P2PEnabledStartingFailsStart() {
+  // If p2p is enabled, but starting it fails ensure we don't do
+  // any housekeeping and do not convey that p2p should be used.
+  MockP2PManager mock_p2p_manager;
+  fake_system_state_.set_p2p_manager(&mock_p2p_manager);
+  mock_p2p_manager.fake().SetP2PEnabled(true);
+  mock_p2p_manager.fake().SetEnsureP2PRunningResult(false);
+  mock_p2p_manager.fake().SetPerformHousekeepingResult(false);
+  EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(0);
+  attempter_.Update("", "", "", "", false, false, false);
+  EXPECT_FALSE(actual_using_p2p_for_downloading());
+  EXPECT_FALSE(actual_using_p2p_for_sharing());
+  ScheduleQuitMainLoop();
+}
+
+TEST_F(UpdateAttempterTest, P2PEnabledHousekeepingFails) {
+  loop_.PostTask(
+      FROM_HERE,
+      base::Bind(&UpdateAttempterTest::P2PEnabledHousekeepingFailsStart,
+                 base::Unretained(this)));
+  loop_.Run();
+}
+
+void UpdateAttempterTest::P2PEnabledHousekeepingFailsStart() {
+  // If p2p is enabled, starting it works but housekeeping fails, ensure
+  // we do not convey p2p is to be used.
+  MockP2PManager mock_p2p_manager;
+  fake_system_state_.set_p2p_manager(&mock_p2p_manager);
+  mock_p2p_manager.fake().SetP2PEnabled(true);
+  mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
+  mock_p2p_manager.fake().SetPerformHousekeepingResult(false);
+  EXPECT_CALL(mock_p2p_manager, PerformHousekeeping());
+  attempter_.Update("", "", "", "", false, false, false);
+  EXPECT_FALSE(actual_using_p2p_for_downloading());
+  EXPECT_FALSE(actual_using_p2p_for_sharing());
+  ScheduleQuitMainLoop();
+}
+
+TEST_F(UpdateAttempterTest, P2PEnabled) {
+  loop_.PostTask(FROM_HERE,
+                 base::Bind(&UpdateAttempterTest::P2PEnabledStart,
+                            base::Unretained(this)));
+  loop_.Run();
+}
+
+void UpdateAttempterTest::P2PEnabledStart() {
+  MockP2PManager mock_p2p_manager;
+  fake_system_state_.set_p2p_manager(&mock_p2p_manager);
+  // If P2P is enabled and starting it works, check that we performed
+  // housekeeping and that we convey p2p should be used.
+  mock_p2p_manager.fake().SetP2PEnabled(true);
+  mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
+  mock_p2p_manager.fake().SetPerformHousekeepingResult(true);
+  EXPECT_CALL(mock_p2p_manager, PerformHousekeeping());
+  attempter_.Update("", "", "", "", false, false, false);
+  EXPECT_TRUE(actual_using_p2p_for_downloading());
+  EXPECT_TRUE(actual_using_p2p_for_sharing());
+  ScheduleQuitMainLoop();
+}
+
+TEST_F(UpdateAttempterTest, P2PEnabledInteractive) {
+  loop_.PostTask(FROM_HERE,
+                 base::Bind(&UpdateAttempterTest::P2PEnabledInteractiveStart,
+                            base::Unretained(this)));
+  loop_.Run();
+}
+
+void UpdateAttempterTest::P2PEnabledInteractiveStart() {
+  MockP2PManager mock_p2p_manager;
+  fake_system_state_.set_p2p_manager(&mock_p2p_manager);
+  // For an interactive check, if P2P is enabled and starting it
+  // works, check that we performed housekeeping and that we convey
+  // p2p should be used for sharing but NOT for downloading.
+  mock_p2p_manager.fake().SetP2PEnabled(true);
+  mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
+  mock_p2p_manager.fake().SetPerformHousekeepingResult(true);
+  EXPECT_CALL(mock_p2p_manager, PerformHousekeeping());
+  attempter_.Update("",
+                    "",
+                    "",
+                    "",
+                    false,
+                    false,
+                    /*interactive=*/true);
+  EXPECT_FALSE(actual_using_p2p_for_downloading());
+  EXPECT_TRUE(actual_using_p2p_for_sharing());
+  ScheduleQuitMainLoop();
+}
+
+TEST_F(UpdateAttempterTest, ReadScatterFactorFromPolicy) {
+  loop_.PostTask(
+      FROM_HERE,
+      base::Bind(&UpdateAttempterTest::ReadScatterFactorFromPolicyTestStart,
+                 base::Unretained(this)));
+  loop_.Run();
+}
+
+// Tests that the scatter_factor_in_seconds value is properly fetched
+// from the device policy.
+void UpdateAttempterTest::ReadScatterFactorFromPolicyTestStart() {
+  int64_t scatter_factor_in_seconds = 36000;
+
+  auto device_policy = std::make_unique<policy::MockDevicePolicy>();
+  EXPECT_CALL(*device_policy, LoadPolicy()).WillRepeatedly(Return(true));
+  fake_system_state_.set_device_policy(device_policy.get());
+
+  EXPECT_CALL(*device_policy, GetScatterFactorInSeconds(_))
+      .WillRepeatedly(
+          DoAll(SetArgPointee<0>(scatter_factor_in_seconds), Return(true)));
+
+  attempter_.policy_provider_.reset(
+      new policy::PolicyProvider(std::move(device_policy)));
+
+  attempter_.Update("", "", "", "", false, false, false);
+  EXPECT_EQ(scatter_factor_in_seconds, attempter_.scatter_factor_.InSeconds());
+
+  ScheduleQuitMainLoop();
+}
+
+TEST_F(UpdateAttempterTest, DecrementUpdateCheckCountTest) {
+  loop_.PostTask(
+      FROM_HERE,
+      base::Bind(&UpdateAttempterTest::DecrementUpdateCheckCountTestStart,
+                 base::Unretained(this)));
+  loop_.Run();
+}
+
+void UpdateAttempterTest::DecrementUpdateCheckCountTestStart() {
+  // Tests that the scatter_factor_in_seconds value is properly fetched
+  // from the device policy and is decremented if value > 0.
+  int64_t initial_value = 5;
+  FakePrefs fake_prefs;
+  attempter_.prefs_ = &fake_prefs;
+
+  fake_system_state_.fake_hardware()->SetIsOOBEComplete(Time::UnixEpoch());
+
+  EXPECT_TRUE(fake_prefs.SetInt64(kPrefsUpdateCheckCount, initial_value));
+
+  int64_t scatter_factor_in_seconds = 10;
+
+  auto device_policy = std::make_unique<policy::MockDevicePolicy>();
+  EXPECT_CALL(*device_policy, LoadPolicy()).WillRepeatedly(Return(true));
+  fake_system_state_.set_device_policy(device_policy.get());
+
+  EXPECT_CALL(*device_policy, GetScatterFactorInSeconds(_))
+      .WillRepeatedly(
+          DoAll(SetArgPointee<0>(scatter_factor_in_seconds), Return(true)));
+
+  attempter_.policy_provider_.reset(
+      new policy::PolicyProvider(std::move(device_policy)));
+
+  attempter_.Update("", "", "", "", false, false, false);
+  EXPECT_EQ(scatter_factor_in_seconds, attempter_.scatter_factor_.InSeconds());
+
+  // Make sure the file still exists.
+  EXPECT_TRUE(fake_prefs.Exists(kPrefsUpdateCheckCount));
+
+  int64_t new_value;
+  EXPECT_TRUE(fake_prefs.GetInt64(kPrefsUpdateCheckCount, &new_value));
+  EXPECT_EQ(initial_value - 1, new_value);
+
+  EXPECT_TRUE(
+      attempter_.omaha_request_params_->update_check_count_wait_enabled());
+
+  // 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, false);
+  EXPECT_TRUE(fake_prefs.Exists(kPrefsUpdateCheckCount));
+  EXPECT_TRUE(fake_prefs.GetInt64(kPrefsUpdateCheckCount, &new_value));
+  EXPECT_EQ(initial_value, new_value);
+
+  ScheduleQuitMainLoop();
+}
+
+TEST_F(UpdateAttempterTest, NoScatteringDoneDuringManualUpdateTestStart) {
+  loop_.PostTask(
+      FROM_HERE,
+      base::Bind(
+          &UpdateAttempterTest::NoScatteringDoneDuringManualUpdateTestStart,
+          base::Unretained(this)));
+  loop_.Run();
+}
+
+void UpdateAttempterTest::NoScatteringDoneDuringManualUpdateTestStart() {
+  // Tests that no scattering logic is enabled if the update check
+  // is manually done (as opposed to a scheduled update check)
+  int64_t initial_value = 8;
+  FakePrefs fake_prefs;
+  attempter_.prefs_ = &fake_prefs;
+
+  fake_system_state_.fake_hardware()->SetIsOOBEComplete(Time::UnixEpoch());
+  fake_system_state_.set_prefs(&fake_prefs);
+
+  EXPECT_TRUE(
+      fake_prefs.SetInt64(kPrefsWallClockScatteringWaitPeriod, initial_value));
+  EXPECT_TRUE(fake_prefs.SetInt64(kPrefsUpdateCheckCount, initial_value));
+
+  // make sure scatter_factor is non-zero as scattering is disabled
+  // otherwise.
+  int64_t scatter_factor_in_seconds = 50;
+
+  auto device_policy = std::make_unique<policy::MockDevicePolicy>();
+  EXPECT_CALL(*device_policy, LoadPolicy()).WillRepeatedly(Return(true));
+  fake_system_state_.set_device_policy(device_policy.get());
+
+  EXPECT_CALL(*device_policy, GetScatterFactorInSeconds(_))
+      .WillRepeatedly(
+          DoAll(SetArgPointee<0>(scatter_factor_in_seconds), Return(true)));
+
+  attempter_.policy_provider_.reset(
+      new policy::PolicyProvider(std::move(device_policy)));
+
+  // Trigger an interactive check so we can test that scattering is disabled.
+  attempter_.Update("",
+                    "",
+                    "",
+                    "",
+                    false,
+                    false,
+                    /*interactive=*/true);
+  EXPECT_EQ(scatter_factor_in_seconds, attempter_.scatter_factor_.InSeconds());
+
+  // Make sure scattering is disabled for manual (i.e. user initiated) update
+  // checks and all artifacts are removed.
+  EXPECT_FALSE(
+      attempter_.omaha_request_params_->wall_clock_based_wait_enabled());
+  EXPECT_FALSE(fake_prefs.Exists(kPrefsWallClockScatteringWaitPeriod));
+  EXPECT_EQ(0, attempter_.omaha_request_params_->waiting_period().InSeconds());
+  EXPECT_FALSE(
+      attempter_.omaha_request_params_->update_check_count_wait_enabled());
+  EXPECT_FALSE(fake_prefs.Exists(kPrefsUpdateCheckCount));
+
+  ScheduleQuitMainLoop();
+}
+
+void UpdateAttempterTest::SetUpStagingTest(const StagingSchedule& schedule,
+                                           FakePrefs* prefs) {
+  attempter_.prefs_ = prefs;
+  fake_system_state_.set_prefs(prefs);
+
+  int64_t initial_value = 8;
+  EXPECT_TRUE(
+      prefs->SetInt64(kPrefsWallClockScatteringWaitPeriod, initial_value));
+  EXPECT_TRUE(prefs->SetInt64(kPrefsUpdateCheckCount, initial_value));
+  attempter_.scatter_factor_ = TimeDelta::FromSeconds(20);
+
+  auto device_policy = std::make_unique<policy::MockDevicePolicy>();
+  EXPECT_CALL(*device_policy, LoadPolicy()).WillRepeatedly(Return(true));
+  fake_system_state_.set_device_policy(device_policy.get());
+  EXPECT_CALL(*device_policy, GetDeviceUpdateStagingSchedule(_))
+      .WillRepeatedly(DoAll(SetArgPointee<0>(schedule), Return(true)));
+
+  attempter_.policy_provider_.reset(
+      new policy::PolicyProvider(std::move(device_policy)));
+}
+
+TEST_F(UpdateAttempterTest, StagingSetsPrefsAndTurnsOffScattering) {
+  loop_.PostTask(
+      FROM_HERE,
+      base::Bind(
+          &UpdateAttempterTest::StagingSetsPrefsAndTurnsOffScatteringStart,
+          base::Unretained(this)));
+  loop_.Run();
+}
+
+void UpdateAttempterTest::StagingSetsPrefsAndTurnsOffScatteringStart() {
+  // Tests that staging sets its prefs properly and turns off scattering.
+  fake_system_state_.fake_hardware()->SetIsOOBEComplete(Time::UnixEpoch());
+  FakePrefs fake_prefs;
+  SetUpStagingTest(kValidStagingSchedule, &fake_prefs);
+
+  attempter_.Update("", "", "", "", false, false, false);
+  // Check that prefs have the correct values.
+  int64_t update_count;
+  EXPECT_TRUE(fake_prefs.GetInt64(kPrefsUpdateCheckCount, &update_count));
+  int64_t waiting_time_days;
+  EXPECT_TRUE(fake_prefs.GetInt64(kPrefsWallClockStagingWaitPeriod,
+                                  &waiting_time_days));
+  EXPECT_GT(waiting_time_days, 0);
+  // Update count should have been decremented.
+  EXPECT_EQ(7, update_count);
+  // Check that Omaha parameters were updated correctly.
+  EXPECT_TRUE(
+      attempter_.omaha_request_params_->update_check_count_wait_enabled());
+  EXPECT_TRUE(
+      attempter_.omaha_request_params_->wall_clock_based_wait_enabled());
+  EXPECT_EQ(waiting_time_days,
+            attempter_.omaha_request_params_->waiting_period().InDays());
+  // Check class variables.
+  EXPECT_EQ(waiting_time_days, attempter_.staging_wait_time_.InDays());
+  EXPECT_EQ(kValidStagingSchedule, attempter_.staging_schedule_);
+  // Check that scattering is turned off
+  EXPECT_EQ(0, attempter_.scatter_factor_.InSeconds());
+  EXPECT_FALSE(fake_prefs.Exists(kPrefsWallClockScatteringWaitPeriod));
+
+  ScheduleQuitMainLoop();
+}
+
+void UpdateAttempterTest::CheckStagingOff() {
+  // Check that all prefs were removed.
+  EXPECT_FALSE(attempter_.prefs_->Exists(kPrefsUpdateCheckCount));
+  EXPECT_FALSE(attempter_.prefs_->Exists(kPrefsWallClockScatteringWaitPeriod));
+  EXPECT_FALSE(attempter_.prefs_->Exists(kPrefsWallClockStagingWaitPeriod));
+  // Check that the Omaha parameters have the correct value.
+  EXPECT_EQ(0, attempter_.omaha_request_params_->waiting_period().InDays());
+  EXPECT_EQ(attempter_.omaha_request_params_->waiting_period(),
+            attempter_.staging_wait_time_);
+  EXPECT_FALSE(
+      attempter_.omaha_request_params_->update_check_count_wait_enabled());
+  EXPECT_FALSE(
+      attempter_.omaha_request_params_->wall_clock_based_wait_enabled());
+  // Check that scattering is turned off too.
+  EXPECT_EQ(0, attempter_.scatter_factor_.InSeconds());
+}
+
+TEST_F(UpdateAttempterTest, StagingOffIfInteractive) {
+  loop_.PostTask(FROM_HERE,
+                 base::Bind(&UpdateAttempterTest::StagingOffIfInteractiveStart,
+                            base::Unretained(this)));
+  loop_.Run();
+}
+
+void UpdateAttempterTest::StagingOffIfInteractiveStart() {
+  // Tests that staging is turned off when an interactive update is requested.
+  fake_system_state_.fake_hardware()->SetIsOOBEComplete(Time::UnixEpoch());
+  FakePrefs fake_prefs;
+  SetUpStagingTest(kValidStagingSchedule, &fake_prefs);
+
+  attempter_.Update("", "", "", "", false, false, /* interactive = */ true);
+  CheckStagingOff();
+
+  ScheduleQuitMainLoop();
+}
+
+TEST_F(UpdateAttempterTest, StagingOffIfOobe) {
+  loop_.PostTask(FROM_HERE,
+                 base::Bind(&UpdateAttempterTest::StagingOffIfOobeStart,
+                            base::Unretained(this)));
+  loop_.Run();
+}
+
+void UpdateAttempterTest::StagingOffIfOobeStart() {
+  // Tests that staging is turned off if OOBE hasn't been completed.
+  fake_system_state_.fake_hardware()->SetIsOOBEEnabled(true);
+  fake_system_state_.fake_hardware()->UnsetIsOOBEComplete();
+  FakePrefs fake_prefs;
+  SetUpStagingTest(kValidStagingSchedule, &fake_prefs);
+
+  attempter_.Update("", "", "", "", false, false, /* interactive = */ true);
+  CheckStagingOff();
+
+  ScheduleQuitMainLoop();
+}
+
+// Checks that we only report daily metrics at most every 24 hours.
+TEST_F(UpdateAttempterTest, ReportDailyMetrics) {
+  FakeClock fake_clock;
+  FakePrefs fake_prefs;
+
+  fake_system_state_.set_clock(&fake_clock);
+  fake_system_state_.set_prefs(&fake_prefs);
+
+  Time epoch = Time::FromInternalValue(0);
+  fake_clock.SetWallclockTime(epoch);
+
+  // If there is no kPrefsDailyMetricsLastReportedAt state variable,
+  // we should report.
+  EXPECT_TRUE(attempter_.CheckAndReportDailyMetrics());
+  // We should not report again if no time has passed.
+  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+
+  // We should not report if only 10 hours has passed.
+  fake_clock.SetWallclockTime(epoch + TimeDelta::FromHours(10));
+  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+
+  // We should not report if only 24 hours - 1 sec has passed.
+  fake_clock.SetWallclockTime(epoch + TimeDelta::FromHours(24) -
+                              TimeDelta::FromSeconds(1));
+  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+
+  // We should report if 24 hours has passed.
+  fake_clock.SetWallclockTime(epoch + TimeDelta::FromHours(24));
+  EXPECT_TRUE(attempter_.CheckAndReportDailyMetrics());
+
+  // But then we should not report again..
+  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+
+  // .. until another 24 hours has passed
+  fake_clock.SetWallclockTime(epoch + TimeDelta::FromHours(47));
+  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+  fake_clock.SetWallclockTime(epoch + TimeDelta::FromHours(48));
+  EXPECT_TRUE(attempter_.CheckAndReportDailyMetrics());
+  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+
+  // .. and another 24 hours
+  fake_clock.SetWallclockTime(epoch + TimeDelta::FromHours(71));
+  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+  fake_clock.SetWallclockTime(epoch + TimeDelta::FromHours(72));
+  EXPECT_TRUE(attempter_.CheckAndReportDailyMetrics());
+  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+
+  // If the span between time of reporting and present time is
+  // negative, we report. This is in order to reset the timestamp and
+  // avoid an edge condition whereby a distant point in the future is
+  // in the state variable resulting in us never ever reporting again.
+  fake_clock.SetWallclockTime(epoch + TimeDelta::FromHours(71));
+  EXPECT_TRUE(attempter_.CheckAndReportDailyMetrics());
+  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+
+  // In this case we should not update until the clock reads 71 + 24 = 95.
+  // Check that.
+  fake_clock.SetWallclockTime(epoch + TimeDelta::FromHours(94));
+  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+  fake_clock.SetWallclockTime(epoch + TimeDelta::FromHours(95));
+  EXPECT_TRUE(attempter_.CheckAndReportDailyMetrics());
+  EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+}
+
+TEST_F(UpdateAttempterTest, BootTimeInUpdateMarkerFile) {
+  FakeClock fake_clock;
+  fake_clock.SetBootTime(Time::FromTimeT(42));
+  fake_system_state_.set_clock(&fake_clock);
+  FakePrefs fake_prefs;
+  fake_system_state_.set_prefs(&fake_prefs);
+  attempter_.Init();
+
+  Time boot_time;
+  EXPECT_FALSE(attempter_.GetBootTimeAtUpdate(&boot_time));
+
+  attempter_.WriteUpdateCompletedMarker();
+
+  EXPECT_TRUE(attempter_.GetBootTimeAtUpdate(&boot_time));
+  EXPECT_EQ(boot_time.ToTimeT(), 42);
+}
+
+TEST_F(UpdateAttempterTest, AnyUpdateSourceAllowedUnofficial) {
+  fake_system_state_.fake_hardware()->SetIsOfficialBuild(false);
+  EXPECT_TRUE(attempter_.IsAnyUpdateSourceAllowed());
+}
+
+TEST_F(UpdateAttempterTest, AnyUpdateSourceAllowedOfficialDevmode) {
+  fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
+  fake_system_state_.fake_hardware()->SetAreDevFeaturesEnabled(true);
+  EXPECT_TRUE(attempter_.IsAnyUpdateSourceAllowed());
+}
+
+TEST_F(UpdateAttempterTest, AnyUpdateSourceDisallowedOfficialNormal) {
+  fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
+  fake_system_state_.fake_hardware()->SetAreDevFeaturesEnabled(false);
+  EXPECT_FALSE(attempter_.IsAnyUpdateSourceAllowed());
+}
+
+TEST_F(UpdateAttempterTest, CheckForUpdateAUDlcTest) {
+  fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
+  fake_system_state_.fake_hardware()->SetAreDevFeaturesEnabled(false);
+
+  const string dlc_module_id = "a_dlc_module_id";
+  vector<string> dlc_module_ids = {dlc_module_id};
+  ON_CALL(mock_dlcservice_, GetInstalled(testing::_))
+      .WillByDefault(DoAll(testing::SetArgPointee<0>(dlc_module_ids),
+                           testing::Return(true)));
+
+  attempter_.CheckForUpdate("", "autest", UpdateAttemptFlags::kNone);
+  EXPECT_EQ(attempter_.dlc_module_ids_.size(), 1);
+  EXPECT_EQ(attempter_.dlc_module_ids_[0], dlc_module_id);
+}
+
+TEST_F(UpdateAttempterTest, CheckForUpdateAUTest) {
+  fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
+  fake_system_state_.fake_hardware()->SetAreDevFeaturesEnabled(false);
+  attempter_.CheckForUpdate("", "autest", UpdateAttemptFlags::kNone);
+  EXPECT_EQ(constants::kOmahaDefaultAUTestURL, attempter_.forced_omaha_url());
+}
+
+TEST_F(UpdateAttempterTest, CheckForUpdateScheduledAUTest) {
+  fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
+  fake_system_state_.fake_hardware()->SetAreDevFeaturesEnabled(false);
+  attempter_.CheckForUpdate("", "autest-scheduled", UpdateAttemptFlags::kNone);
+  EXPECT_EQ(constants::kOmahaDefaultAUTestURL, attempter_.forced_omaha_url());
+}
+
+TEST_F(UpdateAttempterTest, CheckForInstallTest) {
+  fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
+  fake_system_state_.fake_hardware()->SetAreDevFeaturesEnabled(false);
+  attempter_.CheckForInstall({}, "autest");
+  EXPECT_EQ(constants::kOmahaDefaultAUTestURL, attempter_.forced_omaha_url());
+
+  attempter_.CheckForInstall({}, "autest-scheduled");
+  EXPECT_EQ(constants::kOmahaDefaultAUTestURL, attempter_.forced_omaha_url());
+
+  attempter_.CheckForInstall({}, "http://omaha.phishing");
+  EXPECT_EQ("", attempter_.forced_omaha_url());
+}
+
+TEST_F(UpdateAttempterTest, InstallSetsStatusIdle) {
+  attempter_.CheckForInstall({}, "http://foo.bar");
+  attempter_.status_ = UpdateStatus::DOWNLOADING;
+  EXPECT_TRUE(attempter_.is_install_);
+  attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+  UpdateEngineStatus status;
+  attempter_.GetStatus(&status);
+  // Should set status to idle after an install operation.
+  EXPECT_EQ(UpdateStatus::IDLE, status.status);
+}
+
+TEST_F(UpdateAttempterTest, RollbackAfterInstall) {
+  attempter_.is_install_ = true;
+  attempter_.Rollback(false);
+  EXPECT_FALSE(attempter_.is_install_);
+}
+
+TEST_F(UpdateAttempterTest, UpdateAfterInstall) {
+  attempter_.is_install_ = true;
+  attempter_.CheckForUpdate("", "", UpdateAttemptFlags::kNone);
+  EXPECT_FALSE(attempter_.is_install_);
+}
+
+TEST_F(UpdateAttempterTest, TargetVersionPrefixSetAndReset) {
+  attempter_.CalculateUpdateParams("", "", "", "1234", false, false, false);
+  EXPECT_EQ("1234",
+            fake_system_state_.request_params()->target_version_prefix());
+
+  attempter_.CalculateUpdateParams("", "", "", "", false, false, false);
+  EXPECT_TRUE(
+      fake_system_state_.request_params()->target_version_prefix().empty());
+}
+
+TEST_F(UpdateAttempterTest, RollbackAllowedSetAndReset) {
+  attempter_.CalculateUpdateParams("",
+                                   "",
+                                   "",
+                                   "1234",
+                                   /*rollback_allowed=*/true,
+                                   false,
+                                   false);
+  EXPECT_TRUE(fake_system_state_.request_params()->rollback_allowed());
+
+  attempter_.CalculateUpdateParams("",
+                                   "",
+                                   "",
+                                   "1234",
+                                   /*rollback_allowed=*/false,
+                                   false,
+                                   false);
+  EXPECT_FALSE(fake_system_state_.request_params()->rollback_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
+  // completed, with the deferred-update error code.
+  attempter_.ActionCompleted(
+      nullptr, &response_action, ErrorCode::kOmahaUpdateDeferredPerPolicy);
+  {
+    UpdateEngineStatus status;
+    attempter_.GetStatus(&status);
+    EXPECT_EQ(UpdateStatus::UPDATE_AVAILABLE, status.status);
+    EXPECT_TRUE(attempter_.install_plan_);
+    EXPECT_EQ(attempter_.install_plan_->version, status.new_version);
+    EXPECT_EQ(attempter_.install_plan_->system_version,
+              status.new_system_version);
+    EXPECT_EQ(attempter_.install_plan_->payloads[0].size,
+              status.new_size_bytes);
+  }
+  // An "error" event should have been created to tell Omaha that the update is
+  // being deferred.
+  EXPECT_TRUE(nullptr != attempter_.error_event_);
+  EXPECT_EQ(OmahaEvent::kTypeUpdateComplete, attempter_.error_event_->type);
+  EXPECT_EQ(OmahaEvent::kResultUpdateDeferred, attempter_.error_event_->result);
+  ErrorCode expected_code = static_cast<ErrorCode>(
+      static_cast<int>(ErrorCode::kOmahaUpdateDeferredPerPolicy) |
+      static_cast<int>(ErrorCode::kTestOmahaUrlFlag));
+  EXPECT_EQ(expected_code, attempter_.error_event_->error_code);
+  // End the processing
+  attempter_.ProcessingDone(nullptr, ErrorCode::kOmahaUpdateDeferredPerPolicy);
+  // Validate the state of the attempter.
+  {
+    UpdateEngineStatus status;
+    attempter_.GetStatus(&status);
+    EXPECT_EQ(UpdateStatus::REPORTING_ERROR_EVENT, status.status);
+    EXPECT_EQ(response_action.install_plan_.version, status.new_version);
+    EXPECT_EQ(response_action.install_plan_.system_version,
+              status.new_system_version);
+    EXPECT_EQ(response_action.install_plan_.payloads[0].size,
+              status.new_size_bytes);
+  }
+}
+
+TEST_F(UpdateAttempterTest, UpdateIsNotRunningWhenUpdateAvailable) {
+  EXPECT_FALSE(attempter_.IsUpdateRunningOrScheduled());
+  // Verify in-progress update with UPDATE_AVAILABLE is running
+  attempter_.status_ = UpdateStatus::UPDATE_AVAILABLE;
+  EXPECT_TRUE(attempter_.IsUpdateRunningOrScheduled());
+}
+
+TEST_F(UpdateAttempterTest, UpdateAttemptFlagsCachedAtUpdateStart) {
+  attempter_.SetUpdateAttemptFlags(UpdateAttemptFlags::kFlagRestrictDownload);
+
+  UpdateCheckParams params = {.updates_enabled = true};
+  attempter_.OnUpdateScheduled(EvalStatus::kSucceeded, params);
+
+  EXPECT_EQ(UpdateAttemptFlags::kFlagRestrictDownload,
+            attempter_.GetCurrentUpdateAttemptFlags());
+}
+
+TEST_F(UpdateAttempterTest, RollbackNotAllowed) {
+  UpdateCheckParams params = {.updates_enabled = true,
+                              .rollback_allowed = false};
+  attempter_.OnUpdateScheduled(EvalStatus::kSucceeded, params);
+  EXPECT_FALSE(fake_system_state_.request_params()->rollback_allowed());
+}
+
+TEST_F(UpdateAttempterTest, RollbackAllowed) {
+  UpdateCheckParams params = {.updates_enabled = true,
+                              .rollback_allowed = true};
+  attempter_.OnUpdateScheduled(EvalStatus::kSucceeded, params);
+  EXPECT_TRUE(fake_system_state_.request_params()->rollback_allowed());
+}
+
+TEST_F(UpdateAttempterTest, InteractiveUpdateUsesPassedRestrictions) {
+  attempter_.SetUpdateAttemptFlags(UpdateAttemptFlags::kFlagRestrictDownload);
+
+  attempter_.CheckForUpdate("", "", UpdateAttemptFlags::kNone);
+  EXPECT_EQ(UpdateAttemptFlags::kNone,
+            attempter_.GetCurrentUpdateAttemptFlags());
+}
+
+TEST_F(UpdateAttempterTest, NonInteractiveUpdateUsesSetRestrictions) {
+  attempter_.SetUpdateAttemptFlags(UpdateAttemptFlags::kNone);
+
+  // This tests that when CheckForUpdate() is called with the non-interactive
+  // flag set, that it doesn't change the current UpdateAttemptFlags.
+  attempter_.CheckForUpdate("",
+                            "",
+                            UpdateAttemptFlags::kFlagNonInteractive |
+                                UpdateAttemptFlags::kFlagRestrictDownload);
+  EXPECT_EQ(UpdateAttemptFlags::kNone,
+            attempter_.GetCurrentUpdateAttemptFlags());
+}
+
+void UpdateAttempterTest::ResetRollbackHappenedStart(bool is_consumer,
+                                                     bool is_policy_loaded,
+                                                     bool expected_reset) {
+  EXPECT_CALL(*fake_system_state_.mock_payload_state(), GetRollbackHappened())
+      .WillRepeatedly(Return(true));
+  auto mock_policy_provider =
+      std::make_unique<NiceMock<policy::MockPolicyProvider>>();
+  EXPECT_CALL(*mock_policy_provider, IsConsumerDevice())
+      .WillRepeatedly(Return(is_consumer));
+  EXPECT_CALL(*mock_policy_provider, device_policy_is_loaded())
+      .WillRepeatedly(Return(is_policy_loaded));
+  const policy::MockDevicePolicy device_policy;
+  EXPECT_CALL(*mock_policy_provider, GetDevicePolicy())
+      .WillRepeatedly(ReturnRef(device_policy));
+  EXPECT_CALL(*fake_system_state_.mock_payload_state(),
+              SetRollbackHappened(false))
+      .Times(expected_reset ? 1 : 0);
+  attempter_.policy_provider_ = std::move(mock_policy_provider);
+  attempter_.Update("", "", "", "", false, false, false);
+  ScheduleQuitMainLoop();
+}
+
+TEST_F(UpdateAttempterTest, ResetRollbackHappenedOobe) {
+  loop_.PostTask(FROM_HERE,
+                 base::Bind(&UpdateAttempterTest::ResetRollbackHappenedStart,
+                            base::Unretained(this),
+                            /*is_consumer=*/false,
+                            /*is_policy_loaded=*/false,
+                            /*expected_reset=*/false));
+  loop_.Run();
+}
+
+TEST_F(UpdateAttempterTest, ResetRollbackHappenedConsumer) {
+  loop_.PostTask(FROM_HERE,
+                 base::Bind(&UpdateAttempterTest::ResetRollbackHappenedStart,
+                            base::Unretained(this),
+                            /*is_consumer=*/true,
+                            /*is_policy_loaded=*/false,
+                            /*expected_reset=*/true));
+  loop_.Run();
+}
+
+TEST_F(UpdateAttempterTest, ResetRollbackHappenedEnterprise) {
+  loop_.PostTask(FROM_HERE,
+                 base::Bind(&UpdateAttempterTest::ResetRollbackHappenedStart,
+                            base::Unretained(this),
+                            /*is_consumer=*/false,
+                            /*is_policy_loaded=*/true,
+                            /*expected_reset=*/true));
+  loop_.Run();
+}
+
+TEST_F(UpdateAttempterTest, SetRollbackHappenedRollback) {
+  attempter_.install_plan_.reset(new InstallPlan);
+  attempter_.install_plan_->is_rollback = true;
+
+  EXPECT_CALL(*fake_system_state_.mock_payload_state(),
+              SetRollbackHappened(true))
+      .Times(1);
+  attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+}
+
+TEST_F(UpdateAttempterTest, SetRollbackHappenedNotRollback) {
+  attempter_.install_plan_.reset(new InstallPlan);
+  attempter_.install_plan_->is_rollback = false;
+
+  EXPECT_CALL(*fake_system_state_.mock_payload_state(),
+              SetRollbackHappened(true))
+      .Times(0);
+  attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+}
+
+TEST_F(UpdateAttempterTest, RollbackMetricsRollbackSuccess) {
+  attempter_.install_plan_.reset(new InstallPlan);
+  attempter_.install_plan_->is_rollback = true;
+  attempter_.install_plan_->version = kRollbackVersion;
+
+  EXPECT_CALL(*fake_system_state_.mock_metrics_reporter(),
+              ReportEnterpriseRollbackMetrics(true, kRollbackVersion))
+      .Times(1);
+  attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+}
+
+TEST_F(UpdateAttempterTest, RollbackMetricsNotRollbackSuccess) {
+  attempter_.install_plan_.reset(new InstallPlan);
+  attempter_.install_plan_->is_rollback = false;
+  attempter_.install_plan_->version = kRollbackVersion;
+
+  EXPECT_CALL(*fake_system_state_.mock_metrics_reporter(),
+              ReportEnterpriseRollbackMetrics(_, _))
+      .Times(0);
+  attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+}
+
+TEST_F(UpdateAttempterTest, RollbackMetricsRollbackFailure) {
+  attempter_.install_plan_.reset(new InstallPlan);
+  attempter_.install_plan_->is_rollback = true;
+  attempter_.install_plan_->version = kRollbackVersion;
+
+  EXPECT_CALL(*fake_system_state_.mock_metrics_reporter(),
+              ReportEnterpriseRollbackMetrics(false, kRollbackVersion))
+      .Times(1);
+  MockAction action;
+  attempter_.CreatePendingErrorEvent(&action, ErrorCode::kRollbackNotPossible);
+  attempter_.ProcessingDone(nullptr, ErrorCode::kRollbackNotPossible);
+}
+
+TEST_F(UpdateAttempterTest, RollbackMetricsNotRollbackFailure) {
+  attempter_.install_plan_.reset(new InstallPlan);
+  attempter_.install_plan_->is_rollback = false;
+  attempter_.install_plan_->version = kRollbackVersion;
+
+  EXPECT_CALL(*fake_system_state_.mock_metrics_reporter(),
+              ReportEnterpriseRollbackMetrics(_, _))
+      .Times(0);
+  MockAction action;
+  attempter_.CreatePendingErrorEvent(&action, ErrorCode::kRollbackNotPossible);
+  attempter_.ProcessingDone(nullptr, ErrorCode::kRollbackNotPossible);
+}
+
+TEST_F(UpdateAttempterTest, TimeToUpdateAppliedMetricFailure) {
+  EXPECT_CALL(*fake_system_state_.mock_metrics_reporter(),
+              ReportEnterpriseUpdateSeenToDownloadDays(_, _))
+      .Times(0);
+  attempter_.ProcessingDone(nullptr, ErrorCode::kOmahaUpdateDeferredPerPolicy);
+}
+
+TEST_F(UpdateAttempterTest, TimeToUpdateAppliedOnNonEnterprise) {
+  auto device_policy = std::make_unique<policy::MockDevicePolicy>();
+  fake_system_state_.set_device_policy(device_policy.get());
+  // Make device policy return that this is not enterprise enrolled
+  EXPECT_CALL(*device_policy, IsEnterpriseEnrolled()).WillOnce(Return(false));
+
+  // Ensure that the metric is not recorded.
+  EXPECT_CALL(*fake_system_state_.mock_metrics_reporter(),
+              ReportEnterpriseUpdateSeenToDownloadDays(_, _))
+      .Times(0);
+  attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+}
+
+TEST_F(UpdateAttempterTest,
+       TimeToUpdateAppliedWithTimeRestrictionMetricSuccess) {
+  constexpr int kDaysToUpdate = 15;
+  auto device_policy = std::make_unique<policy::MockDevicePolicy>();
+  fake_system_state_.set_device_policy(device_policy.get());
+  // Make device policy return that this is enterprise enrolled
+  EXPECT_CALL(*device_policy, IsEnterpriseEnrolled()).WillOnce(Return(true));
+  // Pretend that there's a time restriction policy in place
+  EXPECT_CALL(*device_policy, GetDisallowedTimeIntervals(_))
+      .WillOnce(Return(true));
+
+  FakePrefs fake_prefs;
+  Time update_first_seen_at = Time::Now();
+  fake_prefs.SetInt64(kPrefsUpdateFirstSeenAt,
+                      update_first_seen_at.ToInternalValue());
+
+  FakeClock fake_clock;
+  Time update_finished_at =
+      update_first_seen_at + TimeDelta::FromDays(kDaysToUpdate);
+  fake_clock.SetWallclockTime(update_finished_at);
+
+  fake_system_state_.set_clock(&fake_clock);
+  fake_system_state_.set_prefs(&fake_prefs);
+
+  EXPECT_CALL(*fake_system_state_.mock_metrics_reporter(),
+              ReportEnterpriseUpdateSeenToDownloadDays(true, kDaysToUpdate))
+      .Times(1);
+  attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+}
+
+TEST_F(UpdateAttempterTest,
+       TimeToUpdateAppliedWithoutTimeRestrictionMetricSuccess) {
+  constexpr int kDaysToUpdate = 15;
+  auto device_policy = std::make_unique<policy::MockDevicePolicy>();
+  fake_system_state_.set_device_policy(device_policy.get());
+  // Make device policy return that this is enterprise enrolled
+  EXPECT_CALL(*device_policy, IsEnterpriseEnrolled()).WillOnce(Return(true));
+  // Pretend that there's no time restriction policy in place
+  EXPECT_CALL(*device_policy, GetDisallowedTimeIntervals(_))
+      .WillOnce(Return(false));
+
+  FakePrefs fake_prefs;
+  Time update_first_seen_at = Time::Now();
+  fake_prefs.SetInt64(kPrefsUpdateFirstSeenAt,
+                      update_first_seen_at.ToInternalValue());
+
+  FakeClock fake_clock;
+  Time update_finished_at =
+      update_first_seen_at + TimeDelta::FromDays(kDaysToUpdate);
+  fake_clock.SetWallclockTime(update_finished_at);
+
+  fake_system_state_.set_clock(&fake_clock);
+  fake_system_state_.set_prefs(&fake_prefs);
+
+  EXPECT_CALL(*fake_system_state_.mock_metrics_reporter(),
+              ReportEnterpriseUpdateSeenToDownloadDays(false, kDaysToUpdate))
+      .Times(1);
+  attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/update_boot_flags_action_unittest.cc b/update_boot_flags_action_unittest.cc
index 26cbe90..1b2bfa5 100644
--- a/update_boot_flags_action_unittest.cc
+++ b/update_boot_flags_action_unittest.cc
@@ -22,17 +22,18 @@
 #include <base/bind.h>
 #include <gtest/gtest.h>
 
-#include "update_engine/common/fake_boot_control.h"
+#include "update_engine/fake_system_state.h"
 
 namespace chromeos_update_engine {
 
 class UpdateBootFlagsActionTest : public ::testing::Test {
- protected:
-  FakeBootControl boot_control_;
+ public:
+  FakeSystemState fake_system_state_;
 };
 
 TEST_F(UpdateBootFlagsActionTest, SimpleTest) {
-  auto action = std::make_unique<UpdateBootFlagsAction>(&boot_control_);
+  auto boot_control = fake_system_state_.fake_boot_control();
+  auto action = std::make_unique<UpdateBootFlagsAction>(boot_control);
   ActionProcessor processor;
   processor.EnqueueAction(std::move(action));
 
@@ -48,8 +49,9 @@
   UpdateBootFlagsAction::updated_boot_flags_ = false;
   UpdateBootFlagsAction::is_running_ = false;
 
-  auto action1 = std::make_unique<UpdateBootFlagsAction>(&boot_control_);
-  auto action2 = std::make_unique<UpdateBootFlagsAction>(&boot_control_);
+  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);
   ActionProcessor processor1, processor2;
   processor1.EnqueueAction(std::move(action1));
   processor2.EnqueueAction(std::move(action2));
diff --git a/update_engine-client.gyp b/update_engine-client.gyp
new file mode 100644
index 0000000..588fc63
--- /dev/null
+++ b/update_engine-client.gyp
@@ -0,0 +1,41 @@
+#
+# 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.
+#
+{
+  'targets': [
+    # update_engine client library generated headers. Used by other daemons and
+    # by the update_engine_client console program to interact with
+    # update_engine.
+    {
+      'target_name': 'libupdate_engine-client-headers',
+      'type': 'none',
+      'actions': [
+        {
+          'action_name': 'update_engine_client-dbus-proxies',
+          'variables': {
+            'dbus_service_config': 'dbus_bindings/dbus-service-config.json',
+            'proxy_output_file': 'include/update_engine/dbus-proxies.h',
+            'mock_output_file': 'include/update_engine/dbus-proxy-mocks.h',
+            'proxy_path_in_mocks': 'update_engine/dbus-proxies.h',
+          },
+          'sources': [
+            'dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml',
+          ],
+          'includes': ['../../../platform2/common-mk/generate-dbus-proxies.gypi'],
+        },
+      ],
+    },
+  ],
+}
diff --git a/update_engine.conf b/update_engine.conf
index b6ca3c4..af213ad 100644
--- a/update_engine.conf
+++ b/update_engine.conf
@@ -1,2 +1,2 @@
 PAYLOAD_MAJOR_VERSION=2
-PAYLOAD_MINOR_VERSION=7
+PAYLOAD_MINOR_VERSION=6
diff --git a/update_engine.conf.chromeos b/update_engine.conf.chromeos
deleted file mode 100644
index af213ad..0000000
--- a/update_engine.conf.chromeos
+++ /dev/null
@@ -1,2 +0,0 @@
-PAYLOAD_MAJOR_VERSION=2
-PAYLOAD_MINOR_VERSION=6
diff --git a/update_engine.gyp b/update_engine.gyp
new file mode 100644
index 0000000..c2c0c62
--- /dev/null
+++ b/update_engine.gyp
@@ -0,0 +1,655 @@
+#
+# 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.
+#
+
+# TODO: Rename these files to pass this check.
+# gyplint: disable=GypLintSourceFileNames
+{
+  'variables': {
+    'USE_chrome_network_proxy': '1',
+    'USE_chrome_kiosk_app': '1',
+  },
+  'target_defaults': {
+    'variables': {
+      'deps': [
+        'libbrillo-<(libbase_ver)',
+        'libchrome-<(libbase_ver)',
+        # system_api depends on protobuf (or protobuf-lite). It must appear
+        # before protobuf here or the linker flags won't be in the right
+        # order.
+        'system_api',
+        'protobuf-lite',
+      ],
+      # The -DUSE_* flags are passed from platform2.py. We use sane defaults
+      # here when these USE flags are not defined. You can set the default value
+      # for the USE flag in the ebuild.
+      'USE_hwid_override%': '0',
+    },
+    'cflags': [
+      '-g',
+      '-ffunction-sections',
+      '-Wall',
+      '-Wextra',
+      '-Werror',
+      '-Wno-unused-parameter',
+    ],
+    'cflags_cc': [
+      '-fno-strict-aliasing',
+      '-Wnon-virtual-dtor',
+    ],
+    'ldflags': [
+      '-Wl,--gc-sections',
+    ],
+    'defines': [
+      '__CHROMEOS__',
+      '_FILE_OFFSET_BITS=64',
+      '_POSIX_C_SOURCE=199309L',
+      'USE_BINDER=<(USE_binder)',
+      'USE_DBUS=<(USE_dbus)',
+      'USE_FEC=0',
+      'USE_HWID_OVERRIDE=<(USE_hwid_override)',
+      'USE_CHROME_KIOSK_APP=<(USE_chrome_kiosk_app)',
+      'USE_CHROME_NETWORK_PROXY=<(USE_chrome_network_proxy)',
+      'USE_MTD=<(USE_mtd)',
+      'USE_OMAHA=1',
+      'USE_SHILL=1',
+    ],
+    'include_dirs': [
+      # We need this include dir because we include all the local code as
+      # "update_engine/...".
+      '<(platform2_root)/../aosp/system',
+      '<(platform2_root)/../aosp/system/update_engine/client_library/include',
+    ],
+  },
+  'targets': [
+    # Protobufs.
+    {
+      'target_name': 'update_metadata-protos',
+      'type': 'static_library',
+      'variables': {
+        'proto_in_dir': '.',
+        'proto_out_dir': 'include/update_engine',
+      },
+      'sources': [
+        'update_metadata.proto',
+      ],
+      'includes': ['../../../platform2/common-mk/protoc.gypi'],
+    },
+    # Chrome D-Bus bindings.
+    {
+      'target_name': 'update_engine-dbus-adaptor',
+      'type': 'none',
+      'variables': {
+        'dbus_adaptors_out_dir': 'include/dbus_bindings',
+        'dbus_xml_extension': 'dbus-xml',
+      },
+      'sources': [
+        'dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml',
+      ],
+      'includes': ['../../../platform2/common-mk/generate-dbus-adaptors.gypi'],
+    },
+    {
+      'target_name': 'update_engine-dbus-kiosk-app-client',
+      'type': 'none',
+      'actions': [{
+        'action_name': 'update_engine-dbus-kiosk-app-client-action',
+        'variables': {
+          'mock_output_file': 'include/kiosk-app/dbus-proxy-mocks.h',
+          'proxy_output_file': 'include/kiosk-app/dbus-proxies.h',
+        },
+        'sources': [
+          'dbus_bindings/org.chromium.KioskAppService.dbus-xml',
+        ],
+        'includes': ['../../../platform2/common-mk/generate-dbus-proxies.gypi'],
+      }],
+    },
+    # The payload application component and common dependencies.
+    {
+      'target_name': 'libpayload_consumer',
+      'type': 'static_library',
+      'dependencies': [
+        'update_metadata-protos',
+      ],
+      # TODO(deymo): Remove unused dependencies once we stop including files
+      # from the root directory.
+      'variables': {
+        'exported_deps': [
+          'libcrypto',
+          'xz-embedded',
+          'libbspatch',
+          'libpuffpatch',
+        ],
+        'deps': ['<@(exported_deps)'],
+      },
+      'all_dependent_settings': {
+        'variables': {
+          'deps': [
+            '<@(exported_deps)',
+          ],
+        },
+      },
+      'link_settings': {
+        'variables': {
+          'deps': [
+            '<@(exported_deps)',
+          ],
+        },
+        'libraries': [
+          '-lbz2',
+          '-lrt',
+        ],
+      },
+      'sources': [
+        'common/action_processor.cc',
+        'common/boot_control_stub.cc',
+        'common/clock.cc',
+        'common/constants.cc',
+        'common/cpu_limiter.cc',
+        'common/error_code_utils.cc',
+        'common/hash_calculator.cc',
+        'common/http_common.cc',
+        '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',
+        'payload_consumer/bzip_extent_writer.cc',
+        'payload_consumer/cached_file_descriptor.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',
+        'payload_consumer/file_descriptor_utils.cc',
+        'payload_consumer/file_writer.cc',
+        'payload_consumer/filesystem_verifier_action.cc',
+        'payload_consumer/install_plan.cc',
+        'payload_consumer/mount_history.cc',
+        'payload_consumer/payload_constants.cc',
+        'payload_consumer/payload_metadata.cc',
+        'payload_consumer/payload_verifier.cc',
+        'payload_consumer/postinstall_runner_action.cc',
+        'payload_consumer/verity_writer_stub.cc',
+        'payload_consumer/xz_extent_writer.cc',
+      ],
+      'conditions': [
+        ['USE_mtd == 1', {
+          'sources': [
+            'payload_consumer/mtd_file_descriptor.cc',
+          ],
+          'link_settings': {
+            'libraries': [
+              '-lmtdutils',
+            ],
+          },
+        }],
+      ],
+    },
+    # The main daemon static_library with all the code used to check for updates
+    # with Omaha and expose a DBus daemon.
+    {
+      'target_name': 'libupdate_engine',
+      'type': 'static_library',
+      'dependencies': [
+        'libpayload_consumer',
+        'update_metadata-protos',
+        'update_engine-dbus-adaptor',
+      ],
+      'variables': {
+        'exported_deps': [
+          'dbus-1',
+          'expat',
+          'libcurl',
+          'libdebugd-client',
+          'libmetrics-<(libbase_ver)',
+          'libpower_manager-client',
+          'libsession_manager-client',
+          'libshill-client',
+          'libssl',
+          'libupdate_engine-client',
+          'vboot_host',
+        ],
+        'conditions':[
+          ['USE_dlc == 1', {
+            'exported_deps' : [
+              'libdlcservice-client',
+            ],
+          }],
+        ],
+        'deps': ['<@(exported_deps)'],
+      },
+      'all_dependent_settings': {
+        'variables': {
+          'deps': [
+            '<@(exported_deps)',
+          ],
+        },
+      },
+      'link_settings': {
+        'variables': {
+          'deps': [
+            '<@(exported_deps)',
+          ],
+        },
+        'libraries': [
+          '-lbz2',
+          '-lpolicy-<(libbase_ver)',
+          '-lrootdev',
+          '-lrt',
+        ],
+      },
+      'sources': [
+        'boot_control_chromeos.cc',
+        'certificate_checker.cc',
+        'common_service.cc',
+        'connection_manager.cc',
+        'connection_utils.cc',
+        'daemon.cc',
+        'dbus_connection.cc',
+        'dbus_service.cc',
+        'hardware_chromeos.cc',
+        'image_properties_chromeos.cc',
+        'libcurl_http_fetcher.cc',
+        'metrics_reporter_omaha.cc',
+        'metrics_utils.cc',
+        'omaha_request_action.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',
+        'update_manager/default_policy.cc',
+        'update_manager/enough_slots_ab_updates_policy_impl.cc',
+        'update_manager/enterprise_device_policy_impl.cc',
+        'update_manager/evaluation_context.cc',
+        'update_manager/interactive_update_policy_impl.cc',
+        'update_manager/next_update_check_policy_impl.cc',
+        'update_manager/official_build_check_policy_impl.cc',
+        'update_manager/out_of_box_experience_policy_impl.cc',
+        'update_manager/policy.cc',
+        'update_manager/policy_test_utils.cc',
+        'update_manager/real_config_provider.cc',
+        'update_manager/real_device_policy_provider.cc',
+        'update_manager/real_random_provider.cc',
+        'update_manager/real_shill_provider.cc',
+        'update_manager/real_system_provider.cc',
+        'update_manager/real_time_provider.cc',
+        'update_manager/real_updater_provider.cc',
+        'update_manager/staging_utils.cc',
+        'update_manager/state_factory.cc',
+        'update_manager/update_manager.cc',
+        'update_manager/update_time_restrictions_policy_impl.cc',
+        'update_manager/weekly_time.cc',
+        'update_status_utils.cc',
+      ],
+      'conditions': [
+        ['USE_chrome_network_proxy == 1', {
+          'sources': [
+            'chrome_browser_proxy_resolver.cc',
+          ],
+        }],
+        ['USE_chrome_kiosk_app == 1', {
+          'dependencies': [
+            'update_engine-dbus-kiosk-app-client',
+          ],
+        }],
+        ['USE_dlc == 1', {
+          'sources': [
+            'dlcservice_chromeos.cc',
+          ],
+        }],
+        ['USE_dlc == 0', {
+          'sources': [
+            'common/dlcservice_stub.cc',
+          ],
+        }],
+      ],
+    },
+    # update_engine daemon.
+    {
+      'target_name': 'update_engine',
+      'type': 'executable',
+      'dependencies': [
+        'libupdate_engine',
+      ],
+      'sources': [
+        'main.cc',
+      ],
+    },
+    # update_engine client library.
+    {
+      'target_name': 'libupdate_engine_client',
+      'type': 'static_library',
+      'variables': {
+        'deps': [
+          'dbus-1',
+          'libupdate_engine-client',
+        ],
+      },
+      'sources': [
+        'client_library/client.cc',
+        'client_library/client_dbus.cc',
+        'update_status_utils.cc',
+      ],
+      'include_dirs': [
+        'client_library/include',
+      ],
+    },
+    # update_engine console client.
+    {
+      'target_name': 'update_engine_client',
+      'type': 'executable',
+      'dependencies': [
+        'libupdate_engine_client',
+      ],
+      'sources': [
+        'common/error_code_utils.cc',
+        'omaha_utils.cc',
+        'update_engine_client.cc',
+      ],
+    },
+    # server-side code. This is used for delta_generator and unittests but not
+    # for any client code.
+    {
+      'target_name': 'libpayload_generator',
+      'type': 'static_library',
+      'dependencies': [
+        'libpayload_consumer',
+        'update_metadata-protos',
+      ],
+      'variables': {
+        'exported_deps': [
+          'ext2fs',
+          'libbsdiff',
+          'libpuffdiff',
+          'liblzma',
+        ],
+        'deps': ['<@(exported_deps)'],
+      },
+      'all_dependent_settings': {
+        'variables': {
+          'deps': [
+            '<@(exported_deps)',
+          ],
+        },
+      },
+      'link_settings': {
+        'variables': {
+          'deps': [
+            '<@(exported_deps)',
+          ],
+        },
+      },
+      'sources': [
+        'common/file_fetcher.cc',
+        'payload_generator/ab_generator.cc',
+        'payload_generator/annotated_operation.cc',
+        'payload_generator/blob_file_writer.cc',
+        'payload_generator/block_mapping.cc',
+        'payload_generator/boot_img_filesystem.cc',
+        'payload_generator/bzip.cc',
+        'payload_generator/cycle_breaker.cc',
+        'payload_generator/deflate_utils.cc',
+        'payload_generator/delta_diff_generator.cc',
+        'payload_generator/delta_diff_utils.cc',
+        'payload_generator/ext2_filesystem.cc',
+        'payload_generator/extent_ranges.cc',
+        'payload_generator/extent_utils.cc',
+        'payload_generator/full_update_generator.cc',
+        'payload_generator/graph_types.cc',
+        'payload_generator/graph_utils.cc',
+        'payload_generator/inplace_generator.cc',
+        'payload_generator/mapfile_filesystem.cc',
+        'payload_generator/payload_file.cc',
+        'payload_generator/payload_generation_config_chromeos.cc',
+        'payload_generator/payload_generation_config.cc',
+        'payload_generator/payload_signer.cc',
+        'payload_generator/raw_filesystem.cc',
+        'payload_generator/squashfs_filesystem.cc',
+        'payload_generator/tarjan.cc',
+        'payload_generator/topological_sort.cc',
+        'payload_generator/xz_chromeos.cc',
+      ],
+    },
+    # server-side delta generator.
+    {
+      'target_name': 'delta_generator',
+      'type': 'executable',
+      'dependencies': [
+        'libpayload_consumer',
+        'libpayload_generator',
+      ],
+      'link_settings': {
+        'ldflags!': [
+          '-pie',
+        ],
+      },
+      'sources': [
+        'payload_generator/generate_delta_main.cc',
+      ],
+    },
+    {
+      'target_name': 'update_engine_test_libs',
+      'type': 'static_library',
+      'variables': {
+        'deps': [
+          'libshill-client-test',
+        ],
+      },
+      'dependencies': [
+        'libupdate_engine',
+      ],
+      'includes': [
+        '../../../platform2/common-mk/common_test.gypi',
+      ],
+      'sources': [
+        'common/fake_prefs.cc',
+        'common/mock_http_fetcher.cc',
+        'common/test_utils.cc',
+        'fake_shill_proxy.cc',
+        'fake_system_state.cc',
+        'payload_consumer/fake_file_descriptor.cc',
+        'payload_generator/fake_filesystem.cc',
+        'update_manager/umtest_utils.cc',
+      ],
+    },
+  ],
+  'conditions': [
+    ['USE_test == 1', {
+      'targets': [
+        # Public keys used for unit testing.
+        {
+          'target_name': 'update_engine-testkeys',
+          'type': 'none',
+          'variables': {
+            'openssl_pem_in_dir': '.',
+            'openssl_pem_out_dir': 'include/update_engine',
+          },
+          'sources': [
+            'unittest_key.pem',
+            'unittest_key2.pem',
+            'unittest_key_RSA4096.pem',
+          ],
+          'includes': ['../../../platform2/common-mk/openssl_pem.gypi'],
+        },
+        # Unpacks sample images used for testing.
+        {
+          'target_name': 'update_engine-test_images',
+          'type': 'none',
+          'variables': {
+            'image_out_dir': '.',
+          },
+          'sources': [
+            'sample_images/sample_images.tar.bz2',
+          ],
+          'includes': ['tar_bunzip2.gypi'],
+        },
+        # Test HTTP Server.
+        {
+          'target_name': 'test_http_server',
+          'type': 'executable',
+          'sources': [
+            'common/http_common.cc',
+            'test_http_server.cc',
+          ],
+        },
+        # Test subprocess helper.
+        {
+          'target_name': 'test_subprocess',
+          'type': 'executable',
+          'sources': [
+            'test_subprocess.cc',
+          ],
+        },
+        # Main unittest file.
+        {
+          'target_name': 'update_engine_unittests',
+          'type': 'executable',
+          'variables': {
+            'deps': [
+              'libbrillo-test-<(libbase_ver)',
+              'libchrome-test-<(libbase_ver)',
+              'libdebugd-client-test',
+              'libpower_manager-client-test',
+              'libsession_manager-client-test',
+              'libshill-client-test',
+            ],
+          },
+          'dependencies': [
+            'libupdate_engine',
+            'libpayload_generator',
+            'update_engine_test_libs',
+          ],
+          'sources': [
+            'boot_control_chromeos_unittest.cc',
+            'certificate_checker_unittest.cc',
+            'common/action_pipe_unittest.cc',
+            'common/action_processor_unittest.cc',
+            'common/action_unittest.cc',
+            'common/cpu_limiter_unittest.cc',
+            'common/hash_calculator_unittest.cc',
+            'common/http_fetcher_unittest.cc',
+            'common/hwid_override_unittest.cc',
+            'common/prefs_unittest.cc',
+            'common/proxy_resolver_unittest.cc',
+            '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',
+            'metrics_reporter_omaha_unittest.cc',
+            'metrics_utils_unittest.cc',
+            'omaha_request_action_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',
+            'payload_consumer/file_writer_unittest.cc',
+            'payload_consumer/filesystem_verifier_action_unittest.cc',
+            'payload_consumer/postinstall_runner_action_unittest.cc',
+            'payload_consumer/xz_extent_writer_unittest.cc',
+            'payload_generator/ab_generator_unittest.cc',
+            'payload_generator/blob_file_writer_unittest.cc',
+            'payload_generator/block_mapping_unittest.cc',
+            'payload_generator/boot_img_filesystem_unittest.cc',
+            'payload_generator/cycle_breaker_unittest.cc',
+            'payload_generator/deflate_utils_unittest.cc',
+            'payload_generator/delta_diff_utils_unittest.cc',
+            'payload_generator/ext2_filesystem_unittest.cc',
+            'payload_generator/extent_ranges_unittest.cc',
+            'payload_generator/extent_utils_unittest.cc',
+            'payload_generator/full_update_generator_unittest.cc',
+            'payload_generator/graph_utils_unittest.cc',
+            'payload_generator/inplace_generator_unittest.cc',
+            'payload_generator/mapfile_filesystem_unittest.cc',
+            'payload_generator/payload_file_unittest.cc',
+            'payload_generator/payload_generation_config_unittest.cc',
+            'payload_generator/payload_signer_unittest.cc',
+            'payload_generator/squashfs_filesystem_unittest.cc',
+            'payload_generator/tarjan_unittest.cc',
+            'payload_generator/topological_sort_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/evaluation_context_unittest.cc',
+            'update_manager/generic_variables_unittest.cc',
+            'update_manager/prng_unittest.cc',
+            'update_manager/real_device_policy_provider_unittest.cc',
+            'update_manager/real_random_provider_unittest.cc',
+            'update_manager/real_shill_provider_unittest.cc',
+            'update_manager/real_system_provider_unittest.cc',
+            'update_manager/real_time_provider_unittest.cc',
+            'update_manager/real_updater_provider_unittest.cc',
+            'update_manager/staging_utils_unittest.cc',
+            'update_manager/update_manager_unittest.cc',
+            'update_manager/update_time_restrictions_policy_impl_unittest.cc',
+            'update_manager/variable_unittest.cc',
+            'update_manager/weekly_time_unittest.cc',
+          ],
+        },
+      ],
+    }],
+    # Fuzzer target.
+    ['USE_fuzzer == 1', {
+      'targets': [
+        {
+          'target_name': 'update_engine_omaha_request_action_fuzzer',
+          'type': 'executable',
+          'variables': {
+            'deps': [
+              'libbrillo-test-<(libbase_ver)',
+              'libchrome-test-<(libbase_ver)',
+            ],
+          },
+          'includes': [
+            '../../../platform2/common-mk/common_fuzzer.gypi',
+          ],
+          'dependencies': [
+            'libupdate_engine',
+            'update_engine_test_libs',
+          ],
+          'sources': [
+            'omaha_request_action_fuzzer.cc',
+          ],
+        },
+      ],
+    }],
+  ],
+}
diff --git a/cros/update_engine_client.cc b/update_engine_client.cc
similarity index 79%
rename from cros/update_engine_client.cc
rename to update_engine_client.cc
index 6f20f11..7446041 100644
--- a/cros/update_engine_client.cc
+++ b/update_engine_client.cc
@@ -26,33 +26,26 @@
 #include <base/command_line.h>
 #include <base/logging.h>
 #include <base/macros.h>
-#include <base/strings/string_number_conversions.h>
 #include <base/strings/string_split.h>
 #include <base/threading/platform_thread.h>
-#include <base/threading/thread_task_runner_handle.h>
 #include <brillo/daemons/daemon.h>
 #include <brillo/flag_helper.h>
-#include <brillo/key_value_store.h>
 
 #include "update_engine/client.h"
 #include "update_engine/common/error_code.h"
 #include "update_engine/common/error_code_utils.h"
-#include "update_engine/cros/omaha_utils.h"
+#include "update_engine/omaha_utils.h"
 #include "update_engine/status_update_handler.h"
 #include "update_engine/update_status.h"
 #include "update_engine/update_status_utils.h"
 
-using brillo::KeyValueStore;
-using chromeos_update_engine::EolDate;
-using chromeos_update_engine::EolDateToString;
+using chromeos_update_engine::EolStatus;
 using chromeos_update_engine::ErrorCode;
-using chromeos_update_engine::UpdateEngineStatusToString;
 using chromeos_update_engine::UpdateStatusToString;
 using chromeos_update_engine::utils::ErrorCodeToString;
 using std::string;
 using std::unique_ptr;
 using std::vector;
-using update_engine::UpdateEngineStatus;
 using update_engine::UpdateStatus;
 
 namespace {
@@ -87,7 +80,7 @@
 
     // We can't call QuitWithExitCode from OnInit(), so we delay the execution
     // of the ProcessFlags method after the Daemon initialization is done.
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
+    base::MessageLoop::current()->task_runner()->PostTask(
         FROM_HERE,
         base::Bind(&UpdateEngineClient::ProcessFlagsAndExit,
                    base::Unretained(this)));
@@ -139,47 +132,80 @@
  public:
   ~WatchingStatusUpdateHandler() override = default;
 
-  void HandleStatusUpdate(const UpdateEngineStatus& status) override;
+  void HandleStatusUpdate(int64_t last_checked_time,
+                          double progress,
+                          UpdateStatus current_operation,
+                          const string& new_version,
+                          int64_t new_size) override;
 };
 
 void WatchingStatusUpdateHandler::HandleStatusUpdate(
-    const UpdateEngineStatus& status) {
-  LOG(INFO) << "Got status update: " << UpdateEngineStatusToString(status);
+    int64_t last_checked_time,
+    double progress,
+    UpdateStatus current_operation,
+    const string& new_version,
+    int64_t new_size) {
+  LOG(INFO) << "Got status update:";
+  LOG(INFO) << "  last_checked_time: " << last_checked_time;
+  LOG(INFO) << "  progress: " << progress;
+  LOG(INFO) << "  current_operation: "
+            << UpdateStatusToString(current_operation);
+  LOG(INFO) << "  new_version: " << new_version;
+  LOG(INFO) << "  new_size: " << new_size;
 }
 
 bool UpdateEngineClient::ShowStatus() {
-  UpdateEngineStatus status;
+  int64_t last_checked_time = 0;
+  double progress = 0.0;
+  UpdateStatus current_op;
+  string new_version;
+  int64_t new_size = 0;
+
   int retry_count = kShowStatusRetryCount;
   while (retry_count > 0) {
-    if (client_->GetStatus(&status)) {
+    if (client_->GetStatus(&last_checked_time,
+                           &progress,
+                           &current_op,
+                           &new_version,
+                           &new_size)) {
       break;
     }
     if (--retry_count == 0) {
       return false;
     }
-    LOG(WARNING)
-        << "Failed to get the update_engine status. This can happen when the"
-           " update_engine is busy doing a heavy operation or if the"
-           " update-engine service is down. If it doesn't resolve, a restart of"
-           " the update-engine service is needed."
-           " Will try "
-        << retry_count << " more times!";
+    LOG(WARNING) << "Will try " << retry_count << " more times!";
     base::PlatformThread::Sleep(
         base::TimeDelta::FromSeconds(kShowStatusRetryIntervalInSeconds));
   }
 
-  printf("%s", UpdateEngineStatusToString(status).c_str());
+  printf("LAST_CHECKED_TIME=%" PRIi64
+         "\nPROGRESS=%f\nCURRENT_OP=%s\n"
+         "NEW_VERSION=%s\nNEW_SIZE=%" PRIi64 "\n",
+         last_checked_time,
+         progress,
+         UpdateStatusToString(current_op),
+         new_version.c_str(),
+         new_size);
 
   return true;
 }
 
 int UpdateEngineClient::GetNeedReboot() {
-  UpdateEngineStatus status;
-  if (!client_->GetStatus(&status)) {
+  int64_t last_checked_time = 0;
+  double progress = 0.0;
+  UpdateStatus current_op;
+  string new_version;
+  int64_t new_size = 0;
+
+  if (!client_->GetStatus(&last_checked_time,
+                          &progress,
+                          &current_op,
+                          &new_version,
+                          &new_size)) {
     return 1;
   }
 
-  if (status.status == UpdateStatus::UPDATED_NEED_REBOOT) {
+  if (current_op == UpdateStatus::UPDATED_NEED_REBOOT) {
     return 0;
   }
 
@@ -194,26 +220,35 @@
 
   ~UpdateWaitHandler() override = default;
 
-  void HandleStatusUpdate(const UpdateEngineStatus& status) override;
+  void HandleStatusUpdate(int64_t last_checked_time,
+                          double progress,
+                          UpdateStatus current_operation,
+                          const string& new_version,
+                          int64_t new_size) override;
 
  private:
   bool exit_on_error_;
   update_engine::UpdateEngineClient* client_;
 };
 
-void UpdateWaitHandler::HandleStatusUpdate(const UpdateEngineStatus& status) {
-  if (exit_on_error_ && status.status == UpdateStatus::IDLE) {
-    int last_attempt_error = static_cast<int>(ErrorCode::kSuccess);
+void UpdateWaitHandler::HandleStatusUpdate(int64_t /* last_checked_time */,
+                                           double /* progress */,
+                                           UpdateStatus current_operation,
+                                           const string& /* new_version */,
+                                           int64_t /* new_size */) {
+  if (exit_on_error_ && current_operation == UpdateStatus::IDLE) {
+    int last_attempt_error;
     ErrorCode code = ErrorCode::kSuccess;
     if (client_ && client_->GetLastAttemptError(&last_attempt_error))
       code = static_cast<ErrorCode>(last_attempt_error);
 
     LOG(ERROR) << "Update failed, current operation is "
-               << UpdateStatusToString(status.status) << ", last error code is "
-               << ErrorCodeToString(code) << "(" << last_attempt_error << ")";
+               << UpdateStatusToString(current_operation)
+               << ", last error code is " << ErrorCodeToString(code) << "("
+               << last_attempt_error << ")";
     exit(1);
   }
-  if (status.status == UpdateStatus::UPDATED_NEED_REBOOT) {
+  if (current_operation == UpdateStatus::UPDATED_NEED_REBOOT) {
     LOG(INFO) << "Update succeeded -- reboot needed.";
     exit(0);
   }
@@ -286,6 +321,8 @@
               "Show the previous OS version used before the update reboot.");
   DEFINE_bool(last_attempt_error, false, "Show the last attempt error.");
   DEFINE_bool(eol_status, false, "Show the current end-of-life status.");
+  DEFINE_bool(install, false, "Requests an install.");
+  DEFINE_string(dlc_module_ids, "", "colon-separated list of DLC IDs.");
 
   // Boilerplate init commands.
   base::CommandLine::Init(argc_, argv_);
@@ -470,6 +507,30 @@
     }
   }
 
+  if (FLAGS_install) {
+    // Parse DLC module IDs.
+    vector<string> dlc_module_ids;
+    if (!FLAGS_dlc_module_ids.empty()) {
+      dlc_module_ids = base::SplitString(FLAGS_dlc_module_ids,
+                                         ":",
+                                         base::TRIM_WHITESPACE,
+                                         base::SPLIT_WANT_ALL);
+    }
+    if (dlc_module_ids.empty()) {
+      LOG(ERROR) << "dlc_module_ids is empty:" << FLAGS_dlc_module_ids;
+      return 1;
+    }
+    if (!client_->AttemptInstall(FLAGS_omaha_url, dlc_module_ids)) {
+      LOG(ERROR) << "AttemptInstall failed.";
+      return 1;
+    }
+    return 0;
+  } else if (!FLAGS_dlc_module_ids.empty()) {
+    LOG(ERROR) << "dlc_module_ids is not empty while install is not set:"
+               << FLAGS_dlc_module_ids;
+    return 1;
+  }
+
   // Initiate an update check, if necessary.
   if (do_update_request) {
     LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored.";
@@ -478,7 +539,7 @@
       app_version = "ForcedUpdate";
       LOG(INFO) << "Forcing an update by setting app_version to ForcedUpdate.";
     }
-    LOG(INFO) << "Initiating update check.";
+    LOG(INFO) << "Initiating update check and install.";
     if (!client_->AttemptUpdate(
             app_version, FLAGS_omaha_url, FLAGS_interactive)) {
       LOG(ERROR) << "Error checking for update.";
@@ -561,26 +622,21 @@
       LOG(ERROR) << "Error getting last attempt error.";
     } else {
       ErrorCode code = static_cast<ErrorCode>(last_attempt_error);
-
-      KeyValueStore last_attempt_error_store;
-      last_attempt_error_store.SetString(
-          "ERROR_CODE", base::NumberToString(last_attempt_error));
-      last_attempt_error_store.SetString("ERROR_MESSAGE",
-                                         ErrorCodeToString(code));
-      printf("%s", last_attempt_error_store.SaveToString().c_str());
+      printf(
+          "ERROR_CODE=%i\n"
+          "ERROR_MESSAGE=%s\n",
+          last_attempt_error,
+          ErrorCodeToString(code).c_str());
     }
   }
 
   if (FLAGS_eol_status) {
-    UpdateEngineStatus status;
-    if (!client_->GetStatus(&status)) {
-      LOG(ERROR) << "Error GetStatus() for getting EOL info.";
+    int eol_status;
+    if (!client_->GetEolStatus(&eol_status)) {
+      LOG(ERROR) << "Error getting the end-of-life status.";
     } else {
-      EolDate eol_date_code = status.eol_date;
-
-      KeyValueStore eol_status_store;
-      eol_status_store.SetString("EOL_DATE", EolDateToString(eol_date_code));
-      printf("%s", eol_status_store.SaveToString().c_str());
+      EolStatus eol_status_code = static_cast<EolStatus>(eol_status);
+      printf("EOL_STATUS=%s\n", EolStatusToString(eol_status_code));
     }
   }
 
diff --git a/aosp/update_engine_client_android.cc b/update_engine_client_android.cc
similarity index 100%
rename from aosp/update_engine_client_android.cc
rename to update_engine_client_android.cc
diff --git a/update_manager/android_things_policy.cc b/update_manager/android_things_policy.cc
new file mode 100644
index 0000000..4afcf12
--- /dev/null
+++ b/update_manager/android_things_policy.cc
@@ -0,0 +1,182 @@
+//
+// 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/update_manager/android_things_policy.h"
+
+#include <string>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/time/time.h>
+
+#include "update_engine/update_manager/api_restricted_downloads_policy_impl.h"
+#include "update_engine/update_manager/enough_slots_ab_updates_policy_impl.h"
+#include "update_engine/update_manager/interactive_update_policy_impl.h"
+#include "update_engine/update_manager/official_build_check_policy_impl.h"
+
+using base::Time;
+using chromeos_update_engine::ErrorCode;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_manager {
+
+const NextUpdateCheckPolicyConstants
+    AndroidThingsPolicy::kNextUpdateCheckPolicyConstants = {
+        .timeout_initial_interval = 7 * 60,
+        .timeout_periodic_interval = 5 * 60 * 60,
+        .timeout_max_backoff_interval = 26 * 60 * 60,
+        .timeout_regular_fuzz = 10 * 60,
+        .attempt_backoff_max_interval_in_days = 16,
+        .attempt_backoff_fuzz_in_hours = 12,
+};
+
+EvalStatus AndroidThingsPolicy::UpdateCheckAllowed(
+    EvaluationContext* ec,
+    State* state,
+    string* error,
+    UpdateCheckParams* result) const {
+  // Set the default return values.
+  result->updates_enabled = true;
+  result->target_channel.clear();
+  result->target_version_prefix.clear();
+  result->rollback_allowed = false;
+  result->rollback_allowed_milestones = -1;
+  result->interactive = false;
+
+  // Build a list of policies to consult.  Note that each policy may modify the
+  // result structure, even if it signals kContinue.
+  EnoughSlotsAbUpdatesPolicyImpl enough_slots_ab_updates_policy;
+  OnlyUpdateOfficialBuildsPolicyImpl only_update_official_builds_policy;
+  InteractiveUpdatePolicyImpl interactive_update_policy;
+  NextUpdateCheckTimePolicyImpl next_update_check_time_policy(
+      kNextUpdateCheckPolicyConstants);
+
+  vector<Policy const*> policies_to_consult = {
+      // Do not perform any updates if there are not enough slots to do
+      // A/B updates
+      &enough_slots_ab_updates_policy,
+
+      // Check to see if an interactive update was requested.
+      &interactive_update_policy,
+
+      // Unofficial builds should not perform periodic update checks.
+      &only_update_official_builds_policy,
+
+      // Ensure that periodic update checks are timed properly.
+      &next_update_check_time_policy,
+  };
+
+  // Now that the list of policy implementations, and the order to consult them,
+  // as been setup, do that.  If none of the policies make a definitive
+  // decisions about whether or not to check for updates, then allow the update
+  // check to happen.
+  EvalStatus status = ConsultPolicies(policies_to_consult,
+                                      &Policy::UpdateCheckAllowed,
+                                      ec,
+                                      state,
+                                      error,
+                                      result);
+  if (status != EvalStatus::kContinue) {
+    return status;
+  } else {
+    // It is time to check for an update.
+    LOG(INFO) << "Allowing update check.";
+    return EvalStatus::kSucceeded;
+  }
+}
+
+// Uses the |UpdateRestrictions| to determine if the download and apply can
+// occur at this time.
+EvalStatus AndroidThingsPolicy::UpdateCanBeApplied(
+    EvaluationContext* ec,
+    State* state,
+    string* error,
+    ErrorCode* result,
+    chromeos_update_engine::InstallPlan* install_plan) const {
+  // Build a list of policies to consult.  Note that each policy may modify the
+  // result structure, even if it signals kContinue.
+  ApiRestrictedDownloadsPolicyImpl api_restricted_downloads_policy;
+
+  vector<Policy const*> policies_to_consult = {
+      // Do not apply the update if all updates are restricted by the API.
+      &api_restricted_downloads_policy,
+  };
+
+  // Now that the list of policy implementations, and the order to consult them,
+  // as been setup, do that.  If none of the policies make a definitive
+  // decisions about whether or not to check for updates, then allow the update
+  // check to happen.
+  EvalStatus status = ConsultPolicies(policies_to_consult,
+                                      &Policy::UpdateCanBeApplied,
+                                      ec,
+                                      state,
+                                      error,
+                                      result,
+                                      install_plan);
+  if (EvalStatus::kContinue != status) {
+    return status;
+  } else {
+    // The update can proceed.
+    LOG(INFO) << "Allowing update to be applied.";
+    *result = ErrorCode::kSuccess;
+    return EvalStatus::kSucceeded;
+  }
+}
+
+// Always returns |EvalStatus::kSucceeded|
+EvalStatus AndroidThingsPolicy::UpdateCanStart(EvaluationContext* ec,
+                                               State* state,
+                                               string* error,
+                                               UpdateDownloadParams* result,
+                                               UpdateState update_state) const {
+  // Update is good to go.
+  result->update_can_start = true;
+  return EvalStatus::kSucceeded;
+}
+
+// Always returns |EvalStatus::kSucceeded|
+EvalStatus AndroidThingsPolicy::UpdateDownloadAllowed(EvaluationContext* ec,
+                                                      State* state,
+                                                      string* error,
+                                                      bool* result) const {
+  // By default, we allow updates.
+  *result = true;
+  return EvalStatus::kSucceeded;
+}
+
+// P2P is always disabled.  Returns |result|==|false| and
+// |EvalStatus::kSucceeded|
+EvalStatus AndroidThingsPolicy::P2PEnabled(EvaluationContext* ec,
+                                           State* state,
+                                           string* error,
+                                           bool* result) const {
+  *result = false;
+  return EvalStatus::kSucceeded;
+}
+
+// This will return immediately with |EvalStatus::kSucceeded| and set
+// |result|==|false|
+EvalStatus AndroidThingsPolicy::P2PEnabledChanged(EvaluationContext* ec,
+                                                  State* state,
+                                                  string* error,
+                                                  bool* result,
+                                                  bool prev_result) const {
+  *result = false;
+  return EvalStatus::kSucceeded;
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/android_things_policy.h b/update_manager/android_things_policy.h
new file mode 100644
index 0000000..9fd8bc4
--- /dev/null
+++ b/update_manager/android_things_policy.h
@@ -0,0 +1,92 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_UPDATE_MANAGER_ANDROID_THINGS_POLICY_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_ANDROID_THINGS_POLICY_H_
+
+#include <string>
+
+#include "update_engine/update_manager/next_update_check_policy_impl.h"
+#include "update_engine/update_manager/policy_utils.h"
+
+namespace chromeos_update_manager {
+
+// AndroidThingsPolicy implements the policy-related logic used in
+// AndroidThings.
+class AndroidThingsPolicy : public Policy {
+ public:
+  AndroidThingsPolicy() = default;
+  ~AndroidThingsPolicy() override = default;
+
+  // Policy overrides.
+  EvalStatus UpdateCheckAllowed(EvaluationContext* ec,
+                                State* state,
+                                std::string* error,
+                                UpdateCheckParams* result) const override;
+
+  // Uses the |UpdateRestrictions| to determine if the download and apply can
+  // occur at this time.
+  EvalStatus UpdateCanBeApplied(
+      EvaluationContext* ec,
+      State* state,
+      std::string* error,
+      chromeos_update_engine::ErrorCode* result,
+      chromeos_update_engine::InstallPlan* install_plan) const override;
+
+  // Always returns |EvalStatus::kSucceeded|
+  EvalStatus UpdateCanStart(EvaluationContext* ec,
+                            State* state,
+                            std::string* error,
+                            UpdateDownloadParams* result,
+                            UpdateState update_state) const override;
+
+  // Always returns |EvalStatus::kSucceeded|
+  EvalStatus UpdateDownloadAllowed(EvaluationContext* ec,
+                                   State* state,
+                                   std::string* error,
+                                   bool* result) const override;
+
+  // P2P is always disabled.  Returns |result|==|false| and
+  // |EvalStatus::kSucceeded|
+  EvalStatus P2PEnabled(EvaluationContext* ec,
+                        State* state,
+                        std::string* error,
+                        bool* result) const override;
+
+  // This will return immediately with |EvalStatus::kSucceeded| and set
+  // |result|==|false|
+  EvalStatus P2PEnabledChanged(EvaluationContext* ec,
+                               State* state,
+                               std::string* error,
+                               bool* result,
+                               bool prev_result) const override;
+
+ protected:
+  // Policy override.
+  std::string PolicyName() const override { return "AndroidThingsPolicy"; }
+
+ private:
+  friend class UmAndroidThingsPolicyTest;
+  FRIEND_TEST(UmAndroidThingsPolicyTest, UpdateCheckAllowedWaitsForTheTimeout);
+
+  static const NextUpdateCheckPolicyConstants kNextUpdateCheckPolicyConstants;
+
+  DISALLOW_COPY_AND_ASSIGN(AndroidThingsPolicy);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_ANDROID_THINGS_POLICY_H_
diff --git a/update_manager/android_things_policy_unittest.cc b/update_manager/android_things_policy_unittest.cc
new file mode 100644
index 0000000..6961efc
--- /dev/null
+++ b/update_manager/android_things_policy_unittest.cc
@@ -0,0 +1,188 @@
+//
+// 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/update_manager/android_things_policy.h"
+
+#include <memory>
+
+#include "update_engine/update_manager/next_update_check_policy_impl.h"
+#include "update_engine/update_manager/policy_test_utils.h"
+
+using base::Time;
+using base::TimeDelta;
+using chromeos_update_engine::ErrorCode;
+using chromeos_update_engine::InstallPlan;
+
+namespace chromeos_update_manager {
+
+class UmAndroidThingsPolicyTest : public UmPolicyTestBase {
+ protected:
+  UmAndroidThingsPolicyTest() {
+    policy_ = std::make_unique<AndroidThingsPolicy>();
+  }
+
+  void SetUpDefaultState() override {
+    UmPolicyTestBase::SetUpDefaultState();
+
+    // For the purpose of the tests, this is an official build
+    fake_state_.system_provider()->var_is_official_build()->reset(
+        new bool(true));
+    // NOLINTNEXTLINE(readability/casting)
+    fake_state_.system_provider()->var_num_slots()->reset(new unsigned int(2));
+  }
+
+  // Configures the policy to return a desired value from UpdateCheckAllowed by
+  // faking the current wall clock time as needed. Restores the default state.
+  // This is used when testing policies that depend on this one.
+  virtual void SetUpdateCheckAllowed(bool allow_check) {
+    Time next_update_check;
+    CallMethodWithContext(&NextUpdateCheckTimePolicyImpl::NextUpdateCheckTime,
+                          &next_update_check,
+                          AndroidThingsPolicy::kNextUpdateCheckPolicyConstants);
+    SetUpDefaultState();
+    Time curr_time = next_update_check;
+    if (allow_check)
+      curr_time += TimeDelta::FromSeconds(1);
+    else
+      curr_time -= TimeDelta::FromSeconds(1);
+    fake_clock_.SetWallclockTime(curr_time);
+  }
+};
+
+TEST_F(UmAndroidThingsPolicyTest, UpdateCheckAllowedWaitsForTheTimeout) {
+  // We get the next update_check timestamp from the policy's private method
+  // and then we check the public method respects that value on the normal
+  // case.
+  Time next_update_check;
+  Time last_checked_time =
+      fake_clock_.GetWallclockTime() + TimeDelta::FromMinutes(1234);
+
+  LOG(INFO) << "last_checked_time: " << last_checked_time;
+  fake_state_.updater_provider()->var_last_checked_time()->reset(
+      new Time(last_checked_time));
+  CallMethodWithContext(&NextUpdateCheckTimePolicyImpl::NextUpdateCheckTime,
+                        &next_update_check,
+                        AndroidThingsPolicy::kNextUpdateCheckPolicyConstants);
+  LOG(INFO) << "Next check allowed at: " << next_update_check;
+
+  // Check that the policy blocks until the next_update_check is reached.
+  SetUpDefaultClock();
+  SetUpDefaultState();
+  fake_state_.updater_provider()->var_last_checked_time()->reset(
+      new Time(last_checked_time));
+  fake_clock_.SetWallclockTime(next_update_check - TimeDelta::FromSeconds(1));
+
+  UpdateCheckParams result;
+  ExpectPolicyStatus(
+      EvalStatus::kAskMeAgainLater, &Policy::UpdateCheckAllowed, &result);
+
+  SetUpDefaultClock();
+  SetUpDefaultState();
+  fake_state_.updater_provider()->var_last_checked_time()->reset(
+      new Time(last_checked_time));
+  fake_clock_.SetWallclockTime(next_update_check + TimeDelta::FromSeconds(1));
+  ExpectPolicyStatus(
+      EvalStatus::kSucceeded, &Policy::UpdateCheckAllowed, &result);
+  EXPECT_TRUE(result.updates_enabled);
+  EXPECT_FALSE(result.interactive);
+}
+
+TEST_F(UmAndroidThingsPolicyTest,
+       UpdateCheckAllowedUpdatesDisabledForUnofficialBuilds) {
+  // UpdateCheckAllowed should return kAskMeAgainLater if this is an unofficial
+  // build; we don't want periodic update checks on developer images.
+
+  fake_state_.system_provider()->var_is_official_build()->reset(
+      new bool(false));
+
+  UpdateCheckParams result;
+  ExpectPolicyStatus(
+      EvalStatus::kAskMeAgainLater, &Policy::UpdateCheckAllowed, &result);
+}
+
+TEST_F(UmAndroidThingsPolicyTest,
+       UpdateCheckAllowedUpdatesDisabledWhenNotEnoughSlotsAbUpdates) {
+  // UpdateCheckAllowed should return false (kSucceeded) if the image booted
+  // without enough slots to do A/B updates.
+
+  // NOLINTNEXTLINE(readability/casting)
+  fake_state_.system_provider()->var_num_slots()->reset(new unsigned int(1));
+
+  UpdateCheckParams result;
+  ExpectPolicyStatus(
+      EvalStatus::kSucceeded, &Policy::UpdateCheckAllowed, &result);
+  EXPECT_FALSE(result.updates_enabled);
+}
+
+TEST_F(UmAndroidThingsPolicyTest,
+       UpdateCheckAllowedForcedUpdateRequestedInteractive) {
+  // UpdateCheckAllowed should return true because a forced update request was
+  // signaled for an interactive update.
+
+  SetUpdateCheckAllowed(true);
+  fake_state_.updater_provider()->var_forced_update_requested()->reset(
+      new UpdateRequestStatus(UpdateRequestStatus::kInteractive));
+
+  UpdateCheckParams result;
+  ExpectPolicyStatus(
+      EvalStatus::kSucceeded, &Policy::UpdateCheckAllowed, &result);
+  EXPECT_TRUE(result.updates_enabled);
+  EXPECT_TRUE(result.interactive);
+}
+
+TEST_F(UmAndroidThingsPolicyTest,
+       UpdateCheckAllowedForcedUpdateRequestedPeriodic) {
+  // UpdateCheckAllowed should return true because a forced update request was
+  // signaled for a periodic check.
+
+  SetUpdateCheckAllowed(true);
+  fake_state_.updater_provider()->var_forced_update_requested()->reset(
+      new UpdateRequestStatus(UpdateRequestStatus::kPeriodic));
+
+  UpdateCheckParams result;
+  ExpectPolicyStatus(
+      EvalStatus::kSucceeded, &Policy::UpdateCheckAllowed, &result);
+  EXPECT_TRUE(result.updates_enabled);
+  EXPECT_FALSE(result.interactive);
+}
+
+TEST_F(UmAndroidThingsPolicyTest, UpdateCanBeAppliedOk) {
+  // UpdateCanBeApplied should return kSucceeded in the base case
+
+  InstallPlan plan;
+  ErrorCode result;
+  ExpectPolicyStatus(
+      EvalStatus::kSucceeded, &Policy::UpdateCanBeApplied, &result, &plan);
+
+  EXPECT_EQ(ErrorCode::kSuccess, result);
+}
+
+TEST_F(UmAndroidThingsPolicyTest, UpdateCanBeAppliedRestricted) {
+  // UpdateCanBeApplied should return kOmahaUpdateDeferredPerPolicy in
+  // when the restricted flag is set in the Updater.
+
+  fake_state_.updater_provider()->var_update_restrictions()->reset(
+      new UpdateRestrictions(UpdateRestrictions::kRestrictDownloading));
+
+  InstallPlan plan;
+  ErrorCode result;
+  ExpectPolicyStatus(
+      EvalStatus::kSucceeded, &Policy::UpdateCanBeApplied, &result, &plan);
+
+  EXPECT_EQ(ErrorCode::kOmahaUpdateDeferredPerPolicy, result);
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/boxed_value.cc b/update_manager/boxed_value.cc
index 907eb95..cee1ece 100644
--- a/update_manager/boxed_value.cc
+++ b/update_manager/boxed_value.cc
@@ -23,10 +23,9 @@
 
 #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"
@@ -52,25 +51,41 @@
 template <>
 string BoxedValue::ValuePrinter<int>(const void* value) {
   const int* val = reinterpret_cast<const int*>(value);
+#if BASE_VER < 576279
+  return base::IntToString(*val);
+#else
   return base::NumberToString(*val);
+#endif
 }
 
 template <>
 string BoxedValue::ValuePrinter<unsigned int>(const void* value) {
   const unsigned int* val = reinterpret_cast<const unsigned int*>(value);
+#if BASE_VER < 576279
+  return base::UintToString(*val);
+#else
   return base::NumberToString(*val);
+#endif
 }
 
 template <>
 string BoxedValue::ValuePrinter<int64_t>(const void* value) {
   const int64_t* val = reinterpret_cast<const int64_t*>(value);
+#if BASE_VER < 576279
+  return base::Int64ToString(*val);
+#else
   return base::NumberToString(*val);
+#endif
 }
 
 template <>
 string BoxedValue::ValuePrinter<uint64_t>(const void* value) {
   const uint64_t* val = reinterpret_cast<const uint64_t*>(value);
+#if BASE_VER < 576279
+  return base::Uint64ToString(*val);
+#else
   return base::NumberToString(*val);
+#endif
 }
 
 template <>
@@ -82,7 +97,11 @@
 template <>
 string BoxedValue::ValuePrinter<double>(const void* value) {
   const double* val = reinterpret_cast<const double*>(value);
+#if BASE_VER < 576279
+  return base::DoubleToString(*val);
+#else
   return base::NumberToString(*val);
+#endif
 }
 
 template <>
@@ -148,6 +167,8 @@
       return "Rollback and powerwash";
     case RollbackToTargetVersion::kRollbackAndRestoreIfPossible:
       return "Rollback and restore if possible";
+    case RollbackToTargetVersion::kRollbackOnlyIfRestorePossible:
+      return "Rollback only if restore is possible";
     case RollbackToTargetVersion::kMaxValue:
       NOTREACHED();
       return "Max value";
@@ -178,8 +199,6 @@
       return "Reporting Error Event";
     case Stage::kAttemptingRollback:
       return "Attempting Rollback";
-    case Stage::kCleanupPreviousUpdate:
-      return "Cleanup Previous Update";
   }
   NOTREACHED();
   return "Unknown";
@@ -235,30 +254,4 @@
   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/boxed_value_unittest.cc b/update_manager/boxed_value_unittest.cc
index 5b87a7b..2fa94ec 100644
--- a/update_manager/boxed_value_unittest.cc
+++ b/update_manager/boxed_value_unittest.cc
@@ -168,6 +168,11 @@
       BoxedValue(new ConnectionType(ConnectionType::kEthernet)).ToString());
   EXPECT_EQ("wifi",
             BoxedValue(new ConnectionType(ConnectionType::kWifi)).ToString());
+  EXPECT_EQ("wimax",
+            BoxedValue(new ConnectionType(ConnectionType::kWimax)).ToString());
+  EXPECT_EQ(
+      "bluetooth",
+      BoxedValue(new ConnectionType(ConnectionType::kBluetooth)).ToString());
   EXPECT_EQ(
       "cellular",
       BoxedValue(new ConnectionType(ConnectionType::kCellular)).ToString());
@@ -210,13 +215,18 @@
       BoxedValue(new RollbackToTargetVersion(
                      RollbackToTargetVersion::kRollbackAndRestoreIfPossible))
           .ToString());
+  EXPECT_EQ(
+      "Rollback only if restore is possible",
+      BoxedValue(new RollbackToTargetVersion(
+                     RollbackToTargetVersion::kRollbackOnlyIfRestorePossible))
+          .ToString());
 }
 
 TEST(UmBoxedValueTest, SetConnectionTypeToString) {
   set<ConnectionType>* set1 = new set<ConnectionType>;
-  set1->insert(ConnectionType::kCellular);
+  set1->insert(ConnectionType::kWimax);
   set1->insert(ConnectionType::kEthernet);
-  EXPECT_EQ("ethernet,cellular", BoxedValue(set1).ToString());
+  EXPECT_EQ("ethernet,wimax", BoxedValue(set1).ToString());
 
   set<ConnectionType>* set2 = new set<ConnectionType>;
   set2->insert(ConnectionType::kWifi);
diff --git a/update_manager/chromeos_policy.cc b/update_manager/chromeos_policy.cc
index 24b8293..1fa8636 100644
--- a/update_manager/chromeos_policy.cc
+++ b/update_manager/chromeos_policy.cc
@@ -17,7 +17,6 @@
 #include "update_engine/update_manager/chromeos_policy.h"
 
 #include <algorithm>
-#include <memory>
 #include <set>
 #include <string>
 #include <vector>
@@ -49,7 +48,6 @@
 using std::min;
 using std::set;
 using std::string;
-using std::unique_ptr;
 using std::vector;
 
 namespace {
@@ -156,7 +154,6 @@
     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) << ")";
@@ -193,10 +190,6 @@
 
 namespace chromeos_update_manager {
 
-unique_ptr<Policy> GetSystemPolicy() {
-  return std::make_unique<ChromeOSPolicy>();
-}
-
 const NextUpdateCheckPolicyConstants
     ChromeOSPolicy::kNextUpdateCheckPolicyConstants = {
         .timeout_initial_interval = 7 * 60,
@@ -217,13 +210,10 @@
   // 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;
-  result->quick_fix_build_token.clear();
 
   EnoughSlotsAbUpdatesPolicyImpl enough_slots_ab_updates_policy;
   EnterpriseDevicePolicyImpl enterprise_device_policy;
@@ -466,6 +456,92 @@
   return EvalStatus::kSucceeded;
 }
 
+// TODO(garnold) Logic in this method is based on
+// ConnectionManager::IsUpdateAllowedOver(); be sure to deprecate the latter.
+//
+// TODO(garnold) The current logic generally treats the list of allowed
+// connections coming from the device policy as a whitelist, meaning that it
+// can only be used for enabling connections, but not disable them. Further,
+// certain connection types (like Bluetooth) cannot be enabled even by policy.
+// In effect, the only thing that device policy can change is to enable
+// updates over a cellular network (disabled by default). We may want to
+// revisit this semantics, allowing greater flexibility in defining specific
+// permissions over all types of networks.
+EvalStatus ChromeOSPolicy::UpdateDownloadAllowed(EvaluationContext* ec,
+                                                 State* state,
+                                                 string* error,
+                                                 bool* result) const {
+  // Get the current connection type.
+  ShillProvider* const shill_provider = state->shill_provider();
+  const ConnectionType* conn_type_p =
+      ec->GetValue(shill_provider->var_conn_type());
+  POLICY_CHECK_VALUE_AND_FAIL(conn_type_p, error);
+  ConnectionType conn_type = *conn_type_p;
+
+  // If we're tethering, treat it as a cellular connection.
+  if (conn_type != ConnectionType::kCellular) {
+    const ConnectionTethering* conn_tethering_p =
+        ec->GetValue(shill_provider->var_conn_tethering());
+    POLICY_CHECK_VALUE_AND_FAIL(conn_tethering_p, error);
+    if (*conn_tethering_p == ConnectionTethering::kConfirmed)
+      conn_type = ConnectionType::kCellular;
+  }
+
+  // By default, we allow updates for all connection types, with exceptions as
+  // noted below. This also determines whether a device policy can override the
+  // default.
+  *result = true;
+  bool device_policy_can_override = false;
+  switch (conn_type) {
+    case ConnectionType::kBluetooth:
+      *result = false;
+      break;
+
+    case ConnectionType::kCellular:
+      *result = false;
+      device_policy_can_override = true;
+      break;
+
+    case ConnectionType::kUnknown:
+      if (error)
+        *error = "Unknown connection type";
+      return EvalStatus::kFailed;
+
+    default:
+      break;  // Nothing to do.
+  }
+
+  // If update is allowed, we're done.
+  if (*result)
+    return EvalStatus::kSucceeded;
+
+  // Check whether the device policy specifically allows this connection.
+  if (device_policy_can_override) {
+    DevicePolicyProvider* const dp_provider = state->device_policy_provider();
+    const bool* device_policy_is_loaded_p =
+        ec->GetValue(dp_provider->var_device_policy_is_loaded());
+    if (device_policy_is_loaded_p && *device_policy_is_loaded_p) {
+      const set<ConnectionType>* allowed_conn_types_p =
+          ec->GetValue(dp_provider->var_allowed_connection_types_for_update());
+      if (allowed_conn_types_p) {
+        if (allowed_conn_types_p->count(conn_type)) {
+          *result = true;
+          return EvalStatus::kSucceeded;
+        }
+      } else if (conn_type == ConnectionType::kCellular) {
+        // Local user settings can allow updates over cellular iff a policy was
+        // loaded but no allowed connections were specified in it.
+        const bool* update_over_cellular_allowed_p =
+            ec->GetValue(state->updater_provider()->var_cellular_enabled());
+        if (update_over_cellular_allowed_p && *update_over_cellular_allowed_p)
+          *result = true;
+      }
+    }
+  }
+
+  return (*result ? EvalStatus::kSucceeded : EvalStatus::kAskMeAgainLater);
+}
+
 EvalStatus ChromeOSPolicy::P2PEnabled(EvaluationContext* ec,
                                       State* state,
                                       string* error,
@@ -484,9 +560,8 @@
     if (policy_au_p2p_enabled_p) {
       enabled = *policy_au_p2p_enabled_p;
     } else {
-      const bool* policy_has_owner_p =
-          ec->GetValue(dp_provider->var_has_owner());
-      if (!policy_has_owner_p || !*policy_has_owner_p)
+      const string* policy_owner_p = ec->GetValue(dp_provider->var_owner());
+      if (!policy_owner_p || policy_owner_p->empty())
         enabled = true;
     }
   }
@@ -520,6 +595,7 @@
     string* error,
     UpdateBackoffAndDownloadUrlResult* result,
     const UpdateState& update_state) const {
+  // Sanity checks.
   DCHECK_GE(update_state.download_errors_max, 0);
 
   // Set default result values.
@@ -591,7 +667,7 @@
   Time prev_err_time;
   bool is_first = true;
   for (const auto& err_tuple : update_state.download_errors) {
-    // Do some validation checks.
+    // Do some sanity checks.
     int used_url_idx = get<0>(err_tuple);
     if (is_first && url_idx >= 0 && used_url_idx != url_idx) {
       LOG(WARNING) << "First URL in error log (" << used_url_idx
diff --git a/update_manager/chromeos_policy.h b/update_manager/chromeos_policy.h
index 3c196da..ded5164 100644
--- a/update_manager/chromeos_policy.h
+++ b/update_manager/chromeos_policy.h
@@ -72,6 +72,11 @@
                             UpdateDownloadParams* result,
                             UpdateState update_state) const override;
 
+  EvalStatus UpdateDownloadAllowed(EvaluationContext* ec,
+                                   State* state,
+                                   std::string* error,
+                                   bool* result) const override;
+
   EvalStatus P2PEnabled(EvaluationContext* ec,
                         State* state,
                         std::string* error,
diff --git a/update_manager/chromeos_policy_unittest.cc b/update_manager/chromeos_policy_unittest.cc
index 5bd416d..5341ebb 100644
--- a/update_manager/chromeos_policy_unittest.cc
+++ b/update_manager/chromeos_policy_unittest.cc
@@ -109,7 +109,7 @@
       curr_time += TimeDelta::FromSeconds(1);
     else
       curr_time -= TimeDelta::FromSeconds(1);
-    fake_clock_->SetWallclockTime(curr_time);
+    fake_clock_.SetWallclockTime(curr_time);
   }
 
   // Sets the policies required for a kiosk app to control Chrome OS version:
@@ -180,7 +180,7 @@
   // case.
   Time next_update_check;
   Time last_checked_time =
-      fake_clock_->GetWallclockTime() + TimeDelta::FromMinutes(1234);
+      fake_clock_.GetWallclockTime() + TimeDelta::FromMinutes(1234);
 
   fake_state_.updater_provider()->var_last_checked_time()->reset(
       new Time(last_checked_time));
@@ -195,7 +195,7 @@
   SetUpDefaultState();
   fake_state_.updater_provider()->var_last_checked_time()->reset(
       new Time(last_checked_time));
-  fake_clock_->SetWallclockTime(next_update_check - TimeDelta::FromSeconds(1));
+  fake_clock_.SetWallclockTime(next_update_check - TimeDelta::FromSeconds(1));
   ExpectPolicyStatus(
       EvalStatus::kAskMeAgainLater, &Policy::UpdateCheckAllowed, &result);
 
@@ -203,7 +203,7 @@
   SetUpDefaultState();
   fake_state_.updater_provider()->var_last_checked_time()->reset(
       new Time(last_checked_time));
-  fake_clock_->SetWallclockTime(next_update_check + TimeDelta::FromSeconds(1));
+  fake_clock_.SetWallclockTime(next_update_check + TimeDelta::FromSeconds(1));
   ExpectPolicyStatus(
       EvalStatus::kSucceeded, &Policy::UpdateCheckAllowed, &result);
   EXPECT_TRUE(result.updates_enabled);
@@ -216,7 +216,7 @@
   // Ensure that update is not allowed even if wait period is satisfied.
   Time next_update_check;
   Time last_checked_time =
-      fake_clock_->GetWallclockTime() + TimeDelta::FromMinutes(1234);
+      fake_clock_.GetWallclockTime() + TimeDelta::FromMinutes(1234);
 
   fake_state_.updater_provider()->var_last_checked_time()->reset(
       new Time(last_checked_time));
@@ -228,7 +228,7 @@
   SetUpDefaultState();
   fake_state_.updater_provider()->var_last_checked_time()->reset(
       new Time(last_checked_time));
-  fake_clock_->SetWallclockTime(next_update_check + TimeDelta::FromSeconds(1));
+  fake_clock_.SetWallclockTime(next_update_check + TimeDelta::FromSeconds(1));
   fake_state_.system_provider()->var_is_oobe_complete()->reset(new bool(false));
 
   UpdateCheckParams result;
@@ -240,7 +240,7 @@
   SetUpDefaultState();
   fake_state_.updater_provider()->var_last_checked_time()->reset(
       new Time(last_checked_time));
-  fake_clock_->SetWallclockTime(next_update_check + TimeDelta::FromSeconds(1));
+  fake_clock_.SetWallclockTime(next_update_check + TimeDelta::FromSeconds(1));
   ExpectPolicyStatus(
       EvalStatus::kSucceeded, &Policy::UpdateCheckAllowed, &result);
   EXPECT_TRUE(result.updates_enabled);
@@ -262,10 +262,6 @@
       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"));
-  fake_state_.device_policy_provider()->var_quick_fix_build_token()->reset(
-      new string("foo-token"));
 
   UpdateCheckParams result;
   ExpectPolicyStatus(
@@ -274,8 +270,6 @@
   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_EQ("foo-token", result.quick_fix_build_token);
   EXPECT_FALSE(result.interactive);
 }
 
@@ -290,6 +284,12 @@
       true, RollbackToTargetVersion::kRollbackAndRestoreIfPossible));
 }
 
+TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedRollbackOnlyIfRestorePossible) {
+  // We're not allowed to do rollback until we support data save and restore.
+  EXPECT_FALSE(TestRollbackAllowed(
+      true, RollbackToTargetVersion::kRollbackOnlyIfRestorePossible));
+}
+
 TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedRollbackDisabled) {
   EXPECT_FALSE(TestRollbackAllowed(true, RollbackToTargetVersion::kDisabled));
 }
@@ -344,26 +344,6 @@
       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
@@ -572,7 +552,7 @@
 
   SetUpdateCheckAllowed(false);
 
-  const Time curr_time = fake_clock_->GetWallclockTime();
+  const Time curr_time = fake_clock_.GetWallclockTime();
   UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(10));
   update_state.download_errors_max = 1;
   update_state.download_errors.emplace_back(
@@ -602,7 +582,7 @@
 
   SetUpdateCheckAllowed(false);
 
-  const Time curr_time = fake_clock_->GetWallclockTime();
+  const Time curr_time = fake_clock_.GetWallclockTime();
   UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(10));
   update_state.download_errors_max = 1;
   update_state.download_errors.emplace_back(
@@ -635,7 +615,7 @@
 
   SetUpdateCheckAllowed(false);
 
-  const Time curr_time = fake_clock_->GetWallclockTime();
+  const Time curr_time = fake_clock_.GetWallclockTime();
   UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(10));
   update_state.download_errors_max = 1;
   update_state.download_errors.emplace_back(
@@ -669,7 +649,7 @@
 
   SetUpdateCheckAllowed(false);
 
-  const Time curr_time = fake_clock_->GetWallclockTime();
+  const Time curr_time = fake_clock_.GetWallclockTime();
   UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(10));
   update_state.download_errors_max = 1;
   update_state.download_errors.emplace_back(
@@ -702,7 +682,7 @@
 
   SetUpdateCheckAllowed(false);
 
-  const Time curr_time = fake_clock_->GetWallclockTime();
+  const Time curr_time = fake_clock_.GetWallclockTime();
   UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(10));
   update_state.download_errors_max = 1;
   update_state.download_errors.emplace_back(
@@ -735,7 +715,7 @@
 
   SetUpdateCheckAllowed(false);
 
-  const Time curr_time = fake_clock_->GetWallclockTime();
+  const Time curr_time = fake_clock_.GetWallclockTime();
   UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(10));
   update_state.download_errors_max = 1;
   update_state.download_errors.emplace_back(
@@ -768,7 +748,7 @@
 
   SetUpdateCheckAllowed(false);
 
-  const Time curr_time = fake_clock_->GetWallclockTime();
+  const Time curr_time = fake_clock_.GetWallclockTime();
   UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(10));
   update_state.download_errors_max = 1;
   update_state.download_errors.emplace_back(
@@ -1144,7 +1124,7 @@
   UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
   update_state.p2p_num_attempts = 1;
   update_state.p2p_first_attempted =
-      fake_clock_->GetWallclockTime() -
+      fake_clock_.GetWallclockTime() -
       TimeDelta::FromSeconds(ChromeOSPolicy::kMaxP2PAttemptsPeriodInSeconds +
                              1);
   UpdateDownloadParams result;
@@ -1218,7 +1198,7 @@
   UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
   update_state.num_checks = 5;
   update_state.download_urls.emplace_back("http://another/fake/url/");
-  Time t = fake_clock_->GetWallclockTime() - TimeDelta::FromSeconds(12);
+  Time t = fake_clock_.GetWallclockTime() - TimeDelta::FromSeconds(12);
   for (int i = 0; i < 5; i++) {
     update_state.download_errors.emplace_back(
         0, ErrorCode::kDownloadTransferError, t);
@@ -1247,7 +1227,7 @@
   UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
   update_state.num_checks = 10;
   update_state.download_urls.emplace_back("http://another/fake/url/");
-  Time t = fake_clock_->GetWallclockTime() - TimeDelta::FromSeconds(12);
+  Time t = fake_clock_.GetWallclockTime() - TimeDelta::FromSeconds(12);
   for (int i = 0; i < 11; i++) {
     update_state.download_errors.emplace_back(
         0, ErrorCode::kDownloadTransferError, t);
@@ -1279,7 +1259,7 @@
   update_state.download_errors.emplace_back(
       0,
       ErrorCode::kPayloadHashMismatchError,
-      fake_clock_->GetWallclockTime() - TimeDelta::FromSeconds(1));
+      fake_clock_.GetWallclockTime() - TimeDelta::FromSeconds(1));
 
   // Check that the UpdateCanStart returns true.
   UpdateDownloadParams result;
@@ -1308,7 +1288,7 @@
   update_state.download_errors.emplace_back(
       1,
       ErrorCode::kPayloadHashMismatchError,
-      fake_clock_->GetWallclockTime() - TimeDelta::FromSeconds(1));
+      fake_clock_.GetWallclockTime() - TimeDelta::FromSeconds(1));
 
   // Check that the UpdateCanStart returns true.
   UpdateDownloadParams result;
@@ -1391,7 +1371,7 @@
 
   // Override specific device policy attributes.
   fake_state_.device_policy_provider()->var_au_p2p_enabled()->reset(nullptr);
-  fake_state_.device_policy_provider()->var_has_owner()->reset(new bool(false));
+  fake_state_.device_policy_provider()->var_owner()->reset(nullptr);
   fake_state_.device_policy_provider()->var_http_downloads_enabled()->reset(
       new bool(false));
 
@@ -1409,6 +1389,148 @@
   EXPECT_FALSE(result.do_increment_failures);
 }
 
+TEST_F(UmChromeOSPolicyTest, UpdateDownloadAllowedEthernetDefault) {
+  // Ethernet is always allowed.
+
+  fake_state_.shill_provider()->var_conn_type()->reset(
+      new ConnectionType(ConnectionType::kEthernet));
+
+  bool result;
+  ExpectPolicyStatus(
+      EvalStatus::kSucceeded, &Policy::UpdateDownloadAllowed, &result);
+  EXPECT_TRUE(result);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateDownloadAllowedWifiDefault) {
+  // Wifi is allowed if not tethered.
+
+  fake_state_.shill_provider()->var_conn_type()->reset(
+      new ConnectionType(ConnectionType::kWifi));
+
+  bool result;
+  ExpectPolicyStatus(
+      EvalStatus::kSucceeded, &Policy::UpdateDownloadAllowed, &result);
+  EXPECT_TRUE(result);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+       UpdateCurrentConnectionNotAllowedWifiTetheredDefault) {
+  // Tethered wifi is not allowed by default.
+
+  fake_state_.shill_provider()->var_conn_type()->reset(
+      new ConnectionType(ConnectionType::kWifi));
+  fake_state_.shill_provider()->var_conn_tethering()->reset(
+      new ConnectionTethering(ConnectionTethering::kConfirmed));
+
+  bool result;
+  ExpectPolicyStatus(
+      EvalStatus::kAskMeAgainLater, &Policy::UpdateDownloadAllowed, &result);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateDownloadAllowedWifiTetheredPolicyOverride) {
+  // Tethered wifi can be allowed by policy.
+
+  fake_state_.shill_provider()->var_conn_type()->reset(
+      new ConnectionType(ConnectionType::kWifi));
+  fake_state_.shill_provider()->var_conn_tethering()->reset(
+      new ConnectionTethering(ConnectionTethering::kConfirmed));
+  set<ConnectionType> allowed_connections;
+  allowed_connections.insert(ConnectionType::kCellular);
+  fake_state_.device_policy_provider()
+      ->var_allowed_connection_types_for_update()
+      ->reset(new set<ConnectionType>(allowed_connections));
+
+  bool result;
+  ExpectPolicyStatus(
+      EvalStatus::kSucceeded, &Policy::UpdateDownloadAllowed, &result);
+  EXPECT_TRUE(result);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateDownloadAllowedWimaxDefault) {
+  // Wimax is always allowed.
+
+  fake_state_.shill_provider()->var_conn_type()->reset(
+      new ConnectionType(ConnectionType::kWifi));
+
+  bool result;
+  ExpectPolicyStatus(
+      EvalStatus::kSucceeded, &Policy::UpdateDownloadAllowed, &result);
+  EXPECT_TRUE(result);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+       UpdateCurrentConnectionNotAllowedBluetoothDefault) {
+  // Bluetooth is never allowed.
+
+  fake_state_.shill_provider()->var_conn_type()->reset(
+      new ConnectionType(ConnectionType::kBluetooth));
+
+  bool result;
+  ExpectPolicyStatus(
+      EvalStatus::kAskMeAgainLater, &Policy::UpdateDownloadAllowed, &result);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+       UpdateCurrentConnectionNotAllowedBluetoothPolicyCannotOverride) {
+  // Bluetooth cannot be allowed even by policy.
+
+  fake_state_.shill_provider()->var_conn_type()->reset(
+      new ConnectionType(ConnectionType::kBluetooth));
+  set<ConnectionType> allowed_connections;
+  allowed_connections.insert(ConnectionType::kBluetooth);
+  fake_state_.device_policy_provider()
+      ->var_allowed_connection_types_for_update()
+      ->reset(new set<ConnectionType>(allowed_connections));
+
+  bool result;
+  ExpectPolicyStatus(
+      EvalStatus::kAskMeAgainLater, &Policy::UpdateDownloadAllowed, &result);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCurrentConnectionNotAllowedCellularDefault) {
+  // Cellular is not allowed by default.
+
+  fake_state_.shill_provider()->var_conn_type()->reset(
+      new ConnectionType(ConnectionType::kCellular));
+
+  bool result;
+  ExpectPolicyStatus(
+      EvalStatus::kAskMeAgainLater, &Policy::UpdateDownloadAllowed, &result);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateDownloadAllowedCellularPolicyOverride) {
+  // Update over cellular can be enabled by policy.
+
+  fake_state_.shill_provider()->var_conn_type()->reset(
+      new ConnectionType(ConnectionType::kCellular));
+  set<ConnectionType> allowed_connections;
+  allowed_connections.insert(ConnectionType::kCellular);
+  fake_state_.device_policy_provider()
+      ->var_allowed_connection_types_for_update()
+      ->reset(new set<ConnectionType>(allowed_connections));
+
+  bool result;
+  ExpectPolicyStatus(
+      EvalStatus::kSucceeded, &Policy::UpdateDownloadAllowed, &result);
+  EXPECT_TRUE(result);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateDownloadAllowedCellularUserOverride) {
+  // Update over cellular can be enabled by user settings, but only if policy
+  // is present and does not determine allowed connections.
+
+  fake_state_.shill_provider()->var_conn_type()->reset(
+      new ConnectionType(ConnectionType::kCellular));
+  set<ConnectionType> allowed_connections;
+  allowed_connections.insert(ConnectionType::kCellular);
+  fake_state_.updater_provider()->var_cellular_enabled()->reset(new bool(true));
+
+  bool result;
+  ExpectPolicyStatus(
+      EvalStatus::kSucceeded, &Policy::UpdateDownloadAllowed, &result);
+  EXPECT_TRUE(result);
+}
+
 TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedScatteringSupressedDueToP2P) {
   // The UpdateCanStart policy returns true; scattering should have applied, but
   // P2P download is allowed. Scattering values are nonetheless returned, and so
@@ -1443,7 +1565,7 @@
 
   SetUpdateCheckAllowed(false);
 
-  const Time curr_time = fake_clock_->GetWallclockTime();
+  const Time curr_time = fake_clock_.GetWallclockTime();
   UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(10));
   update_state.download_errors_max = 1;
   update_state.download_errors.emplace_back(
@@ -1494,7 +1616,7 @@
 
 TEST_F(UmChromeOSPolicyTest, P2PEnabledAllowedDeviceEnterpriseEnrolled) {
   fake_state_.device_policy_provider()->var_au_p2p_enabled()->reset(nullptr);
-  fake_state_.device_policy_provider()->var_has_owner()->reset(new bool(false));
+  fake_state_.device_policy_provider()->var_owner()->reset(nullptr);
 
   bool result;
   ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::P2PEnabled, &result);
@@ -1509,7 +1631,7 @@
 
 TEST_F(UmChromeOSPolicyTest,
        UpdateCanBeAppliedForcedUpdatesDisablesTimeRestrictions) {
-  Time curr_time = fake_clock_->GetWallclockTime();
+  Time curr_time = fake_clock_.GetWallclockTime();
   fake_state_.updater_provider()->var_forced_update_requested()->reset(
       new UpdateRequestStatus(UpdateRequestStatus::kInteractive));
   // Should return kAskMeAgainLater when updated are not forced.
@@ -1522,7 +1644,7 @@
 }
 
 TEST_F(UmChromeOSPolicyTest, UpdateCanBeAppliedFailsInDisallowedTime) {
-  Time curr_time = fake_clock_->GetWallclockTime();
+  Time curr_time = fake_clock_.GetWallclockTime();
   TestDisallowedTimeIntervals(
       {WeeklyTimeInterval(
           WeeklyTime::FromTime(curr_time),
@@ -1532,7 +1654,7 @@
 }
 
 TEST_F(UmChromeOSPolicyTest, UpdateCanBeAppliedOutsideDisallowedTime) {
-  Time curr_time = fake_clock_->GetWallclockTime();
+  Time curr_time = fake_clock_.GetWallclockTime();
   TestDisallowedTimeIntervals(
       {WeeklyTimeInterval(
           WeeklyTime::FromTime(curr_time - TimeDelta::FromHours(3)),
@@ -1542,7 +1664,7 @@
 }
 
 TEST_F(UmChromeOSPolicyTest, UpdateCanBeAppliedPassesOnNonKiosk) {
-  Time curr_time = fake_clock_->GetWallclockTime();
+  Time curr_time = fake_clock_.GetWallclockTime();
   TestDisallowedTimeIntervals(
       {WeeklyTimeInterval(
           WeeklyTime::FromTime(curr_time),
diff --git a/update_manager/default_policy.cc b/update_manager/default_policy.cc
index 0713e06..81ab795 100644
--- a/update_manager/default_policy.cc
+++ b/update_manager/default_policy.cc
@@ -14,12 +14,10 @@
 // limitations under the License.
 //
 
-#include "update_engine/common/system_state.h"
 #include "update_engine/update_manager/default_policy.h"
 
 using chromeos_update_engine::ErrorCode;
 using chromeos_update_engine::InstallPlan;
-using chromeos_update_engine::SystemState;
 
 namespace {
 
@@ -33,19 +31,19 @@
 
 namespace chromeos_update_manager {
 
+DefaultPolicy::DefaultPolicy(chromeos_update_engine::ClockInterface* clock)
+    : clock_(clock), aux_state_(new DefaultPolicyState()) {}
+
 EvalStatus DefaultPolicy::UpdateCheckAllowed(EvaluationContext* ec,
                                              State* state,
                                              std::string* error,
                                              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;
-  result->quick_fix_build_token.clear();
 
   // Ensure that the minimum interval is set. If there's no clock, this defaults
   // to always allowing the update.
@@ -53,8 +51,8 @@
       ec->IsMonotonicTimeGreaterThan(
           aux_state_->last_check_allowed_time() +
           base::TimeDelta::FromSeconds(kCheckIntervalInSeconds))) {
-    aux_state_->set_last_check_allowed_time(
-        SystemState::Get()->clock()->GetMonotonicTime());
+    if (clock_)
+      aux_state_->set_last_check_allowed_time(clock_->GetMonotonicTime());
     return EvalStatus::kSucceeded;
   }
 
@@ -89,6 +87,14 @@
   return EvalStatus::kSucceeded;
 }
 
+EvalStatus DefaultPolicy::UpdateDownloadAllowed(EvaluationContext* ec,
+                                                State* state,
+                                                std::string* error,
+                                                bool* result) const {
+  *result = true;
+  return EvalStatus::kSucceeded;
+}
+
 EvalStatus DefaultPolicy::P2PEnabled(EvaluationContext* ec,
                                      State* state,
                                      std::string* error,
diff --git a/update_manager/default_policy.h b/update_manager/default_policy.h
index c93bb46..1b284f4 100644
--- a/update_manager/default_policy.h
+++ b/update_manager/default_policy.h
@@ -22,6 +22,7 @@
 
 #include <base/time/time.h>
 
+#include "update_engine/common/clock_interface.h"
 #include "update_engine/update_manager/policy.h"
 
 namespace chromeos_update_manager {
@@ -59,8 +60,9 @@
 // actual policy being used by the UpdateManager.
 class DefaultPolicy : public Policy {
  public:
-  DefaultPolicy() : aux_state_(new DefaultPolicyState()) {}
-  ~DefaultPolicy() override = default;
+  explicit DefaultPolicy(chromeos_update_engine::ClockInterface* clock);
+  DefaultPolicy() : DefaultPolicy(nullptr) {}
+  ~DefaultPolicy() override {}
 
   // Policy overrides.
   EvalStatus UpdateCheckAllowed(EvaluationContext* ec,
@@ -81,6 +83,11 @@
                             UpdateDownloadParams* result,
                             UpdateState update_state) const override;
 
+  EvalStatus UpdateDownloadAllowed(EvaluationContext* ec,
+                                   State* state,
+                                   std::string* error,
+                                   bool* result) const override;
+
   EvalStatus P2PEnabled(EvaluationContext* ec,
                         State* state,
                         std::string* error,
@@ -97,6 +104,9 @@
   std::string PolicyName() const override { return "DefaultPolicy"; }
 
  private:
+  // A clock interface.
+  chromeos_update_engine::ClockInterface* clock_;
+
   // An auxiliary state object.
   std::unique_ptr<DefaultPolicyState> aux_state_;
 
diff --git a/update_manager/device_policy_provider.h b/update_manager/device_policy_provider.h
index 5112f68..873282e 100644
--- a/update_manager/device_policy_provider.h
+++ b/update_manager/device_policy_provider.h
@@ -21,7 +21,6 @@
 #include <string>
 
 #include <base/time/time.h>
-#include <base/version.h>
 #include <policy/libpolicy.h>
 
 #include "update_engine/update_manager/provider.h"
@@ -45,8 +44,6 @@
 
   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;
@@ -69,9 +66,9 @@
   virtual Variable<std::set<chromeos_update_engine::ConnectionType>>*
   var_allowed_connection_types_for_update() = 0;
 
-  // Variable stating whether the device has an owner. For enterprise enrolled
-  // devices, this will be false as the device owner has an empty string.
-  virtual Variable<bool>* var_has_owner() = 0;
+  // Variable stating the name of the device owner. For enterprise enrolled
+  // devices, this will be an empty string.
+  virtual Variable<std::string>* var_owner() = 0;
 
   virtual Variable<bool>* var_http_downloads_enabled() = 0;
 
@@ -88,19 +85,6 @@
   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;
-
-  // Variable that contains a token which maps to a Chrome OS Quick Fix Build to
-  // which the device would be updated if not blocked by another policy.
-  virtual Variable<std::string>* var_quick_fix_build_token() = 0;
-
  protected:
   DevicePolicyProvider() {}
 
diff --git a/update_manager/enterprise_device_policy_impl.cc b/update_manager/enterprise_device_policy_impl.cc
index b9a11e1..a3430ef 100644
--- a/update_manager/enterprise_device_policy_impl.cc
+++ b/update_manager/enterprise_device_policy_impl.cc
@@ -62,37 +62,13 @@
           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 {
@@ -115,18 +91,22 @@
         case RollbackToTargetVersion::kDisabled:
           LOG(INFO) << "Policy disables rollbacks.";
           result->rollback_allowed = false;
-          result->rollback_data_save_requested = false;
           break;
         case RollbackToTargetVersion::kRollbackAndPowerwash:
           LOG(INFO) << "Policy allows rollbacks with powerwash.";
           result->rollback_allowed = true;
-          result->rollback_data_save_requested = false;
           break;
         case RollbackToTargetVersion::kRollbackAndRestoreIfPossible:
           LOG(INFO)
               << "Policy allows rollbacks, also tries to restore if possible.";
+          // We don't support restore yet, but policy still allows rollback.
           result->rollback_allowed = true;
-          result->rollback_data_save_requested = true;
+          break;
+        case RollbackToTargetVersion::kRollbackOnlyIfRestorePossible:
+          LOG(INFO) << "Policy only allows rollbacks if restore is possible.";
+          // We don't support restore yet, policy doesn't allow rollback in this
+          // case.
+          result->rollback_allowed = false;
           break;
         case RollbackToTargetVersion::kMaxValue:
           NOTREACHED();
@@ -141,35 +121,14 @@
     if (rollback_allowed_milestones_p)
       result->rollback_allowed_milestones = *rollback_allowed_milestones_p;
 
-    // Determine whether a target channel is dictated by policy and whether we
-    // should rollback in case that channel is more stable.
+    // Determine whether a target channel is dictated by policy.
     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;
-    }
-
-    const string* quick_fix_build_token_p =
-        ec->GetValue(dp_provider->var_quick_fix_build_token());
-    if (quick_fix_build_token_p) {
-      result->quick_fix_build_token = *quick_fix_build_token_p;
     }
   }
   return EvalStatus::kContinue;
diff --git a/update_manager/enterprise_device_policy_impl_unittest.cc b/update_manager/enterprise_device_policy_impl_unittest.cc
deleted file mode 100644
index 30f54b1..0000000
--- a/update_manager/enterprise_device_policy_impl_unittest.cc
+++ /dev/null
@@ -1,170 +0,0 @@
-//
-// 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);
-}
-
-TEST_F(UmEnterpriseDevicePolicyImplTest, QuickFixBuildToken) {
-  fake_state_.device_policy_provider()->var_quick_fix_build_token()->reset(
-      new std::string("token"));
-  UpdateCheckParams result;
-  ExpectPolicyStatus(
-      EvalStatus::kContinue, &Policy::UpdateCheckAllowed, &result);
-  EXPECT_EQ(result.quick_fix_build_token, "token");
-}
-
-}  // namespace chromeos_update_manager
diff --git a/update_manager/enterprise_rollback_policy_impl.cc b/update_manager/enterprise_rollback_policy_impl.cc
deleted file mode 100644
index ab4e38c..0000000
--- a/update_manager/enterprise_rollback_policy_impl.cc
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-// 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_rollback_policy_impl.h"
-
-using chromeos_update_engine::ErrorCode;
-using chromeos_update_engine::InstallPlan;
-using std::string;
-
-namespace chromeos_update_manager {
-
-EvalStatus EnterpriseRollbackPolicyImpl::UpdateCanBeApplied(
-    EvaluationContext* ec,
-    State* state,
-    string* error,
-    ErrorCode* result,
-    InstallPlan* install_plan) const {
-  if (install_plan && install_plan->is_rollback) {
-    LOG(INFO)
-        << "Update is enterprise rollback, allowing update to be applied.";
-    *result = ErrorCode::kSuccess;
-    return EvalStatus::kSucceeded;
-  }
-  return EvalStatus::kContinue;
-}
-
-}  // namespace chromeos_update_manager
diff --git a/update_manager/enterprise_rollback_policy_impl.h b/update_manager/enterprise_rollback_policy_impl.h
deleted file mode 100644
index bcaf95e..0000000
--- a/update_manager/enterprise_rollback_policy_impl.h
+++ /dev/null
@@ -1,56 +0,0 @@
-//
-// 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_UPDATE_MANAGER_ENTERPRISE_ROLLBACK_POLICY_IMPL_H_
-#define UPDATE_ENGINE_UPDATE_MANAGER_ENTERPRISE_ROLLBACK_POLICY_IMPL_H_
-
-#include <string>
-
-#include "update_engine/common/error_code.h"
-#include "update_engine/payload_consumer/install_plan.h"
-#include "update_engine/update_manager/policy_utils.h"
-
-namespace chromeos_update_manager {
-
-// If the update is an enterprise rollback, this should not block the update
-// to be applied.
-class EnterpriseRollbackPolicyImpl : public PolicyImplBase {
- public:
-  EnterpriseRollbackPolicyImpl() = default;
-  ~EnterpriseRollbackPolicyImpl() override = default;
-
-  // Policy overrides.
-  EvalStatus UpdateCanBeApplied(
-      EvaluationContext* ec,
-      State* state,
-      std::string* error,
-      chromeos_update_engine::ErrorCode* result,
-      chromeos_update_engine::InstallPlan* install_plan) const override;
-
- protected:
-  std::string PolicyName() const override {
-    return "EnterpriseRollbackPolicyImpl";
-  }
-
- private:
-  EnterpriseRollbackPolicyImpl(const EnterpriseRollbackPolicyImpl&) = delete;
-  EnterpriseRollbackPolicyImpl& operator=(const EnterpriseRollbackPolicyImpl&) =
-      delete;
-};
-
-}  // namespace chromeos_update_manager
-
-#endif  // UPDATE_ENGINE_UPDATE_MANAGER_ENTERPRISE_ROLLBACK_POLICY_IMPL_H_
diff --git a/update_manager/enterprise_rollback_policy_impl_unittest.cc b/update_manager/enterprise_rollback_policy_impl_unittest.cc
deleted file mode 100644
index 5cc5c75..0000000
--- a/update_manager/enterprise_rollback_policy_impl_unittest.cc
+++ /dev/null
@@ -1,56 +0,0 @@
-//
-// 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 <memory>
-
-#include "update_engine/update_manager/enterprise_rollback_policy_impl.h"
-#include "update_engine/update_manager/policy_test_utils.h"
-#include "update_engine/update_manager/weekly_time.h"
-
-using chromeos_update_engine::ErrorCode;
-using chromeos_update_engine::InstallPlan;
-
-namespace chromeos_update_manager {
-
-class UmEnterpriseRollbackPolicyImplTest : public UmPolicyTestBase {
- protected:
-  UmEnterpriseRollbackPolicyImplTest() {
-    policy_ = std::make_unique<EnterpriseRollbackPolicyImpl>();
-  }
-};
-
-TEST_F(UmEnterpriseRollbackPolicyImplTest,
-       ContinueWhenUpdateIsNotEnterpriseRollback) {
-  InstallPlan install_plan{.is_rollback = false};
-  ErrorCode result;
-  ExpectPolicyStatus(EvalStatus::kContinue,
-                     &Policy::UpdateCanBeApplied,
-                     &result,
-                     &install_plan);
-}
-
-TEST_F(UmEnterpriseRollbackPolicyImplTest,
-       SuccessWhenUpdateIsEnterpriseRollback) {
-  InstallPlan install_plan{.is_rollback = true};
-  ErrorCode result;
-  ExpectPolicyStatus(EvalStatus::kSucceeded,
-                     &Policy::UpdateCanBeApplied,
-                     &result,
-                     &install_plan);
-  EXPECT_EQ(result, ErrorCode::kSuccess);
-}
-
-}  // namespace chromeos_update_manager
diff --git a/update_manager/evaluation_context-inl.h b/update_manager/evaluation_context-inl.h
index 82861fa..59d85da 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 && !var->IsMissingOk()) {
+  if (result == nullptr) {
     LOG(WARNING) << "Error reading Variable " << var->GetName() << ": \""
                  << errmsg << "\"";
   }
diff --git a/update_manager/evaluation_context.cc b/update_manager/evaluation_context.cc
index b86f41c..e796fec 100644
--- a/update_manager/evaluation_context.cc
+++ b/update_manager/evaluation_context.cc
@@ -27,7 +27,6 @@
 #include <base/strings/string_util.h>
 #include <base/values.h>
 
-#include "update_engine/common/system_state.h"
 #include "update_engine/common/utils.h"
 
 using base::Callback;
@@ -35,7 +34,7 @@
 using base::Time;
 using base::TimeDelta;
 using brillo::MessageLoop;
-using chromeos_update_engine::SystemState;
+using chromeos_update_engine::ClockInterface;
 using std::string;
 using std::unique_ptr;
 
@@ -66,10 +65,12 @@
 namespace chromeos_update_manager {
 
 EvaluationContext::EvaluationContext(
+    ClockInterface* clock,
     TimeDelta evaluation_timeout,
     TimeDelta expiration_timeout,
     unique_ptr<Callback<void(EvaluationContext*)>> unregister_cb)
-    : evaluation_timeout_(evaluation_timeout),
+    : clock_(clock),
+      evaluation_timeout_(evaluation_timeout),
       expiration_timeout_(expiration_timeout),
       unregister_cb_(std::move(unregister_cb)),
       weak_ptr_factory_(this) {
@@ -97,15 +98,13 @@
 TimeDelta EvaluationContext::RemainingTime(Time monotonic_deadline) const {
   if (monotonic_deadline.is_max())
     return TimeDelta::Max();
-  TimeDelta remaining =
-      monotonic_deadline - SystemState::Get()->clock()->GetMonotonicTime();
+  TimeDelta remaining = monotonic_deadline - clock_->GetMonotonicTime();
   return std::max(remaining, TimeDelta());
 }
 
 Time EvaluationContext::MonotonicDeadline(TimeDelta timeout) {
-  return (timeout.is_max()
-              ? Time::Max()
-              : SystemState::Get()->clock()->GetMonotonicTime() + timeout);
+  return (timeout.is_max() ? Time::Max()
+                           : clock_->GetMonotonicTime() + timeout);
 }
 
 void EvaluationContext::ValueChanged(BaseVariable* var) {
@@ -140,9 +139,8 @@
 }
 
 void EvaluationContext::ResetEvaluation() {
-  const auto* clock = SystemState::Get()->clock();
-  evaluation_start_wallclock_ = clock->GetWallclockTime();
-  evaluation_start_monotonic_ = clock->GetMonotonicTime();
+  evaluation_start_wallclock_ = clock_->GetWallclockTime();
+  evaluation_start_monotonic_ = clock_->GetMonotonicTime();
   reevaluation_time_wallclock_ = Time::Max();
   reevaluation_time_monotonic_ = Time::Max();
   evaluation_monotonic_deadline_ = MonotonicDeadline(evaluation_timeout_);
diff --git a/update_manager/evaluation_context.h b/update_manager/evaluation_context.h
index 3460f2a..c68c430 100644
--- a/update_manager/evaluation_context.h
+++ b/update_manager/evaluation_context.h
@@ -23,10 +23,12 @@
 
 #include <base/bind.h>
 #include <base/callback.h>
+#include <base/memory/ref_counted.h>
 #include <base/memory/weak_ptr.h>
 #include <base/time/time.h>
 #include <brillo/message_loops/message_loop.h>
 
+#include "update_engine/common/clock_interface.h"
 #include "update_engine/update_manager/boxed_value.h"
 #include "update_engine/update_manager/variable.h"
 
@@ -44,7 +46,7 @@
 //
 // Example:
 //
-//   auto ec = std::make_shared<EvaluationContext>(...);
+//   scoped_refptr<EvaluationContext> ec = new EvaluationContext(...);
 //
 //   ...
 //   // The following call to ResetEvaluation() is optional. Use it to reset the
@@ -60,14 +62,18 @@
 //   // If the provided |closure| wants to re-evaluate the policy, it should
 //   // call ec->ResetEvaluation() to start a new evaluation.
 //
-class EvaluationContext : private BaseVariable::ObserverInterface {
+class EvaluationContext : public base::RefCounted<EvaluationContext>,
+                          private BaseVariable::ObserverInterface {
  public:
   EvaluationContext(
+      chromeos_update_engine::ClockInterface* clock,
       base::TimeDelta evaluation_timeout,
       base::TimeDelta expiration_timeout,
       std::unique_ptr<base::Callback<void(EvaluationContext*)>> unregister_cb);
-  explicit EvaluationContext(base::TimeDelta evaluation_timeout)
+  EvaluationContext(chromeos_update_engine::ClockInterface* clock,
+                    base::TimeDelta evaluation_timeout)
       : EvaluationContext(
+            clock,
             evaluation_timeout,
             base::TimeDelta::Max(),
             std::unique_ptr<base::Callback<void(EvaluationContext*)>>()) {}
@@ -168,6 +174,9 @@
   // Whether the evaluation context has indeed expired.
   bool is_expired_ = false;
 
+  // Pointer to the mockable clock interface;
+  chromeos_update_engine::ClockInterface* const clock_;
+
   // The timestamps when the evaluation of this EvaluationContext started,
   // corresponding to ClockInterface::GetWallclockTime() and
   // ClockInterface::GetMonotonicTime(), respectively. These values are reset
diff --git a/update_manager/evaluation_context_unittest.cc b/update_manager/evaluation_context_unittest.cc
index fdb408b..eb42eb7 100644
--- a/update_manager/evaluation_context_unittest.cc
+++ b/update_manager/evaluation_context_unittest.cc
@@ -26,7 +26,6 @@
 #include <gtest/gtest.h>
 
 #include "update_engine/common/fake_clock.h"
-#include "update_engine/cros/fake_system_state.h"
 #include "update_engine/update_manager/fake_variable.h"
 #include "update_engine/update_manager/generic_variables.h"
 #include "update_engine/update_manager/mock_variable.h"
@@ -40,8 +39,6 @@
 using brillo::MessageLoopRunMaxIterations;
 using brillo::MessageLoopRunUntil;
 using chromeos_update_engine::FakeClock;
-using chromeos_update_engine::FakeSystemState;
-using std::shared_ptr;
 using std::string;
 using std::unique_ptr;
 using testing::_;
@@ -62,14 +59,14 @@
 }
 
 template <typename T>
-void ReadVar(shared_ptr<EvaluationContext> ec, Variable<T>* var) {
+void ReadVar(scoped_refptr<EvaluationContext> ec, Variable<T>* var) {
   ec->GetValue(var);
 }
 
 // Runs |evaluation|; if the value pointed by |count_p| is greater than zero,
 // decrement it and schedule a reevaluation; otherwise, writes true to |done_p|.
 void EvaluateRepeatedly(Closure evaluation,
-                        shared_ptr<EvaluationContext> ec,
+                        scoped_refptr<EvaluationContext> ec,
                         int* count_p,
                         bool* done_p) {
   evaluation.Run();
@@ -90,17 +87,16 @@
 class UmEvaluationContextTest : public ::testing::Test {
  protected:
   void SetUp() override {
-    FakeSystemState::CreateInstance();
-    fake_clock_ = FakeSystemState::Get()->fake_clock();
     loop_.SetAsCurrent();
     // Apr 22, 2009 19:25:00 UTC (this is a random reference point).
-    fake_clock_->SetMonotonicTime(Time::FromTimeT(1240428300));
+    fake_clock_.SetMonotonicTime(Time::FromTimeT(1240428300));
     // Mar 2, 2006 1:23:45 UTC.
-    fake_clock_->SetWallclockTime(Time::FromTimeT(1141262625));
-    eval_ctx_.reset(new EvaluationContext(
+    fake_clock_.SetWallclockTime(Time::FromTimeT(1141262625));
+    eval_ctx_ = new EvaluationContext(
+        &fake_clock_,
         default_timeout_,
         default_timeout_,
-        unique_ptr<base::Callback<void(EvaluationContext*)>>(nullptr)));
+        unique_ptr<base::Callback<void(EvaluationContext*)>>(nullptr));
   }
 
   void TearDown() override {
@@ -129,8 +125,8 @@
   TimeDelta default_timeout_ = TimeDelta::FromSeconds(5);
 
   brillo::FakeMessageLoop loop_{nullptr};
-  FakeClock* fake_clock_;
-  shared_ptr<EvaluationContext> eval_ctx_;
+  FakeClock fake_clock_;
+  scoped_refptr<EvaluationContext> eval_ctx_;
 
   // FakeVariables used for testing the EvaluationContext. These are required
   // here to prevent them from going away *before* the EvaluationContext under
@@ -214,7 +210,13 @@
   fake_const_var_.reset(new string("Hello world!"));
   EXPECT_EQ(*eval_ctx_->GetValue(&fake_const_var_), "Hello world!");
 
-  EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(base::DoNothing()));
+  EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(
+#if BASE_VER < 576279
+      Bind(&base::DoNothing)
+#else
+      base::DoNothing()
+#endif
+          ));
 }
 
 // Test that reevaluation occurs when an async variable it depends on changes.
@@ -284,11 +286,23 @@
   EXPECT_TRUE(value);
 
   // Ensure that we cannot reschedule an evaluation.
-  EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(base::DoNothing()));
+  EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(
+#if BASE_VER < 576279
+      Bind(&base::DoNothing)
+#else
+      base::DoNothing()
+#endif
+          ));
 
   // Ensure that we can reschedule an evaluation after resetting expiration.
   eval_ctx_->ResetExpiration();
-  EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(base::DoNothing()));
+  EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(
+#if BASE_VER < 576279
+      Bind(&base::DoNothing)
+#else
+      base::DoNothing()
+#endif
+          ));
 }
 
 // Test that we clear the events when destroying the EvaluationContext.
@@ -334,7 +348,13 @@
   fake_poll_var_.reset(new string("Polled value"));
   eval_ctx_->GetValue(&fake_async_var_);
   eval_ctx_->GetValue(&fake_poll_var_);
-  EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(base::DoNothing()));
+  EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(
+#if BASE_VER < 576279
+      Bind(&base::DoNothing)
+#else
+      base::DoNothing()
+#endif
+          ));
   // TearDown() checks for leaked observers on this async_variable, which means
   // that our object is still alive after removing its reference.
 }
@@ -368,8 +388,8 @@
 }
 
 TEST_F(UmEvaluationContextTest, TimeoutUpdatesWithMonotonicTime) {
-  fake_clock_->SetMonotonicTime(fake_clock_->GetMonotonicTime() +
-                                TimeDelta::FromSeconds(1));
+  fake_clock_.SetMonotonicTime(fake_clock_.GetMonotonicTime() +
+                               TimeDelta::FromSeconds(1));
 
   TimeDelta timeout = default_timeout_ - TimeDelta::FromSeconds(1);
 
@@ -378,9 +398,9 @@
 }
 
 TEST_F(UmEvaluationContextTest, ResetEvaluationResetsTimesWallclock) {
-  Time cur_time = fake_clock_->GetWallclockTime();
+  Time cur_time = fake_clock_.GetWallclockTime();
   // Advance the time on the clock but don't call ResetEvaluation yet.
-  fake_clock_->SetWallclockTime(cur_time + TimeDelta::FromSeconds(4));
+  fake_clock_.SetWallclockTime(cur_time + TimeDelta::FromSeconds(4));
 
   EXPECT_TRUE(eval_ctx_->IsWallclockTimeGreaterThan(cur_time -
                                                     TimeDelta::FromSeconds(1)));
@@ -390,7 +410,7 @@
   // Call ResetEvaluation now, which should use the new evaluation time.
   eval_ctx_->ResetEvaluation();
 
-  cur_time = fake_clock_->GetWallclockTime();
+  cur_time = fake_clock_.GetWallclockTime();
   EXPECT_TRUE(eval_ctx_->IsWallclockTimeGreaterThan(cur_time -
                                                     TimeDelta::FromSeconds(1)));
   EXPECT_FALSE(eval_ctx_->IsWallclockTimeGreaterThan(cur_time));
@@ -399,9 +419,9 @@
 }
 
 TEST_F(UmEvaluationContextTest, ResetEvaluationResetsTimesMonotonic) {
-  Time cur_time = fake_clock_->GetMonotonicTime();
+  Time cur_time = fake_clock_.GetMonotonicTime();
   // Advance the time on the clock but don't call ResetEvaluation yet.
-  fake_clock_->SetMonotonicTime(cur_time + TimeDelta::FromSeconds(4));
+  fake_clock_.SetMonotonicTime(cur_time + TimeDelta::FromSeconds(4));
 
   EXPECT_TRUE(eval_ctx_->IsMonotonicTimeGreaterThan(cur_time -
                                                     TimeDelta::FromSeconds(1)));
@@ -411,7 +431,7 @@
   // Call ResetEvaluation now, which should use the new evaluation time.
   eval_ctx_->ResetEvaluation();
 
-  cur_time = fake_clock_->GetMonotonicTime();
+  cur_time = fake_clock_.GetMonotonicTime();
   EXPECT_TRUE(eval_ctx_->IsMonotonicTimeGreaterThan(cur_time -
                                                     TimeDelta::FromSeconds(1)));
   EXPECT_FALSE(eval_ctx_->IsMonotonicTimeGreaterThan(cur_time));
@@ -422,21 +442,33 @@
 TEST_F(UmEvaluationContextTest,
        IsWallclockTimeGreaterThanSignalsTriggerReevaluation) {
   EXPECT_FALSE(eval_ctx_->IsWallclockTimeGreaterThan(
-      fake_clock_->GetWallclockTime() + TimeDelta::FromSeconds(1)));
+      fake_clock_.GetWallclockTime() + TimeDelta::FromSeconds(1)));
 
   // The "false" from IsWallclockTimeGreaterThan means that's not that timestamp
   // yet, so this should schedule a callback for when that happens.
-  EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(base::DoNothing()));
+  EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(
+#if BASE_VER < 576279
+      Bind(&base::DoNothing)
+#else
+      base::DoNothing()
+#endif
+          ));
 }
 
 TEST_F(UmEvaluationContextTest,
        IsMonotonicTimeGreaterThanSignalsTriggerReevaluation) {
   EXPECT_FALSE(eval_ctx_->IsMonotonicTimeGreaterThan(
-      fake_clock_->GetMonotonicTime() + TimeDelta::FromSeconds(1)));
+      fake_clock_.GetMonotonicTime() + TimeDelta::FromSeconds(1)));
 
   // The "false" from IsMonotonicTimeGreaterThan means that's not that timestamp
   // yet, so this should schedule a callback for when that happens.
-  EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(base::DoNothing()));
+  EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(
+#if BASE_VER < 576279
+      Bind(&base::DoNothing)
+#else
+      base::DoNothing()
+#endif
+          ));
 }
 
 TEST_F(UmEvaluationContextTest,
@@ -444,12 +476,18 @@
   // IsWallclockTimeGreaterThan() should ignore timestamps on the past for
   // reevaluation.
   EXPECT_TRUE(eval_ctx_->IsWallclockTimeGreaterThan(
-      fake_clock_->GetWallclockTime() - TimeDelta::FromSeconds(20)));
+      fake_clock_.GetWallclockTime() - TimeDelta::FromSeconds(20)));
   EXPECT_TRUE(eval_ctx_->IsWallclockTimeGreaterThan(
-      fake_clock_->GetWallclockTime() - TimeDelta::FromSeconds(1)));
+      fake_clock_.GetWallclockTime() - TimeDelta::FromSeconds(1)));
 
   // Callback should not be scheduled.
-  EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(base::DoNothing()));
+  EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(
+#if BASE_VER < 576279
+      Bind(&base::DoNothing)
+#else
+      base::DoNothing()
+#endif
+          ));
 }
 
 TEST_F(UmEvaluationContextTest,
@@ -457,12 +495,18 @@
   // IsMonotonicTimeGreaterThan() should ignore timestamps on the past for
   // reevaluation.
   EXPECT_TRUE(eval_ctx_->IsMonotonicTimeGreaterThan(
-      fake_clock_->GetMonotonicTime() - TimeDelta::FromSeconds(20)));
+      fake_clock_.GetMonotonicTime() - TimeDelta::FromSeconds(20)));
   EXPECT_TRUE(eval_ctx_->IsMonotonicTimeGreaterThan(
-      fake_clock_->GetMonotonicTime() - TimeDelta::FromSeconds(1)));
+      fake_clock_.GetMonotonicTime() - TimeDelta::FromSeconds(1)));
 
   // Callback should not be scheduled.
-  EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(base::DoNothing()));
+  EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(
+#if BASE_VER < 576279
+      Bind(&base::DoNothing)
+#else
+      base::DoNothing()
+#endif
+          ));
 }
 
 TEST_F(UmEvaluationContextTest, DumpContext) {
diff --git a/update_manager/fake_device_policy_provider.h b/update_manager/fake_device_policy_provider.h
index 762bfc5..7cd4d7b 100644
--- a/update_manager/fake_device_policy_provider.h
+++ b/update_manager/fake_device_policy_provider.h
@@ -42,10 +42,6 @@
     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_;
   }
@@ -72,7 +68,7 @@
     return &var_allowed_connection_types_for_update_;
   }
 
-  FakeVariable<bool>* var_has_owner() override { return &var_has_owner_; }
+  FakeVariable<std::string>* var_owner() override { return &var_owner_; }
 
   FakeVariable<bool>* var_http_downloads_enabled() override {
     return &var_http_downloads_enabled_;
@@ -95,19 +91,6 @@
     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_;
-  }
-
-  FakeVariable<std::string>* var_quick_fix_build_token() override {
-    return &var_quick_fix_build_token_;
-  }
-
  private:
   FakeVariable<bool> var_device_policy_is_loaded_{"policy_is_loaded",
                                                   kVariableModePoll};
@@ -115,8 +98,6 @@
                                                  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};
@@ -129,7 +110,7 @@
   FakeVariable<std::set<chromeos_update_engine::ConnectionType>>
       var_allowed_connection_types_for_update_{
           "allowed_connection_types_for_update", kVariableModePoll};
-  FakeVariable<bool> var_has_owner_{"owner", kVariableModePoll};
+  FakeVariable<std::string> var_owner_{"owner", kVariableModePoll};
   FakeVariable<bool> var_http_downloads_enabled_{"http_downloads_enabled",
                                                  kVariableModePoll};
   FakeVariable<bool> var_au_p2p_enabled_{"au_p2p_enabled", kVariableModePoll};
@@ -138,13 +119,7 @@
   FakeVariable<std::string> var_auto_launched_kiosk_app_id_{
       "auto_launched_kiosk_app_id", kVariableModePoll};
   FakeVariable<WeeklyTimeIntervalVector> var_disallowed_time_intervals_{
-      "disallowed_time_intervals", kVariableModeAsync};
-  FakeVariable<ChannelDowngradeBehavior> var_channel_downgrade_behavior_{
-      "channel_downgrade_behavior", kVariableModePoll};
-  FakeVariable<base::Version> var_device_minimum_version_{
-      "device_minimum_version", kVariableModePoll};
-  FakeVariable<std::string> var_quick_fix_build_token_{"quick_fix_build_token",
-                                                       kVariableModePoll};
+      "disallowed_time_intervals", kVariableModePoll};
 
   DISALLOW_COPY_AND_ASSIGN(FakeDevicePolicyProvider);
 };
diff --git a/update_manager/fake_system_provider.h b/update_manager/fake_system_provider.h
index b320c01..f54951b 100644
--- a/update_manager/fake_system_provider.h
+++ b/update_manager/fake_system_provider.h
@@ -50,10 +50,6 @@
     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};
@@ -64,8 +60,6 @@
   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_update_manager.h b/update_manager/fake_update_manager.h
index b880582..173b1a9 100644
--- a/update_manager/fake_update_manager.h
+++ b/update_manager/fake_update_manager.h
@@ -26,12 +26,13 @@
 
 class FakeUpdateManager : public UpdateManager {
  public:
-  FakeUpdateManager()
-      : UpdateManager(base::TimeDelta::FromSeconds(5),
+  explicit FakeUpdateManager(chromeos_update_engine::ClockInterface* clock)
+      : UpdateManager(clock,
+                      base::TimeDelta::FromSeconds(5),
                       base::TimeDelta::FromHours(1),
                       new FakeState()) {
     // The FakeUpdateManager uses a DefaultPolicy.
-    set_policy(new DefaultPolicy());
+    set_policy(new DefaultPolicy(clock));
   }
 
   // UpdateManager overrides.
diff --git a/update_manager/fake_updater_provider.h b/update_manager/fake_updater_provider.h
index d967f42..7295765 100644
--- a/update_manager/fake_updater_provider.h
+++ b/update_manager/fake_updater_provider.h
@@ -83,10 +83,6 @@
     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};
@@ -112,8 +108,6 @@
       "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/minimum_version_policy_impl.cc b/update_manager/minimum_version_policy_impl.cc
deleted file mode 100644
index fb94ee4..0000000
--- a/update_manager/minimum_version_policy_impl.cc
+++ /dev/null
@@ -1,56 +0,0 @@
-//
-// 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/minimum_version_policy_impl.h"
-
-#include <base/version.h>
-
-using chromeos_update_engine::ErrorCode;
-using chromeos_update_engine::InstallPlan;
-
-namespace chromeos_update_manager {
-
-EvalStatus MinimumVersionPolicyImpl::UpdateCanBeApplied(
-    EvaluationContext* ec,
-    State* state,
-    std::string* error,
-    ErrorCode* result,
-    InstallPlan* install_plan) const {
-  const base::Version* current_version(
-      ec->GetValue(state->system_provider()->var_chromeos_version()));
-  if (current_version == nullptr || !current_version->IsValid()) {
-    LOG(WARNING) << "Unable to access current version";
-    return EvalStatus::kContinue;
-  }
-
-  const base::Version* minimum_version = ec->GetValue(
-      state->device_policy_provider()->var_device_minimum_version());
-  if (minimum_version == nullptr || !minimum_version->IsValid()) {
-    LOG(WARNING) << "Unable to access minimum version";
-    return EvalStatus::kContinue;
-  }
-
-  if (*current_version < *minimum_version) {
-    LOG(INFO) << "Updating from version less than minimum required"
-                 ", allowing update to be applied.";
-    *result = ErrorCode::kSuccess;
-    return EvalStatus::kSucceeded;
-  }
-
-  return EvalStatus::kContinue;
-}
-
-}  // namespace chromeos_update_manager
diff --git a/update_manager/minimum_version_policy_impl.h b/update_manager/minimum_version_policy_impl.h
deleted file mode 100644
index 600d624..0000000
--- a/update_manager/minimum_version_policy_impl.h
+++ /dev/null
@@ -1,54 +0,0 @@
-//
-// 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_UPDATE_MANAGER_MINIMUM_VERSION_POLICY_IMPL_H_
-#define UPDATE_ENGINE_UPDATE_MANAGER_MINIMUM_VERSION_POLICY_IMPL_H_
-
-#include <string>
-
-#include "update_engine/common/error_code.h"
-#include "update_engine/payload_consumer/install_plan.h"
-#include "update_engine/update_manager/policy_utils.h"
-
-namespace chromeos_update_manager {
-
-// Check to see if an update happens from a version less than the minimum
-// required one.
-class MinimumVersionPolicyImpl : public PolicyImplBase {
- public:
-  MinimumVersionPolicyImpl() = default;
-  ~MinimumVersionPolicyImpl() override = default;
-
-  // If current version is less than the minimum required one, then this should
-  // not block the update to be applied.
-  EvalStatus UpdateCanBeApplied(
-      EvaluationContext* ec,
-      State* state,
-      std::string* error,
-      chromeos_update_engine::ErrorCode* result,
-      chromeos_update_engine::InstallPlan* install_plan) const override;
-
- protected:
-  std::string PolicyName() const override { return "MinimumVersionPolicyImpl"; }
-
- private:
-  MinimumVersionPolicyImpl(const MinimumVersionPolicyImpl&) = delete;
-  MinimumVersionPolicyImpl& operator=(const MinimumVersionPolicyImpl&) = delete;
-};
-
-}  // namespace chromeos_update_manager
-
-#endif  // UPDATE_ENGINE_UPDATE_MANAGER_MINIMUM_VERSION_POLICY_IMPL_H_
diff --git a/update_manager/minimum_version_policy_impl_unittest.cc b/update_manager/minimum_version_policy_impl_unittest.cc
deleted file mode 100644
index 8e4dba5..0000000
--- a/update_manager/minimum_version_policy_impl_unittest.cc
+++ /dev/null
@@ -1,111 +0,0 @@
-//
-// 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 <memory>
-
-#include "update_engine/update_manager/minimum_version_policy_impl.h"
-#include "update_engine/update_manager/policy_test_utils.h"
-
-using chromeos_update_engine::ErrorCode;
-using chromeos_update_engine::InstallPlan;
-
-namespace {
-
-const char* kInvalidVersion = "13315.woops.12";
-const char* kOldVersion = "13315.60.12";
-const char* kNewVersion = "13315.60.15";
-
-}  // namespace
-
-namespace chromeos_update_manager {
-
-class UmMinimumVersionPolicyImplTest : public UmPolicyTestBase {
- protected:
-  UmMinimumVersionPolicyImplTest() {
-    policy_ = std::make_unique<MinimumVersionPolicyImpl>();
-  }
-
-  void SetCurrentVersion(const std::string& version) {
-    fake_state_.system_provider()->var_chromeos_version()->reset(
-        new base::Version(version));
-  }
-
-  void SetMinimumVersion(const std::string& version) {
-    fake_state_.device_policy_provider()->var_device_minimum_version()->reset(
-        new base::Version(version));
-  }
-
-  void TestPolicy(const EvalStatus& expected_status) {
-    InstallPlan install_plan;
-    ErrorCode result;
-    ExpectPolicyStatus(
-        expected_status, &Policy::UpdateCanBeApplied, &result, &install_plan);
-    if (expected_status == EvalStatus::kSucceeded)
-      EXPECT_EQ(result, ErrorCode::kSuccess);
-  }
-};
-
-TEST_F(UmMinimumVersionPolicyImplTest, ContinueWhenCurrentVersionIsNotSet) {
-  SetMinimumVersion(kNewVersion);
-
-  TestPolicy(EvalStatus::kContinue);
-}
-
-TEST_F(UmMinimumVersionPolicyImplTest, ContinueWhenCurrentVersionIsInvalid) {
-  SetCurrentVersion(kInvalidVersion);
-  SetMinimumVersion(kNewVersion);
-
-  TestPolicy(EvalStatus::kContinue);
-}
-
-TEST_F(UmMinimumVersionPolicyImplTest, ContinueWhenMinumumVersionIsNotSet) {
-  SetCurrentVersion(kOldVersion);
-
-  TestPolicy(EvalStatus::kContinue);
-}
-
-TEST_F(UmMinimumVersionPolicyImplTest, ContinueWhenMinumumVersionIsInvalid) {
-  SetCurrentVersion(kOldVersion);
-  SetMinimumVersion(kInvalidVersion);
-
-  TestPolicy(EvalStatus::kContinue);
-}
-
-TEST_F(UmMinimumVersionPolicyImplTest,
-       ContinueWhenCurrentVersionIsGreaterThanMinimumVersion) {
-  SetCurrentVersion(kNewVersion);
-  SetMinimumVersion(kOldVersion);
-
-  TestPolicy(EvalStatus::kContinue);
-}
-
-TEST_F(UmMinimumVersionPolicyImplTest,
-       ContinueWhenCurrentVersionIsEqualToMinimumVersion) {
-  SetCurrentVersion(kNewVersion);
-  SetMinimumVersion(kNewVersion);
-
-  TestPolicy(EvalStatus::kContinue);
-}
-
-TEST_F(UmMinimumVersionPolicyImplTest,
-       SuccessWhenCurrentVersionIsLessThanMinimumVersion) {
-  SetCurrentVersion(kOldVersion);
-  SetMinimumVersion(kNewVersion);
-
-  TestPolicy(EvalStatus::kSucceeded);
-}
-
-}  // namespace chromeos_update_manager
diff --git a/update_manager/mock_policy.h b/update_manager/mock_policy.h
index 3c6313f..46b6c78 100644
--- a/update_manager/mock_policy.h
+++ b/update_manager/mock_policy.h
@@ -29,7 +29,8 @@
 // A mocked implementation of Policy.
 class MockPolicy : public Policy {
  public:
-  MockPolicy() {
+  explicit MockPolicy(chromeos_update_engine::ClockInterface* clock)
+      : default_policy_(clock) {
     // We defer to the corresponding DefaultPolicy methods, by default.
     ON_CALL(*this,
             UpdateCheckAllowed(testing::_, testing::_, testing::_, testing::_))
@@ -45,6 +46,11 @@
                 testing::_, testing::_, testing::_, testing::_, testing::_))
         .WillByDefault(
             testing::Invoke(&default_policy_, &DefaultPolicy::UpdateCanStart));
+    ON_CALL(
+        *this,
+        UpdateDownloadAllowed(testing::_, testing::_, testing::_, testing::_))
+        .WillByDefault(testing::Invoke(&default_policy_,
+                                       &DefaultPolicy::UpdateDownloadAllowed));
     ON_CALL(*this, P2PEnabled(testing::_, testing::_, testing::_, testing::_))
         .WillByDefault(
             testing::Invoke(&default_policy_, &DefaultPolicy::P2PEnabled));
@@ -54,6 +60,8 @@
         .WillByDefault(testing::Invoke(&default_policy_,
                                        &DefaultPolicy::P2PEnabledChanged));
   }
+
+  MockPolicy() : MockPolicy(nullptr) {}
   ~MockPolicy() override {}
 
   // Policy overrides.
diff --git a/update_manager/mock_update_manager.h b/update_manager/mock_update_manager.h
deleted file mode 100644
index 06e17d8..0000000
--- a/update_manager/mock_update_manager.h
+++ /dev/null
@@ -1,44 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef UPDATE_ENGINE_MOCK_UPDATE_MANAGER_H
-#define UPDATE_ENGINE_MOCK_UPDATE_MANAGER_H
-
-#include <string>
-
-#include "update_engine/update_manager/update_manager.h"
-
-#include <gmock/gmock.h>
-
-namespace chromeos_update_manager {
-
-class MockUpdateManager : public UpdateManager {
- public:
-  MockUpdateManager()
-      : UpdateManager(base::TimeDelta(), base::TimeDelta(), nullptr) {}
-
-  MOCK_METHOD2(
-      AsyncPolicyRequestUpdateCheckAllowed,
-      void(base::Callback<void(EvalStatus, const UpdateCheckParams& result)>
-               callback,
-           EvalStatus (Policy::*policy_method)(
-               EvaluationContext*, State*, std::string*, UpdateCheckParams*)
-               const));
-};
-
-}  // namespace chromeos_update_manager
-
-#endif  // UPDATE_ENGINE_MOCK_UPDATE_MANAGER_H
diff --git a/update_manager/next_update_check_policy_impl.cc b/update_manager/next_update_check_policy_impl.cc
index 0a78718..6f9748e 100644
--- a/update_manager/next_update_check_policy_impl.cc
+++ b/update_manager/next_update_check_policy_impl.cc
@@ -72,11 +72,6 @@
       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());
 
@@ -88,21 +83,13 @@
   // If this is the first attempt, compute and return an initial value.
   if (last_checked_time == nullptr ||
       *last_checked_time < *updater_started_time) {
-    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;
+    *next_update_check = *updater_started_time +
+                         FuzzedInterval(&prng,
+                                        constants.timeout_initial_interval,
+                                        constants.timeout_regular_fuzz);
     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/next_update_check_policy_impl_unittest.cc b/update_manager/next_update_check_policy_impl_unittest.cc
index d80063d..58aff66 100644
--- a/update_manager/next_update_check_policy_impl_unittest.cc
+++ b/update_manager/next_update_check_policy_impl_unittest.cc
@@ -52,14 +52,14 @@
   // Set the last update time so it'll appear as if this is a first update check
   // in the lifetime of the current updater.
   fake_state_.updater_provider()->var_last_checked_time()->reset(
-      new Time(fake_clock_->GetWallclockTime() - TimeDelta::FromMinutes(10)));
+      new Time(fake_clock_.GetWallclockTime() - TimeDelta::FromMinutes(10)));
 
   CallMethodWithContext(&NextUpdateCheckTimePolicyImpl::NextUpdateCheckTime,
                         &next_update_check,
                         policy_test_constants);
 
-  EXPECT_LE(fake_clock_->GetWallclockTime(), next_update_check);
-  EXPECT_GE(fake_clock_->GetWallclockTime() +
+  EXPECT_LE(fake_clock_.GetWallclockTime(), next_update_check);
+  EXPECT_GE(fake_clock_.GetWallclockTime() +
                 TimeDelta::FromSeconds(
                     policy_test_constants.timeout_initial_interval +
                     policy_test_constants.timeout_regular_fuzz / 2),
@@ -75,12 +75,12 @@
                         &next_update_check,
                         policy_test_constants);
 
-  EXPECT_LE(fake_clock_->GetWallclockTime() +
+  EXPECT_LE(fake_clock_.GetWallclockTime() +
                 TimeDelta::FromSeconds(
                     policy_test_constants.timeout_periodic_interval -
                     policy_test_constants.timeout_regular_fuzz / 2),
             next_update_check);
-  EXPECT_GE(fake_clock_->GetWallclockTime() +
+  EXPECT_GE(fake_clock_.GetWallclockTime() +
                 TimeDelta::FromSeconds(
                     policy_test_constants.timeout_periodic_interval +
                     policy_test_constants.timeout_regular_fuzz / 2),
@@ -103,11 +103,11 @@
 
   int expected_interval = policy_test_constants.timeout_periodic_interval * 4;
   EXPECT_LE(
-      fake_clock_->GetWallclockTime() +
+      fake_clock_.GetWallclockTime() +
           TimeDelta::FromSeconds(expected_interval - expected_interval / 2),
       next_update_check);
   EXPECT_GE(
-      fake_clock_->GetWallclockTime() +
+      fake_clock_.GetWallclockTime() +
           TimeDelta::FromSeconds(expected_interval + expected_interval / 2),
       next_update_check);
 }
@@ -129,10 +129,10 @@
                &next_update_check,
                policy_test_constants);
 
-  EXPECT_LE(fake_clock_->GetWallclockTime() +
+  EXPECT_LE(fake_clock_.GetWallclockTime() +
                 TimeDelta::FromSeconds(kInterval - kInterval / 2),
             next_update_check);
-  EXPECT_GE(fake_clock_->GetWallclockTime() +
+  EXPECT_GE(fake_clock_.GetWallclockTime() +
                 TimeDelta::FromSeconds(kInterval + kInterval / 2),
             next_update_check);
 }
@@ -148,12 +148,12 @@
                &next_update_check,
                policy_test_constants);
 
-  EXPECT_LE(fake_clock_->GetWallclockTime() +
+  EXPECT_LE(fake_clock_.GetWallclockTime() +
                 TimeDelta::FromSeconds(
                     policy_test_constants.timeout_max_backoff_interval -
                     policy_test_constants.timeout_max_backoff_interval / 2),
             next_update_check);
-  EXPECT_GE(fake_clock_->GetWallclockTime() +
+  EXPECT_GE(fake_clock_.GetWallclockTime() +
                 TimeDelta::FromSeconds(
                     policy_test_constants.timeout_max_backoff_interval +
                     policy_test_constants.timeout_max_backoff_interval / 2),
diff --git a/update_manager/official_build_check_policy_impl.cc b/update_manager/official_build_check_policy_impl.cc
index e80c09f..096f7bf 100644
--- a/update_manager/official_build_check_policy_impl.cc
+++ b/update_manager/official_build_check_policy_impl.cc
@@ -27,16 +27,8 @@
   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)) {
-    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.";
+    LOG(INFO) << "Unofficial build, blocking periodic update checks.";
+    return EvalStatus::kAskMeAgainLater;
   }
   return EvalStatus::kContinue;
 }
diff --git a/update_manager/policy.h b/update_manager/policy.h
index fb4a129..5d65d9a 100644
--- a/update_manager/policy.h
+++ b/update_manager/policy.h
@@ -17,7 +17,6 @@
 #ifndef UPDATE_ENGINE_UPDATE_MANAGER_POLICY_H_
 #define UPDATE_ENGINE_UPDATE_MANAGER_POLICY_H_
 
-#include <memory>
 #include <string>
 #include <tuple>
 #include <vector>
@@ -43,35 +42,24 @@
 // Parameters of an update check. These parameters are determined by the
 // UpdateCheckAllowed policy.
 struct UpdateCheckParams {
-  // Whether the auto-updates are enabled on this build.
-  bool updates_enabled{true};
+  bool updates_enabled;  // Whether the auto-updates are enabled on this build.
 
   // 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{false};
-  // Specifies if rollbacks should attempt to preserve some system state.
-  bool rollback_data_save_requested{false};
+  bool rollback_allowed;
   // 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{0};
-  // Whether a rollback with data save should be initiated on channel
-  // downgrade (e.g. beta to stable).
-  bool rollback_on_channel_downgrade{false};
+  int rollback_allowed_milestones;
   // 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;
-  // Specifies a token which maps to a Chrome OS Quick Fix Build, if imposed by
-  // policy; otherwise, an empty string.
-  std::string quick_fix_build_token;
 
   // Whether the allowed update is interactive (user-initiated) or periodic.
-  bool interactive{false};
+  bool interactive;
 };
 
 // Input arguments to UpdateCanStart.
@@ -230,6 +218,9 @@
     if (reinterpret_cast<typeof(&Policy::UpdateCanStart)>(policy_method) ==
         &Policy::UpdateCanStart)
       return class_name + "UpdateCanStart";
+    if (reinterpret_cast<typeof(&Policy::UpdateDownloadAllowed)>(
+            policy_method) == &Policy::UpdateDownloadAllowed)
+      return class_name + "UpdateDownloadAllowed";
     if (reinterpret_cast<typeof(&Policy::P2PEnabled)>(policy_method) ==
         &Policy::P2PEnabled)
       return class_name + "P2PEnabled";
@@ -278,6 +269,17 @@
                                     UpdateDownloadParams* result,
                                     UpdateState update_state) const = 0;
 
+  // Checks whether downloading of an update is allowed; currently, this checks
+  // whether the network connection type is suitable for updating over.  May
+  // consult the shill provider as well as the device policy (if available).
+  // Returns |EvalStatus::kSucceeded|, setting |result| according to whether or
+  // not the current connection can be used; on error, returns
+  // |EvalStatus::kFailed| and sets |error| accordingly.
+  virtual EvalStatus UpdateDownloadAllowed(EvaluationContext* ec,
+                                           State* state,
+                                           std::string* error,
+                                           bool* result) const = 0;
+
   // Checks whether P2P is enabled. This may consult device policy and other
   // global settings.
   virtual EvalStatus P2PEnabled(EvaluationContext* ec,
@@ -306,9 +308,6 @@
   DISALLOW_COPY_AND_ASSIGN(Policy);
 };
 
-// Get system dependent policy implementation.
-std::unique_ptr<Policy> GetSystemPolicy();
-
 }  // namespace chromeos_update_manager
 
 #endif  // UPDATE_ENGINE_UPDATE_MANAGER_POLICY_H_
diff --git a/update_manager/policy_test_utils.cc b/update_manager/policy_test_utils.cc
index e8961b1..5491e00 100644
--- a/update_manager/policy_test_utils.cc
+++ b/update_manager/policy_test_utils.cc
@@ -20,13 +20,11 @@
 #include <tuple>
 #include <vector>
 
-#include "update_engine/cros/fake_system_state.h"
 #include "update_engine/update_manager/next_update_check_policy_impl.h"
 
 using base::Time;
 using base::TimeDelta;
 using chromeos_update_engine::ErrorCode;
-using chromeos_update_engine::FakeSystemState;
 using std::string;
 using std::tuple;
 using std::vector;
@@ -35,10 +33,8 @@
 
 void UmPolicyTestBase::SetUp() {
   loop_.SetAsCurrent();
-  FakeSystemState::CreateInstance();
-  fake_clock_ = FakeSystemState::Get()->fake_clock();
   SetUpDefaultClock();
-  eval_ctx_.reset(new EvaluationContext(TimeDelta::FromSeconds(5)));
+  eval_ctx_ = new EvaluationContext(&fake_clock_, TimeDelta::FromSeconds(5));
   SetUpDefaultState();
 }
 
@@ -48,12 +44,12 @@
 
 // Sets the clock to fixed values.
 void UmPolicyTestBase::SetUpDefaultClock() {
-  fake_clock_->SetMonotonicTime(Time::FromInternalValue(12345678L));
-  fake_clock_->SetWallclockTime(Time::FromInternalValue(12345678901234L));
+  fake_clock_.SetMonotonicTime(Time::FromInternalValue(12345678L));
+  fake_clock_.SetWallclockTime(Time::FromInternalValue(12345678901234L));
 }
 
 void UmPolicyTestBase::SetUpDefaultTimeProvider() {
-  Time current_time = FakeSystemState::Get()->clock()->GetWallclockTime();
+  Time current_time = fake_clock_.GetWallclockTime();
   base::Time::Exploded exploded;
   current_time.LocalExplode(&exploded);
   fake_state_.time_provider()->var_curr_hour()->reset(new int(exploded.hour));
@@ -65,9 +61,9 @@
 
 void UmPolicyTestBase::SetUpDefaultState() {
   fake_state_.updater_provider()->var_updater_started_time()->reset(
-      new Time(fake_clock_->GetWallclockTime()));
+      new Time(fake_clock_.GetWallclockTime()));
   fake_state_.updater_provider()->var_last_checked_time()->reset(
-      new Time(fake_clock_->GetWallclockTime()));
+      new Time(fake_clock_.GetWallclockTime()));
   fake_state_.updater_provider()->var_consecutive_failed_update_checks()->reset(
       new unsigned int(0));  // NOLINT(readability/casting)
   fake_state_.updater_provider()->var_server_dictated_poll_interval()->reset(
@@ -82,8 +78,7 @@
 // Returns a default UpdateState structure:
 UpdateState UmPolicyTestBase::GetDefaultUpdateState(
     TimeDelta first_seen_period) {
-  Time first_seen_time =
-      FakeSystemState::Get()->clock()->GetWallclockTime() - first_seen_period;
+  Time first_seen_time = fake_clock_.GetWallclockTime() - first_seen_period;
   UpdateState update_state = UpdateState();
 
   // This is a non-interactive check returning a delta payload, seen for the
diff --git a/update_manager/policy_test_utils.h b/update_manager/policy_test_utils.h
index 72bd3bc..eb5758f 100644
--- a/update_manager/policy_test_utils.h
+++ b/update_manager/policy_test_utils.h
@@ -91,9 +91,9 @@
   }
 
   brillo::FakeMessageLoop loop_{nullptr};
-  chromeos_update_engine::FakeClock* fake_clock_;
+  chromeos_update_engine::FakeClock fake_clock_;
   FakeState fake_state_;
-  std::shared_ptr<EvaluationContext> eval_ctx_;
+  scoped_refptr<EvaluationContext> eval_ctx_;
   std::unique_ptr<Policy> policy_;
 };
 
diff --git a/update_manager/policy_utils.h b/update_manager/policy_utils.h
index aedb90c..3204780 100644
--- a/update_manager/policy_utils.h
+++ b/update_manager/policy_utils.h
@@ -55,6 +55,7 @@
     EvalStatus status =
         (policy->*policy_method)(ec, state, error, result, args...);
     if (status != EvalStatus::kContinue) {
+      LOG(INFO) << "decision by " << policy->PolicyRequestName(policy_method);
       return status;
     }
   }
@@ -92,6 +93,13 @@
     return EvalStatus::kContinue;
   };
 
+  EvalStatus UpdateDownloadAllowed(EvaluationContext* ec,
+                                   State* state,
+                                   std::string* error,
+                                   bool* result) const override {
+    return EvalStatus::kContinue;
+  };
+
   EvalStatus P2PEnabled(EvaluationContext* ec,
                         State* state,
                         std::string* error,
diff --git a/update_manager/real_device_policy_provider.cc b/update_manager/real_device_policy_provider.cc
index e7b964b..586ee3e 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;
@@ -105,12 +105,10 @@
 
 template <typename T>
 void RealDevicePolicyProvider::UpdateVariable(
-    AsyncCopyVariable<T>* var,
-    // NOLINTNEXTLINE(readability/casting)
-    bool (DevicePolicy::*getter)(T*) const) {
+    AsyncCopyVariable<T>* var, bool (DevicePolicy::*getter_method)(T*) const) {
   T new_value;
   if (policy_provider_->device_policy_is_loaded() &&
-      (policy_provider_->GetDevicePolicy().*getter)(&new_value)) {
+      (policy_provider_->GetDevicePolicy().*getter_method)(&new_value)) {
     var->SetValue(new_value);
   } else {
     var->UnsetValue();
@@ -120,10 +118,10 @@
 template <typename T>
 void RealDevicePolicyProvider::UpdateVariable(
     AsyncCopyVariable<T>* var,
-    bool (RealDevicePolicyProvider::*getter)(T*) const) {
+    bool (RealDevicePolicyProvider::*getter_method)(T*) const) {
   T new_value;
   if (policy_provider_->device_policy_is_loaded() &&
-      (this->*getter)(&new_value)) {
+      (this->*getter_method)(&new_value)) {
     var->SetValue(new_value);
   } else {
     var->UnsetValue();
@@ -200,30 +198,6 @@
   return true;
 }
 
-bool RealDevicePolicyProvider::ConvertHasOwner(bool* has_owner) const {
-  string owner;
-  if (!policy_provider_->GetDevicePolicy().GetOwner(&owner)) {
-    return false;
-  }
-  *has_owner = !owner.empty();
-  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.";
@@ -235,7 +209,6 @@
   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);
@@ -252,7 +225,7 @@
   UpdateVariable(
       &var_allowed_connection_types_for_update_,
       &RealDevicePolicyProvider::ConvertAllowedConnectionTypesForUpdate);
-  UpdateVariable(&var_has_owner_, &RealDevicePolicyProvider::ConvertHasOwner);
+  UpdateVariable(&var_owner_, &DevicePolicy::GetOwner);
   UpdateVariable(&var_http_downloads_enabled_,
                  &DevicePolicy::GetHttpDownloadsEnabled);
   UpdateVariable(&var_au_p2p_enabled_, &DevicePolicy::GetAuP2PEnabled);
@@ -262,12 +235,6 @@
                  &DevicePolicy::GetAutoLaunchedKioskAppId);
   UpdateVariable(&var_disallowed_time_intervals_,
                  &RealDevicePolicyProvider::ConvertDisallowedTimeIntervals);
-  UpdateVariable(&var_channel_downgrade_behavior_,
-                 &RealDevicePolicyProvider::ConvertChannelDowngradeBehavior);
-  UpdateVariable(&var_device_minimum_version_,
-                 &DevicePolicy::GetHighestDeviceMinimumVersion);
-  UpdateVariable(&var_quick_fix_build_token_,
-                 &DevicePolicy::GetDeviceQuickFixBuildToken);
 }
 
 }  // namespace chromeos_update_manager
diff --git a/update_manager/real_device_policy_provider.h b/update_manager/real_device_policy_provider.h
index 0f84b77..bda4cff 100644
--- a/update_manager/real_device_policy_provider.h
+++ b/update_manager/real_device_policy_provider.h
@@ -34,7 +34,7 @@
 
 namespace chromeos_update_manager {
 
-// |DevicePolicyProvider| concrete implementation.
+// DevicePolicyProvider concrete implementation.
 class RealDevicePolicyProvider : public DevicePolicyProvider {
  public:
 #if USE_DBUS
@@ -64,10 +64,6 @@
     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_;
   }
@@ -93,7 +89,7 @@
     return &var_allowed_connection_types_for_update_;
   }
 
-  Variable<bool>* var_has_owner() override { return &var_has_owner_; }
+  Variable<std::string>* var_owner() override { return &var_owner_; }
 
   Variable<bool>* var_http_downloads_enabled() override {
     return &var_http_downloads_enabled_;
@@ -113,30 +109,16 @@
     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_;
-  }
-
-  Variable<std::string>* var_quick_fix_build_token() override {
-    return &var_quick_fix_build_token_;
-  }
-
  private:
   FRIEND_TEST(UmRealDevicePolicyProviderTest, RefreshScheduledTest);
   FRIEND_TEST(UmRealDevicePolicyProviderTest, NonExistentDevicePolicyReloaded);
   FRIEND_TEST(UmRealDevicePolicyProviderTest, ValuesUpdated);
-  FRIEND_TEST(UmRealDevicePolicyProviderTest, HasOwnerConverted);
 
-  // A static handler for the |PropertyChangedCompleted| signal from the session
+  // A static handler for the PropertyChangedCompleted signal from the session
   // manager used as a callback.
   void OnPropertyChangedCompletedSignal(const std::string& success);
 
-  // Called when the signal in |UpdateEngineLibcrosProxyResolvedInterface| is
+  // Called when the signal in UpdateEngineLibcrosProxyResolvedInterface is
   // connected.
   void OnSignalConnected(const std::string& interface_name,
                          const std::string& signal_name,
@@ -152,46 +134,36 @@
   // passed, which is a DevicePolicy getter method.
   template <typename T>
   void UpdateVariable(AsyncCopyVariable<T>* var,
-                      bool (policy::DevicePolicy::*getter)(T*) const);
+                      bool (policy::DevicePolicy::*getter_method)(T*) const);
 
   // Updates the async variable |var| based on the result value of the getter
   // method passed, which is a wrapper getter on this class.
   template <typename T>
   void UpdateVariable(AsyncCopyVariable<T>* var,
-                      bool (RealDevicePolicyProvider::*getter)(T*) const);
+                      bool (RealDevicePolicyProvider::*getter_method)(T*)
+                          const);
 
-  // Wrapper for |DevicePolicy::GetRollbackToTargetVersion()| that converts the
-  // result to |RollbackToTargetVersion|.
+  // Wrapper for DevicePolicy::GetRollbackToTargetVersion() that converts the
+  // result to RollbackToTargetVersion.
   bool ConvertRollbackToTargetVersion(
       RollbackToTargetVersion* rollback_to_target_version) const;
 
-  // Wrapper for |DevicePolicy::GetScatterFactorInSeconds()| that converts the
-  // result to a |base::TimeDelta|. It returns the same value as
-  // |GetScatterFactorInSeconds()|.
+  // Wrapper for DevicePolicy::GetScatterFactorInSeconds() that converts the
+  // result to a base::TimeDelta. It returns the same value as
+  // GetScatterFactorInSeconds().
   bool ConvertScatterFactor(base::TimeDelta* scatter_factor) const;
 
-  // Wrapper for |DevicePolicy::GetAllowedConnectionTypesForUpdate()| that
-  // converts the result to a set of |ConnectionType| elements instead of
-  // strings.
+  // Wrapper for DevicePolicy::GetAllowedConnectionTypesForUpdate() that
+  // converts the result to a set of ConnectionType elements instead of strings.
   bool ConvertAllowedConnectionTypesForUpdate(
       std::set<chromeos_update_engine::ConnectionType>* allowed_types) const;
 
-  // Wrapper for |DevicePolicy::GetUpdateTimeRestrictions()| that converts
-  // the |DevicePolicy::WeeklyTimeInterval| structs to |WeeklyTimeInterval|
-  // objects, which offer more functionality.
+  // Wrapper for DevicePolicy::GetUpdateTimeRestrictions() that converts
+  // the DevicePolicy::WeeklyTimeInterval structs to WeeklyTimeInterval objects,
+  // which offer more functionality.
   bool ConvertDisallowedTimeIntervals(
       WeeklyTimeIntervalVector* disallowed_intervals_out) const;
 
-  // Wrapper for |DevicePolicy::GetOwner()| that converts the result to a
-  // boolean of whether the device has an owner. (Enterprise enrolled
-  // 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_;
 
@@ -209,11 +181,10 @@
   AsyncCopyVariable<bool> var_device_policy_is_loaded_{"policy_is_loaded",
                                                        false};
 
-  // Variables mapping the exposed methods from the |policy::DevicePolicy|.
+  // Variables mapping the exposed methods from the policy::DevicePolicy.
   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"};
@@ -225,7 +196,7 @@
   AsyncCopyVariable<std::set<chromeos_update_engine::ConnectionType>>
       var_allowed_connection_types_for_update_{
           "allowed_connection_types_for_update"};
-  AsyncCopyVariable<bool> var_has_owner_{"owner"};
+  AsyncCopyVariable<std::string> var_owner_{"owner"};
   AsyncCopyVariable<bool> var_http_downloads_enabled_{"http_downloads_enabled"};
   AsyncCopyVariable<bool> var_au_p2p_enabled_{"au_p2p_enabled"};
   AsyncCopyVariable<bool> var_allow_kiosk_app_control_chrome_version_{
@@ -234,12 +205,6 @@
       "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"};
-  AsyncCopyVariable<std::string> var_quick_fix_build_token_{
-      "quick_fix_build_token"};
 
   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 fd55859..0d7b0d0 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/cros/dbus_test_utils.h"
+#include "update_engine/dbus_test_utils.h"
 #endif  // USE_DBUS
 #include "update_engine/update_manager/umtest_utils.h"
 
@@ -177,7 +177,6 @@
 
   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(
@@ -187,7 +186,7 @@
   UmTestUtils::ExpectVariableNotSet(provider_->var_scatter_factor());
   UmTestUtils::ExpectVariableNotSet(
       provider_->var_allowed_connection_types_for_update());
-  UmTestUtils::ExpectVariableNotSet(provider_->var_has_owner());
+  UmTestUtils::ExpectVariableNotSet(provider_->var_owner());
   UmTestUtils::ExpectVariableNotSet(provider_->var_http_downloads_enabled());
   UmTestUtils::ExpectVariableNotSet(provider_->var_au_p2p_enabled());
   UmTestUtils::ExpectVariableNotSet(
@@ -195,9 +194,6 @@
   UmTestUtils::ExpectVariableNotSet(
       provider_->var_auto_launched_kiosk_app_id());
   UmTestUtils::ExpectVariableNotSet(provider_->var_disallowed_time_intervals());
-  UmTestUtils::ExpectVariableNotSet(
-      provider_->var_channel_downgrade_behavior());
-  UmTestUtils::ExpectVariableNotSet(provider_->var_quick_fix_build_token());
 }
 
 TEST_F(UmRealDevicePolicyProviderTest, ValuesUpdated) {
@@ -234,26 +230,6 @@
       string("myapp"), provider_->var_auto_launched_kiosk_app_id());
 }
 
-TEST_F(UmRealDevicePolicyProviderTest, HasOwnerConverted) {
-  SetUpExistentDevicePolicy();
-  EXPECT_TRUE(provider_->Init());
-  loop_.RunOnce(false);
-  Mock::VerifyAndClearExpectations(&mock_policy_provider_);
-
-  EXPECT_CALL(mock_device_policy_, GetOwner(_))
-      .Times(2)
-      .WillOnce(DoAll(SetArgPointee<0>(string("")), Return(true)))
-      .WillOnce(DoAll(SetArgPointee<0>(string("abc@test.org")), Return(true)));
-
-  // Enterprise enrolled device.
-  provider_->RefreshDevicePolicy();
-  UmTestUtils::ExpectVariableHasValue(false, provider_->var_has_owner());
-
-  // Has a device owner.
-  provider_->RefreshDevicePolicy();
-  UmTestUtils::ExpectVariableHasValue(true, provider_->var_has_owner());
-}
-
 TEST_F(UmRealDevicePolicyProviderTest, RollbackToTargetVersionConverted) {
   SetUpExistentDevicePolicy();
   EXPECT_CALL(mock_device_policy_, GetRollbackToTargetVersion(_))
@@ -348,14 +324,14 @@
 #else
       .Times(1)
 #endif  // USE_DBUS
-      .WillRepeatedly(
-          DoAll(SetArgPointee<0>(set<string>{"ethernet", "wifi", "not-a-type"}),
-                Return(true)));
+      .WillRepeatedly(DoAll(
+          SetArgPointee<0>(set<string>{"bluetooth", "wifi", "not-a-type"}),
+          Return(true)));
   EXPECT_TRUE(provider_->Init());
   loop_.RunOnce(false);
 
   UmTestUtils::ExpectVariableHasValue(
-      set<ConnectionType>{ConnectionType::kWifi, ConnectionType::kEthernet},
+      set<ConnectionType>{ConnectionType::kWifi, ConnectionType::kBluetooth},
       provider_->var_allowed_connection_types_for_update());
 }
 
@@ -380,83 +356,4 @@
       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());
-}
-
-TEST_F(UmRealDevicePolicyProviderTest, DeviceQuickFixBuildTokenSet) {
-  SetUpExistentDevicePolicy();
-
-  EXPECT_CALL(mock_device_policy_, GetDeviceQuickFixBuildToken(_))
-      .WillRepeatedly(
-          DoAll(SetArgPointee<0>(string("some_token")), Return(true)));
-  EXPECT_TRUE(provider_->Init());
-  loop_.RunOnce(false);
-
-  UmTestUtils::ExpectVariableHasValue(string("some_token"),
-                                      provider_->var_quick_fix_build_token());
-}
-
 }  // namespace chromeos_update_manager
diff --git a/update_manager/real_shill_provider.cc b/update_manager/real_shill_provider.cc
index 4d067fd..0144603 100644
--- a/update_manager/real_shill_provider.cc
+++ b/update_manager/real_shill_provider.cc
@@ -24,7 +24,6 @@
 #include <shill/dbus-constants.h>
 #include <shill/dbus-proxies.h>
 
-using chromeos_update_engine::SystemState;
 using chromeos_update_engine::connection_utils::ParseConnectionType;
 using org::chromium::flimflam::ManagerProxyInterface;
 using org::chromium::flimflam::ServiceProxyInterface;
@@ -98,8 +97,7 @@
   bool is_connected =
       (default_service_path_.IsValid() && default_service_path_.value() != "/");
   var_is_connected_.SetValue(is_connected);
-  var_conn_last_changed_.SetValue(
-      SystemState::Get()->clock()->GetWallclockTime());
+  var_conn_last_changed_.SetValue(clock_->GetWallclockTime());
 
   if (!is_connected) {
     var_conn_type_.UnsetValue();
diff --git a/update_manager/real_shill_provider.h b/update_manager/real_shill_provider.h
index cd53d92..ec5c570 100644
--- a/update_manager/real_shill_provider.h
+++ b/update_manager/real_shill_provider.h
@@ -27,8 +27,8 @@
 #include <base/time/time.h>
 #include <dbus/object_path.h>
 
-#include "update_engine/common/system_state.h"
-#include "update_engine/cros/shill_proxy_interface.h"
+#include "update_engine/common/clock_interface.h"
+#include "update_engine/shill_proxy_interface.h"
 #include "update_engine/update_manager/generic_variables.h"
 #include "update_engine/update_manager/shill_provider.h"
 
@@ -37,9 +37,9 @@
 // ShillProvider concrete implementation.
 class RealShillProvider : public ShillProvider {
  public:
-  explicit RealShillProvider(
-      chromeos_update_engine::ShillProxyInterface* shill_proxy)
-      : shill_proxy_(shill_proxy) {}
+  RealShillProvider(chromeos_update_engine::ShillProxyInterface* shill_proxy,
+                    chromeos_update_engine::ClockInterface* clock)
+      : shill_proxy_(shill_proxy), clock_(clock) {}
 
   ~RealShillProvider() override = default;
 
@@ -81,6 +81,9 @@
   // The mockable interface to access the shill DBus proxies.
   std::unique_ptr<chromeos_update_engine::ShillProxyInterface> shill_proxy_;
 
+  // A clock abstraction (mockable).
+  chromeos_update_engine::ClockInterface* const clock_;
+
   // The provider's variables.
   AsyncCopyVariable<bool> var_is_connected_{"is_connected"};
   AsyncCopyVariable<chromeos_update_engine::ConnectionType> var_conn_type_{
diff --git a/update_manager/real_shill_provider_unittest.cc b/update_manager/real_shill_provider_unittest.cc
index 9a2d8a8..dcc729a 100644
--- a/update_manager/real_shill_provider_unittest.cc
+++ b/update_manager/real_shill_provider_unittest.cc
@@ -27,17 +27,17 @@
 #include <shill/dbus-proxies.h>
 #include <shill/dbus-proxy-mocks.h>
 
+#include "update_engine/common/fake_clock.h"
 #include "update_engine/common/test_utils.h"
-#include "update_engine/cros/dbus_test_utils.h"
-#include "update_engine/cros/fake_shill_proxy.h"
-#include "update_engine/cros/fake_system_state.h"
+#include "update_engine/dbus_test_utils.h"
+#include "update_engine/fake_shill_proxy.h"
 #include "update_engine/update_manager/umtest_utils.h"
 
 using base::Time;
 using base::TimeDelta;
 using chromeos_update_engine::ConnectionTethering;
 using chromeos_update_engine::ConnectionType;
-using chromeos_update_engine::FakeSystemState;
+using chromeos_update_engine::FakeClock;
 using org::chromium::flimflam::ManagerProxyMock;
 using org::chromium::flimflam::ServiceProxyMock;
 using std::unique_ptr;
@@ -51,6 +51,8 @@
 // Fake service paths.
 const char* const kFakeEthernetServicePath = "/fake/ethernet/service";
 const char* const kFakeWifiServicePath = "/fake/wifi/service";
+const char* const kFakeWimaxServicePath = "/fake/wimax/service";
+const char* const kFakeBluetoothServicePath = "/fake/bluetooth/service";
 const char* const kFakeCellularServicePath = "/fake/cellular/service";
 const char* const kFakeVpnServicePath = "/fake/vpn/service";
 const char* const kFakeUnknownServicePath = "/fake/unknown/service";
@@ -63,10 +65,10 @@
  protected:
   // Initialize the RealShillProvider under test.
   void SetUp() override {
-    FakeSystemState::Get()->fake_clock()->SetWallclockTime(InitTime());
+    fake_clock_.SetWallclockTime(InitTime());
     loop_.SetAsCurrent();
     fake_shill_proxy_ = new chromeos_update_engine::FakeShillProxy();
-    provider_.reset(new RealShillProvider(fake_shill_proxy_));
+    provider_.reset(new RealShillProvider(fake_shill_proxy_, &fake_clock_));
 
     ManagerProxyMock* manager_proxy_mock = fake_shill_proxy_->GetManagerProxy();
 
@@ -127,12 +129,11 @@
   void SendDefaultServiceSignal(const std::string& service_path,
                                 Time* conn_change_time_p) {
     const Time conn_change_time = ConnChangedTime();
-    FakeSystemState::Get()->fake_clock()->SetWallclockTime(conn_change_time);
+    fake_clock_.SetWallclockTime(conn_change_time);
     ASSERT_TRUE(manager_property_changed_.IsHandlerRegistered());
     manager_property_changed_.signal_callback().Run(
         shill::kDefaultServiceProperty, dbus::ObjectPath(service_path));
-    FakeSystemState::Get()->fake_clock()->SetWallclockTime(
-        conn_change_time + TimeDelta::FromSeconds(5));
+    fake_clock_.SetWallclockTime(conn_change_time + TimeDelta::FromSeconds(5));
     if (conn_change_time_p)
       *conn_change_time_p = conn_change_time;
   }
@@ -203,6 +204,7 @@
   }
 
   brillo::FakeMessageLoop loop_{nullptr};
+  FakeClock fake_clock_;
   chromeos_update_engine::FakeShillProxy* fake_shill_proxy_;
 
   // The registered signal handler for the signal Manager.PropertyChanged.
@@ -315,6 +317,21 @@
       kFakeWifiServicePath, shill::kTypeWifi, ConnectionType::kWifi);
 }
 
+// Test that Wimax connection is identified correctly.
+TEST_F(UmRealShillProviderTest, ReadConnTypeWimax) {
+  InitWithDefaultService("/");
+  SetupConnectionAndTestType(
+      kFakeWimaxServicePath, shill::kTypeWimax, ConnectionType::kWimax);
+}
+
+// Test that Bluetooth connection is identified correctly.
+TEST_F(UmRealShillProviderTest, ReadConnTypeBluetooth) {
+  InitWithDefaultService("/");
+  SetupConnectionAndTestType(kFakeBluetoothServicePath,
+                             shill::kTypeBluetooth,
+                             ConnectionType::kBluetooth);
+}
+
 // Test that Cellular connection is identified correctly.
 TEST_F(UmRealShillProviderTest, ReadConnTypeCellular) {
   InitWithDefaultService("/");
diff --git a/update_manager/real_system_provider.cc b/update_manager/real_system_provider.cc
index 34397f3..a900071 100644
--- a/update_manager/real_system_provider.cc
+++ b/update_manager/real_system_provider.cc
@@ -20,17 +20,14 @@
 #include <base/callback.h>
 #include <base/logging.h>
 #include <base/time/time.h>
+#if USE_CHROME_KIOSK_APP
 #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/system_state.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"
 
-using chromeos_update_engine::SystemState;
 using std::string;
 
 namespace chromeos_update_manager {
@@ -67,10 +64,9 @@
     std::unique_ptr<T> result(new T());
     if (!func_.Run(result.get())) {
       if (failed_attempts_ >= kRetryPollVariableMaxRetry) {
-        // Give up on the retries and set back the desired polling interval.
+        // Give up on the retries, set back the desired polling interval and
+        // return the default.
         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(
@@ -100,20 +96,19 @@
 
 bool RealSystemProvider::Init() {
   var_is_normal_boot_mode_.reset(new ConstCopyVariable<bool>(
-      "is_normal_boot_mode",
-      SystemState::Get()->hardware()->IsNormalBootMode()));
+      "is_normal_boot_mode", hardware_->IsNormalBootMode()));
 
   var_is_official_build_.reset(new ConstCopyVariable<bool>(
-      "is_official_build", SystemState::Get()->hardware()->IsOfficialBuild()));
+      "is_official_build", hardware_->IsOfficialBuild()));
 
   var_is_oobe_complete_.reset(new CallCopyVariable<bool>(
       "is_oobe_complete",
       base::Bind(&chromeos_update_engine::HardwareInterface::IsOOBEComplete,
-                 base::Unretained(SystemState::Get()->hardware()),
+                 base::Unretained(hardware_),
                  nullptr)));
 
   var_num_slots_.reset(new ConstCopyVariable<unsigned int>(
-      "num_slots", SystemState::Get()->boot_control()->GetNumSlots()));
+      "num_slots", boot_control_->GetNumSlots()));
 
   var_kiosk_required_platform_version_.reset(new RetryPollVariable<string>(
       "kiosk_required_platform_version",
@@ -121,15 +116,12 @@
       base::Bind(&RealSystemProvider::GetKioskAppRequiredPlatformVersion,
                  base::Unretained(this))));
 
-  var_chromeos_version_.reset(new ConstCopyVariable<base::Version>(
-      "chromeos_version",
-      base::Version(SystemState::Get()->request_params()->app_version())));
-
   return true;
 }
 
 bool RealSystemProvider::GetKioskAppRequiredPlatformVersion(
     string* required_platform_version) {
+#if USE_CHROME_KIOSK_APP
   brillo::ErrorPtr error;
   if (!kiosk_app_proxy_->GetRequiredPlatformVersion(required_platform_version,
                                                     &error)) {
@@ -137,6 +129,7 @@
     required_platform_version->clear();
     return false;
   }
+#endif  // USE_CHROME_KIOSK_APP
 
   return true;
 }
diff --git a/update_manager/real_system_provider.h b/update_manager/real_system_provider.h
index 558d3be..114c6ea 100644
--- a/update_manager/real_system_provider.h
+++ b/update_manager/real_system_provider.h
@@ -20,8 +20,8 @@
 #include <memory>
 #include <string>
 
-#include <base/version.h>
-
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/hardware_interface.h"
 #include "update_engine/update_manager/system_provider.h"
 
 namespace org {
@@ -36,8 +36,18 @@
 class RealSystemProvider : public SystemProvider {
  public:
   RealSystemProvider(
+      chromeos_update_engine::HardwareInterface* hardware,
+      chromeos_update_engine::BootControlInterface* boot_control,
       org::chromium::KioskAppServiceInterfaceProxyInterface* kiosk_app_proxy)
-      : kiosk_app_proxy_(kiosk_app_proxy) {}
+      : hardware_(hardware),
+#if USE_CHROME_KIOSK_APP
+        boot_control_(boot_control),
+        kiosk_app_proxy_(kiosk_app_proxy) {
+  }
+#else
+        boot_control_(boot_control) {
+  }
+#endif  // USE_CHROME_KIOSK_APP
 
   // Initializes the provider and returns whether it succeeded.
   bool Init();
@@ -62,10 +72,6 @@
     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);
@@ -75,9 +81,12 @@
   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_;
+#if USE_CHROME_KIOSK_APP
   org::chromium::KioskAppServiceInterfaceProxyInterface* const kiosk_app_proxy_;
+#endif  // USE_CHROME_KIOSK_APP
 
   DISALLOW_COPY_AND_ASSIGN(RealSystemProvider);
 };
diff --git a/update_manager/real_system_provider_unittest.cc b/update_manager/real_system_provider_unittest.cc
index 9abcad0..f654f7a 100644
--- a/update_manager/real_system_provider_unittest.cc
+++ b/update_manager/real_system_provider_unittest.cc
@@ -21,44 +21,55 @@
 #include <base/time/time.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-#include <kiosk-app/dbus-proxies.h>
-#include <kiosk-app/dbus-proxy-mocks.h>
 
 #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"
+#include "kiosk-app/dbus-proxy-mocks.h"
 
-using chromeos_update_engine::FakeSystemState;
 using org::chromium::KioskAppServiceInterfaceProxyMock;
+#endif  // USE_CHROME_KIOSK_APP
 using std::unique_ptr;
 using testing::_;
 using testing::DoAll;
 using testing::Return;
 using testing::SetArgPointee;
 
+#if USE_CHROME_KIOSK_APP
 namespace {
 const char kRequiredPlatformVersion[] = "1234.0.0";
 }  // namespace
+#endif  // USE_CHROME_KIOSK_APP
 
 namespace chromeos_update_manager {
 
 class UmRealSystemProviderTest : public ::testing::Test {
  protected:
   void SetUp() override {
-    FakeSystemState::CreateInstance();
+#if USE_CHROME_KIOSK_APP
     kiosk_app_proxy_mock_.reset(new KioskAppServiceInterfaceProxyMock());
     ON_CALL(*kiosk_app_proxy_mock_, GetRequiredPlatformVersion(_, _, _))
         .WillByDefault(
             DoAll(SetArgPointee<0>(kRequiredPlatformVersion), Return(true)));
 
-    provider_.reset(new RealSystemProvider(kiosk_app_proxy_mock_.get()));
+    provider_.reset(new RealSystemProvider(
+        &fake_hardware_, &fake_boot_control_, kiosk_app_proxy_mock_.get()));
+#else
+    provider_.reset(
+        new RealSystemProvider(&fake_hardware_, &fake_boot_control_, nullptr));
+#endif  // USE_CHROME_KIOSK_APP
     EXPECT_TRUE(provider_->Init());
   }
 
+  chromeos_update_engine::FakeHardware fake_hardware_;
+  chromeos_update_engine::FakeBootControl fake_boot_control_;
   unique_ptr<RealSystemProvider> provider_;
 
+#if USE_CHROME_KIOSK_APP
   unique_ptr<KioskAppServiceInterfaceProxyMock> kiosk_app_proxy_mock_;
+#endif  // USE_CHROME_KIOSK_APP
 };
 
 TEST_F(UmRealSystemProviderTest, InitTest) {
@@ -66,29 +77,19 @@
   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) {
-  FakeSystemState::Get()->fake_hardware()->SetIsOOBEComplete(base::Time());
+  fake_hardware_.SetIsOOBEComplete(base::Time());
   UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_oobe_complete());
 }
 
 TEST_F(UmRealSystemProviderTest, IsOOBECompleteFalse) {
-  FakeSystemState::Get()->fake_hardware()->UnsetIsOOBEComplete();
+  fake_hardware_.UnsetIsOOBEComplete();
   UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_oobe_complete());
 }
 
-TEST_F(UmRealSystemProviderTest, VersionFromRequestParams) {
-  FakeSystemState::Get()->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(
       std::string(kRequiredPlatformVersion),
@@ -118,21 +119,11 @@
       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());
-  }
+#else
+TEST_F(UmRealSystemProviderTest, KioskRequiredPlatformVersion) {
   UmTestUtils::ExpectVariableHasValue(
-      std::string(""), provider_->var_kiosk_required_platform_version());
+      std::string(), provider_->var_kiosk_required_platform_version());
 }
+#endif  // USE_CHROME_KIOSK_APP
 
 }  // namespace chromeos_update_manager
diff --git a/update_manager/real_time_provider.cc b/update_manager/real_time_provider.cc
index 2b71fa0..efd1747 100644
--- a/update_manager/real_time_provider.cc
+++ b/update_manager/real_time_provider.cc
@@ -20,11 +20,11 @@
 
 #include <base/time/time.h>
 
-#include "update_engine/common/system_state.h"
+#include "update_engine/common/clock_interface.h"
 
 using base::Time;
 using base::TimeDelta;
-using chromeos_update_engine::SystemState;
+using chromeos_update_engine::ClockInterface;
 using std::string;
 
 namespace chromeos_update_manager {
@@ -34,13 +34,13 @@
  public:
   // TODO(garnold) Turn this into an async variable with the needed callback
   // logic for when it value changes.
-  explicit CurrDateVariable(const string& name)
-      : Variable<Time>(name, TimeDelta::FromHours(1)) {}
+  CurrDateVariable(const string& name, ClockInterface* clock)
+      : Variable<Time>(name, TimeDelta::FromHours(1)), clock_(clock) {}
 
  protected:
   virtual const Time* GetValue(TimeDelta /* timeout */, string* /* errmsg */) {
     Time::Exploded now_exp;
-    SystemState::Get()->clock()->GetWallclockTime().LocalExplode(&now_exp);
+    clock_->GetWallclockTime().LocalExplode(&now_exp);
     now_exp.hour = now_exp.minute = now_exp.second = now_exp.millisecond = 0;
     Time* now = new Time();
     bool success = Time::FromLocalExploded(now_exp, now);
@@ -49,6 +49,8 @@
   }
 
  private:
+  ClockInterface* clock_;
+
   DISALLOW_COPY_AND_ASSIGN(CurrDateVariable);
 };
 
@@ -57,40 +59,44 @@
  public:
   // TODO(garnold) Turn this into an async variable with the needed callback
   // logic for when it value changes.
-  explicit CurrHourVariable(const string& name)
-      : Variable<int>(name, TimeDelta::FromMinutes(5)) {}
+  CurrHourVariable(const string& name, ClockInterface* clock)
+      : Variable<int>(name, TimeDelta::FromMinutes(5)), clock_(clock) {}
 
  protected:
   virtual const int* GetValue(TimeDelta /* timeout */, string* /* errmsg */) {
     Time::Exploded exploded;
-    SystemState::Get()->clock()->GetWallclockTime().LocalExplode(&exploded);
+    clock_->GetWallclockTime().LocalExplode(&exploded);
     return new int(exploded.hour);
   }
 
  private:
+  ClockInterface* clock_;
+
   DISALLOW_COPY_AND_ASSIGN(CurrHourVariable);
 };
 
 class CurrMinuteVariable : public Variable<int> {
  public:
-  explicit CurrMinuteVariable(const string& name)
-      : Variable<int>(name, TimeDelta::FromSeconds(15)) {}
+  CurrMinuteVariable(const string& name, ClockInterface* clock)
+      : Variable<int>(name, TimeDelta::FromSeconds(15)), clock_(clock) {}
 
  protected:
   virtual const int* GetValue(TimeDelta /* timeout */, string* /* errmsg */) {
     Time::Exploded exploded;
-    SystemState::Get()->clock()->GetWallclockTime().LocalExplode(&exploded);
+    clock_->GetWallclockTime().LocalExplode(&exploded);
     return new int(exploded.minute);
   }
 
  private:
+  ClockInterface* clock_;
+
   DISALLOW_COPY_AND_ASSIGN(CurrMinuteVariable);
 };
 
 bool RealTimeProvider::Init() {
-  var_curr_date_.reset(new CurrDateVariable("curr_date"));
-  var_curr_hour_.reset(new CurrHourVariable("curr_hour"));
-  var_curr_minute_.reset(new CurrMinuteVariable("curr_minute"));
+  var_curr_date_.reset(new CurrDateVariable("curr_date", clock_));
+  var_curr_hour_.reset(new CurrHourVariable("curr_hour", clock_));
+  var_curr_minute_.reset(new CurrMinuteVariable("curr_minute", clock_));
   return true;
 }
 
diff --git a/update_manager/real_time_provider.h b/update_manager/real_time_provider.h
index 58b0fa5..40dab36 100644
--- a/update_manager/real_time_provider.h
+++ b/update_manager/real_time_provider.h
@@ -21,6 +21,7 @@
 
 #include <base/time/time.h>
 
+#include "update_engine/common/clock_interface.h"
 #include "update_engine/update_manager/time_provider.h"
 
 namespace chromeos_update_manager {
@@ -28,7 +29,8 @@
 // TimeProvider concrete implementation.
 class RealTimeProvider : public TimeProvider {
  public:
-  RealTimeProvider() = default;
+  explicit RealTimeProvider(chromeos_update_engine::ClockInterface* clock)
+      : clock_(clock) {}
 
   // Initializes the provider and returns whether it succeeded.
   bool Init();
@@ -42,6 +44,9 @@
   Variable<int>* var_curr_minute() override { return var_curr_minute_.get(); }
 
  private:
+  // A clock abstraction (fakeable).
+  chromeos_update_engine::ClockInterface* const clock_;
+
   std::unique_ptr<Variable<base::Time>> var_curr_date_;
   std::unique_ptr<Variable<int>> var_curr_hour_;
   std::unique_ptr<Variable<int>> var_curr_minute_;
diff --git a/update_manager/real_time_provider_unittest.cc b/update_manager/real_time_provider_unittest.cc
index f8ed0d2..ce2a718 100644
--- a/update_manager/real_time_provider_unittest.cc
+++ b/update_manager/real_time_provider_unittest.cc
@@ -22,11 +22,11 @@
 #include <base/time/time.h>
 #include <gtest/gtest.h>
 
-#include "update_engine/cros/fake_system_state.h"
+#include "update_engine/common/fake_clock.h"
 #include "update_engine/update_manager/umtest_utils.h"
 
 using base::Time;
-using chromeos_update_engine::FakeSystemState;
+using chromeos_update_engine::FakeClock;
 using std::unique_ptr;
 
 namespace chromeos_update_manager {
@@ -34,9 +34,8 @@
 class UmRealTimeProviderTest : public ::testing::Test {
  protected:
   void SetUp() override {
-    FakeSystemState::CreateInstance();
     // The provider initializes correctly.
-    provider_.reset(new RealTimeProvider());
+    provider_.reset(new RealTimeProvider(&fake_clock_));
     ASSERT_NE(nullptr, provider_.get());
     ASSERT_TRUE(provider_->Init());
   }
@@ -57,6 +56,7 @@
     return time;
   }
 
+  FakeClock fake_clock_;
   unique_ptr<RealTimeProvider> provider_;
 };
 
@@ -71,7 +71,7 @@
   Time expected;
   ignore_result(Time::FromLocalExploded(exploded, &expected));
 
-  FakeSystemState::Get()->fake_clock()->SetWallclockTime(now);
+  fake_clock_.SetWallclockTime(now);
   UmTestUtils::ExpectVariableHasValue(expected, provider_->var_curr_date());
 }
 
@@ -79,7 +79,7 @@
   const Time now = CurrTime();
   Time::Exploded expected;
   now.LocalExplode(&expected);
-  FakeSystemState::Get()->fake_clock()->SetWallclockTime(now);
+  fake_clock_.SetWallclockTime(now);
   UmTestUtils::ExpectVariableHasValue(expected.hour,
                                       provider_->var_curr_hour());
 }
@@ -88,7 +88,7 @@
   const Time now = CurrTime();
   Time::Exploded expected;
   now.LocalExplode(&expected);
-  FakeSystemState::Get()->fake_clock()->SetWallclockTime(now);
+  fake_clock_.SetWallclockTime(now);
   UmTestUtils::ExpectVariableHasValue(expected.minute,
                                       provider_->var_curr_minute());
 }
diff --git a/update_manager/real_updater_provider.cc b/update_manager/real_updater_provider.cc
index 5b76332..134db69 100644
--- a/update_manager/real_updater_provider.cc
+++ b/update_manager/real_updater_provider.cc
@@ -18,7 +18,6 @@
 
 #include <inttypes.h>
 
-#include <algorithm>
 #include <string>
 
 #include <base/bind.h>
@@ -27,10 +26,10 @@
 #include <update_engine/dbus-constants.h>
 
 #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/common/system_state.h"
-#include "update_engine/cros/omaha_request_params.h"
-#include "update_engine/cros/update_attempter.h"
+#include "update_engine/omaha_request_params.h"
+#include "update_engine/update_attempter.h"
 #include "update_engine/update_status_utils.h"
 
 using base::StringPrintf;
@@ -49,16 +48,25 @@
 template <typename T>
 class UpdaterVariableBase : public Variable<T> {
  public:
-  UpdaterVariableBase(const string& name, VariableMode mode)
-      : Variable<T>(name, mode) {}
+  UpdaterVariableBase(const string& name,
+                      VariableMode mode,
+                      SystemState* system_state)
+      : Variable<T>(name, mode), system_state_(system_state) {}
+
+ protected:
+  // The system state used for pulling information from the updater.
+  inline SystemState* system_state() const { return system_state_; }
+
+ private:
+  SystemState* const system_state_;
 };
 
 // Helper class for issuing a GetStatus() to the UpdateAttempter.
 class GetStatusHelper {
  public:
-  explicit GetStatusHelper(string* errmsg) {
-    is_success_ = SystemState::Get()->update_attempter()->GetStatus(
-        &update_engine_status_);
+  GetStatusHelper(SystemState* system_state, string* errmsg) {
+    is_success_ =
+        system_state->update_attempter()->GetStatus(&update_engine_status_);
     if (!is_success_ && errmsg) {
       *errmsg = "Failed to get a status update from the update engine";
     }
@@ -88,12 +96,12 @@
 // A variable reporting the time when a last update check was issued.
 class LastCheckedTimeVariable : public UpdaterVariableBase<Time> {
  public:
-  explicit LastCheckedTimeVariable(const string& name)
-      : UpdaterVariableBase<Time>(name, kVariableModePoll) {}
+  LastCheckedTimeVariable(const string& name, SystemState* system_state)
+      : UpdaterVariableBase<Time>(name, kVariableModePoll, system_state) {}
 
  private:
   const Time* GetValue(TimeDelta /* timeout */, string* errmsg) override {
-    GetStatusHelper raw(errmsg);
+    GetStatusHelper raw(system_state(), errmsg);
     if (!raw.is_success())
       return nullptr;
 
@@ -107,12 +115,12 @@
 // between 0.0 and 1.0.
 class ProgressVariable : public UpdaterVariableBase<double> {
  public:
-  explicit ProgressVariable(const string& name)
-      : UpdaterVariableBase<double>(name, kVariableModePoll) {}
+  ProgressVariable(const string& name, SystemState* system_state)
+      : UpdaterVariableBase<double>(name, kVariableModePoll, system_state) {}
 
  private:
   const double* GetValue(TimeDelta /* timeout */, string* errmsg) override {
-    GetStatusHelper raw(errmsg);
+    GetStatusHelper raw(system_state(), errmsg);
     if (!raw.is_success())
       return nullptr;
 
@@ -133,8 +141,8 @@
 // A variable reporting the stage in which the update process is.
 class StageVariable : public UpdaterVariableBase<Stage> {
  public:
-  explicit StageVariable(const string& name)
-      : UpdaterVariableBase<Stage>(name, kVariableModePoll) {}
+  StageVariable(const string& name, SystemState* system_state)
+      : UpdaterVariableBase<Stage>(name, kVariableModePoll, system_state) {}
 
  private:
   struct CurrOpStrToStage {
@@ -161,12 +169,10 @@
      Stage::kReportingErrorEvent},
     {update_engine::kUpdateStatusAttemptingRollback,
      Stage::kAttemptingRollback},
-    {update_engine::kUpdateStatusCleanupPreviousUpdate,
-     Stage::kCleanupPreviousUpdate},
 };
 
 const Stage* StageVariable::GetValue(TimeDelta /* timeout */, string* errmsg) {
-  GetStatusHelper raw(errmsg);
+  GetStatusHelper raw(system_state(), errmsg);
   if (!raw.is_success())
     return nullptr;
 
@@ -182,12 +188,12 @@
 // A variable reporting the version number that an update is updating to.
 class NewVersionVariable : public UpdaterVariableBase<string> {
  public:
-  explicit NewVersionVariable(const string& name)
-      : UpdaterVariableBase<string>(name, kVariableModePoll) {}
+  NewVersionVariable(const string& name, SystemState* system_state)
+      : UpdaterVariableBase<string>(name, kVariableModePoll, system_state) {}
 
  private:
   const string* GetValue(TimeDelta /* timeout */, string* errmsg) override {
-    GetStatusHelper raw(errmsg);
+    GetStatusHelper raw(system_state(), errmsg);
     if (!raw.is_success())
       return nullptr;
 
@@ -200,12 +206,12 @@
 // A variable reporting the size of the update being processed in bytes.
 class PayloadSizeVariable : public UpdaterVariableBase<uint64_t> {
  public:
-  explicit PayloadSizeVariable(const string& name)
-      : UpdaterVariableBase<uint64_t>(name, kVariableModePoll) {}
+  PayloadSizeVariable(const string& name, SystemState* system_state)
+      : UpdaterVariableBase<uint64_t>(name, kVariableModePoll, system_state) {}
 
  private:
   const uint64_t* GetValue(TimeDelta /* timeout */, string* errmsg) override {
-    GetStatusHelper raw(errmsg);
+    GetStatusHelper raw(system_state(), errmsg);
     if (!raw.is_success())
       return nullptr;
 
@@ -224,20 +230,20 @@
 // policy request.
 class UpdateCompletedTimeVariable : public UpdaterVariableBase<Time> {
  public:
-  explicit UpdateCompletedTimeVariable(const string& name)
-      : UpdaterVariableBase<Time>(name, kVariableModePoll) {}
+  UpdateCompletedTimeVariable(const string& name, SystemState* system_state)
+      : UpdaterVariableBase<Time>(name, kVariableModePoll, system_state) {}
 
  private:
   const Time* GetValue(TimeDelta /* timeout */, string* errmsg) override {
     Time update_boottime;
-    if (!SystemState::Get()->update_attempter()->GetBootTimeAtUpdate(
+    if (!system_state()->update_attempter()->GetBootTimeAtUpdate(
             &update_boottime)) {
       if (errmsg)
         *errmsg = "Update completed time could not be read";
       return nullptr;
     }
 
-    const auto* clock = SystemState::Get()->clock();
+    chromeos_update_engine::ClockInterface* clock = system_state()->clock();
     Time curr_boottime = clock->GetBootTime();
     if (curr_boottime < update_boottime) {
       if (errmsg)
@@ -254,12 +260,12 @@
 // Variables reporting the current image channel.
 class CurrChannelVariable : public UpdaterVariableBase<string> {
  public:
-  explicit CurrChannelVariable(const string& name)
-      : UpdaterVariableBase<string>(name, kVariableModePoll) {}
+  CurrChannelVariable(const string& name, SystemState* system_state)
+      : UpdaterVariableBase<string>(name, kVariableModePoll, system_state) {}
 
  private:
   const string* GetValue(TimeDelta /* timeout */, string* errmsg) override {
-    OmahaRequestParams* request_params = SystemState::Get()->request_params();
+    OmahaRequestParams* request_params = system_state()->request_params();
     string channel = request_params->current_channel();
     if (channel.empty()) {
       if (errmsg)
@@ -275,12 +281,12 @@
 // Variables reporting the new image channel.
 class NewChannelVariable : public UpdaterVariableBase<string> {
  public:
-  explicit NewChannelVariable(const string& name)
-      : UpdaterVariableBase<string>(name, kVariableModePoll) {}
+  NewChannelVariable(const string& name, SystemState* system_state)
+      : UpdaterVariableBase<string>(name, kVariableModePoll, system_state) {}
 
  private:
   const string* GetValue(TimeDelta /* timeout */, string* errmsg) override {
-    OmahaRequestParams* request_params = SystemState::Get()->request_params();
+    OmahaRequestParams* request_params = system_state()->request_params();
     string channel = request_params->target_channel();
     if (channel.empty()) {
       if (errmsg)
@@ -299,25 +305,24 @@
       public chromeos_update_engine::PrefsInterface::ObserverInterface {
  public:
   BooleanPrefVariable(const string& name,
+                      chromeos_update_engine::PrefsInterface* prefs,
                       const char* key,
                       bool default_val)
       : AsyncCopyVariable<bool>(name),
+        prefs_(prefs),
         key_(key),
         default_val_(default_val) {
-    SystemState::Get()->prefs()->AddObserver(key, this);
+    prefs->AddObserver(key, this);
     OnPrefSet(key);
   }
-  ~BooleanPrefVariable() {
-    SystemState::Get()->prefs()->RemoveObserver(key_, this);
-  }
+  ~BooleanPrefVariable() { prefs_->RemoveObserver(key_, this); }
 
  private:
   // Reads the actual value from the Prefs instance and updates the Variable
   // value.
   void OnPrefSet(const string& key) override {
     bool result = default_val_;
-    auto* prefs = SystemState::Get()->prefs();
-    if (prefs->Exists(key_) && !prefs->GetBoolean(key_, &result))
+    if (prefs_ && prefs_->Exists(key_) && !prefs_->GetBoolean(key_, &result))
       result = default_val_;
     // AsyncCopyVariable will take care of values that didn't change.
     SetValue(result);
@@ -325,6 +330,8 @@
 
   void OnPrefDeleted(const string& key) override { SetValue(default_val_); }
 
+  chromeos_update_engine::PrefsInterface* prefs_;
+
   // The Boolean preference key and default value.
   const char* const key_;
   const bool default_val_;
@@ -336,16 +343,16 @@
 class ConsecutiveFailedUpdateChecksVariable
     : public UpdaterVariableBase<unsigned int> {
  public:
-  explicit ConsecutiveFailedUpdateChecksVariable(const string& name)
-      : UpdaterVariableBase<unsigned int>(name, kVariableModePoll) {}
+  ConsecutiveFailedUpdateChecksVariable(const string& name,
+                                        SystemState* system_state)
+      : UpdaterVariableBase<unsigned int>(
+            name, kVariableModePoll, system_state) {}
 
  private:
   const unsigned int* GetValue(TimeDelta /* timeout */,
                                string* /* errmsg */) override {
-    // NOLINTNEXTLINE(readability/casting)
-    return new unsigned int(SystemState::Get()
-                                ->update_attempter()
-                                ->consecutive_failed_update_checks());
+    return new unsigned int(
+        system_state()->update_attempter()->consecutive_failed_update_checks());
   }
 
   DISALLOW_COPY_AND_ASSIGN(ConsecutiveFailedUpdateChecksVariable);
@@ -355,16 +362,16 @@
 class ServerDictatedPollIntervalVariable
     : public UpdaterVariableBase<unsigned int> {
  public:
-  explicit ServerDictatedPollIntervalVariable(const string& name)
-      : UpdaterVariableBase<unsigned int>(name, kVariableModePoll) {}
+  ServerDictatedPollIntervalVariable(const string& name,
+                                     SystemState* system_state)
+      : UpdaterVariableBase<unsigned int>(
+            name, kVariableModePoll, system_state) {}
 
  private:
   const unsigned int* GetValue(TimeDelta /* timeout */,
                                string* /* errmsg */) override {
-    // NOLINTNEXTLINE(readability/casting)
-    return new unsigned int(SystemState::Get()
-                                ->update_attempter()
-                                ->server_dictated_poll_interval());
+    return new unsigned int(
+        system_state()->update_attempter()->server_dictated_poll_interval());
   }
 
   DISALLOW_COPY_AND_ASSIGN(ServerDictatedPollIntervalVariable);
@@ -374,10 +381,10 @@
 class ForcedUpdateRequestedVariable
     : public UpdaterVariableBase<UpdateRequestStatus> {
  public:
-  explicit ForcedUpdateRequestedVariable(const string& name)
+  ForcedUpdateRequestedVariable(const string& name, SystemState* system_state)
       : UpdaterVariableBase<UpdateRequestStatus>::UpdaterVariableBase(
-            name, kVariableModeAsync) {
-    SystemState::Get()->update_attempter()->set_forced_update_pending_callback(
+            name, kVariableModeAsync, system_state) {
+    system_state->update_attempter()->set_forced_update_pending_callback(
         new base::Callback<void(bool, bool)>(  // NOLINT(readability/function)
             base::Bind(&ForcedUpdateRequestedVariable::Reset,
                        base::Unretained(this))));
@@ -409,14 +416,15 @@
 class UpdateRestrictionsVariable
     : public UpdaterVariableBase<UpdateRestrictions> {
  public:
-  explicit UpdateRestrictionsVariable(const string& name)
-      : UpdaterVariableBase<UpdateRestrictions>(name, kVariableModePoll) {}
+  UpdateRestrictionsVariable(const string& name, SystemState* system_state)
+      : UpdaterVariableBase<UpdateRestrictions>(
+            name, kVariableModePoll, system_state) {}
 
  private:
   const UpdateRestrictions* GetValue(TimeDelta /* timeout */,
                                      string* /* errmsg */) override {
     UpdateAttemptFlags attempt_flags =
-        SystemState::Get()->update_attempter()->GetCurrentUpdateAttemptFlags();
+        system_state()->update_attempter()->GetCurrentUpdateAttemptFlags();
     UpdateRestrictions restriction_flags = UpdateRestrictions::kNone;
     // Don't blindly copy the whole value, test and set bits that should
     // transfer from one set of flags to the other.
@@ -431,73 +439,39 @@
   DISALLOW_COPY_AND_ASSIGN(UpdateRestrictionsVariable);
 };
 
-// A variable class for reading timeout interval prefs value.
-class TestUpdateCheckIntervalTimeoutVariable : public Variable<int64_t> {
- public:
-  explicit TestUpdateCheckIntervalTimeoutVariable(const string& name)
-      : Variable<int64_t>(name, kVariableModePoll), read_count_(0) {
-    SetMissingOk();
-  }
-  ~TestUpdateCheckIntervalTimeoutVariable() = default;
-
- private:
-  const int64_t* GetValue(TimeDelta /* timeout */,
-                          string* /* errmsg */) override {
-    auto key = chromeos_update_engine::kPrefsTestUpdateCheckIntervalTimeout;
-    auto* prefs = SystemState::Get()->prefs();
-    int64_t result;
-    if (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;
-  }
-
-  // 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()
-    : var_updater_started_time_(
-          "updater_started_time",
-          SystemState::Get()->clock()->GetWallclockTime()),
-      var_last_checked_time_(new LastCheckedTimeVariable("last_checked_time")),
-      var_update_completed_time_(
-          new UpdateCompletedTimeVariable("update_completed_time")),
-      var_progress_(new ProgressVariable("progress")),
-      var_stage_(new StageVariable("stage")),
-      var_new_version_(new NewVersionVariable("new_version")),
-      var_payload_size_(new PayloadSizeVariable("payload_size")),
-      var_curr_channel_(new CurrChannelVariable("curr_channel")),
-      var_new_channel_(new NewChannelVariable("new_channel")),
-      var_p2p_enabled_(new BooleanPrefVariable(
-          "p2p_enabled", chromeos_update_engine::kPrefsP2PEnabled, false)),
+RealUpdaterProvider::RealUpdaterProvider(SystemState* system_state)
+    : system_state_(system_state),
+      var_updater_started_time_("updater_started_time",
+                                system_state->clock()->GetWallclockTime()),
+      var_last_checked_time_(
+          new LastCheckedTimeVariable("last_checked_time", system_state_)),
+      var_update_completed_time_(new UpdateCompletedTimeVariable(
+          "update_completed_time", system_state_)),
+      var_progress_(new ProgressVariable("progress", system_state_)),
+      var_stage_(new StageVariable("stage", system_state_)),
+      var_new_version_(new NewVersionVariable("new_version", system_state_)),
+      var_payload_size_(new PayloadSizeVariable("payload_size", system_state_)),
+      var_curr_channel_(new CurrChannelVariable("curr_channel", system_state_)),
+      var_new_channel_(new NewChannelVariable("new_channel", system_state_)),
+      var_p2p_enabled_(
+          new BooleanPrefVariable("p2p_enabled",
+                                  system_state_->prefs(),
+                                  chromeos_update_engine::kPrefsP2PEnabled,
+                                  false)),
       var_cellular_enabled_(new BooleanPrefVariable(
           "cellular_enabled",
+          system_state_->prefs(),
           chromeos_update_engine::kPrefsUpdateOverCellularPermission,
           false)),
       var_consecutive_failed_update_checks_(
           new ConsecutiveFailedUpdateChecksVariable(
-              "consecutive_failed_update_checks")),
+              "consecutive_failed_update_checks", system_state_)),
       var_server_dictated_poll_interval_(new ServerDictatedPollIntervalVariable(
-          "server_dictated_poll_interval")),
-      var_forced_update_requested_(
-          new ForcedUpdateRequestedVariable("forced_update_requested")),
-      var_update_restrictions_(
-          new UpdateRestrictionsVariable("update_restrictions")),
-      var_test_update_check_interval_timeout_(
-          new TestUpdateCheckIntervalTimeoutVariable(
-              "test_update_check_interval_timeout")) {}
+          "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_)) {}
 }  // namespace chromeos_update_manager
diff --git a/update_manager/real_updater_provider.h b/update_manager/real_updater_provider.h
index 24298d7..1b46895 100644
--- a/update_manager/real_updater_provider.h
+++ b/update_manager/real_updater_provider.h
@@ -20,6 +20,7 @@
 #include <memory>
 #include <string>
 
+#include "update_engine/system_state.h"
 #include "update_engine/update_manager/generic_variables.h"
 #include "update_engine/update_manager/updater_provider.h"
 
@@ -33,7 +34,8 @@
   // guarantees that parts of the system state can be mocked out at any time
   // during testing. We further assume that, by the time Init() is called, the
   // system state object is fully populated and usable.
-  RealUpdaterProvider();
+  explicit RealUpdaterProvider(
+      chromeos_update_engine::SystemState* system_state);
 
   // Initializes the provider and returns whether it succeeded.
   bool Init() { return true; }
@@ -92,11 +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_;
+
   // Variable implementations.
   ConstCopyVariable<base::Time> var_updater_started_time_;
   std::unique_ptr<Variable<base::Time>> var_last_checked_time_;
@@ -113,7 +114,6 @@
   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 4afe7fc..fb7a763 100644
--- a/update_manager/real_updater_provider_unittest.cc
+++ b/update_manager/real_updater_provider_unittest.cc
@@ -23,13 +23,16 @@
 #include <gtest/gtest.h>
 #include <update_engine/dbus-constants.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/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/update_manager/umtest_utils.h"
 
 using base::Time;
 using base::TimeDelta;
+using chromeos_update_engine::FakeClock;
 using chromeos_update_engine::FakePrefs;
 using chromeos_update_engine::FakeSystemState;
 using chromeos_update_engine::OmahaRequestParams;
@@ -97,8 +100,10 @@
 class UmRealUpdaterProviderTest : public ::testing::Test {
  protected:
   void SetUp() override {
-    FakeSystemState::CreateInstance();
-    provider_.reset(new RealUpdaterProvider());
+    fake_clock_ = fake_sys_state_.fake_clock();
+    fake_sys_state_.set_prefs(&fake_prefs_);
+    provider_.reset(new RealUpdaterProvider(&fake_sys_state_));
+    ASSERT_NE(nullptr, provider_.get());
     // Check that provider initializes correctly.
     ASSERT_TRUE(provider_->Init());
   }
@@ -112,31 +117,31 @@
     const Time kCurrBootTime = (valid ? kUpdateBootTime + kDurationSinceUpdate
                                       : kUpdateBootTime - kDurationSinceUpdate);
     const Time kCurrWallclockTime = FixedTime();
-    EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(),
+    EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
                 GetBootTimeAtUpdate(_))
         .WillOnce(DoAll(SetArgPointee<0>(kUpdateBootTime), Return(true)));
-    FakeSystemState::Get()->fake_clock()->SetBootTime(kCurrBootTime);
-    FakeSystemState::Get()->fake_clock()->SetWallclockTime(kCurrWallclockTime);
+    fake_clock_->SetBootTime(kCurrBootTime);
+    fake_clock_->SetWallclockTime(kCurrWallclockTime);
     return kCurrWallclockTime - kDurationSinceUpdate;
   }
 
+  FakeSystemState fake_sys_state_;
+  FakeClock* fake_clock_;  // Short for fake_sys_state_.fake_clock()
+  FakePrefs fake_prefs_;
   unique_ptr<RealUpdaterProvider> provider_;
 };
 
 TEST_F(UmRealUpdaterProviderTest, UpdaterStartedTimeIsWallclockTime) {
-  FakeSystemState::Get()->fake_clock()->SetWallclockTime(
-      Time::FromDoubleT(123.456));
-  FakeSystemState::Get()->fake_clock()->SetMonotonicTime(
-      Time::FromDoubleT(456.123));
-  // Re-initialize to re-setup the provider under test to use these values.
-  provider_.reset(new RealUpdaterProvider());
-  ASSERT_TRUE(provider_->Init());
+  fake_clock_->SetWallclockTime(Time::FromDoubleT(123.456));
+  fake_clock_->SetMonotonicTime(Time::FromDoubleT(456.123));
+  // Run SetUp again to re-setup the provider under test to use these values.
+  SetUp();
   UmTestUtils::ExpectVariableHasValue(Time::FromDoubleT(123.456),
                                       provider_->var_updater_started_time());
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetLastCheckedTimeOkay) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(DoAll(
           ActionSetUpdateEngineStatusLastCheckedTime(FixedTime().ToTimeT()),
           Return(true)));
@@ -145,49 +150,49 @@
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetLastCheckedTimeFailNoValue) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(Return(false));
   UmTestUtils::ExpectVariableNotSet(provider_->var_last_checked_time());
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetProgressOkayMin) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(DoAll(ActionSetUpdateEngineStatusProgress(0.0), Return(true)));
   UmTestUtils::ExpectVariableHasValue(0.0, provider_->var_progress());
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetProgressOkayMid) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(DoAll(ActionSetUpdateEngineStatusProgress(0.3), Return(true)));
   UmTestUtils::ExpectVariableHasValue(0.3, provider_->var_progress());
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetProgressOkayMax) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(DoAll(ActionSetUpdateEngineStatusProgress(1.0), Return(true)));
   UmTestUtils::ExpectVariableHasValue(1.0, provider_->var_progress());
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetProgressFailNoValue) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(Return(false));
   UmTestUtils::ExpectVariableNotSet(provider_->var_progress());
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetProgressFailTooSmall) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(DoAll(ActionSetUpdateEngineStatusProgress(-2.0), Return(true)));
   UmTestUtils::ExpectVariableNotSet(provider_->var_progress());
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetProgressFailTooBig) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(DoAll(ActionSetUpdateEngineStatusProgress(2.0), Return(true)));
   UmTestUtils::ExpectVariableNotSet(provider_->var_progress());
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetStageOkayIdle) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(DoAll(
           ActionSetUpdateEngineStatusStatus(update_engine::UpdateStatus::IDLE),
           Return(true)));
@@ -195,7 +200,7 @@
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetStageOkayCheckingForUpdate) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(DoAll(ActionSetUpdateEngineStatusStatus(
                           update_engine::UpdateStatus::CHECKING_FOR_UPDATE),
                       Return(true)));
@@ -204,7 +209,7 @@
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetStageOkayUpdateAvailable) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(DoAll(ActionSetUpdateEngineStatusStatus(
                           update_engine::UpdateStatus::UPDATE_AVAILABLE),
                       Return(true)));
@@ -213,7 +218,7 @@
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetStageOkayDownloading) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(DoAll(ActionSetUpdateEngineStatusStatus(
                           update_engine::UpdateStatus::DOWNLOADING),
                       Return(true)));
@@ -222,7 +227,7 @@
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetStageOkayVerifying) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(DoAll(ActionSetUpdateEngineStatusStatus(
                           update_engine::UpdateStatus::VERIFYING),
                       Return(true)));
@@ -231,7 +236,7 @@
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetStageOkayFinalizing) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(DoAll(ActionSetUpdateEngineStatusStatus(
                           update_engine::UpdateStatus::FINALIZING),
                       Return(true)));
@@ -240,7 +245,7 @@
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetStageOkayUpdatedNeedReboot) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(DoAll(ActionSetUpdateEngineStatusStatus(
                           update_engine::UpdateStatus::UPDATED_NEED_REBOOT),
                       Return(true)));
@@ -249,7 +254,7 @@
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetStageOkayReportingErrorEvent) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(DoAll(ActionSetUpdateEngineStatusStatus(
                           update_engine::UpdateStatus::REPORTING_ERROR_EVENT),
                       Return(true)));
@@ -258,7 +263,7 @@
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetStageOkayAttemptingRollback) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(DoAll(ActionSetUpdateEngineStatusStatus(
                           update_engine::UpdateStatus::ATTEMPTING_ROLLBACK),
                       Return(true)));
@@ -267,13 +272,13 @@
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetStageFailNoValue) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(Return(false));
   UmTestUtils::ExpectVariableNotSet(provider_->var_stage());
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetNewVersionOkay) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(
           DoAll(ActionSetUpdateEngineStatusNewVersion("1.2.0"), Return(true)));
   UmTestUtils::ExpectVariableHasValue(string("1.2.0"),
@@ -281,13 +286,13 @@
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetNewVersionFailNoValue) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(Return(false));
   UmTestUtils::ExpectVariableNotSet(provider_->var_new_version());
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetPayloadSizeOkayZero) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(DoAll(
           ActionSetUpdateEngineStatusNewSizeBytes(static_cast<uint64_t>(0)),
           Return(true)));
@@ -296,7 +301,7 @@
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetPayloadSizeOkayArbitrary) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(DoAll(ActionSetUpdateEngineStatusNewSizeBytes(
                           static_cast<uint64_t>(567890)),
                       Return(true)));
@@ -305,7 +310,7 @@
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetPayloadSizeOkayTwoGigabytes) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(DoAll(ActionSetUpdateEngineStatusNewSizeBytes(
                           static_cast<uint64_t>(1) << 31),
                       Return(true)));
@@ -314,44 +319,44 @@
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetPayloadSizeFailNoValue) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetStatus(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
       .WillOnce(Return(false));
   UmTestUtils::ExpectVariableNotSet(provider_->var_payload_size());
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetCurrChannelOkay) {
   const string kChannelName("foo-channel");
-  OmahaRequestParams request_params;
-  request_params.Init("", "", {});
+  OmahaRequestParams request_params(&fake_sys_state_);
+  request_params.Init("", "", false);
   request_params.set_current_channel(kChannelName);
-  FakeSystemState::Get()->set_request_params(&request_params);
+  fake_sys_state_.set_request_params(&request_params);
   UmTestUtils::ExpectVariableHasValue(kChannelName,
                                       provider_->var_curr_channel());
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetCurrChannelFailEmpty) {
-  OmahaRequestParams request_params;
-  request_params.Init("", "", {});
+  OmahaRequestParams request_params(&fake_sys_state_);
+  request_params.Init("", "", false);
   request_params.set_current_channel("");
-  FakeSystemState::Get()->set_request_params(&request_params);
+  fake_sys_state_.set_request_params(&request_params);
   UmTestUtils::ExpectVariableNotSet(provider_->var_curr_channel());
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetNewChannelOkay) {
   const string kChannelName("foo-channel");
-  OmahaRequestParams request_params;
-  request_params.Init("", "", {});
+  OmahaRequestParams request_params(&fake_sys_state_);
+  request_params.Init("", "", false);
   request_params.set_target_channel(kChannelName);
-  FakeSystemState::Get()->set_request_params(&request_params);
+  fake_sys_state_.set_request_params(&request_params);
   UmTestUtils::ExpectVariableHasValue(kChannelName,
                                       provider_->var_new_channel());
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetNewChannelFailEmpty) {
-  OmahaRequestParams request_params;
-  request_params.Init("", "", {});
+  OmahaRequestParams request_params(&fake_sys_state_);
+  request_params.Init("", "", false);
   request_params.set_target_channel("");
-  FakeSystemState::Get()->set_request_params(&request_params);
+  fake_sys_state_.set_request_params(&request_params);
   UmTestUtils::ExpectVariableNotSet(provider_->var_new_channel());
 }
 
@@ -360,26 +365,22 @@
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetP2PEnabledOkayPrefReadsFalse) {
-  FakeSystemState::Get()->fake_prefs()->SetBoolean(
-      chromeos_update_engine::kPrefsP2PEnabled, false);
+  fake_prefs_.SetBoolean(chromeos_update_engine::kPrefsP2PEnabled, false);
   UmTestUtils::ExpectVariableHasValue(false, provider_->var_p2p_enabled());
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetP2PEnabledReadWhenInitialized) {
-  FakeSystemState::Get()->fake_prefs()->SetBoolean(
-      chromeos_update_engine::kPrefsP2PEnabled, true);
-  provider_.reset(new RealUpdaterProvider());
-  ASSERT_TRUE(provider_->Init());
+  fake_prefs_.SetBoolean(chromeos_update_engine::kPrefsP2PEnabled, true);
+  SetUp();
   UmTestUtils::ExpectVariableHasValue(true, provider_->var_p2p_enabled());
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetP2PEnabledUpdated) {
-  auto* fake_prefs = FakeSystemState::Get()->fake_prefs();
-  fake_prefs->SetBoolean(chromeos_update_engine::kPrefsP2PEnabled, false);
+  fake_prefs_.SetBoolean(chromeos_update_engine::kPrefsP2PEnabled, false);
   UmTestUtils::ExpectVariableHasValue(false, provider_->var_p2p_enabled());
-  fake_prefs->SetBoolean(chromeos_update_engine::kPrefsP2PEnabled, true);
+  fake_prefs_.SetBoolean(chromeos_update_engine::kPrefsP2PEnabled, true);
   UmTestUtils::ExpectVariableHasValue(true, provider_->var_p2p_enabled());
-  fake_prefs->Delete(chromeos_update_engine::kPrefsP2PEnabled);
+  fake_prefs_.Delete(chromeos_update_engine::kPrefsP2PEnabled);
   UmTestUtils::ExpectVariableHasValue(false, provider_->var_p2p_enabled());
 }
 
@@ -388,7 +389,7 @@
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetCellularEnabledOkayPrefReadsTrue) {
-  FakeSystemState::Get()->fake_prefs()->SetBoolean(
+  fake_prefs_.SetBoolean(
       chromeos_update_engine::kPrefsUpdateOverCellularPermission, true);
   UmTestUtils::ExpectVariableHasValue(true, provider_->var_cellular_enabled());
 }
@@ -400,8 +401,7 @@
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetUpdateCompletedTimeFailNoValue) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(),
-              GetBootTimeAtUpdate(_))
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetBootTimeAtUpdate(_))
       .WillOnce(Return(false));
   UmTestUtils::ExpectVariableNotSet(provider_->var_update_completed_time());
 }
@@ -413,7 +413,7 @@
 
 TEST_F(UmRealUpdaterProviderTest, GetConsecutiveFailedUpdateChecks) {
   const unsigned int kNumFailedChecks = 3;
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(),
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
               consecutive_failed_update_checks())
       .WillRepeatedly(Return(kNumFailedChecks));
   UmTestUtils::ExpectVariableHasValue(
@@ -422,7 +422,7 @@
 
 TEST_F(UmRealUpdaterProviderTest, GetServerDictatedPollInterval) {
   const unsigned int kPollInterval = 2 * 60 * 60;  // Two hours.
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(),
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
               server_dictated_poll_interval())
       .WillRepeatedly(Return(kPollInterval));
   UmTestUtils::ExpectVariableHasValue(
@@ -430,7 +430,7 @@
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetUpdateRestrictions) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(),
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
               GetCurrentUpdateAttemptFlags())
       .WillRepeatedly(Return(UpdateAttemptFlags::kFlagRestrictDownload |
                              UpdateAttemptFlags::kFlagNonInteractive));
@@ -439,36 +439,10 @@
 }
 
 TEST_F(UmRealUpdaterProviderTest, GetUpdateRestrictionsNone) {
-  EXPECT_CALL(*FakeSystemState::Get()->mock_update_attempter(),
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
               GetCurrentUpdateAttemptFlags())
       .WillRepeatedly(Return(UpdateAttemptFlags::kNone));
   UmTestUtils::ExpectVariableHasValue(UpdateRestrictions::kNone,
                                       provider_->var_update_restrictions());
 }
-
-TEST_F(UmRealUpdaterProviderTest, TestUpdateCheckIntervalTimeout) {
-  UmTestUtils::ExpectVariableNotSet(
-      provider_->var_test_update_check_interval_timeout());
-  auto* fake_prefs = FakeSystemState::Get()->fake_prefs();
-  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 6cbc447..11d09d6 100644
--- a/update_manager/rollback_prefs.h
+++ b/update_manager/rollback_prefs.h
@@ -31,21 +31,9 @@
   kDisabled = 1,
   kRollbackAndPowerwash = 2,
   kRollbackAndRestoreIfPossible = 3,
+  kRollbackOnlyIfRestorePossible = 4,
   // This value must be the last entry.
-  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
+  kMaxValue = 5
 };
 
 }  // namespace chromeos_update_manager
diff --git a/update_manager/shill_provider.h b/update_manager/shill_provider.h
index ebe7a3a..c7bb2e2 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/common/connection_utils.h"
+#include "update_engine/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 a992975..4835ab2 100644
--- a/update_manager/staging_utils.cc
+++ b/update_manager/staging_utils.cc
@@ -26,10 +26,12 @@
 
 #include "update_engine/common/constants.h"
 #include "update_engine/common/hardware_interface.h"
-#include "update_engine/common/system_state.h"
+#include "update_engine/common/prefs_interface.h"
+#include "update_engine/system_state.h"
 
 using base::TimeDelta;
 using chromeos_update_engine::kPrefsWallClockStagingWaitPeriod;
+using chromeos_update_engine::PrefsInterface;
 using chromeos_update_engine::SystemState;
 using policy::DevicePolicy;
 
@@ -97,6 +99,7 @@
 }
 
 StagingCase CalculateStagingCase(const DevicePolicy* device_policy,
+                                 PrefsInterface* prefs,
                                  TimeDelta* staging_wait_time,
                                  StagingSchedule* staging_schedule) {
   // Check that the schedule in the device policy is correct.
@@ -126,8 +129,7 @@
   int64_t wait_period_in_days;
   // There exists a persisted value that is valid. That is, it's smaller than
   // the maximum amount of days of staging set by the user.
-  if (SystemState::Get()->prefs()->GetInt64(kPrefsWallClockStagingWaitPeriod,
-                                            &wait_period_in_days) &&
+  if (prefs->GetInt64(kPrefsWallClockStagingWaitPeriod, &wait_period_in_days) &&
       wait_period_in_days > 0 && wait_period_in_days <= max_days) {
     *staging_wait_time = TimeDelta::FromDays(wait_period_in_days);
     return StagingCase::kSetStagingFromPref;
diff --git a/update_manager/staging_utils.h b/update_manager/staging_utils.h
index 0de1dfd..e91bfeb 100644
--- a/update_manager/staging_utils.h
+++ b/update_manager/staging_utils.h
@@ -62,6 +62,7 @@
 // contain the previous staging schedule, if there is a new schedule found, its
 // value will be replaced with the new one.
 StagingCase CalculateStagingCase(const policy::DevicePolicy* device_policy,
+                                 chromeos_update_engine::PrefsInterface* prefs,
                                  base::TimeDelta* staging_wait_time,
                                  StagingSchedule* staging_schedule);
 
diff --git a/update_manager/staging_utils_unittest.cc b/update_manager/staging_utils_unittest.cc
index 126617f..8d75acd 100644
--- a/update_manager/staging_utils_unittest.cc
+++ b/update_manager/staging_utils_unittest.cc
@@ -24,10 +24,10 @@
 #include <policy/mock_device_policy.h>
 
 #include "update_engine/common/constants.h"
-#include "update_engine/cros/fake_system_state.h"
+#include "update_engine/common/fake_prefs.h"
 
 using base::TimeDelta;
-using chromeos_update_engine::FakeSystemState;
+using chromeos_update_engine::FakePrefs;
 using chromeos_update_engine::kPrefsWallClockStagingWaitPeriod;
 using testing::_;
 using testing::DoAll;
@@ -44,7 +44,6 @@
 class StagingUtilsScheduleTest : public testing::Test {
  protected:
   void SetUp() override {
-    FakeSystemState::CreateInstance();
     test_wait_time_ = TimeDelta();
     test_staging_schedule_ = StagingSchedule();
   }
@@ -56,13 +55,14 @@
   }
 
   void SetPersistedStagingVal(int64_t wait_time) {
-    EXPECT_TRUE(FakeSystemState::Get()->fake_prefs()->SetInt64(
-        kPrefsWallClockStagingWaitPeriod, wait_time));
+    EXPECT_TRUE(
+        fake_prefs_.SetInt64(kPrefsWallClockStagingWaitPeriod, wait_time));
   }
 
   void TestStagingCase(const StagingCase& expected) {
     EXPECT_EQ(expected,
               CalculateStagingCase(&device_policy_,
+                                   &fake_prefs_,
                                    &test_wait_time_,
                                    &test_staging_schedule_));
   }
@@ -75,6 +75,7 @@
   policy::MockDevicePolicy device_policy_;
   TimeDelta test_wait_time_;
   StagingSchedule test_staging_schedule_;
+  FakePrefs fake_prefs_;
 };
 
 // Last element should be 100, if not return false.
diff --git a/update_manager/state_factory.cc b/update_manager/state_factory.cc
index 0ab4f7b..78cec6a 100644
--- a/update_manager/state_factory.cc
+++ b/update_manager/state_factory.cc
@@ -23,31 +23,34 @@
 #include <session_manager/dbus-proxies.h>
 #endif  // USE_DBUS
 
+#include "update_engine/common/clock_interface.h"
 #if USE_DBUS
-#include "update_engine/cros/dbus_connection.h"
+#include "update_engine/dbus_connection.h"
 #endif  // USE_DBUS
-#include "update_engine/common/system_state.h"
-#include "update_engine/cros/shill_proxy.h"
 #include "update_engine/update_manager/fake_shill_provider.h"
 #include "update_engine/update_manager/real_config_provider.h"
 #include "update_engine/update_manager/real_device_policy_provider.h"
 #include "update_engine/update_manager/real_random_provider.h"
-#include "update_engine/update_manager/real_shill_provider.h"
 #include "update_engine/update_manager/real_state.h"
 #include "update_engine/update_manager/real_system_provider.h"
 #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/update_manager/real_shill_provider.h"
+#endif  // USE_SHILL
 
-using chromeos_update_engine::SystemState;
 using std::unique_ptr;
 
 namespace chromeos_update_manager {
 
 State* DefaultStateFactory(
     policy::PolicyProvider* policy_provider,
-    org::chromium::KioskAppServiceInterfaceProxyInterface* kiosk_app_proxy) {
+    org::chromium::KioskAppServiceInterfaceProxyInterface* kiosk_app_proxy,
+    chromeos_update_engine::SystemState* system_state) {
+  chromeos_update_engine::ClockInterface* const clock = system_state->clock();
   unique_ptr<RealConfigProvider> config_provider(
-      new RealConfigProvider(SystemState::Get()->hardware()));
+      new RealConfigProvider(system_state->hardware()));
 #if USE_DBUS
   scoped_refptr<dbus::Bus> bus =
       chromeos_update_engine::DBusConnection::Get()->GetDBus();
@@ -59,18 +62,25 @@
   unique_ptr<RealDevicePolicyProvider> device_policy_provider(
       new RealDevicePolicyProvider(policy_provider));
 #endif  // USE_DBUS
+#if USE_SHILL
   unique_ptr<RealShillProvider> shill_provider(
-      new RealShillProvider(new chromeos_update_engine::ShillProxy()));
+      new RealShillProvider(new chromeos_update_engine::ShillProxy(), clock));
+#else
+  unique_ptr<FakeShillProvider> shill_provider(new FakeShillProvider());
+#endif  // USE_SHILL
   unique_ptr<RealRandomProvider> random_provider(new RealRandomProvider());
-  unique_ptr<RealSystemProvider> system_provider(
-      new RealSystemProvider(kiosk_app_proxy));
+  unique_ptr<RealSystemProvider> system_provider(new RealSystemProvider(
+      system_state->hardware(), system_state->boot_control(), kiosk_app_proxy));
 
-  unique_ptr<RealTimeProvider> time_provider(new RealTimeProvider());
-  unique_ptr<RealUpdaterProvider> updater_provider(new RealUpdaterProvider());
+  unique_ptr<RealTimeProvider> time_provider(new RealTimeProvider(clock));
+  unique_ptr<RealUpdaterProvider> updater_provider(
+      new RealUpdaterProvider(system_state));
 
   if (!(config_provider->Init() && device_policy_provider->Init() &&
         random_provider->Init() &&
+#if USE_SHILL
         shill_provider->Init() &&
+#endif  // USE_SHILL
         system_provider->Init() && time_provider->Init() &&
         updater_provider->Init())) {
     LOG(ERROR) << "Error initializing providers";
diff --git a/update_manager/state_factory.h b/update_manager/state_factory.h
index c53bb9c..1c1c1d9 100644
--- a/update_manager/state_factory.h
+++ b/update_manager/state_factory.h
@@ -17,6 +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/update_manager/state.h"
 
 namespace org {
@@ -34,7 +35,8 @@
 // to initialize.
 State* DefaultStateFactory(
     policy::PolicyProvider* policy_provider,
-    org::chromium::KioskAppServiceInterfaceProxyInterface* kiosk_app_proxy);
+    org::chromium::KioskAppServiceInterfaceProxyInterface* kiosk_app_proxy,
+    chromeos_update_engine::SystemState* system_state);
 
 }  // namespace chromeos_update_manager
 
diff --git a/update_manager/system_provider.h b/update_manager/system_provider.h
index 8eb14e3..13e188b 100644
--- a/update_manager/system_provider.h
+++ b/update_manager/system_provider.h
@@ -17,10 +17,6 @@
 #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"
 
@@ -50,9 +46,6 @@
   // 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 045ecff..e9dee3f 100644
--- a/update_manager/update_manager-inl.h
+++ b/update_manager/update_manager-inl.h
@@ -49,6 +49,7 @@
   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;
@@ -70,12 +71,14 @@
     }
   }
 
+  LOG(INFO) << policy_name << ": END";
+
   return status;
 }
 
 template <typename R, typename... Args>
 void UpdateManager::OnPolicyReadyToEvaluate(
-    std::shared_ptr<EvaluationContext> ec,
+    scoped_refptr<EvaluationContext> ec,
     base::Callback<void(EvalStatus status, const R& result)> callback,
     EvalStatus (Policy::*policy_method)(
         EvaluationContext*, State*, std::string*, R*, Args...) const,
@@ -116,7 +119,8 @@
         EvaluationContext*, State*, std::string*, R*, ExpectedArgs...) const,
     R* result,
     ActualArgs... args) {
-  auto ec = std::make_shared<EvaluationContext>(evaluation_timeout_);
+  scoped_refptr<EvaluationContext> ec(
+      new EvaluationContext(clock_, evaluation_timeout_));
   // A PolicyRequest always consists on a single evaluation on a new
   // EvaluationContext.
   // IMPORTANT: To ensure that ActualArgs can be converted to ExpectedArgs, we
@@ -137,14 +141,15 @@
     EvalStatus (Policy::*policy_method)(
         EvaluationContext*, State*, std::string*, R*, ExpectedArgs...) const,
     ActualArgs... args) {
-  auto ec = std::make_shared<EvaluationContext>(
+  scoped_refptr<EvaluationContext> ec = new EvaluationContext(
+      clock_,
       evaluation_timeout_,
       expiration_timeout_,
       std::unique_ptr<base::Callback<void(EvaluationContext*)>>(
           new base::Callback<void(EvaluationContext*)>(
               base::Bind(&UpdateManager::UnregisterEvalContext,
                          weak_ptr_factory_.GetWeakPtr()))));
-  if (!ec_repo_.insert(ec).second) {
+  if (!ec_repo_.insert(ec.get()).second) {
     LOG(ERROR) << "Failed to register evaluation context; this is a bug.";
   }
 
diff --git a/update_manager/update_manager.cc b/update_manager/update_manager.cc
index dbb6b33..5dfc09c 100644
--- a/update_manager/update_manager.cc
+++ b/update_manager/update_manager.cc
@@ -15,18 +15,32 @@
 //
 
 #include "update_engine/update_manager/update_manager.h"
+
+#ifdef __ANDROID__
+#include "update_engine/update_manager/android_things_policy.h"
+#else
+#include "update_engine/update_manager/chromeos_policy.h"
+#endif  // __ANDROID__
 #include "update_engine/update_manager/state.h"
 
 namespace chromeos_update_manager {
 
-UpdateManager::UpdateManager(base::TimeDelta evaluation_timeout,
+UpdateManager::UpdateManager(chromeos_update_engine::ClockInterface* clock,
+                             base::TimeDelta evaluation_timeout,
                              base::TimeDelta expiration_timeout,
                              State* state)
-    : policy_(GetSystemPolicy()),
+    : default_policy_(clock),
       state_(state),
+      clock_(clock),
       evaluation_timeout_(evaluation_timeout),
       expiration_timeout_(expiration_timeout),
-      weak_ptr_factory_(this) {}
+      weak_ptr_factory_(this) {
+#ifdef __ANDROID__
+  policy_.reset(new AndroidThingsPolicy());
+#else
+  policy_.reset(new ChromeOSPolicy());
+#endif  // __ANDROID__
+}
 
 UpdateManager::~UpdateManager() {
   // Remove pending main loop events associated with any of the outstanding
@@ -36,19 +50,8 @@
     ec->RemoveObserversAndTimeout();
 }
 
-void UpdateManager::AsyncPolicyRequestUpdateCheckAllowed(
-    base::Callback<void(EvalStatus, const UpdateCheckParams& result)> callback,
-    EvalStatus (Policy::*policy_method)(
-        EvaluationContext*, State*, std::string*, UpdateCheckParams*) const) {
-  AsyncPolicyRequest(callback, policy_method);
-}
-
 void UpdateManager::UnregisterEvalContext(EvaluationContext* ec) {
-  // Since |ec_repo_|'s compare function is based on the value of the raw
-  // pointer |ec|, we can just create a |shared_ptr| here and pass it along to
-  // be erased.
-  if (!ec_repo_.erase(
-          std::shared_ptr<EvaluationContext>(ec, [](EvaluationContext*) {}))) {
+  if (!ec_repo_.erase(ec)) {
     LOG(ERROR) << "Unregistering an unknown evaluation context, this is a bug.";
   }
 }
diff --git a/update_manager/update_manager.h b/update_manager/update_manager.h
index e266b57..b0fd97f 100644
--- a/update_manager/update_manager.h
+++ b/update_manager/update_manager.h
@@ -22,9 +22,10 @@
 #include <string>
 
 #include <base/callback.h>
+#include <base/memory/ref_counted.h>
 #include <base/time/time.h>
 
-#include "update_engine/common/system_state.h"
+#include "update_engine/common/clock_interface.h"
 #include "update_engine/update_manager/default_policy.h"
 #include "update_engine/update_manager/evaluation_context.h"
 #include "update_engine/update_manager/policy.h"
@@ -32,31 +33,22 @@
 
 namespace chromeos_update_manager {
 
-// Please do not move this class into a new file for simplicity.
-// This pure virtual class is purely created for purpose of testing. The reason
-// was that |UpdateManager|'s member functions are templatized, which does not
-// play nicely when testing (mocking + faking). Whenever a specialized member of
-// |UpdateManager| must be tested, please add a specialized template member
-// function within this class for testing.
-class SpecializedPolicyRequestInterface {
- public:
-  virtual ~SpecializedPolicyRequestInterface() = default;
-
-  virtual void AsyncPolicyRequestUpdateCheckAllowed(
-      base::Callback<void(EvalStatus, const UpdateCheckParams& result)>
-          callback,
-      EvalStatus (Policy::*policy_method)(EvaluationContext*,
-                                          State*,
-                                          std::string*,
-                                          UpdateCheckParams*) const) = 0;
+// Comparator for scoped_refptr objects.
+template <typename T>
+struct ScopedRefPtrLess {
+  bool operator()(const scoped_refptr<T>& first,
+                  const scoped_refptr<T>& second) const {
+    return first.get() < second.get();
+  }
 };
 
 // The main Update Manager singleton class.
-class UpdateManager : public SpecializedPolicyRequestInterface {
+class UpdateManager {
  public:
   // Creates the UpdateManager instance, assuming ownership on the provided
   // |state|.
-  UpdateManager(base::TimeDelta evaluation_timeout,
+  UpdateManager(chromeos_update_engine::ClockInterface* clock,
+                base::TimeDelta evaluation_timeout,
                 base::TimeDelta expiration_timeout,
                 State* state);
 
@@ -99,14 +91,6 @@
           EvaluationContext*, State*, std::string*, R*, ExpectedArgs...) const,
       ActualArgs... args);
 
-  void AsyncPolicyRequestUpdateCheckAllowed(
-      base::Callback<void(EvalStatus, const UpdateCheckParams& result)>
-          callback,
-      EvalStatus (Policy::*policy_method)(EvaluationContext*,
-                                          State*,
-                                          std::string*,
-                                          UpdateCheckParams*) const) override;
-
  protected:
   // The UpdateManager receives ownership of the passed Policy instance.
   void set_policy(const Policy* policy) { policy_.reset(policy); }
@@ -141,7 +125,7 @@
   // the evaluation will be re-scheduled to be called later.
   template <typename R, typename... Args>
   void OnPolicyReadyToEvaluate(
-      std::shared_ptr<EvaluationContext> ec,
+      scoped_refptr<EvaluationContext> ec,
       base::Callback<void(EvalStatus status, const R& result)> callback,
       EvalStatus (Policy::*policy_method)(
           EvaluationContext*, State*, std::string*, R*, Args...) const,
@@ -161,6 +145,9 @@
   // State Providers.
   std::unique_ptr<State> state_;
 
+  // Pointer to the mockable clock interface;
+  chromeos_update_engine::ClockInterface* clock_;
+
   // Timeout for a policy evaluation.
   const base::TimeDelta evaluation_timeout_;
 
@@ -172,7 +159,9 @@
   // destructed; alternatively, when the UpdateManager instance is destroyed, it
   // will remove all pending events associated with all outstanding contexts
   // (which should, in turn, trigger their destruction).
-  std::set<std::shared_ptr<EvaluationContext>> ec_repo_;
+  std::set<scoped_refptr<EvaluationContext>,
+           ScopedRefPtrLess<EvaluationContext>>
+      ec_repo_;
 
   base::WeakPtrFactory<UpdateManager> weak_ptr_factory_;
 
diff --git a/update_manager/update_manager_unittest.cc b/update_manager/update_manager_unittest.cc
index a02d7ef..f1a8d17 100644
--- a/update_manager/update_manager_unittest.cc
+++ b/update_manager/update_manager_unittest.cc
@@ -34,7 +34,7 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include "update_engine/cros/fake_system_state.h"
+#include "update_engine/common/fake_clock.h"
 #include "update_engine/update_manager/default_policy.h"
 #include "update_engine/update_manager/fake_state.h"
 #include "update_engine/update_manager/mock_policy.h"
@@ -48,7 +48,6 @@
 using brillo::MessageLoopRunMaxIterations;
 using chromeos_update_engine::ErrorCode;
 using chromeos_update_engine::FakeClock;
-using chromeos_update_engine::FakeSystemState;
 using std::pair;
 using std::string;
 using std::tuple;
@@ -81,10 +80,11 @@
  protected:
   void SetUp() override {
     loop_.SetAsCurrent();
-    FakeSystemState::CreateInstance();
     fake_state_ = new FakeState();
-    umut_.reset(new UpdateManager(
-        TimeDelta::FromSeconds(5), TimeDelta::FromSeconds(1), fake_state_));
+    umut_.reset(new UpdateManager(&fake_clock_,
+                                  TimeDelta::FromSeconds(5),
+                                  TimeDelta::FromSeconds(1),
+                                  fake_state_));
   }
 
   void TearDown() override { EXPECT_FALSE(loop_.PendingTasks()); }
@@ -92,6 +92,7 @@
   base::SimpleTestClock test_clock_;
   brillo::FakeMessageLoop loop_{&test_clock_};
   FakeState* fake_state_;  // Owned by the umut_.
+  FakeClock fake_clock_;
   unique_ptr<UpdateManager> umut_;
 };
 
@@ -290,14 +291,13 @@
 }
 
 TEST_F(UmUpdateManagerTest, AsyncPolicyRequestTimesOut) {
-  auto* fake_clock = FakeSystemState::Get()->fake_clock();
   // Set up an async policy call to exceed its expiration timeout, make sure
   // that the default policy was not used (no callback) and that evaluation is
   // reattempted.
   int num_called = 0;
   umut_->set_policy(new DelayPolicy(
       0,
-      fake_clock->GetWallclockTime() + TimeDelta::FromSeconds(3),
+      fake_clock_.GetWallclockTime() + TimeDelta::FromSeconds(3),
       &num_called));
 
   vector<pair<EvalStatus, UpdateCheckParams>> calls;
@@ -314,7 +314,7 @@
   // ensure that reevaluation occurred but callback was not invoked (i.e.
   // default policy was not consulted).
   test_clock_.Advance(TimeDelta::FromSeconds(2));
-  fake_clock->SetWallclockTime(fake_clock->GetWallclockTime() +
+  fake_clock_.SetWallclockTime(fake_clock_.GetWallclockTime() +
                                TimeDelta::FromSeconds(2));
   MessageLoopRunMaxIterations(MessageLoop::current(), 10);
   EXPECT_EQ(2, num_called);
@@ -322,7 +322,7 @@
   // Wait for reevaluation due to delay to happen, ensure that it occurs and
   // that the callback is invoked.
   test_clock_.Advance(TimeDelta::FromSeconds(2));
-  fake_clock->SetWallclockTime(fake_clock->GetWallclockTime() +
+  fake_clock_.SetWallclockTime(fake_clock_.GetWallclockTime() +
                                TimeDelta::FromSeconds(2));
   MessageLoopRunMaxIterations(MessageLoop::current(), 10);
   EXPECT_EQ(3, num_called);
diff --git a/update_manager/update_time_restrictions_monitor.cc b/update_manager/update_time_restrictions_monitor.cc
deleted file mode 100644
index 00e6ec3..0000000
--- a/update_manager/update_time_restrictions_monitor.cc
+++ /dev/null
@@ -1,132 +0,0 @@
-//
-// 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/update_time_restrictions_monitor.h"
-
-#include <base/bind.h>
-#include <base/time/time.h>
-
-#include "update_engine/common/system_state.h"
-
-using base::TimeDelta;
-using brillo::MessageLoop;
-using chromeos_update_engine::SystemState;
-
-namespace chromeos_update_manager {
-
-namespace {
-
-const WeeklyTimeInterval* FindNextNearestInterval(
-    const WeeklyTimeIntervalVector& intervals, const WeeklyTime& now) {
-  const WeeklyTimeInterval* result_interval = nullptr;
-  // As we are dealing with weekly time here, the maximum duration can be one
-  // week.
-  TimeDelta duration_till_next_interval = TimeDelta::FromDays(7);
-  for (const auto& interval : intervals) {
-    if (interval.InRange(now)) {
-      return &interval;
-    }
-    const TimeDelta current_duration = now.GetDurationTo(interval.start());
-    if (current_duration < duration_till_next_interval) {
-      result_interval = &interval;
-      duration_till_next_interval = current_duration;
-    }
-  }
-  return result_interval;
-}
-
-WeeklyTime Now() {
-  return WeeklyTime::FromTime(SystemState::Get()->clock()->GetWallclockTime());
-}
-
-}  // namespace
-
-UpdateTimeRestrictionsMonitor::UpdateTimeRestrictionsMonitor(
-    DevicePolicyProvider* device_policy_provider, Delegate* delegate)
-    : evaluation_context_(/* evaluation_timeout = */ TimeDelta::Max(),
-                          /* expiration_timeout = */ TimeDelta::Max(),
-                          /* unregister_cb = */ {}),
-      device_policy_provider_(device_policy_provider),
-      delegate_(delegate),
-      weak_ptr_factory_(this) {
-  if (device_policy_provider_ != nullptr && delegate_ != nullptr)
-    StartMonitoring();
-}
-
-UpdateTimeRestrictionsMonitor::~UpdateTimeRestrictionsMonitor() {
-  StopMonitoring();
-}
-
-void UpdateTimeRestrictionsMonitor::StartMonitoring() {
-  DCHECK(device_policy_provider_);
-  const WeeklyTimeIntervalVector* new_intervals = evaluation_context_.GetValue(
-      device_policy_provider_->var_disallowed_time_intervals());
-  if (new_intervals && !new_intervals->empty())
-    WaitForRestrictedIntervalStarts(*new_intervals);
-
-  const bool is_registered = evaluation_context_.RunOnValueChangeOrTimeout(
-      base::Bind(&UpdateTimeRestrictionsMonitor::OnIntervalsChanged,
-                 base::Unretained(this)));
-  DCHECK(is_registered);
-}
-
-void UpdateTimeRestrictionsMonitor::WaitForRestrictedIntervalStarts(
-    const WeeklyTimeIntervalVector& restricted_time_intervals) {
-  DCHECK(!restricted_time_intervals.empty());
-
-  const WeeklyTimeInterval* current_interval =
-      FindNextNearestInterval(restricted_time_intervals, Now());
-  if (current_interval == nullptr) {
-    LOG(WARNING) << "Could not find next nearest restricted interval.";
-    return;
-  }
-
-  // If |current_interval| happens right now, set delay to zero.
-  const TimeDelta duration_till_start =
-      current_interval->InRange(Now())
-          ? TimeDelta::FromMicroseconds(0)
-          : Now().GetDurationTo(current_interval->start());
-  LOG(INFO) << "Found restricted interval starting at "
-            << (SystemState::Get()->clock()->GetWallclockTime() +
-                duration_till_start);
-
-  timeout_event_ = MessageLoop::current()->PostDelayedTask(
-      FROM_HERE,
-      base::Bind(&UpdateTimeRestrictionsMonitor::HandleRestrictedIntervalStarts,
-                 weak_ptr_factory_.GetWeakPtr()),
-      duration_till_start);
-}
-
-void UpdateTimeRestrictionsMonitor::HandleRestrictedIntervalStarts() {
-  timeout_event_ = MessageLoop::kTaskIdNull;
-  if (delegate_)
-    delegate_->OnRestrictedIntervalStarts();
-}
-
-void UpdateTimeRestrictionsMonitor::StopMonitoring() {
-  MessageLoop::current()->CancelTask(timeout_event_);
-  timeout_event_ = MessageLoop::kTaskIdNull;
-}
-
-void UpdateTimeRestrictionsMonitor::OnIntervalsChanged() {
-  DCHECK(!evaluation_context_.is_expired());
-
-  StopMonitoring();
-  evaluation_context_.ResetEvaluation();
-  StartMonitoring();
-}
-
-}  // namespace chromeos_update_manager
diff --git a/update_manager/update_time_restrictions_monitor.h b/update_manager/update_time_restrictions_monitor.h
deleted file mode 100644
index 034ac87..0000000
--- a/update_manager/update_time_restrictions_monitor.h
+++ /dev/null
@@ -1,105 +0,0 @@
-//
-// 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_UPDATE_MANAGER_UPDATE_TIME_RESTRICTIONS_MONITOR_H_
-#define UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_TIME_RESTRICTIONS_MONITOR_H_
-
-#include <memory>
-
-#include <base/memory/weak_ptr.h>
-#include <brillo/message_loops/message_loop.h>
-
-#include "update_engine/update_manager/device_policy_provider.h"
-#include "update_engine/update_manager/evaluation_context.h"
-#include "update_engine/update_manager/weekly_time.h"
-
-namespace chromeos_update_manager {
-
-// Represents a monitor tracking start of restricted time intervals during which
-// update download is not allowed. It reads |var_disallowed_time_intervals|,
-// chooses the next interval according to current time, awaits its start and
-// notifies the delegate. If the chosen interval is already happening, the
-// monitor notifies immediately. The monitor will never notify the delegate
-// while the current list of restricted intervals is empty.
-//
-// The monitor detects changes in the restricted intervals and handles the
-// change with following cases:
-// 1. No restricted time intervals or none of the intervals is in progress -> no
-//    new restricted intervals or none of the new intervals matches the current
-//    time.
-//    The monitor starts tracking the next interval from the new ones, if any.
-// 2. No restricted time intervals or none of the intervals is in progress ->
-//    there is a new interval matching current time.
-//    The monitor shall pick this new interval and notify the delegate
-//    immediately about the start of the restricted interval.
-class UpdateTimeRestrictionsMonitor {
- public:
-  // Interface to handle start of a restricted time interval.
-  class Delegate {
-   public:
-    virtual ~Delegate() = default;
-
-    virtual void OnRestrictedIntervalStarts() = 0;
-  };
-
-  // Creates an instance and starts monitoring the next nearest restricted time
-  // interval if present. If no intervals are available yet the monitor will be
-  // idle until intervals list changes.
-  UpdateTimeRestrictionsMonitor(DevicePolicyProvider* device_policy_provider,
-                                Delegate* delegate);
-
-  UpdateTimeRestrictionsMonitor(const UpdateTimeRestrictionsMonitor&) = delete;
-  UpdateTimeRestrictionsMonitor& operator=(
-      const UpdateTimeRestrictionsMonitor&) = delete;
-
-  ~UpdateTimeRestrictionsMonitor();
-
-  bool IsMonitoringInterval() {
-    return timeout_event_ != brillo::MessageLoop::kTaskIdNull;
-  }
-
- private:
-  // Starts monitoring the start of nearest restricted time interval if present
-  // and any change in restricted time intervals from policy.
-  void StartMonitoring();
-  void WaitForRestrictedIntervalStarts(
-      const WeeklyTimeIntervalVector& restricted_time_intervals);
-
-  // Called when current time lies within a restricted interval.
-  void HandleRestrictedIntervalStarts();
-
-  // Stop monotoring any restricted intervals.
-  void StopMonitoring();
-
-  // Called upon change of restricted intervals.
-  void OnIntervalsChanged();
-
-  // To access restricted time intervals from |device_policy_provider_|.
-  EvaluationContext evaluation_context_;
-
-  DevicePolicyProvider* const device_policy_provider_;
-  Delegate* const delegate_;
-
-  // The TaskId returned by the message loop identifying the timeout callback.
-  // Used for cancelling the timeout callback.
-  brillo::MessageLoop::TaskId timeout_event_{brillo::MessageLoop::kTaskIdNull};
-
-  base::WeakPtrFactory<UpdateTimeRestrictionsMonitor> weak_ptr_factory_;
-};
-
-}  // namespace chromeos_update_manager
-
-#endif  // UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_TIME_RESTRICTIONS_MONITOR_H_
diff --git a/update_manager/update_time_restrictions_monitor_unittest.cc b/update_manager/update_time_restrictions_monitor_unittest.cc
deleted file mode 100644
index 2e474e2..0000000
--- a/update_manager/update_time_restrictions_monitor_unittest.cc
+++ /dev/null
@@ -1,279 +0,0 @@
-//
-// 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 <memory>
-
-#include <base/optional.h>
-#include <base/time/time.h>
-#include <base/test/simple_test_clock.h>
-#include <brillo/message_loops/fake_message_loop.h>
-#include <brillo/message_loops/message_loop_utils.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include "update_engine/cros/fake_system_state.h"
-#include "update_engine/update_manager/fake_state.h"
-#include "update_engine/update_manager/update_time_restrictions_monitor.h"
-
-using brillo::FakeMessageLoop;
-using brillo::MessageLoop;
-using brillo::MessageLoopRunMaxIterations;
-using chromeos_update_engine::FakeSystemState;
-
-namespace chromeos_update_manager {
-
-namespace {
-
-constexpr base::TimeDelta kDurationOffset = base::TimeDelta::FromMinutes(1);
-constexpr base::TimeDelta kHourDuration = base::TimeDelta::FromHours(1);
-constexpr base::TimeDelta kMinuteDuration = base::TimeDelta::FromMinutes(1);
-// Initial time: Monday, May 4th 2020 8:13 AM before interval.
-constexpr base::Time::Exploded kInitialTimeBeforeInterval{
-    2020, 5, 0, 4, 10, 13, 0, 0};
-// Initial time: Monday, May 4th 2020 10:20 AM within interval.
-constexpr base::Time::Exploded kInitialTimeWithinInterval{
-    2020, 5, 0, 4, 10, 20, 0, 0};
-const int current_restricted_interval_index = 0;
-
-const WeeklyTimeIntervalVector kTestOneDisallowedTimeIntervals{
-    // Monday 8:15 AM to Monday 9:30 PM.
-    WeeklyTimeInterval(WeeklyTime(1, kHourDuration * 8 + kMinuteDuration * 15),
-                       WeeklyTime(1, kHourDuration * 9 + kMinuteDuration * 30)),
-};
-
-const WeeklyTimeIntervalVector kTestTwoDisallowedTimeIntervals{
-    // Monday 10:15 AM to Monday 3:30 PM.
-    WeeklyTimeInterval(
-        WeeklyTime(1, kHourDuration * 10 + kMinuteDuration * 15),
-        WeeklyTime(1, kHourDuration * 15 + kMinuteDuration * 30)),
-    // Wednesday 8:30 PM to Thursday 8:40 AM.
-    WeeklyTimeInterval(WeeklyTime(3, kHourDuration * 20 + kMinuteDuration * 30),
-                       WeeklyTime(4, kHourDuration * 8 + kMinuteDuration * 40)),
-};
-
-}  // namespace
-
-class MockUpdateTimeRestrictionsMonitorDelegate
-    : public UpdateTimeRestrictionsMonitor::Delegate {
- public:
-  virtual ~MockUpdateTimeRestrictionsMonitorDelegate() = default;
-
-  MOCK_METHOD0(OnRestrictedIntervalStarts, void());
-};
-
-class UmUpdateTimeRestrictionsMonitorTest : public ::testing::Test {
- protected:
-  UmUpdateTimeRestrictionsMonitorTest() {
-    fake_loop_.SetAsCurrent();
-    FakeSystemState::CreateInstance();
-  }
-
-  void TearDown() override { EXPECT_FALSE(fake_loop_.PendingTasks()); }
-
-  bool SetNow(const base::Time::Exploded& exploded_now) {
-    base::Time now;
-    if (!base::Time::FromLocalExploded(exploded_now, &now))
-      return false;
-
-    test_clock_.SetNow(now);
-    FakeSystemState::Get()->fake_clock()->SetWallclockTime(now);
-    return true;
-  }
-
-  void AdvanceAfterTimestamp(const WeeklyTime& timestamp) {
-    const WeeklyTime now = WeeklyTime::FromTime(test_clock_.Now());
-    const base::TimeDelta duration =
-        now.GetDurationTo(timestamp) + kDurationOffset;
-    test_clock_.Advance(duration);
-    FakeSystemState::Get()->fake_clock()->SetWallclockTime(test_clock_.Now());
-  }
-
-  void VerifyExpectationsOnDelegate() {
-    testing::Mock::VerifyAndClearExpectations(&mock_delegate_);
-  }
-
-  void UpdateRestrictedIntervals(const WeeklyTimeIntervalVector& policy_value) {
-    auto* policy_variable =
-        fake_state_.device_policy_provider()->var_disallowed_time_intervals();
-    policy_variable->reset(new WeeklyTimeIntervalVector(policy_value));
-    policy_variable->NotifyValueChanged();
-  }
-
-  bool IsMonitoringInterval() {
-    return monitor_.has_value() && monitor_.value().IsMonitoringInterval();
-  }
-
-  void BuildMonitorAndVerify(const WeeklyTimeIntervalVector* policy_value,
-                             bool expect_delegate_called,
-                             bool expect_monitoring) {
-    if (expect_delegate_called)
-      EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(1);
-    else
-      EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(0);
-
-    fake_state_.device_policy_provider()
-        ->var_disallowed_time_intervals()
-        ->reset(policy_value != nullptr
-                    ? new WeeklyTimeIntervalVector(*policy_value)
-                    : nullptr);
-    monitor_.emplace(fake_state_.device_policy_provider(), &mock_delegate_);
-    if (expect_delegate_called)
-      MessageLoopRunMaxIterations(MessageLoop::current(), 10);
-    VerifyExpectationsOnDelegate();
-
-    if (expect_monitoring)
-      EXPECT_TRUE(IsMonitoringInterval());
-    else
-      EXPECT_FALSE(IsMonitoringInterval());
-  }
-
-  base::SimpleTestClock test_clock_;
-  FakeMessageLoop fake_loop_{&test_clock_};
-  FakeState fake_state_;
-  MockUpdateTimeRestrictionsMonitorDelegate mock_delegate_;
-  base::Optional<UpdateTimeRestrictionsMonitor> monitor_;
-};
-
-TEST_F(UmUpdateTimeRestrictionsMonitorTest, PolicyIsNotSet) {
-  BuildMonitorAndVerify(
-      nullptr, /*expect_delegate_called=*/false, /*expect_monitoring=*/false);
-}
-
-TEST_F(UmUpdateTimeRestrictionsMonitorTest, PolicyHasEmptyIntervalList) {
-  WeeklyTimeIntervalVector empty_policy;
-  BuildMonitorAndVerify(&empty_policy,
-                        /*expect_delegate_called=*/false,
-                        /*expect_monitoring=*/false);
-}
-
-TEST_F(UmUpdateTimeRestrictionsMonitorTest,
-       CurrentTimeOutsideOfRestrictedInterval) {
-  ASSERT_TRUE(SetNow(kInitialTimeBeforeInterval));
-  BuildMonitorAndVerify(&kTestTwoDisallowedTimeIntervals,
-                        /*expect_delegate_called=*/false,
-                        /*expect_monitoring=*/true);
-
-  // Monitor should only notify start when passing start of interval.
-  EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(1);
-  AdvanceAfterTimestamp(
-      kTestTwoDisallowedTimeIntervals[current_restricted_interval_index]
-          .start());
-  MessageLoopRunMaxIterations(MessageLoop::current(), 10);
-  VerifyExpectationsOnDelegate();
-}
-
-TEST_F(UmUpdateTimeRestrictionsMonitorTest,
-       CurrentTimeWithinRestrictedInterval) {
-  // Monitor should notify start when it is built with current
-  // time within interval.
-  ASSERT_TRUE(SetNow(kInitialTimeWithinInterval));
-  BuildMonitorAndVerify(&kTestTwoDisallowedTimeIntervals,
-                        /*expect_delegate_called=*/true,
-                        /*expect_monitoring=*/false);
-}
-
-TEST_F(UmUpdateTimeRestrictionsMonitorTest,
-       PolicyChangeFromNotSetToOutsideInterval) {
-  // Build monitor with empty initial list of intervals.
-  BuildMonitorAndVerify(
-      nullptr, /*expect_delegate_called=*/false, /*expect_monitoring=*/false);
-
-  // Monitor should not do any notification right after intervals update.
-  ASSERT_TRUE(SetNow(kInitialTimeBeforeInterval));
-  EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(0);
-  UpdateRestrictedIntervals(kTestTwoDisallowedTimeIntervals);
-  MessageLoopRunMaxIterations(MessageLoop::current(), 10);
-  VerifyExpectationsOnDelegate();
-  EXPECT_TRUE(IsMonitoringInterval());
-
-  // Advance time within new interval and check that notification happen.
-  EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(1);
-  AdvanceAfterTimestamp(
-      kTestTwoDisallowedTimeIntervals[current_restricted_interval_index]
-          .start());
-  MessageLoopRunMaxIterations(MessageLoop::current(), 10);
-  VerifyExpectationsOnDelegate();
-}
-
-TEST_F(UmUpdateTimeRestrictionsMonitorTest,
-       PolicyChangeFromNotSetToWithinInterval) {
-  // Build monitor with empty initial list of intervals.
-  BuildMonitorAndVerify(
-      nullptr, /*expect_delegate_called=*/false, /*expect_monitoring=*/false);
-
-  // Advance time inside upcoming new interval and update the intervals.
-  // Monitor should immediately notify about started interval.
-  ASSERT_TRUE(SetNow(kInitialTimeWithinInterval));
-  EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(1);
-  UpdateRestrictedIntervals(kTestTwoDisallowedTimeIntervals);
-  MessageLoopRunMaxIterations(MessageLoop::current(), 10);
-  VerifyExpectationsOnDelegate();
-}
-
-TEST_F(UmUpdateTimeRestrictionsMonitorTest,
-       PolicyChangeFromNotSetToEmptyInterval) {
-  BuildMonitorAndVerify(
-      nullptr, /*expect_delegate_called=*/false, /*expect_monitoring=*/false);
-
-  EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(0);
-  UpdateRestrictedIntervals(WeeklyTimeIntervalVector());
-  MessageLoopRunMaxIterations(MessageLoop::current(), 10);
-  VerifyExpectationsOnDelegate();
-  EXPECT_FALSE(IsMonitoringInterval());
-}
-
-TEST_F(UmUpdateTimeRestrictionsMonitorTest,
-       PolicyChangeFromOneOutsideIntervalToAnother) {
-  // Build monitor with current time outside the intervals.
-  BuildMonitorAndVerify(&kTestTwoDisallowedTimeIntervals,
-                        /*expect_delegate_called=*/false,
-                        /*expect_monitoring=*/true);
-
-  // Update the intervals to outide of current time and no notification should
-  // happen yet.
-  EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(0);
-  UpdateRestrictedIntervals(kTestOneDisallowedTimeIntervals);
-  MessageLoopRunMaxIterations(MessageLoop::current(), 10);
-  VerifyExpectationsOnDelegate();
-
-  // Advance time within new interval. Monitor should notify about started
-  // interval.
-  EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(1);
-  AdvanceAfterTimestamp(
-      kTestOneDisallowedTimeIntervals[current_restricted_interval_index]
-          .start());
-  MessageLoopRunMaxIterations(MessageLoop::current(), 10);
-  VerifyExpectationsOnDelegate();
-}
-
-TEST_F(UmUpdateTimeRestrictionsMonitorTest,
-       PolicyChangeFromOutsideIntervalToWithin) {
-  ASSERT_TRUE(SetNow(kInitialTimeWithinInterval));
-
-  // Build monitor with current time outside the intervals.
-  BuildMonitorAndVerify(&kTestOneDisallowedTimeIntervals,
-                        /*expect_delegate_called=*/false,
-                        /*expect_monitoring=*/true);
-
-  // Update interval such that current time is within it. Monitor should notify
-  // about started interval.
-  EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(1);
-  UpdateRestrictedIntervals(kTestTwoDisallowedTimeIntervals);
-  MessageLoopRunMaxIterations(MessageLoop::current(), 10);
-  VerifyExpectationsOnDelegate();
-}
-
-}  // namespace chromeos_update_manager
diff --git a/update_manager/update_time_restrictions_policy_impl_unittest.cc b/update_manager/update_time_restrictions_policy_impl_unittest.cc
index f99a285..74e7f3c 100644
--- a/update_manager/update_time_restrictions_policy_impl_unittest.cc
+++ b/update_manager/update_time_restrictions_policy_impl_unittest.cc
@@ -60,7 +60,7 @@
 
     Time time;
     EXPECT_TRUE(Time::FromLocalExploded(exploded, &time));
-    fake_clock_->SetWallclockTime(time);
+    fake_clock_.SetWallclockTime(time);
     SetUpDefaultTimeProvider();
     fake_state_.device_policy_provider()
         ->var_disallowed_time_intervals()
diff --git a/update_manager/updater_provider.h b/update_manager/updater_provider.h
index 86af1c8..cb62623 100644
--- a/update_manager/updater_provider.h
+++ b/update_manager/updater_provider.h
@@ -36,7 +36,6 @@
   kUpdatedNeedReboot,
   kReportingErrorEvent,
   kAttemptingRollback,
-  kCleanupPreviousUpdate,
 };
 
 enum class UpdateRequestStatus {
@@ -116,10 +115,6 @@
   // 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 9ac7dae..6c7d350 100644
--- a/update_manager/variable.h
+++ b/update_manager/variable.h
@@ -83,10 +83,6 @@
   // 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.
@@ -119,8 +115,6 @@
     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
@@ -146,8 +140,7 @@
       : name_(name),
         mode_(mode),
         poll_interval_(mode == kVariableModePoll ? poll_interval
-                                                 : base::TimeDelta()),
-        missing_ok_(false) {}
+                                                 : base::TimeDelta()) {}
 
   void OnValueChangedNotification() {
     // A ValueChanged() method can change the list of observers, for example
@@ -181,9 +174,6 @@
   // 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 93e4e2e..9bc0d8a 100644
--- a/update_metadata.proto
+++ b/update_metadata.proto
@@ -78,7 +78,7 @@
 //   new partition.
 // - ZERO: Write zeros to the destination dst_extents.
 // - DISCARD: Discard the destination dst_extents blocks on the physical medium.
-//   the data read from those blocks is undefined.
+//   the data read from those block is undefined.
 // - REPLACE_XZ: Replace the dst_extents with the contents of the attached
 //   xz file after decompression. The xz file should only use crc32 or no crc at
 //   all to be compatible with xz-embedded.
@@ -153,35 +153,35 @@
 //
 // All fields will be set, if this message is present.
 message ImageInfo {
-  optional string board = 1 [deprecated = true];
-  optional string key = 2 [deprecated = true];
-  optional string channel = 3 [deprecated = true];
-  optional string version = 4 [deprecated = true];
+  optional string board = 1;
+  optional string key = 2;
+  optional string channel = 3;
+  optional string version = 4;
 
   // 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 [deprecated = true];
-  optional string build_version = 6 [deprecated = true];
+  optional string build_channel = 5;
+  optional string build_version = 6;
 }
 
 message InstallOperation {
   enum Type {
-    REPLACE = 0;     // Replace destination extents w/ attached data.
-    REPLACE_BZ = 1;  // Replace destination extents w/ attached bzipped data.
-    MOVE = 2 [deprecated = true];    // Move source extents to target extents.
-    BSDIFF = 3 [deprecated = true];  // The data is a bsdiff binary diff.
+    REPLACE = 0;  // Replace destination extents w/ attached data
+    REPLACE_BZ = 1;  // Replace destination extents w/ attached bzipped data
+    MOVE = 2 [deprecated = true];  // Move source extents to destination extents
+    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.
 
@@ -225,22 +225,6 @@
   optional bytes src_sha256_hash = 9;
 }
 
-// Hints to VAB snapshot to skip writing some blocks if these blocks are
-// identical to the ones on the source image. The src & dst extents for each
-// CowMergeOperation should be contiguous, and they're a subset of an OTA
-// InstallOperation.
-// During merge time, we need to follow the pre-computed sequence to avoid
-// read after write, similar to the inplace update schema.
-message CowMergeOperation {
-  enum Type {
-    COW_COPY = 0;  // identical blocks
-  }
-  optional Type type = 1;
-
-  optional Extent src_extent = 2;
-  optional Extent dst_extent = 3;
-}
-
 // Describes the update to apply to a single partition.
 message PartitionUpdate {
   // A platform-specific name to identify the partition set being updated. For
@@ -304,21 +288,6 @@
 
   // The number of FEC roots.
   optional uint32 fec_roots = 16 [default = 2];
-
-  // Per-partition version used for downgrade detection, added
-  // as an effort to support partial updates. For most partitions,
-  // this is the build timestamp.
-  optional string version = 17;
-
-  // A sorted list of CowMergeOperation. When writing cow, we can choose to
-  // 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 {
@@ -348,44 +317,14 @@
   // partitions if possible. If this is unset, the update_engine daemon MUST
   // NOT create snapshots for dynamic partitions.
   optional bool snapshot_enabled = 2;
-
-  // If this is set to false, update_engine should not use VABC regardless. If
-  // this is set to true, update_engine may choose to use VABC if device
-  // supports it, but not guaranteed.
-  // VABC stands for Virtual AB Compression
-  optional bool vabc_enabled = 3;
-
-  // The compression algorithm used by VABC. Available ones are "gz", "brotli".
-  // See system/core/fs_mgr/libsnapshot/cow_writer.cpp for available options,
-  // as this parameter is ultimated forwarded to libsnapshot's CowWriter
-  optional string vabc_compression_param = 4;
-
-  // COW version used by VABC. The represents the major version in the COW
-  // header
-  optional uint32 cow_version = 5;
-}
-
-// Definition has been duplicated from
-// $ANDROID_BUILD_TOP/build/tools/releasetools/ota_metadata.proto. Keep in sync.
-message ApexInfo {
-  optional string package_name = 1;
-  optional int64 version = 2;
-  optional bool is_compressed = 3;
-  optional int64 decompressed_size = 4;
-}
-
-// Definition has been duplicated from
-// $ANDROID_BUILD_TOP/build/tools/releasetools/ota_metadata.proto. Keep in sync.
-message ApexMetadata {
-  repeated ApexInfo apex_info = 1;
 }
 
 message DeltaArchiveManifest {
   // Only present in major version = 1. List of install operations for the
   // kernel and rootfs partitions. For major version = 2 see the |partitions|
   // field.
-  repeated InstallOperation install_operations = 1 [deprecated = true];
-  repeated InstallOperation kernel_install_operations = 2 [deprecated = true];
+  repeated InstallOperation install_operations = 1;
+  repeated InstallOperation kernel_install_operations = 2;
 
   // (At time of writing) usually 4096
   optional uint32 block_size = 3 [default = 4096];
@@ -400,15 +339,15 @@
 
   // Only present in major version = 1. Partition metadata used to validate the
   // update. For major version = 2 see the |partitions| field.
-  optional PartitionInfo old_kernel_info = 6 [deprecated = true];
-  optional PartitionInfo new_kernel_info = 7 [deprecated = true];
-  optional PartitionInfo old_rootfs_info = 8 [deprecated = true];
-  optional PartitionInfo new_rootfs_info = 9 [deprecated = true];
+  optional PartitionInfo old_kernel_info = 6;
+  optional PartitionInfo new_kernel_info = 7;
+  optional PartitionInfo old_rootfs_info = 8;
+  optional PartitionInfo new_rootfs_info = 9;
 
   // old_image_info will only be present for delta images.
-  optional ImageInfo old_image_info = 10 [deprecated = true];
+  optional ImageInfo old_image_info = 10;
 
-  optional ImageInfo new_image_info = 11 [deprecated = true];
+  optional ImageInfo new_image_info = 11;
 
   // The minor version, also referred as "delta version", of the payload.
   // Minor version 0 is full payload, everything else is delta payload.
@@ -428,11 +367,4 @@
 
   // Metadata related to all dynamic partitions.
   optional DynamicPartitionMetadata dynamic_partition_metadata = 15;
-
-  // If the payload only updates a subset of partitions on the device.
-  optional bool partial_update = 16;
-
-  // Information on compressed APEX to figure out how much space is required for
-  // their decompression
-  repeated ApexInfo apex_info = 17;
 }
diff --git a/update_status_utils.cc b/update_status_utils.cc
index a702c61..11fd299 100644
--- a/update_status_utils.cc
+++ b/update_status_utils.cc
@@ -16,32 +16,12 @@
 #include "update_engine/update_status_utils.h"
 
 #include <base/logging.h>
-#include <base/strings/string_number_conversions.h>
-#include <brillo/key_value_store.h>
 #include <update_engine/dbus-constants.h>
 
-using brillo::KeyValueStore;
-using std::string;
-using update_engine::UpdateEngineStatus;
 using update_engine::UpdateStatus;
 
 namespace chromeos_update_engine {
 
-namespace {
-
-// Note: Do not change these, autotest depends on these string variables being
-// exactly these matches.
-const char kCurrentOp[] = "CURRENT_OP";
-const char kIsInstall[] = "IS_INSTALL";
-const char kIsEnterpriseRollback[] = "IS_ENTERPRISE_ROLLBACK";
-const char kLastCheckedTime[] = "LAST_CHECKED_TIME";
-const char kNewSize[] = "NEW_SIZE";
-const char kNewVersion[] = "NEW_VERSION";
-const char kProgress[] = "PROGRESS";
-const char kWillPowerwashAfterReboot[] = "WILL_POWERWASH_AFTER_REBOOT";
-
-}  // namespace
-
 const char* UpdateStatusToString(const UpdateStatus& status) {
   switch (status) {
     case UpdateStatus::IDLE:
@@ -74,23 +54,45 @@
   return nullptr;
 }
 
-string UpdateEngineStatusToString(const UpdateEngineStatus& status) {
-  KeyValueStore key_value_store;
-
-  key_value_store.SetString(kLastCheckedTime,
-                            base::NumberToString(status.last_checked_time));
-  key_value_store.SetString(kProgress, base::NumberToString(status.progress));
-  key_value_store.SetString(kNewSize,
-                            base::NumberToString(status.new_size_bytes));
-  key_value_store.SetString(kCurrentOp, UpdateStatusToString(status.status));
-  key_value_store.SetString(kNewVersion, status.new_version);
-  key_value_store.SetBoolean(kIsEnterpriseRollback,
-                             status.is_enterprise_rollback);
-  key_value_store.SetBoolean(kIsInstall, status.is_install);
-  key_value_store.SetBoolean(kWillPowerwashAfterReboot,
-                             status.will_powerwash_after_reboot);
-
-  return key_value_store.SaveToString();
+bool StringToUpdateStatus(const std::string& s, UpdateStatus* status) {
+  if (s == update_engine::kUpdateStatusIdle) {
+    *status = UpdateStatus::IDLE;
+    return true;
+  } else if (s == update_engine::kUpdateStatusCheckingForUpdate) {
+    *status = UpdateStatus::CHECKING_FOR_UPDATE;
+    return true;
+  } else if (s == update_engine::kUpdateStatusUpdateAvailable) {
+    *status = UpdateStatus::UPDATE_AVAILABLE;
+    return true;
+  } else if (s == update_engine::kUpdateStatusNeedPermissionToUpdate) {
+    *status = UpdateStatus::NEED_PERMISSION_TO_UPDATE;
+    return true;
+  } else if (s == update_engine::kUpdateStatusDownloading) {
+    *status = UpdateStatus::DOWNLOADING;
+    return true;
+  } else if (s == update_engine::kUpdateStatusVerifying) {
+    *status = UpdateStatus::VERIFYING;
+    return true;
+  } else if (s == update_engine::kUpdateStatusFinalizing) {
+    *status = UpdateStatus::FINALIZING;
+    return true;
+  } else if (s == update_engine::kUpdateStatusUpdatedNeedReboot) {
+    *status = UpdateStatus::UPDATED_NEED_REBOOT;
+    return true;
+  } else if (s == update_engine::kUpdateStatusReportingErrorEvent) {
+    *status = UpdateStatus::REPORTING_ERROR_EVENT;
+    return true;
+  } else if (s == update_engine::kUpdateStatusAttemptingRollback) {
+    *status = UpdateStatus::ATTEMPTING_ROLLBACK;
+    return true;
+  } else if (s == update_engine::kUpdateStatusDisabled) {
+    *status = UpdateStatus::DISABLED;
+    return true;
+  } else if (s == update_engine::kUpdateStatusCleanupPreviousUpdate) {
+    *status = UpdateStatus::CLEANUP_PREVIOUS_UPDATE;
+    return true;
+  }
+  return false;
 }
 
 }  // namespace chromeos_update_engine
diff --git a/update_status_utils.h b/update_status_utils.h
index 1e3fdde..30ae53b 100644
--- a/update_status_utils.h
+++ b/update_status_utils.h
@@ -25,8 +25,8 @@
 
 const char* UpdateStatusToString(const update_engine::UpdateStatus& status);
 
-std::string UpdateEngineStatusToString(
-    const update_engine::UpdateEngineStatus& status);
+bool StringToUpdateStatus(const std::string& update_status_as_string,
+                          update_engine::UpdateStatus* status);
 
 }  // namespace chromeos_update_engine
 
diff --git a/update_status_utils_unittest.cc b/update_status_utils_unittest.cc
deleted file mode 100644
index 228201c..0000000
--- a/update_status_utils_unittest.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include "update_engine/update_status_utils.h"
-
-#include <string>
-
-#include <gtest/gtest.h>
-
-using std::string;
-
-namespace chromeos_update_engine {
-
-TEST(UpdateStatusUtilsTest, UpdateEngineStatusToStringTest) {
-  // Keep field assignments in same order as they were declared,
-  // to prevent compiler warning, -Wreorder-init-fields.
-  update_engine::UpdateEngineStatus update_engine_status = {
-      .last_checked_time = 156000000,
-      .status = update_engine::UpdateStatus::CHECKING_FOR_UPDATE,
-      .progress = 0.5,
-      .new_size_bytes = 888,
-      .new_version = "12345.0.0",
-      .is_enterprise_rollback = true,
-      .is_install = true,
-      .will_powerwash_after_reboot = true,
-  };
-  string print =
-      R"(CURRENT_OP=UPDATE_STATUS_CHECKING_FOR_UPDATE
-IS_ENTERPRISE_ROLLBACK=true
-IS_INSTALL=true
-LAST_CHECKED_TIME=156000000
-NEW_SIZE=888
-NEW_VERSION=12345.0.0
-PROGRESS=0.5
-WILL_POWERWASH_AFTER_REBOOT=true
-)";
-  EXPECT_EQ(print, UpdateEngineStatusToString(update_engine_status));
-}
-
-}  // namespace chromeos_update_engine