Import brillo_update_payload from Chrome OS dev-utils.

brillo_update_payload was developed in Chrome OS' dev-utils repo. This
CL merges it here to make it available from an AOSP checkout.

Bug: 25631934
TEST=File is present on the checkout.

Change-Id: Idc6eac200b4f725b103847c3f77260a25ea55a7b
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d6dd317
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+# Build/test generated files
+/*.pub.pem
+/app.info
+/delta_generator
+/html/
+/test_http_server
+/update_engine
+/update_engine.dbusclient.h
+/update_engine.dbusserver.h
+/update_engine_client
+/update_engine_unittests
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..265ed23
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,484 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(my-dir)
+
+# Default values for the USE flags. Override these USE flags from your product.
+BRILLO_USE_DBUS ?= 1
+BRILLO_USE_HWID_OVERRIDE ?= 0
+BRILLO_USE_MTD ?= 0
+BRILLO_USE_POWER_MANAGEMENT ?= 0
+
+ue_common_cflags := \
+    -DUSE_DBUS=$(BRILLO_USE_DBUS) \
+    -DUSE_HWID_OVERRIDE=$(BRILLO_USE_HWID_OVERRIDE) \
+    -DUSE_MTD=$(BRILLO_USE_MTD) \
+    -DUSE_POWER_MANAGEMENT=$(BRILLO_USE_POWER_MANAGEMENT) \
+    -D_FILE_OFFSET_BITS=64 \
+    -D_POSIX_C_SOURCE=199309L \
+    -Wa,--noexecstack \
+    -Wall \
+    -Werror \
+    -Wextra \
+    -Wformat=2 \
+    -Wno-psabi \
+    -Wno-unused-parameter \
+    -ffunction-sections \
+    -fstack-protector-strong \
+    -fvisibility=hidden
+ue_common_cppflags := \
+    -Wnon-virtual-dtor \
+    -fno-strict-aliasing \
+    -std=gnu++11
+ue_common_ldflags := \
+    -Wl,--gc-sections
+ue_common_c_includes := \
+    $(LOCAL_PATH)/client_library/include \
+    external/gtest/include \
+    system
+ue_common_shared_libraries := \
+    libbrillo \
+    libbrillo-http \
+    libbrillo-stream \
+    libchrome
+
+ifeq ($(BRILLO_USE_DBUS),1)
+ue_common_shared_libraries += \
+    libbrillo-dbus \
+    libchrome-dbus
+endif  # BRILLO_USE_DBUS == 1
+
+
+ifeq ($(BRILLO_USE_DBUS),1)
+
+# update_engine_client-dbus-proxies (from generate-dbus-proxies.gypi)
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := update_engine_client-dbus-proxies
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_SRC_FILES := \
+    dbus_bindings/dbus-service-config.json \
+    dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
+LOCAL_DBUS_PROXY_PREFIX := update_engine
+include $(BUILD_STATIC_LIBRARY)
+
+endif  # BRILLO_USE_DBUS == 1
+
+# update_metadata-protos (type: static_library)
+# ========================================================
+# Protobufs.
+ue_update_metadata_protos_exported_static_libraries := \
+    update_metadata-protos
+ue_update_metadata_protos_exported_shared_libraries := \
+    libprotobuf-cpp-lite-rtti
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := update_metadata-protos
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+generated_sources_dir := $(call local-generated-sources-dir)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(generated_sources_dir)/proto/system
+LOCAL_SRC_FILES := \
+    update_metadata.proto
+include $(BUILD_STATIC_LIBRARY)
+
+ifeq ($(BRILLO_USE_DBUS),1)
+
+# update_engine-dbus-adaptor (from generate-dbus-adaptors.gypi)
+# ========================================================
+# Chrome D-Bus bindings.
+include $(CLEAR_VARS)
+LOCAL_MODULE := update_engine-dbus-adaptor
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_SRC_FILES := \
+    dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
+include $(BUILD_STATIC_LIBRARY)
+
+# update_engine-dbus-libcros-client (from generate-dbus-proxies.gypi)
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := update_engine-dbus-libcros-client
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_SRC_FILES := \
+    dbus_bindings/org.chromium.LibCrosService.dbus-xml
+LOCAL_DBUS_PROXY_PREFIX := libcros
+include $(BUILD_STATIC_LIBRARY)
+
+endif  # BRILLO_USE_DBUS == 1
+
+# libpayload_consumer (type: static_library)
+# ========================================================
+# The payload application component and common dependencies.
+ue_libpayload_consumer_exported_static_libraries := \
+    update_metadata-protos \
+    libxz \
+    libbz \
+    $(ue_update_metadata_protos_exported_static_libraries)
+ue_libpayload_consumer_exported_shared_libraries := \
+    libcrypto \
+    libcurl \
+    libssl \
+    $(ue_update_metadata_protos_exported_shared_libraries)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libpayload_consumer
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_CLANG := true
+LOCAL_CFLAGS := $(ue_common_cflags)
+LOCAL_CPPFLAGS := $(ue_common_cppflags)
+LOCAL_LDFLAGS := $(ue_common_ldflags)
+LOCAL_C_INCLUDES := \
+    $(ue_common_c_includes) \
+    external/e2fsprogs/lib
+LOCAL_STATIC_LIBRARIES := \
+    update_metadata-protos \
+    $(ue_libpayload_consumer_exported_static_libraries) \
+    $(ue_update_metadata_protos_exported_static_libraries)
+LOCAL_SHARED_LIBRARIES := \
+    $(ue_common_shared_libraries) \
+    $(ue_libpayload_consumer_exported_shared_libraries) \
+    $(ue_update_metadata_protos_exported_shared_libraries)
+LOCAL_SRC_FILES := \
+    common/action_processor.cc \
+    common/boot_control_stub.cc \
+    common/certificate_checker.cc \
+    common/clock.cc \
+    common/constants.cc \
+    common/hash_calculator.cc \
+    common/http_common.cc \
+    common/http_fetcher.cc \
+    common/hwid_override.cc \
+    common/libcurl_http_fetcher.cc \
+    common/multi_range_http_fetcher.cc \
+    common/platform_constants_android.cc \
+    common/prefs.cc \
+    common/subprocess.cc \
+    common/terminator.cc \
+    common/utils.cc \
+    payload_consumer/bzip_extent_writer.cc \
+    payload_consumer/delta_performer.cc \
+    payload_consumer/download_action.cc \
+    payload_consumer/extent_writer.cc \
+    payload_consumer/file_descriptor.cc \
+    payload_consumer/file_writer.cc \
+    payload_consumer/filesystem_verifier_action.cc \
+    payload_consumer/install_plan.cc \
+    payload_consumer/payload_constants.cc \
+    payload_consumer/payload_verifier.cc \
+    payload_consumer/postinstall_runner_action.cc \
+    payload_consumer/xz_extent_writer.cc
+include $(BUILD_STATIC_LIBRARY)
+
+ifeq ($(BRILLO_USE_DBUS),1)
+
+# libupdate_engine (type: static_library)
+# ========================================================
+# The main daemon static_library with all the code used to check for updates
+# with Omaha and expose a DBus daemon.
+ue_libupdate_engine_exported_c_includes := \
+    $(LOCAL_PATH)/include \
+    external/cros/system_api/dbus
+ue_libupdate_engine_exported_static_libraries := \
+    libpayload_consumer \
+    update_metadata-protos \
+    update_engine-dbus-adaptor \
+    update_engine-dbus-libcros-client \
+    update_engine_client-dbus-proxies \
+    libbz \
+    libfs_mgr \
+    $(ue_libpayload_consumer_exported_static_libraries) \
+    $(ue_update_metadata_protos_exported_static_libraries)
+ue_libupdate_engine_exported_shared_libraries := \
+    libdbus \
+    libmetrics \
+    libshill-client \
+    libexpat \
+    libbrillo-policy \
+    libhardware \
+    libcutils \
+    $(ue_libpayload_consumer_exported_shared_libraries) \
+    $(ue_update_metadata_protos_exported_shared_libraries)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libupdate_engine
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_CLANG := true
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(ue_libupdate_engine_exported_c_includes)
+LOCAL_CFLAGS := $(ue_common_cflags)
+LOCAL_CPPFLAGS := $(ue_common_cppflags)
+LOCAL_LDFLAGS := $(ue_common_ldflags)
+LOCAL_C_INCLUDES := \
+    $(ue_common_c_includes) \
+    $(ue_libupdate_engine_exported_c_includes)
+LOCAL_STATIC_LIBRARIES := \
+    libpayload_consumer \
+    update_metadata-protos \
+    update_engine-dbus-adaptor \
+    update_engine-dbus-libcros-client \
+    update_engine_client-dbus-proxies \
+    $(ue_libupdate_engine_exported_static_libraries) \
+    $(ue_libpayload_consumer_exported_static_libraries) \
+    $(ue_update_metadata_protos_exported_static_libraries)
+LOCAL_SHARED_LIBRARIES := \
+    $(ue_common_shared_libraries) \
+    $(ue_libupdate_engine_exported_shared_libraries) \
+    $(ue_libpayload_consumer_exported_shared_libraries) \
+    $(ue_update_metadata_protos_exported_shared_libraries)
+LOCAL_SRC_FILES := \
+    boot_control_android.cc \
+    chrome_browser_proxy_resolver.cc \
+    connection_manager.cc \
+    daemon.cc \
+    dbus_service.cc \
+    hardware_android.cc \
+    image_properties_android.cc \
+    libcros_proxy.cc \
+    metrics.cc \
+    metrics_utils.cc \
+    omaha_request_action.cc \
+    omaha_request_params.cc \
+    omaha_response_handler_action.cc \
+    p2p_manager.cc \
+    payload_state.cc \
+    proxy_resolver.cc \
+    real_system_state.cc \
+    shill_proxy.cc \
+    update_attempter.cc \
+    update_manager/boxed_value.cc \
+    update_manager/chromeos_policy.cc \
+    update_manager/default_policy.cc \
+    update_manager/evaluation_context.cc \
+    update_manager/policy.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/state_factory.cc \
+    update_manager/update_manager.cc \
+    update_status_utils.cc
+include $(BUILD_STATIC_LIBRARY)
+
+endif  # BRILLO_USE_DBUS == 1
+
+# update_engine (type: executable)
+# ========================================================
+# update_engine daemon.
+include $(CLEAR_VARS)
+LOCAL_MODULE := update_engine
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_REQUIRED_MODULES := \
+    bspatch \
+    cacerts_google
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CLANG := true
+LOCAL_CFLAGS := $(ue_common_cflags)
+LOCAL_CPPFLAGS := $(ue_common_cppflags)
+LOCAL_LDFLAGS := $(ue_common_ldflags)
+LOCAL_C_INCLUDES := \
+    $(ue_common_c_includes)
+
+ifdef BRILLO
+
+LOCAL_C_INCLUDES += \
+    $(ue_libupdate_engine_exported_c_includes)
+LOCAL_STATIC_LIBRARIES := \
+    libupdate_engine \
+    $(ue_libupdate_engine_exported_static_libraries)
+
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_SHARED_LIBRARIES := \
+    $(ue_common_shared_libraries) \
+    $(ue_libupdate_engine_exported_shared_libraries)
+LOCAL_SRC_FILES := \
+    main.cc
+
+else  # !defined(BRILLO)
+
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/binder_bindings
+LOCAL_SHARED_LIBRARIES := \
+    libbinder \
+    liblog \
+    libutils
+LOCAL_SRC_FILES := \
+    binder_bindings/android/os/IUpdateEngine.aidl \
+    binder_bindings/android/os/IUpdateEnginePayloadApplicationCallback.aidl \
+    binder_main.cc \
+    binder_service.cc
+
+endif  # defined(BRILLO)
+
+LOCAL_INIT_RC := update_engine.rc
+include $(BUILD_EXECUTABLE)
+
+ifeq ($(BRILLO_USE_DBUS),1)
+
+# update_engine_client (type: executable)
+# ========================================================
+# update_engine console client.
+include $(CLEAR_VARS)
+LOCAL_MODULE := update_engine_client
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_CLANG := true
+LOCAL_CFLAGS := $(ue_common_cflags)
+LOCAL_CPPFLAGS := $(ue_common_cppflags)
+LOCAL_LDFLAGS := $(ue_common_ldflags)
+LOCAL_C_INCLUDES := \
+    $(ue_common_c_includes) \
+    $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := update_engine_client-dbus-proxies
+LOCAL_SHARED_LIBRARIES := $(ue_common_shared_libraries)
+LOCAL_SRC_FILES := \
+    update_engine_client.cc
+include $(BUILD_EXECUTABLE)
+
+endif  # BRILLO_USE_DBUS == 1
+
+# libpayload_generator (type: static_library)
+# ========================================================
+# server-side code. This is used for delta_generator and unittests but not
+# for any client code.
+ue_libpayload_generator_exported_static_libraries := \
+    libpayload_consumer \
+    update_metadata-protos \
+    $(ue_libpayload_consumer_exported_static_libraries) \
+    $(ue_update_metadata_protos_exported_static_libraries)
+ue_libpayload_generator_exported_shared_libraries := \
+    libext2fs \
+    $(ue_libpayload_consumer_exported_shared_libraries) \
+    $(ue_update_metadata_protos_exported_shared_libraries)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libpayload_generator
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_CLANG := true
+LOCAL_CFLAGS := $(ue_common_cflags)
+LOCAL_CPPFLAGS := $(ue_common_cppflags)
+LOCAL_LDFLAGS := $(ue_common_ldflags)
+LOCAL_C_INCLUDES := $(ue_common_c_includes)
+LOCAL_STATIC_LIBRARIES := \
+    libpayload_consumer \
+    update_metadata-protos \
+    $(ue_libpayload_consumer_exported_static_libraries) \
+    $(ue_update_metadata_protos_exported_static_libraries)
+LOCAL_SHARED_LIBRARIES := \
+    $(ue_common_shared_libraries) \
+    $(ue_libpayload_generator_exported_shared_libraries) \
+    $(ue_libpayload_consumer_exported_shared_libraries) \
+    $(ue_update_metadata_protos_exported_shared_libraries)
+LOCAL_SRC_FILES := \
+    payload_generator/ab_generator.cc \
+    payload_generator/annotated_operation.cc \
+    payload_generator/blob_file_writer.cc \
+    payload_generator/block_mapping.cc \
+    payload_generator/bzip.cc \
+    payload_generator/cycle_breaker.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/payload_file.cc \
+    payload_generator/payload_generation_config.cc \
+    payload_generator/payload_signer.cc \
+    payload_generator/raw_filesystem.cc \
+    payload_generator/tarjan.cc \
+    payload_generator/topological_sort.cc
+include $(BUILD_STATIC_LIBRARY)
+
+# delta_generator (type: executable)
+# ========================================================
+# server-side delta generator.
+include $(CLEAR_VARS)
+LOCAL_MODULE := delta_generator
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_CLANG := true
+LOCAL_CFLAGS := $(ue_common_cflags)
+LOCAL_CPPFLAGS := $(ue_common_cppflags)
+LOCAL_LDFLAGS := $(ue_common_ldflags)
+LOCAL_C_INCLUDES := $(ue_common_c_includes)
+LOCAL_STATIC_LIBRARIES := \
+    libpayload_consumer \
+    libpayload_generator \
+    $(ue_libpayload_consumer_exported_static_libraries) \
+    $(ue_libpayload_generator_exported_static_libraries)
+LOCAL_SHARED_LIBRARIES := \
+    $(ue_common_shared_libraries) \
+    $(ue_libpayload_consumer_exported_shared_libraries) \
+    $(ue_libpayload_generator_exported_shared_libraries)
+LOCAL_SRC_FILES := \
+    payload_generator/generate_delta_main.cc
+include $(BUILD_EXECUTABLE)
+
+ifeq ($(BRILLO_USE_DBUS),1)
+
+# libupdate_engine_client
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libupdate_engine_client
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_CFLAGS := \
+    -Wall \
+    -Werror \
+    -Wno-unused-parameter
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/client_library/include \
+    external/cros/system_api/dbus \
+    system \
+    external/gtest/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/client_library/include
+LOCAL_SHARED_LIBRARIES := \
+    libchrome \
+    libchrome-dbus \
+    libbrillo \
+    libbrillo-dbus
+LOCAL_STATIC_LIBRARIES := \
+    update_engine_client-dbus-proxies
+LOCAL_SRC_FILES := \
+    client_library/client.cc \
+    client_library/client_impl.cc \
+    update_status_utils.cc
+include $(BUILD_SHARED_LIBRARY)
+
+endif  # BRILLO_USE_DBUS == 1
+
+# Update payload signing public key.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := brillo-update-payload-key
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/update_engine
+LOCAL_MODULE_STEM := update-payload-key.pub.pem
+LOCAL_SRC_FILES := update_payload_key/brillo-update-payload-key.pub.pem
+LOCAL_BUILT_MODULE_STEM := update_payload_key/brillo-update-payload-key.pub.pem
+include $(BUILD_PREBUILT)
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..fafbecc
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,4 @@
+set noparent
+deymo@chromium.org
+garnold@chromium.org
+zeuthen@chromium.org
diff --git a/PRESUBMIT.cfg b/PRESUBMIT.cfg
new file mode 100644
index 0000000..087dfa3
--- /dev/null
+++ b/PRESUBMIT.cfg
@@ -0,0 +1,3 @@
+[Hook Overrides]
+cros_license_check: false
+aosp_license_check: true
diff --git a/UpdateEngine.conf b/UpdateEngine.conf
new file mode 100644
index 0000000..8a91607
--- /dev/null
+++ b/UpdateEngine.conf
@@ -0,0 +1,64 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+  "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+  <policy user="root">
+    <allow own="org.chromium.UpdateEngine" />
+    <allow send_destination="org.chromium.UpdateEngine" />
+  </policy>
+  <policy user="chronos">
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="AttemptUpdate"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="AttemptUpdateWithFlags"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="AttemptRollback"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="CanRollback"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="GetRollbackPartition"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="ResetStatus"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="GetStatus"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="RebootIfNeeded"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="SetChannel"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="GetChannel"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="SetP2PUpdatePermission"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="GetP2PUpdatePermission"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="SetUpdateOverCellularPermission"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="GetUpdateOverCellularPermission"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="GetDurationSinceUpdate"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="GetPrevVersion"/>
+    <allow send_interface="org.chromium.UpdateEngineLibcrosProxyResolvedInterface" />
+  </policy>
+  <policy user="power">
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="GetStatus"/>
+  </policy>
+</busconfig>
diff --git a/WATCHLISTS b/WATCHLISTS
new file mode 100644
index 0000000..bcce0de
--- /dev/null
+++ b/WATCHLISTS
@@ -0,0 +1,14 @@
+# See http://dev.chromium.org/developers/contributing-code/watchlists for
+# a description of this file's format.
+# Please keep these keys in alphabetical order.
+
+{
+  'WATCHLIST_DEFINITIONS': {
+    'all': {
+      'filepath': '.',
+    },
+  },
+  'WATCHLISTS': {
+    'all': ['adlr@chromium.org', 'petkov@chromium.org']
+  },
+}
diff --git a/binder_bindings/android/os/IUpdateEngine.aidl b/binder_bindings/android/os/IUpdateEngine.aidl
new file mode 100644
index 0000000..ebc3ffb
--- /dev/null
+++ b/binder_bindings/android/os/IUpdateEngine.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.os.IUpdateEnginePayloadApplicationCallback;
+
+interface IUpdateEngine {
+  int applyPayload(String url,
+                   in String[] headerKeyValuePairs,
+                   IUpdateEnginePayloadApplicationCallback callback);
+  void suspend();
+  void resume();
+  void cancel();
+}
diff --git a/binder_bindings/android/os/IUpdateEnginePayloadApplicationCallback.aidl b/binder_bindings/android/os/IUpdateEnginePayloadApplicationCallback.aidl
new file mode 100644
index 0000000..871ef1d
--- /dev/null
+++ b/binder_bindings/android/os/IUpdateEnginePayloadApplicationCallback.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+oneway interface IUpdateEnginePayloadApplicationCallback {
+  void onStatusUpdate(int status_code, float percentage);
+  void onPayloadApplicationComplete(int error_code);
+}
diff --git a/binder_main.cc b/binder_main.cc
new file mode 100644
index 0000000..7d5c975
--- /dev/null
+++ b/binder_main.cc
@@ -0,0 +1,91 @@
+//
+// 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 <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/Looper.h>
+#include <utils/StrongPointer.h>
+
+#include "update_engine/binder_service.h"
+
+// Log to logcat as update_engine.
+#undef LOG_TAG
+#define LOG_TAG "update_engine"
+
+namespace android {
+namespace {
+
+class BinderCallback : public LooperCallback {
+ public:
+  BinderCallback() {}
+  ~BinderCallback() override {}
+
+  int handleEvent(int /* fd */, int /* events */, void* /* data */) override {
+    IPCThreadState::self()->handlePolledCommands();
+    return 1;  // Continue receiving callbacks.
+  }
+};
+
+bool run(const sp<IBinder>& service) {
+  sp<Looper> looper(Looper::prepare(0 /* opts */));
+
+  ALOGD("Connecting to binder driver");
+  int binder_fd = -1;
+  ProcessState::self()->setThreadPoolMaxThreadCount(0);
+  IPCThreadState::self()->disableBackgroundScheduling(true);
+  IPCThreadState::self()->setupPolling(&binder_fd);
+  if (binder_fd < 0) {
+    return false;
+  }
+
+  sp<BinderCallback> cb(new BinderCallback);
+  if (looper->addFd(binder_fd, Looper::POLL_CALLBACK, Looper::EVENT_INPUT, cb,
+                    nullptr) != 1) {
+    ALOGE("Failed to add binder FD to Looper");
+    return false;
+  }
+
+  ALOGD("Registering update_engine with the service manager");
+  status_t status = defaultServiceManager()->addService(
+      service->getInterfaceDescriptor(), service);
+  if (status != android::OK) {
+    ALOGE("Failed to register update_engine with the service manager.");
+    return false;
+  }
+
+  ALOGD("Entering update_engine mainloop");
+  while (true) {
+    const int result = looper->pollAll(-1 /* timeoutMillis */);
+    ALOGD("Looper returned %d", result);
+  }
+  // We should never get here.
+  return false;
+}
+
+}  // namespace
+}  // namespace android
+
+int main(int argc, char** argv) {
+  android::sp<android::IBinder> service(
+      new chromeos_update_engine::BinderService);
+  if (!android::run(service)) {
+    return 1;
+  }
+  return 0;
+}
diff --git a/binder_service.cc b/binder_service.cc
new file mode 100644
index 0000000..8029505
--- /dev/null
+++ b/binder_service.cc
@@ -0,0 +1,49 @@
+//
+// 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.h"
+
+using android::OK;
+using android::String16;
+using android::os::IUpdateEnginePayloadApplicationCallback;
+using android::sp;
+using android::binder::Status;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+Status BinderService::applyPayload(
+    const String16& url,
+    const vector<String16>& header_kv_pairs,
+    const sp<IUpdateEnginePayloadApplicationCallback>& callback,
+    int32_t* return_value) {
+  *return_value = 0;
+  return Status::ok();
+}
+
+Status BinderService::suspend() {
+  return Status::ok();
+}
+
+Status BinderService::resume() {
+  return Status::ok();
+}
+
+Status BinderService::cancel() {
+  return Status::ok();
+}
+
+}  // namespace chromeos_update_engine
diff --git a/binder_service.h b/binder_service.h
new file mode 100644
index 0000000..bbbe5b6
--- /dev/null
+++ b/binder_service.h
@@ -0,0 +1,52 @@
+//
+// 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_BINDER_SERVICE_H_
+#define UPDATE_ENGINE_BINDER_SERVICE_H_
+
+#include <vector>
+
+#include <utils/Errors.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+
+#include "android/os/BnUpdateEngine.h"
+#include "android/os/IUpdateEnginePayloadApplicationCallback.h"
+
+namespace chromeos_update_engine {
+
+class BinderService : public android::os::BnUpdateEngine {
+ public:
+  BinderService() = default;
+  virtual ~BinderService() = default;
+
+  android::binder::Status applyPayload(
+      const android::String16& url,
+      const std::vector<android::String16>& header_kv_pairs,
+      const android::sp<android::os::IUpdateEnginePayloadApplicationCallback>&
+          callback,
+      int32_t* return_value) override;
+
+  android::binder::Status suspend() override;
+
+  android::binder::Status resume() override;
+
+  android::binder::Status cancel() override;
+};  // class BinderService
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_BINDER_SERVICE_H_
diff --git a/boot_control_android.cc b/boot_control_android.cc
new file mode 100644
index 0000000..275d2aa
--- /dev/null
+++ b/boot_control_android.cc
@@ -0,0 +1,200 @@
+//
+// 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/boot_control_android.h"
+
+#include <base/bind.h>
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <brillo/make_unique_ptr.h>
+#include <brillo/message_loops/message_loop.h>
+#include <cutils/properties.h>
+#include <fs_mgr.h>
+
+#include "update_engine/common/utils.h"
+
+using std::string;
+
+namespace {
+
+// Open the appropriate fstab file and fallback to /fstab.device if
+// that's what's being used.
+static struct fstab* OpenFSTab() {
+  char propbuf[PROPERTY_VALUE_MAX];
+  struct fstab* fstab;
+
+  property_get("ro.hardware", propbuf, "");
+  string fstab_name = string("/fstab.") + propbuf;
+  fstab = fs_mgr_read_fstab(fstab_name.c_str());
+  if (fstab != nullptr)
+    return fstab;
+
+  fstab = fs_mgr_read_fstab("/fstab.device");
+  return fstab;
+}
+
+}  // namespace
+
+
+namespace chromeos_update_engine {
+
+namespace boot_control {
+
+// Factory defined in boot_control.h.
+std::unique_ptr<BootControlInterface> CreateBootControl() {
+  std::unique_ptr<BootControlAndroid> boot_control(new BootControlAndroid());
+  if (!boot_control->Init()) {
+    return nullptr;
+  }
+  return brillo::make_unique_ptr(boot_control.release());
+}
+
+}  // namespace boot_control
+
+bool BootControlAndroid::Init() {
+  const hw_module_t* hw_module;
+  int ret;
+
+  ret = hw_get_module(BOOT_CONTROL_HARDWARE_MODULE_ID, &hw_module);
+  if (ret != 0) {
+    LOG(ERROR) << "Error loading boot_control HAL implementation.";
+    return false;
+  }
+
+  module_ = reinterpret_cast<boot_control_module_t*>(const_cast<hw_module_t*>(hw_module));
+  module_->init(module_);
+
+  LOG(INFO) << "Loaded boot_control HAL "
+            << "'" << hw_module->name << "' "
+            << "version " << (hw_module->module_api_version>>8) << "."
+            << (hw_module->module_api_version&0xff) << " "
+            << "authored by '" << hw_module->author << "'.";
+  return true;
+}
+
+unsigned int BootControlAndroid::GetNumSlots() const {
+  return module_->getNumberSlots(module_);
+}
+
+BootControlInterface::Slot BootControlAndroid::GetCurrentSlot() const {
+  return module_->getCurrentSlot(module_);
+}
+
+bool BootControlAndroid::GetPartitionDevice(const string& partition_name,
+                                            Slot slot,
+                                            string* device) const {
+  struct fstab* fstab;
+  struct fstab_rec* record;
+
+  // We can't use fs_mgr to look up |partition_name| because fstab
+  // doesn't list every slot partition (it uses the slotselect option
+  // to mask the suffix).
+  //
+  // We can however assume that there's an entry for the /misc mount
+  // point and use that to get the device file for the misc
+  // partition. This helps us locate the disk that |partition_name|
+  // resides on. From there we'll assume that a by-name scheme is used
+  // so we can just replace the trailing "misc" by the given
+  // |partition_name| and suffix corresponding to |slot|, e.g.
+  //
+  //   /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
+  //   /dev/block/platform/soc.0/7824900.sdhci/by-name/boot_a
+  //
+  // If needed, it's possible to relax the by-name assumption in the
+  // future by trawling /sys/block looking for the appropriate sibling
+  // of misc and then finding an entry in /dev matching the sysfs
+  // entry.
+
+  fstab = OpenFSTab();
+  if (fstab == nullptr) {
+    LOG(ERROR) << "Error opening fstab file.";
+    return false;
+  }
+  record = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
+  if (record == nullptr) {
+    LOG(ERROR) << "Error finding /misc entry in fstab file.";
+    fs_mgr_free_fstab(fstab);
+    return false;
+  }
+
+  base::FilePath misc_device = base::FilePath(record->blk_device);
+  fs_mgr_free_fstab(fstab);
+
+  if (misc_device.BaseName() != base::FilePath("misc")) {
+    LOG(ERROR) << "Device file " << misc_device.value() << " for /misc "
+               << "is not in the expected format.";
+    return false;
+  }
+
+  const char* suffix = module_->getSuffix(module_, slot);
+  if (suffix == nullptr) {
+    LOG(ERROR) << "boot_control impl returned no suffix for slot "
+               << SlotName(slot);
+    return false;
+  }
+
+  base::FilePath path = misc_device.DirName().Append(partition_name + suffix);
+  if (!base::PathExists(path)) {
+    LOG(ERROR) << "Device file " << path.value() << " does not exist.";
+    return false;
+  }
+
+  *device = path.value();
+  return true;
+}
+
+bool BootControlAndroid::IsSlotBootable(Slot slot) const {
+  int ret = module_->isSlotBootable(module_, slot);
+  if (ret < 0) {
+    LOG(ERROR) << "Unable to determine if slot " << SlotName(slot)
+               << " is bootable: " << strerror(-ret);
+    return false;
+  }
+  return ret == 1;
+}
+
+bool BootControlAndroid::MarkSlotUnbootable(Slot slot) {
+  int ret = module_->setSlotAsUnbootable(module_, slot);
+  if (ret < 0) {
+    LOG(ERROR) << "Unable to mark slot " << SlotName(slot)
+               << " as bootable: " << strerror(-ret);
+    return false;
+  }
+  return ret == 0;
+}
+
+bool BootControlAndroid::SetActiveBootSlot(Slot slot) {
+  int ret = module_->setActiveBootSlot(module_, slot);
+  if (ret < 0) {
+    LOG(ERROR) << "Unable to set the active slot to slot " << SlotName(slot)
+               << ": " << strerror(-ret);
+  }
+  return ret == 0;
+}
+
+bool BootControlAndroid::MarkBootSuccessfulAsync(
+    base::Callback<void(bool)> callback) {
+  int ret = module_->markBootSuccessful(module_);
+  if (ret < 0) {
+    LOG(ERROR) << "Unable to mark boot successful: " << strerror(-ret);
+  }
+  return brillo::MessageLoop::current()->PostTask(
+             FROM_HERE, base::Bind(callback, ret == 0)) !=
+         brillo::MessageLoop::kTaskIdNull;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/boot_control_android.h b/boot_control_android.h
new file mode 100644
index 0000000..a5a6255
--- /dev/null
+++ b/boot_control_android.h
@@ -0,0 +1,61 @@
+//
+// 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_BOOT_CONTROL_ANDROID_H_
+#define UPDATE_ENGINE_BOOT_CONTROL_ANDROID_H_
+
+#include <string>
+
+#include <hardware/boot_control.h>
+#include <hardware/hardware.h>
+
+#include "update_engine/common/boot_control.h"
+
+namespace chromeos_update_engine {
+
+// The Android implementation of the BootControlInterface. This implementation
+// uses the libhardware's boot_control HAL to access the bootloader.
+class BootControlAndroid : public BootControlInterface {
+ public:
+  BootControlAndroid() = default;
+  ~BootControlAndroid() = default;
+
+  // Load boot_control HAL implementation using libhardware and
+  // initializes it. Returns false if an error occurred.
+  bool Init();
+
+  // BootControlInterface overrides.
+  unsigned int GetNumSlots() const override;
+  BootControlInterface::Slot GetCurrentSlot() const override;
+  bool GetPartitionDevice(const std::string& partition_name,
+                          BootControlInterface::Slot slot,
+                          std::string* device) const override;
+  bool IsSlotBootable(BootControlInterface::Slot slot) const override;
+  bool MarkSlotUnbootable(BootControlInterface::Slot slot) override;
+  bool SetActiveBootSlot(BootControlInterface::Slot slot) override;
+  bool MarkBootSuccessfulAsync(base::Callback<void(bool)> callback) override;
+
+ private:
+  // NOTE: There is no way to release/unload HAL implementations so
+  // this is essentially leaked on object destruction.
+  boot_control_module_t* module_;
+
+  DISALLOW_COPY_AND_ASSIGN(BootControlAndroid);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_BOOT_CONTROL_ANDROID_H_
diff --git a/boot_control_chromeos.cc b/boot_control_chromeos.cc
new file mode 100644
index 0000000..fd248ab
--- /dev/null
+++ b/boot_control_chromeos.cc
@@ -0,0 +1,304 @@
+//
+// 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/boot_control_chromeos.h"
+
+#include <string>
+
+#include <base/bind.h>
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+#include <brillo/make_unique_ptr.h>
+#include <rootdev/rootdev.h>
+
+extern "C" {
+#include <vboot/vboot_host.h>
+}
+
+#include "update_engine/common/boot_control.h"
+#include "update_engine/common/subprocess.h"
+#include "update_engine/common/utils.h"
+
+using std::string;
+
+namespace {
+
+const char* kChromeOSPartitionNameKernel = "kernel";
+const char* kChromeOSPartitionNameRoot = "root";
+const char* kAndroidPartitionNameKernel = "boot";
+const char* kAndroidPartitionNameRoot = "system";
+
+// Returns the currently booted rootfs partition. "/dev/sda3", for example.
+string GetBootDevice() {
+  char boot_path[PATH_MAX];
+  // Resolve the boot device path fully, including dereferencing through
+  // dm-verity.
+  int ret = rootdev(boot_path, sizeof(boot_path), true, false);
+  if (ret < 0) {
+    LOG(ERROR) << "rootdev failed to find the root device";
+    return "";
+  }
+  LOG_IF(WARNING, ret > 0) << "rootdev found a device name with no device node";
+
+  // This local variable is used to construct the return string and is not
+  // passed around after use.
+  return boot_path;
+}
+
+// ExecCallback called when the execution of setgoodkernel finishes. Notifies
+// the caller of MarkBootSuccessfullAsync() by calling |callback| with the
+// result.
+void OnMarkBootSuccessfulDone(base::Callback<void(bool)> callback,
+                              int return_code,
+                              const string& output) {
+  callback.Run(return_code == 0);
+}
+
+}  // namespace
+
+namespace chromeos_update_engine {
+
+namespace boot_control {
+
+// Factory defined in boot_control.h.
+std::unique_ptr<BootControlInterface> CreateBootControl() {
+  std::unique_ptr<BootControlChromeOS> boot_control_chromeos(
+      new BootControlChromeOS());
+  if (!boot_control_chromeos->Init()) {
+    LOG(ERROR) << "Ignoring BootControlChromeOS failure. We won't run updates.";
+  }
+  return brillo::make_unique_ptr(boot_control_chromeos.release());
+}
+
+}  // namespace boot_control
+
+bool BootControlChromeOS::Init() {
+  string boot_device = GetBootDevice();
+  if (boot_device.empty())
+    return false;
+
+  int partition_num;
+  if (!utils::SplitPartitionName(boot_device, &boot_disk_name_, &partition_num))
+    return false;
+
+  // All installed Chrome OS devices have two slots. We don't update removable
+  // devices, so we will pretend we have only one slot in that case.
+  if (IsRemovableDevice(boot_disk_name_)) {
+    LOG(INFO)
+        << "Booted from a removable device, pretending we have only one slot.";
+    num_slots_ = 1;
+  } else {
+    // TODO(deymo): Look at the actual number of slots reported in the GPT.
+    num_slots_ = 2;
+  }
+
+  // Search through the slots to see which slot has the partition_num we booted
+  // from. This should map to one of the existing slots, otherwise something is
+  // very wrong.
+  current_slot_ = 0;
+  while (current_slot_ < num_slots_ &&
+         partition_num !=
+             GetPartitionNumber(kChromeOSPartitionNameRoot, current_slot_)) {
+    current_slot_++;
+  }
+  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.";
+    num_slots_ = 1;
+    current_slot_ = BootControlInterface::kInvalidSlot;
+    return false;
+  }
+
+  LOG(INFO) << "Booted from slot " << current_slot_ << " (slot "
+            << SlotName(current_slot_) << ") of " << num_slots_
+            << " slots present on disk " << boot_disk_name_;
+  return true;
+}
+
+unsigned int BootControlChromeOS::GetNumSlots() const {
+  return num_slots_;
+}
+
+BootControlInterface::Slot BootControlChromeOS::GetCurrentSlot() const {
+  return current_slot_;
+}
+
+bool BootControlChromeOS::GetPartitionDevice(const string& partition_name,
+                                             unsigned int slot,
+                                             string* device) const {
+  int partition_num = GetPartitionNumber(partition_name, slot);
+  if (partition_num < 0)
+    return false;
+
+  string part_device = utils::MakePartitionName(boot_disk_name_, partition_num);
+  if (part_device.empty())
+    return false;
+
+  *device = part_device;
+  return true;
+}
+
+bool BootControlChromeOS::IsSlotBootable(Slot slot) const {
+  int partition_num = GetPartitionNumber(kChromeOSPartitionNameKernel, slot);
+  if (partition_num < 0)
+    return false;
+
+  CgptAddParams params;
+  memset(&params, '\0', sizeof(params));
+  params.drive_name = const_cast<char*>(boot_disk_name_.c_str());
+  params.partition = partition_num;
+
+  int retval = CgptGetPartitionDetails(&params);
+  if (retval != CGPT_OK)
+    return false;
+
+  return params.successful || params.tries > 0;
+}
+
+bool BootControlChromeOS::MarkSlotUnbootable(Slot slot) {
+  LOG(INFO) << "Marking slot " << SlotName(slot) << " unbootable";
+
+  if (slot == current_slot_) {
+    LOG(ERROR) << "Refusing to mark current slot as unbootable.";
+    return false;
+  }
+
+  int partition_num = GetPartitionNumber(kChromeOSPartitionNameKernel, slot);
+  if (partition_num < 0)
+    return false;
+
+  CgptAddParams params;
+  memset(&params, 0, sizeof(params));
+
+  params.drive_name = const_cast<char*>(boot_disk_name_.c_str());
+  params.partition = partition_num;
+
+  params.successful = false;
+  params.set_successful = true;
+
+  params.tries = 0;
+  params.set_tries = true;
+
+  int retval = CgptSetAttributes(&params);
+  if (retval != CGPT_OK) {
+    LOG(ERROR) << "Marking kernel unbootable failed.";
+    return false;
+  }
+
+  return true;
+}
+
+bool BootControlChromeOS::SetActiveBootSlot(Slot slot) {
+  LOG(INFO) << "Marking slot " << SlotName(slot) << " active.";
+
+  int partition_num = GetPartitionNumber(kChromeOSPartitionNameKernel, slot);
+  if (partition_num < 0)
+    return false;
+
+  CgptPrioritizeParams prio_params;
+  memset(&prio_params, 0, sizeof(prio_params));
+
+  prio_params.drive_name = const_cast<char*>(boot_disk_name_.c_str());
+  prio_params.set_partition = partition_num;
+
+  prio_params.max_priority = 0;
+
+  int retval = CgptPrioritize(&prio_params);
+  if (retval != CGPT_OK) {
+    LOG(ERROR) << "Unable to set highest priority for slot " << SlotName(slot)
+               << " (partition " << partition_num << ").";
+    return false;
+  }
+
+  CgptAddParams add_params;
+  memset(&add_params, 0, sizeof(add_params));
+
+  add_params.drive_name = const_cast<char*>(boot_disk_name_.c_str());
+  add_params.partition = partition_num;
+
+  add_params.tries = 6;
+  add_params.set_tries = true;
+
+  retval = CgptSetAttributes(&add_params);
+  if (retval != CGPT_OK) {
+    LOG(ERROR) << "Unable to set NumTriesLeft to " << add_params.tries
+               << " for slot " << SlotName(slot) << " (partition "
+               << partition_num << ").";
+    return false;
+  }
+
+  return true;
+}
+
+bool BootControlChromeOS::MarkBootSuccessfulAsync(
+    base::Callback<void(bool)> callback) {
+  return Subprocess::Get().Exec(
+             {"/usr/sbin/chromeos-setgoodkernel"},
+             base::Bind(&OnMarkBootSuccessfulDone, callback)) != 0;
+}
+
+// static
+string BootControlChromeOS::SysfsBlockDevice(const string& device) {
+  base::FilePath device_path(device);
+  if (device_path.DirName().value() != "/dev") {
+    return "";
+  }
+  return base::FilePath("/sys/block").Append(device_path.BaseName()).value();
+}
+
+// static
+bool BootControlChromeOS::IsRemovableDevice(const string& device) {
+  string sysfs_block = SysfsBlockDevice(device);
+  string removable;
+  if (sysfs_block.empty() ||
+      !base::ReadFileToString(base::FilePath(sysfs_block).Append("removable"),
+                              &removable)) {
+    return false;
+  }
+  base::TrimWhitespaceASCII(removable, base::TRIM_ALL, &removable);
+  return removable == "1";
+}
+
+int BootControlChromeOS::GetPartitionNumber(
+    const string partition_name,
+    BootControlInterface::Slot slot) const {
+  if (slot >= num_slots_) {
+    LOG(ERROR) << "Invalid slot number: " << slot << ", we only have "
+               << num_slots_ << " slot(s)";
+    return -1;
+  }
+
+  // In Chrome OS, the partition numbers are hard-coded:
+  //   KERNEL-A=2, ROOT-A=3, KERNEL-B=4, ROOT-B=4, ...
+  // To help compatibility between different we accept both lowercase and
+  // uppercase names in the ChromeOS or Brillo standard names.
+  // See http://www.chromium.org/chromium-os/chromiumos-design-docs/disk-format
+  string partition_lower = base::StringToLowerASCII(partition_name);
+  int base_part_num = 2 + 2 * slot;
+  if (partition_lower == kChromeOSPartitionNameKernel ||
+      partition_lower == kAndroidPartitionNameKernel)
+    return base_part_num + 0;
+  if (partition_lower == kChromeOSPartitionNameRoot ||
+      partition_lower == kAndroidPartitionNameRoot)
+    return base_part_num + 1;
+  LOG(ERROR) << "Unknown Chrome OS partition name \"" << partition_name << "\"";
+  return -1;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/boot_control_chromeos.h b/boot_control_chromeos.h
new file mode 100644
index 0000000..a1d57fe
--- /dev/null
+++ b/boot_control_chromeos.h
@@ -0,0 +1,85 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_BOOT_CONTROL_CHROMEOS_H_
+#define UPDATE_ENGINE_BOOT_CONTROL_CHROMEOS_H_
+
+#include <string>
+
+#include <base/callback.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "update_engine/common/boot_control_interface.h"
+
+namespace chromeos_update_engine {
+
+// The Chrome OS implementation of the BootControlInterface. This interface
+// assumes the partition names and numbers used in Chrome OS devices.
+class BootControlChromeOS : public BootControlInterface {
+ public:
+  BootControlChromeOS() = default;
+  ~BootControlChromeOS() = default;
+
+  // Initialize the BootControl instance loading the constant values. Returns
+  // whether the operation succeeded. In case of failure, normally meaning
+  // some critical failure such as we couldn't determine the slot that we
+  // booted from, the implementation will pretend that there's only one slot and
+  // therefore A/B updates are disabled.
+  bool Init();
+
+  // BootControlInterface overrides.
+  unsigned int GetNumSlots() const override;
+  BootControlInterface::Slot GetCurrentSlot() const override;
+  bool GetPartitionDevice(const std::string& partition_name,
+                          BootControlInterface::Slot slot,
+                          std::string* device) const override;
+  bool IsSlotBootable(BootControlInterface::Slot slot) const override;
+  bool MarkSlotUnbootable(BootControlInterface::Slot slot) override;
+  bool SetActiveBootSlot(BootControlInterface::Slot slot) override;
+  bool MarkBootSuccessfulAsync(base::Callback<void(bool)> callback) override;
+
+ private:
+  friend class BootControlChromeOSTest;
+  FRIEND_TEST(BootControlChromeOSTest, SysfsBlockDeviceTest);
+  FRIEND_TEST(BootControlChromeOSTest, GetPartitionNumberTest);
+
+  // Returns the sysfs block device for a root block device. For example,
+  // SysfsBlockDevice("/dev/sda") returns "/sys/block/sda". Returns an empty
+  // string if the input device is not of the "/dev/xyz" form.
+  static std::string SysfsBlockDevice(const std::string& device);
+
+  // Returns true if the root |device| (e.g., "/dev/sdb") is known to be
+  // removable, false otherwise.
+  static bool IsRemovableDevice(const std::string& device);
+
+  // Return the hard-coded partition number used in Chrome OS for the passed
+  // |partition_name| and |slot|. In case of invalid data, returns -1.
+  int GetPartitionNumber(const std::string partition_name,
+                         BootControlInterface::Slot slot) const;
+
+  // Cached values for GetNumSlots() and GetCurrentSlot().
+  BootControlInterface::Slot num_slots_{1};
+  BootControlInterface::Slot current_slot_{BootControlInterface::kInvalidSlot};
+
+  // The block device of the disk we booted from, without the partition number.
+  std::string boot_disk_name_;
+
+  DISALLOW_COPY_AND_ASSIGN(BootControlChromeOS);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_BOOT_CONTROL_CHROMEOS_H_
diff --git a/chrome_browser_proxy_resolver.cc b/chrome_browser_proxy_resolver.cc
new file mode 100644
index 0000000..4971d74
--- /dev/null
+++ b/chrome_browser_proxy_resolver.cc
@@ -0,0 +1,194 @@
+//
+// 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/chrome_browser_proxy_resolver.h"
+
+#include <deque>
+#include <map>
+#include <string>
+#include <utility>
+
+#include <base/bind.h>
+#include <base/strings/string_tokenizer.h>
+#include <base/strings/string_util.h>
+
+#include "update_engine/common/utils.h"
+
+namespace chromeos_update_engine {
+
+using base::StringTokenizer;
+using base::TimeDelta;
+using brillo::MessageLoop;
+using std::deque;
+using std::make_pair;
+using std::pair;
+using std::string;
+
+const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
+const char kLibCrosProxyResolveName[] = "ProxyResolved";
+const char kLibCrosProxyResolveSignalInterface[] =
+    "org.chromium.UpdateEngineLibcrosProxyResolvedInterface";
+
+namespace {
+
+const int kTimeout = 5;  // seconds
+
+}  // namespace
+
+ChromeBrowserProxyResolver::ChromeBrowserProxyResolver(
+    LibCrosProxy* libcros_proxy)
+    : libcros_proxy_(libcros_proxy), timeout_(kTimeout) {}
+
+bool ChromeBrowserProxyResolver::Init() {
+  libcros_proxy_->ue_proxy_resolved_interface()
+      ->RegisterProxyResolvedSignalHandler(
+          base::Bind(&ChromeBrowserProxyResolver::OnProxyResolvedSignal,
+                     base::Unretained(this)),
+          base::Bind(&ChromeBrowserProxyResolver::OnSignalConnected,
+                     base::Unretained(this)));
+  return true;
+}
+
+ChromeBrowserProxyResolver::~ChromeBrowserProxyResolver() {
+  // Kill outstanding timers.
+  for (auto& timer : timers_) {
+    MessageLoop::current()->CancelTask(timer.second);
+    timer.second = MessageLoop::kTaskIdNull;
+  }
+}
+
+bool ChromeBrowserProxyResolver::GetProxiesForUrl(const string& url,
+                                                  ProxiesResolvedFn callback,
+                                                  void* data) {
+  int timeout = timeout_;
+  brillo::ErrorPtr error;
+  if (!libcros_proxy_->service_interface_proxy()->ResolveNetworkProxy(
+          url.c_str(),
+          kLibCrosProxyResolveSignalInterface,
+          kLibCrosProxyResolveName,
+          &error)) {
+    LOG(WARNING) << "Can't resolve the proxy. Continuing with no proxy.";
+    timeout = 0;
+  }
+
+  callbacks_.insert(make_pair(url, make_pair(callback, data)));
+  MessageLoop::TaskId timer = MessageLoop::current()->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&ChromeBrowserProxyResolver::HandleTimeout,
+                 base::Unretained(this),
+                 url),
+      TimeDelta::FromSeconds(timeout));
+  timers_.insert(make_pair(url, timer));
+  return true;
+}
+
+bool ChromeBrowserProxyResolver::DeleteUrlState(
+    const string& source_url,
+    bool delete_timer,
+    pair<ProxiesResolvedFn, void*>* callback) {
+  {
+    CallbacksMap::iterator it = callbacks_.lower_bound(source_url);
+    TEST_AND_RETURN_FALSE(it != callbacks_.end());
+    TEST_AND_RETURN_FALSE(it->first == source_url);
+    if (callback)
+      *callback = it->second;
+    callbacks_.erase(it);
+  }
+  {
+    TimeoutsMap::iterator it = timers_.lower_bound(source_url);
+    TEST_AND_RETURN_FALSE(it != timers_.end());
+    TEST_AND_RETURN_FALSE(it->first == source_url);
+    if (delete_timer)
+      MessageLoop::current()->CancelTask(it->second);
+    timers_.erase(it);
+  }
+  return true;
+}
+
+void ChromeBrowserProxyResolver::OnSignalConnected(const string& interface_name,
+                                                   const string& signal_name,
+                                                   bool successful) {
+  if (!successful) {
+    LOG(ERROR) << "Couldn't connect to the signal " << interface_name << "."
+               << signal_name;
+  }
+}
+
+void ChromeBrowserProxyResolver::OnProxyResolvedSignal(
+    const string& source_url,
+    const string& proxy_info,
+    const string& error_message) {
+  pair<ProxiesResolvedFn, void*> callback;
+  TEST_AND_RETURN(DeleteUrlState(source_url, true, &callback));
+  if (!error_message.empty()) {
+    LOG(WARNING) << "ProxyResolved error: " << error_message;
+  }
+  (*callback.first)(ParseProxyString(proxy_info), callback.second);
+}
+
+void ChromeBrowserProxyResolver::HandleTimeout(string source_url) {
+  LOG(INFO) << "Timeout handler called. Seems Chrome isn't responding.";
+  pair<ProxiesResolvedFn, void*> callback;
+  TEST_AND_RETURN(DeleteUrlState(source_url, false, &callback));
+  deque<string> proxies;
+  proxies.push_back(kNoProxy);
+  (*callback.first)(proxies, callback.second);
+}
+
+deque<string> ChromeBrowserProxyResolver::ParseProxyString(
+    const string& input) {
+  deque<string> ret;
+  // Some of this code taken from
+  // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and
+  // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc
+  StringTokenizer entry_tok(input, ";");
+  while (entry_tok.GetNext()) {
+    string token = entry_tok.token();
+    base::TrimWhitespaceASCII(token, base::TRIM_ALL, &token);
+
+    // Start by finding the first space (if any).
+    string::iterator space;
+    for (space = token.begin(); space != token.end(); ++space) {
+      if (IsAsciiWhitespace(*space)) {
+        break;
+      }
+    }
+
+    string scheme = string(token.begin(), space);
+    base::StringToLowerASCII(&scheme);
+    // Chrome uses "socks" to mean socks4 and "proxy" to mean http.
+    if (scheme == "socks")
+      scheme += "4";
+    else if (scheme == "proxy")
+      scheme = "http";
+    else if (scheme != "https" &&
+             scheme != "socks4" &&
+             scheme != "socks5" &&
+             scheme != "direct")
+      continue;  // Invalid proxy scheme
+
+    string host_and_port = string(space, token.end());
+    base::TrimWhitespaceASCII(host_and_port, base::TRIM_ALL, &host_and_port);
+    if (scheme != "direct" && host_and_port.empty())
+      continue;  // Must supply host/port when non-direct proxy used.
+    ret.push_back(scheme + "://" + host_and_port);
+  }
+  if (ret.empty() || *ret.rbegin() != kNoProxy)
+    ret.push_back(kNoProxy);
+  return ret;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/chrome_browser_proxy_resolver.h b/chrome_browser_proxy_resolver.h
new file mode 100644
index 0000000..84b0c28
--- /dev/null
+++ b/chrome_browser_proxy_resolver.h
@@ -0,0 +1,98 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_CHROME_BROWSER_PROXY_RESOLVER_H_
+#define UPDATE_ENGINE_CHROME_BROWSER_PROXY_RESOLVER_H_
+
+#include <deque>
+#include <map>
+#include <string>
+#include <utility>
+
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include <brillo/message_loops/message_loop.h>
+
+#include "update_engine/libcros_proxy.h"
+#include "update_engine/proxy_resolver.h"
+
+namespace chromeos_update_engine {
+
+extern const char kLibCrosServiceName[];
+extern const char kLibCrosProxyResolveName[];
+extern const char kLibCrosProxyResolveSignalInterface[];
+
+class ChromeBrowserProxyResolver : public ProxyResolver {
+ public:
+  explicit ChromeBrowserProxyResolver(LibCrosProxy* libcros_proxy);
+  ~ChromeBrowserProxyResolver() override;
+
+  // Initialize the ProxyResolver using the provided DBus proxies.
+  bool Init();
+
+  bool GetProxiesForUrl(const std::string& url,
+                        ProxiesResolvedFn callback,
+                        void* data) override;
+
+ private:
+  FRIEND_TEST(ChromeBrowserProxyResolverTest, ParseTest);
+  FRIEND_TEST(ChromeBrowserProxyResolverTest, SuccessTest);
+  typedef std::multimap<std::string, std::pair<ProxiesResolvedFn, void*>>
+      CallbacksMap;
+  typedef std::multimap<std::string, brillo::MessageLoop::TaskId> TimeoutsMap;
+
+  // Called when the signal in UpdateEngineLibcrosProxyResolvedInterface is
+  // connected.
+  void OnSignalConnected(const std::string& interface_name,
+                         const std::string& signal_name,
+                         bool successful);
+
+  // Handle a reply from Chrome:
+  void OnProxyResolvedSignal(const std::string& source_url,
+                             const std::string& proxy_info,
+                             const std::string& error_message);
+
+  // Handle no reply:
+  void HandleTimeout(std::string source_url);
+
+  // Parses a string-encoded list of proxies and returns a deque
+  // of individual proxies. The last one will always be kNoProxy.
+  static std::deque<std::string> ParseProxyString(const std::string& input);
+
+  // Deletes internal state for the first instance of url in the state.
+  // If delete_timer is set, calls CancelTask on the timer id.
+  // Returns the callback in an out parameter. Returns true on success.
+  bool DeleteUrlState(const std::string& url,
+                      bool delete_timer,
+                      std::pair<ProxiesResolvedFn, void*>* callback);
+
+  // Shutdown the dbus proxy object.
+  void Shutdown();
+
+  // DBus proxies to request a HTTP proxy resolution. The request is done in the
+  // service_interface_proxy() interface and the response is received as a
+  // signal in the ue_proxy_resolved_interface().
+  LibCrosProxy* libcros_proxy_;
+
+  int timeout_;
+  TimeoutsMap timers_;
+  CallbacksMap callbacks_;
+  DISALLOW_COPY_AND_ASSIGN(ChromeBrowserProxyResolver);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_CHROME_BROWSER_PROXY_RESOLVER_H_
diff --git a/chrome_browser_proxy_resolver_unittest.cc b/chrome_browser_proxy_resolver_unittest.cc
new file mode 100644
index 0000000..7a4de3d
--- /dev/null
+++ b/chrome_browser_proxy_resolver_unittest.cc
@@ -0,0 +1,211 @@
+//
+// 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/chrome_browser_proxy_resolver.h"
+
+#include <deque>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <base/bind.h>
+#include <brillo/make_unique_ptr.h>
+#include <brillo/message_loops/fake_message_loop.h>
+
+#include "libcros/dbus-proxies.h"
+#include "libcros/dbus-proxy-mocks.h"
+#include "update_engine/dbus_test_utils.h"
+
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::_;
+using brillo::MessageLoop;
+using org::chromium::LibCrosServiceInterfaceProxyMock;
+using org::chromium::UpdateEngineLibcrosProxyResolvedInterfaceProxyMock;
+using std::deque;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+class ChromeBrowserProxyResolverTest : public ::testing::Test {
+ protected:
+  ChromeBrowserProxyResolverTest()
+      : service_interface_mock_(new LibCrosServiceInterfaceProxyMock()),
+        ue_proxy_resolved_interface_mock_(
+            new UpdateEngineLibcrosProxyResolvedInterfaceProxyMock()),
+        libcros_proxy_(
+            brillo::make_unique_ptr(service_interface_mock_),
+            brillo::make_unique_ptr(ue_proxy_resolved_interface_mock_)) {}
+
+  void SetUp() override {
+    loop_.SetAsCurrent();
+    // The ProxyResolved signal should be subscribed to.
+    MOCK_SIGNAL_HANDLER_EXPECT_SIGNAL_HANDLER(
+        ue_proxy_resolved_signal_,
+        *ue_proxy_resolved_interface_mock_,
+        ProxyResolved);
+
+    EXPECT_TRUE(resolver_.Init());
+    // Run the loop once to dispatch the successfully registered signal handler.
+    EXPECT_TRUE(loop_.RunOnce(false));
+  }
+
+  void TearDown() override {
+    EXPECT_FALSE(loop_.PendingTasks());
+  }
+
+  // Send the signal to the callback passed during registration of the
+  // ProxyResolved.
+  void SendReplySignal(const string& source_url,
+                       const string& proxy_info,
+                       const string& error_message);
+
+  void RunTest(bool chrome_replies, bool chrome_alive);
+
+ private:
+  brillo::FakeMessageLoop loop_{nullptr};
+
+  // Local pointers to the mocks. The instances are owned by the
+  // |libcros_proxy_|.
+  LibCrosServiceInterfaceProxyMock* service_interface_mock_;
+  UpdateEngineLibcrosProxyResolvedInterfaceProxyMock*
+      ue_proxy_resolved_interface_mock_;
+
+  // The registered signal handler for the signal
+  // UpdateEngineLibcrosProxyResolvedInterface.ProxyResolved.
+  chromeos_update_engine::dbus_test_utils::MockSignalHandler<
+      void(const string&, const string&, const string&)>
+      ue_proxy_resolved_signal_;
+
+  LibCrosProxy libcros_proxy_;
+  ChromeBrowserProxyResolver resolver_{&libcros_proxy_};
+};
+
+
+void ChromeBrowserProxyResolverTest::SendReplySignal(
+    const string& source_url,
+    const string& proxy_info,
+    const string& error_message) {
+  ASSERT_TRUE(ue_proxy_resolved_signal_.IsHandlerRegistered());
+  ue_proxy_resolved_signal_.signal_callback().Run(
+      source_url, proxy_info, error_message);
+}
+
+namespace {
+void CheckResponseResolved(const deque<string>& proxies,
+                           void* /* pirv_data */) {
+  EXPECT_EQ(2, proxies.size());
+  EXPECT_EQ("socks5://192.168.52.83:5555", proxies[0]);
+  EXPECT_EQ(kNoProxy, proxies[1]);
+  MessageLoop::current()->BreakLoop();
+}
+
+void CheckResponseNoReply(const deque<string>& proxies, void* /* pirv_data */) {
+  EXPECT_EQ(1, proxies.size());
+  EXPECT_EQ(kNoProxy, proxies[0]);
+  MessageLoop::current()->BreakLoop();
+}
+}  // namespace
+
+// chrome_replies should be set to whether or not we fake a reply from
+// chrome. If there's no reply, the resolver should time out.
+// If chrome_alive is false, assume that sending to chrome fails.
+void ChromeBrowserProxyResolverTest::RunTest(bool chrome_replies,
+                                             bool chrome_alive) {
+  char kUrl[] = "http://example.com/blah";
+  char kProxyConfig[] = "SOCKS5 192.168.52.83:5555;DIRECT";
+
+  EXPECT_CALL(*service_interface_mock_,
+              ResolveNetworkProxy(StrEq(kUrl),
+                                  StrEq(kLibCrosProxyResolveSignalInterface),
+                                  StrEq(kLibCrosProxyResolveName),
+                                  _,
+                                  _))
+      .WillOnce(Return(chrome_alive));
+
+  ProxiesResolvedFn get_proxies_response = &CheckResponseNoReply;
+  if (chrome_replies) {
+    get_proxies_response = &CheckResponseResolved;
+    MessageLoop::current()->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&ChromeBrowserProxyResolverTest::SendReplySignal,
+                   base::Unretained(this),
+                   kUrl,
+                   kProxyConfig,
+                   ""),
+        base::TimeDelta::FromSeconds(1));
+  }
+
+  EXPECT_TRUE(resolver_.GetProxiesForUrl(kUrl, get_proxies_response, nullptr));
+  MessageLoop::current()->Run();
+}
+
+
+TEST_F(ChromeBrowserProxyResolverTest, ParseTest) {
+  // Test ideas from
+  // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list_unittest.cc
+  vector<string> inputs = {
+      "PROXY foopy:10",
+      " DIRECT",  // leading space.
+      "PROXY foopy1 ; proxy foopy2;\t DIRECT",
+      "proxy foopy1 ; SOCKS foopy2",
+      "DIRECT ; proxy foopy1 ; DIRECT ; SOCKS5 foopy2;DIRECT ",
+      "DIRECT ; proxy foopy1:80; DIRECT ; DIRECT",
+      "PROXY-foopy:10",
+      "PROXY",
+      "PROXY foopy1 ; JUNK ; JUNK ; SOCKS5 foopy2 ; ;",
+      "HTTP foopy1; SOCKS5 foopy2"};
+  vector<deque<string>> outputs = {
+      {"http://foopy:10", kNoProxy},
+      {kNoProxy},
+      {"http://foopy1", "http://foopy2", kNoProxy},
+      {"http://foopy1", "socks4://foopy2", kNoProxy},
+      {kNoProxy, "http://foopy1", kNoProxy, "socks5://foopy2", kNoProxy},
+      {kNoProxy, "http://foopy1:80", kNoProxy, kNoProxy},
+      {kNoProxy},
+      {kNoProxy},
+      {"http://foopy1", "socks5://foopy2", kNoProxy},
+      {"socks5://foopy2", kNoProxy}};
+  ASSERT_EQ(inputs.size(), outputs.size());
+
+  for (size_t i = 0; i < inputs.size(); i++) {
+    deque<string> results =
+        ChromeBrowserProxyResolver::ParseProxyString(inputs[i]);
+    deque<string>& expected = outputs[i];
+    EXPECT_EQ(results.size(), expected.size()) << "i = " << i;
+    if (expected.size() != results.size())
+      continue;
+    for (size_t j = 0; j < expected.size(); j++) {
+      EXPECT_EQ(expected[j], results[j]) << "i = " << i;
+    }
+  }
+}
+
+TEST_F(ChromeBrowserProxyResolverTest, SuccessTest) {
+  RunTest(true, true);
+}
+
+TEST_F(ChromeBrowserProxyResolverTest, NoReplyTest) {
+  RunTest(false, true);
+}
+
+TEST_F(ChromeBrowserProxyResolverTest, NoChromeTest) {
+  RunTest(false, false);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/client_library/client.cc b/client_library/client.cc
new file mode 100644
index 0000000..d6e7382
--- /dev/null
+++ b/client_library/client.cc
@@ -0,0 +1,31 @@
+//
+// 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>
+
+#include "update_engine/client_library/client_impl.h"
+
+using std::unique_ptr;
+
+namespace update_engine {
+
+std::unique_ptr<UpdateEngineClient> UpdateEngineClient::CreateInstance() {
+  return unique_ptr<UpdateEngineClient>{new internal::UpdateEngineClientImpl{}};
+}
+
+}  // namespace update_engine
diff --git a/client_library/client_impl.cc b/client_library/client_impl.cc
new file mode 100644
index 0000000..84ca184
--- /dev/null
+++ b/client_library/client_impl.cc
@@ -0,0 +1,91 @@
+//
+// 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_impl.h"
+
+#include <dbus/bus.h>
+#include <update_engine/dbus-constants.h>
+
+#include "update_engine/update_status_utils.h"
+
+using chromeos_update_engine::StringToUpdateStatus;
+using dbus::Bus;
+using org::chromium::UpdateEngineInterfaceProxy;
+using std::string;
+
+namespace update_engine {
+namespace internal {
+
+UpdateEngineClientImpl::UpdateEngineClientImpl() {
+  Bus::Options options;
+  options.bus_type = Bus::SYSTEM;
+  scoped_refptr<Bus> bus{new Bus{options}};
+  proxy_.reset(new UpdateEngineInterfaceProxy{bus});
+}
+
+bool UpdateEngineClientImpl::AttemptUpdate(const string& in_app_version,
+                                           const string& in_omaha_url,
+                                           bool at_user_request) {
+  return proxy_->AttemptUpdateWithFlags(
+      in_app_version,
+      in_omaha_url,
+      (at_user_request) ? 0 : kAttemptUpdateFlagNonInteractive,
+      nullptr);
+}
+
+bool UpdateEngineClientImpl::GetStatus(int64_t* out_last_checked_time,
+                                       double* out_progress,
+                                       UpdateStatus* out_update_status,
+                                       string* out_new_version,
+                                       int64_t* out_new_size) {
+  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;
+  }
+
+  return StringToUpdateStatus(status_as_string, out_update_status);
+}
+
+bool UpdateEngineClientImpl::SetTargetChannel(const string& in_target_channel) {
+  return proxy_->SetChannel(
+      in_target_channel,
+      true,
+      nullptr);
+}
+
+bool UpdateEngineClientImpl::GetTargetChannel(string* out_channel) {
+  return proxy_->GetChannel(
+      false,  // Get the target channel.
+      out_channel,
+      nullptr);
+}
+
+bool UpdateEngineClientImpl::GetChannel(string* out_channel) {
+  return proxy_->GetChannel(
+      true,  // Get the current channel.
+      out_channel,
+      nullptr);
+}
+
+}  // namespace internal
+}  // namespace update_engine
diff --git a/client_library/client_impl.h b/client_library/client_impl.h
new file mode 100644
index 0000000..e6194d2
--- /dev/null
+++ b/client_library/client_impl.h
@@ -0,0 +1,62 @@
+//
+// 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_CLIENT_LIBRARY_CLIENT_IMPL_H_
+#define UPDATE_ENGINE_CLIENT_LIBRARY_CLIENT_IMPL_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include <base/macros.h>
+
+#include "update_engine/client_library/include/update_engine/client.h"
+#include "update_engine/dbus-proxies.h"
+
+namespace update_engine {
+namespace internal {
+
+class UpdateEngineClientImpl : public UpdateEngineClient {
+ public:
+  UpdateEngineClientImpl();
+  virtual ~UpdateEngineClientImpl() = default;
+
+  bool AttemptUpdate(const std::string& app_version,
+                     const std::string& omaha_url,
+                     bool at_user_request) 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) override;
+
+  bool SetTargetChannel(const std::string& target_channel) override;
+
+  bool GetTargetChannel(std::string* out_channel) override;
+
+  bool GetChannel(std::string* out_channel) override;
+
+ private:
+  std::unique_ptr<org::chromium::UpdateEngineInterfaceProxy> proxy_;
+
+  DISALLOW_COPY_AND_ASSIGN(UpdateEngineClientImpl);
+};  // class UpdateEngineClientImpl
+
+}  // namespace internal
+}  // namespace update_engine
+
+#endif  // UPDATE_ENGINE_CLIENT_LIBRARY_CLIENT_IMPL_H_
diff --git a/client_library/include/update_engine/client.h b/client_library/include/update_engine/client.h
new file mode 100644
index 0000000..b3cef66
--- /dev/null
+++ b/client_library/include/update_engine/client.h
@@ -0,0 +1,89 @@
+//
+// 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_CLIENT_LIBRARY_INCLUDE_UPDATE_ENGINE_CLIENT_H_
+#define UPDATE_ENGINE_CLIENT_LIBRARY_INCLUDE_UPDATE_ENGINE_CLIENT_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "update_engine/update_status.h"
+
+namespace update_engine {
+
+class UpdateEngineClient {
+ public:
+  static std::unique_ptr<UpdateEngineClient> CreateInstance();
+
+  virtual ~UpdateEngineClient() = default;
+
+  // Force the update_engine to attempt an update.
+  // |app_version|
+  //     Attempt to update to this version.  An empty string indicates that
+  //     update engine should pick the most recent image on the current channel.
+  // |omaha_url|
+  //     Force update_engine to look for updates from the given server.  Passing
+  //     empty indicates update_engine should get this parameter from its
+  //     config.  Note that update_engine will ignore this parameter in
+  //     production mode to avoid pulling untrusted updates.
+  // |at_user_request|
+  //     This update was directly requested by the user.
+  virtual bool AttemptUpdate(const std::string& app_version,
+                             const std::string& omaha_url,
+                             bool at_user_request) = 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) = 0;
+
+  // Changes the current channel of the device to the target channel.
+  virtual bool SetTargetChannel(const std::string& target_channel) = 0;
+
+  // Get the channel the device will switch to on reboot.
+  virtual bool GetTargetChannel(std::string* out_channel) = 0;
+
+  // Get the channel the device is currently on.
+  virtual bool GetChannel(std::string* out_channel) = 0;
+
+ protected:
+  // Use CreateInstance().
+  UpdateEngineClient() = default;
+
+ private:
+  UpdateEngineClient(const UpdateEngineClient&) = delete;
+  void operator=(const UpdateEngineClient&) = delete;
+};  // class UpdateEngineClient
+
+}  // namespace update_engine
+
+#endif  // UPDATE_ENGINE_CLIENT_LIBRARY_INCLUDE_UPDATE_ENGINE_CLIENT_H_
diff --git a/client_library/include/update_engine/update_status.h b/client_library/include/update_engine/update_status.h
new file mode 100644
index 0000000..3e9af5b
--- /dev/null
+++ b/client_library/include/update_engine/update_status.h
@@ -0,0 +1,37 @@
+//
+// 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_CLIENT_LIBRARY_INCLUDE_UPDATE_ENGINE_UPDATE_STATUS_H_
+#define UPDATE_ENGINE_CLIENT_LIBRARY_INCLUDE_UPDATE_ENGINE_UPDATE_STATUS_H_
+
+namespace update_engine {
+
+enum class UpdateStatus {
+  IDLE = 0,
+  CHECKING_FOR_UPDATE,
+  UPDATE_AVAILABLE,
+  DOWNLOADING,
+  VERIFYING,
+  FINALIZING,
+  UPDATED_NEED_REBOOT,
+  REPORTING_ERROR_EVENT,
+  ATTEMPTING_ROLLBACK,
+  DISABLED,
+};
+
+}  // namespace update_engine
+
+#endif  // UPDATE_ENGINE_CLIENT_LIBRARY_INCLUDE_UPDATE_ENGINE_UPDATE_STATUS_H_
diff --git a/common/action.h b/common/action.h
new file mode 100644
index 0000000..d8049ac
--- /dev/null
+++ b/common/action.h
@@ -0,0 +1,218 @@
+//
+// 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_COMMON_ACTION_H_
+#define UPDATE_ENGINE_COMMON_ACTION_H_
+
+#include <stdio.h>
+
+#include <memory>
+#include <string>
+
+#include <base/logging.h>
+#include <base/macros.h>
+
+#include "update_engine/common/action_pipe.h"
+#include "update_engine/common/action_processor.h"
+
+// The structure of these classes (Action, ActionPipe, ActionProcessor, etc.)
+// is based on the KSAction* classes from the Google Update Engine code at
+// http://code.google.com/p/update-engine/ . The author of this file sends
+// a big thanks to that team for their high quality design, implementation,
+// and documentation.
+//
+// Readers may want to consult this wiki page from the Update Engine site:
+// http://code.google.com/p/update-engine/wiki/ActionProcessor
+// Although it's referring to the Objective-C KSAction* classes, much
+// applies here as well.
+//
+// How it works:
+//
+// First off, there is only one thread and all I/O should be asynchronous.
+// A message loop blocks whenever there is no work to be done. This happens
+// where there is no CPU work to be done and no I/O ready to transfer in or
+// out. Two kinds of events can wake up the message loop: timer alarm or file
+// descriptors. If either of these happens, the message loop finds out the owner
+// of what fired and calls the appropriate code to handle it. As such, all the
+// code in the Action* classes and the code that is calls is non-blocking.
+//
+// An ActionProcessor contains a queue of Actions to perform. When
+// ActionProcessor::StartProcessing() is called, it executes the first action.
+// Each action tells the processor when it has completed, which causes the
+// Processor to execute the next action. ActionProcessor may have a delegate
+// (an object of type ActionProcessorDelegate). If it does, the delegate
+// is called to be notified of events as they happen.
+//
+// ActionPipe classes
+//
+// See action_pipe.h
+//
+// ActionTraits
+//
+// We need to use an extra class ActionTraits. ActionTraits is a simple
+// templated class that contains only two typedefs: OutputObjectType and
+// InputObjectType. Each action class also has two typedefs of the same name
+// that are of the same type. So, to get the input/output types of, e.g., the
+// DownloadAction class, we look at the type of
+// DownloadAction::InputObjectType.
+//
+// Each concrete Action class derives from Action<T>. This means that during
+// template instantiation of Action<T>, T is declared but not defined, which
+// means that T::InputObjectType (and OutputObjectType) is not defined.
+// However, the traits class is constructed in such a way that it will be
+// template instantiated first, so Action<T> *can* find the types it needs by
+// consulting ActionTraits<T>::InputObjectType (and OutputObjectType).
+// This is why the ActionTraits classes are needed.
+
+namespace chromeos_update_engine {
+
+// It is handy to have a non-templated base class of all Actions.
+class AbstractAction {
+ public:
+  AbstractAction() : processor_(nullptr) {}
+  virtual ~AbstractAction() = default;
+
+  // Begin performing the action. Since this code is asynchronous, when this
+  // method returns, it means only that the action has started, not necessarily
+  // completed. However, it's acceptable for this method to perform the
+  // action synchronously; Action authors should understand the implications
+  // of synchronously performing, though, because this is a single-threaded
+  // app, the entire process will be blocked while the action performs.
+  //
+  // When the action is complete, it must call
+  // ActionProcessor::ActionComplete(this); to notify the processor that it's
+  // done.
+  virtual void PerformAction() = 0;
+
+  // Called on ActionProcess::ActionComplete() by ActionProcessor.
+  virtual void ActionCompleted(ErrorCode code) {}
+
+  // Called by the ActionProcessor to tell this Action which processor
+  // it belongs to.
+  void SetProcessor(ActionProcessor* processor) {
+    if (processor)
+      CHECK(!processor_);
+    else
+      CHECK(processor_);
+    processor_ = processor;
+  }
+
+  // Returns true iff the action is the current action of its ActionProcessor.
+  bool IsRunning() const {
+    if (!processor_)
+      return false;
+    return processor_->current_action() == this;
+  }
+
+  // Called on asynchronous actions if canceled. Actions may implement if
+  // there's any cleanup to do. There is no need to call
+  // ActionProcessor::ActionComplete() because the processor knows this
+  // action is terminating.
+  // Only the ActionProcessor should call this.
+  virtual void TerminateProcessing() {}
+
+  // These methods are useful for debugging. TODO(adlr): consider using
+  // std::type_info for this?
+  // Type() returns a string of the Action type. I.e., for DownloadAction,
+  // Type() would return "DownloadAction".
+  virtual std::string Type() const = 0;
+
+ protected:
+  // A weak pointer to the processor that owns this Action.
+  ActionProcessor* processor_;
+};
+
+// Forward declare a couple classes we use.
+template<typename T>
+class ActionPipe;
+template<typename T>
+class ActionTraits;
+
+template<typename SubClass>
+class Action : public AbstractAction {
+ public:
+  ~Action() override {}
+
+  // Attaches an input pipe to this Action. This is optional; an Action
+  // doesn't need to have an input pipe. The input pipe must be of the type
+  // of object that this class expects.
+  // This is generally called by ActionPipe::Bond()
+  void set_in_pipe(
+      // this type is a fancy way of saying: a shared_ptr to an
+      // ActionPipe<InputObjectType>.
+      const std::shared_ptr<ActionPipe<
+          typename ActionTraits<SubClass>::InputObjectType>>& in_pipe) {
+    in_pipe_ = in_pipe;
+  }
+
+  // Attaches an output pipe to this Action. This is optional; an Action
+  // doesn't need to have an output pipe. The output pipe must be of the type
+  // of object that this class expects.
+  // This is generally called by ActionPipe::Bond()
+  void set_out_pipe(
+      // this type is a fancy way of saying: a shared_ptr to an
+      // ActionPipe<OutputObjectType>.
+      const std::shared_ptr<ActionPipe<
+          typename ActionTraits<SubClass>::OutputObjectType>>& out_pipe) {
+    out_pipe_ = out_pipe;
+  }
+
+  // Returns true iff there is an associated input pipe. If there's an input
+  // pipe, there's an input object, but it may have been constructed with the
+  // default ctor if the previous action didn't call SetOutputObject().
+  bool HasInputObject() const { return in_pipe_.get(); }
+
+  // returns a const reference to the object in the input pipe.
+  const typename ActionTraits<SubClass>::InputObjectType& GetInputObject()
+      const {
+    CHECK(HasInputObject());
+    return in_pipe_->contents();
+  }
+
+  // Returns true iff there's an output pipe.
+  bool HasOutputPipe() const {
+    return out_pipe_.get();
+  }
+
+  // Copies the object passed into the output pipe. It will be accessible to
+  // the next Action via that action's input pipe (which is the same as this
+  // Action's output pipe).
+  void SetOutputObject(
+      const typename ActionTraits<SubClass>::OutputObjectType& out_obj) {
+    CHECK(HasOutputPipe());
+    out_pipe_->set_contents(out_obj);
+  }
+
+  // Returns a reference to the object sitting in the output pipe.
+  const typename ActionTraits<SubClass>::OutputObjectType& GetOutputObject() {
+    CHECK(HasOutputPipe());
+    return out_pipe_->contents();
+  }
+
+ protected:
+  // We use a shared_ptr to the pipe. shared_ptr objects destroy what they
+  // point to when the last such shared_ptr object dies. We consider the
+  // Actions on either end of a pipe to "own" the pipe. When the last Action
+  // of the two dies, the ActionPipe will die, too.
+  std::shared_ptr<ActionPipe<typename ActionTraits<SubClass>::InputObjectType>>
+      in_pipe_;
+  std::shared_ptr<ActionPipe<typename ActionTraits<SubClass>::OutputObjectType>>
+      out_pipe_;
+};
+
+};  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_ACTION_H_
diff --git a/common/action_pipe.h b/common/action_pipe.h
new file mode 100644
index 0000000..362817d
--- /dev/null
+++ b/common/action_pipe.h
@@ -0,0 +1,101 @@
+//
+// 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_COMMON_ACTION_PIPE_H_
+#define UPDATE_ENGINE_COMMON_ACTION_PIPE_H_
+
+#include <stdio.h>
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include <base/logging.h>
+#include <base/macros.h>
+
+// The structure of these classes (Action, ActionPipe, ActionProcessor, etc.)
+// is based on the KSAction* classes from the Google Update Engine code at
+// http://code.google.com/p/update-engine/ . The author of this file sends
+// a big thanks to that team for their high quality design, implementation,
+// and documentation.
+
+// This class serves as a temporary holding area for an object passed out
+// from one Action and into another Action. It's templated so that it may
+// contain any type of object that an Action outputs/inputs. Actions
+// cannot be bonded (i.e., connected with a pipe) if their output/input
+// object types differ (a compiler error will result).
+//
+// An ActionPipe is generally created with the Bond() method and owned by
+// the two Action objects. a shared_ptr is used so that when the last Action
+// pointing to an ActionPipe dies, the ActionPipe dies, too.
+
+namespace chromeos_update_engine {
+
+// Used by Actions an InputObjectType or OutputObjectType to specify that
+// for that type, no object is taken/given.
+class NoneType {};
+
+template<typename T>
+class Action;
+
+template<typename ObjectType>
+class ActionPipe {
+ public:
+  virtual ~ActionPipe() {}
+
+  // This should be called by an Action on its input pipe.
+  // Returns a reference to the stored object.
+  const ObjectType& contents() const { return contents_; }
+
+  // This should be called by an Action on its output pipe.
+  // Stores a copy of the passed object in this pipe.
+  void set_contents(const ObjectType& contents) { contents_ = contents; }
+
+  // Bonds two Actions together with a new ActionPipe. The ActionPipe is
+  // jointly owned by the two Actions and will be automatically destroyed
+  // when the last Action is destroyed.
+  template<typename FromAction, typename ToAction>
+  static void Bond(FromAction* from, ToAction* to) {
+    std::shared_ptr<ActionPipe<ObjectType>> pipe(new ActionPipe<ObjectType>);
+    from->set_out_pipe(pipe);
+
+    to->set_in_pipe(pipe);  // If you get an error on this line, then
+    // it most likely means that the From object's OutputObjectType is
+    // different from the To object's InputObjectType.
+  }
+
+ private:
+  ObjectType contents_;
+
+  // The ctor is private. This is because this class should construct itself
+  // via the static Bond() method.
+  ActionPipe() {}
+  DISALLOW_COPY_AND_ASSIGN(ActionPipe);
+};
+
+// Utility function
+template<typename FromAction, typename ToAction>
+void BondActions(FromAction* from, ToAction* to) {
+  // TODO(adlr): find something like this that the compiler accepts:
+  // COMPILE_ASSERT(typeof(typename FromAction::OutputObjectType) ==
+  //                typeof(typename ToAction::InputObjectType),
+  //     FromAction_OutputObjectType_doesnt_match_ToAction_InputObjectType);
+  ActionPipe<typename FromAction::OutputObjectType>::Bond(from, to);
+}
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_ACTION_PIPE_H_
diff --git a/common/action_pipe_unittest.cc b/common/action_pipe_unittest.cc
new file mode 100644
index 0000000..9bfbc83
--- /dev/null
+++ b/common/action_pipe_unittest.cc
@@ -0,0 +1,60 @@
+//
+// 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/common/action_pipe.h"
+
+#include <gtest/gtest.h>
+#include <string>
+#include "update_engine/common/action.h"
+
+using std::string;
+
+namespace chromeos_update_engine {
+
+using chromeos_update_engine::ActionPipe;
+
+class ActionPipeTestAction;
+
+template<>
+class ActionTraits<ActionPipeTestAction> {
+ public:
+  typedef string OutputObjectType;
+  typedef string InputObjectType;
+};
+
+// This is a simple Action class for testing.
+class ActionPipeTestAction : public Action<ActionPipeTestAction> {
+ public:
+  typedef string InputObjectType;
+  typedef string OutputObjectType;
+  ActionPipe<string>* in_pipe() { return in_pipe_.get(); }
+  ActionPipe<string>* out_pipe() { return out_pipe_.get(); }
+  void PerformAction() {}
+  string Type() const { return "ActionPipeTestAction"; }
+};
+
+class ActionPipeTest : public ::testing::Test { };
+
+// This test creates two simple Actions and sends a message via an ActionPipe
+// from one to the other.
+TEST(ActionPipeTest, SimpleTest) {
+  ActionPipeTestAction a, b;
+  BondActions(&a, &b);
+  a.out_pipe()->set_contents("foo");
+  EXPECT_EQ("foo", b.in_pipe()->contents());
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/action_processor.cc b/common/action_processor.cc
new file mode 100644
index 0000000..7ccdfbd
--- /dev/null
+++ b/common/action_processor.cc
@@ -0,0 +1,100 @@
+//
+// 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/common/action_processor.h"
+
+#include <string>
+
+#include <base/logging.h>
+
+#include "update_engine/common/action.h"
+
+using std::string;
+
+namespace chromeos_update_engine {
+
+ActionProcessor::ActionProcessor()
+    : current_action_(nullptr), delegate_(nullptr) {}
+
+ActionProcessor::~ActionProcessor() {
+  if (IsRunning())
+    StopProcessing();
+  for (auto action : actions_)
+    action->SetProcessor(nullptr);
+}
+
+void ActionProcessor::EnqueueAction(AbstractAction* action) {
+  actions_.push_back(action);
+  action->SetProcessor(this);
+}
+
+void ActionProcessor::StartProcessing() {
+  CHECK(!IsRunning());
+  if (!actions_.empty()) {
+    current_action_ = actions_.front();
+    LOG(INFO) << "ActionProcessor::StartProcessing: "
+              << current_action_->Type();
+    actions_.pop_front();
+    current_action_->PerformAction();
+  }
+}
+
+void ActionProcessor::StopProcessing() {
+  CHECK(IsRunning());
+  CHECK(current_action_);
+  current_action_->TerminateProcessing();
+  CHECK(current_action_);
+  current_action_->SetProcessor(nullptr);
+  LOG(INFO) << "ActionProcessor::StopProcessing: aborted "
+            << current_action_->Type();
+  current_action_ = nullptr;
+  if (delegate_)
+    delegate_->ProcessingStopped(this);
+}
+
+void ActionProcessor::ActionComplete(AbstractAction* actionptr,
+                                     ErrorCode code) {
+  CHECK_EQ(actionptr, current_action_);
+  if (delegate_)
+    delegate_->ActionCompleted(this, actionptr, code);
+  string old_type = current_action_->Type();
+  current_action_->ActionCompleted(code);
+  current_action_->SetProcessor(nullptr);
+  current_action_ = nullptr;
+  if (actions_.empty()) {
+    LOG(INFO) << "ActionProcessor::ActionComplete: finished last action of"
+                 " type " << old_type;
+  } else if (code != ErrorCode::kSuccess) {
+    LOG(INFO) << "ActionProcessor::ActionComplete: " << old_type
+              << " action failed. Aborting processing.";
+    actions_.clear();
+  }
+  if (actions_.empty()) {
+    LOG(INFO) << "ActionProcessor::ActionComplete: finished last action of"
+                 " type " << old_type;
+    if (delegate_) {
+      delegate_->ProcessingDone(this, code);
+    }
+    return;
+  }
+  current_action_ = actions_.front();
+  actions_.pop_front();
+  LOG(INFO) << "ActionProcessor::ActionComplete: finished " << old_type
+            << ", starting " << current_action_->Type();
+  current_action_->PerformAction();
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/action_processor.h b/common/action_processor.h
new file mode 100644
index 0000000..d61e12d
--- /dev/null
+++ b/common/action_processor.h
@@ -0,0 +1,116 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_COMMON_ACTION_PROCESSOR_H_
+#define UPDATE_ENGINE_COMMON_ACTION_PROCESSOR_H_
+
+#include <deque>
+
+#include <base/macros.h>
+
+#include "update_engine/common/error_code.h"
+
+// The structure of these classes (Action, ActionPipe, ActionProcessor, etc.)
+// is based on the KSAction* classes from the Google Update Engine code at
+// http://code.google.com/p/update-engine/ . The author of this file sends
+// a big thanks to that team for their high quality design, implementation,
+// and documentation.
+
+// See action.h for an overview of this class and other Action* classes.
+
+// An ActionProcessor keeps a queue of Actions and processes them in order.
+
+namespace chromeos_update_engine {
+
+class AbstractAction;
+class ActionProcessorDelegate;
+
+class ActionProcessor {
+ public:
+  ActionProcessor();
+
+  virtual ~ActionProcessor();
+
+  // Starts processing the first Action in the queue. If there's a delegate,
+  // when all processing is complete, ProcessingDone() will be called on the
+  // delegate.
+  virtual void StartProcessing();
+
+  // Aborts processing. If an Action is running, it will have
+  // TerminateProcessing() called on it. The Action that was running
+  // will be lost and must be re-enqueued if this Processor is to use it.
+  void StopProcessing();
+
+  // Returns true iff an Action is currently processing.
+  bool IsRunning() const { return nullptr != current_action_; }
+
+  // Adds another Action to the end of the queue.
+  virtual void EnqueueAction(AbstractAction* action);
+
+  // Sets/gets the current delegate. Set to null to remove a delegate.
+  ActionProcessorDelegate* delegate() const { return delegate_; }
+  void set_delegate(ActionProcessorDelegate *delegate) {
+    delegate_ = delegate;
+  }
+
+  // Returns a pointer to the current Action that's processing.
+  AbstractAction* current_action() const {
+    return current_action_;
+  }
+
+  // Called by an action to notify processor that it's done. Caller passes self.
+  void ActionComplete(AbstractAction* actionptr, ErrorCode code);
+
+ private:
+  // Actions that have not yet begun processing, in the order in which
+  // they'll be processed.
+  std::deque<AbstractAction*> actions_;
+
+  // A pointer to the currently processing Action, if any.
+  AbstractAction* current_action_;
+
+  // A pointer to the delegate, or null if none.
+  ActionProcessorDelegate *delegate_;
+  DISALLOW_COPY_AND_ASSIGN(ActionProcessor);
+};
+
+// A delegate object can be used to be notified of events that happen
+// in an ActionProcessor. An instance of this class can be passed to an
+// ActionProcessor to register itself.
+class ActionProcessorDelegate {
+ public:
+  virtual ~ActionProcessorDelegate() = default;
+
+  // Called when all processing in an ActionProcessor has completed. A pointer
+  // to the ActionProcessor is passed. |code| is set to the exit code of the
+  // last completed action.
+  virtual void ProcessingDone(const ActionProcessor* processor,
+                              ErrorCode code) {}
+
+  // Called when processing has stopped. Does not mean that all Actions have
+  // completed. If/when all Actions complete, ProcessingDone() will be called.
+  virtual void ProcessingStopped(const ActionProcessor* processor) {}
+
+  // Called whenever an action has finished processing, either successfully
+  // or otherwise.
+  virtual void ActionCompleted(ActionProcessor* processor,
+                               AbstractAction* action,
+                               ErrorCode code) {}
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_ACTION_PROCESSOR_H_
diff --git a/common/action_processor_unittest.cc b/common/action_processor_unittest.cc
new file mode 100644
index 0000000..8285470
--- /dev/null
+++ b/common/action_processor_unittest.cc
@@ -0,0 +1,190 @@
+//
+// 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/common/action_processor.h"
+
+#include <gtest/gtest.h>
+#include <string>
+#include "update_engine/common/action.h"
+
+using std::string;
+
+namespace chromeos_update_engine {
+
+using chromeos_update_engine::ActionPipe;
+
+class ActionProcessorTestAction;
+
+template<>
+class ActionTraits<ActionProcessorTestAction> {
+ public:
+  typedef string OutputObjectType;
+  typedef string InputObjectType;
+};
+
+// This is a simple Action class for testing.
+class ActionProcessorTestAction : public Action<ActionProcessorTestAction> {
+ public:
+  typedef string InputObjectType;
+  typedef string OutputObjectType;
+  ActionPipe<string>* in_pipe() { return in_pipe_.get(); }
+  ActionPipe<string>* out_pipe() { return out_pipe_.get(); }
+  ActionProcessor* processor() { return processor_; }
+  void PerformAction() {}
+  void CompleteAction() {
+    ASSERT_TRUE(processor());
+    processor()->ActionComplete(this, ErrorCode::kSuccess);
+  }
+  string Type() const { return "ActionProcessorTestAction"; }
+};
+
+class ActionProcessorTest : public ::testing::Test { };
+
+// This test creates two simple Actions and sends a message via an ActionPipe
+// from one to the other.
+TEST(ActionProcessorTest, SimpleTest) {
+  ActionProcessorTestAction action;
+  ActionProcessor action_processor;
+  EXPECT_FALSE(action_processor.IsRunning());
+  action_processor.EnqueueAction(&action);
+  EXPECT_FALSE(action_processor.IsRunning());
+  EXPECT_FALSE(action.IsRunning());
+  action_processor.StartProcessing();
+  EXPECT_TRUE(action_processor.IsRunning());
+  EXPECT_TRUE(action.IsRunning());
+  EXPECT_EQ(action_processor.current_action(), &action);
+  action.CompleteAction();
+  EXPECT_FALSE(action_processor.IsRunning());
+  EXPECT_FALSE(action.IsRunning());
+}
+
+namespace {
+class MyActionProcessorDelegate : public ActionProcessorDelegate {
+ public:
+  explicit MyActionProcessorDelegate(const ActionProcessor* processor)
+      : processor_(processor),
+        processing_done_called_(false),
+        processing_stopped_called_(false),
+        action_completed_called_(false),
+        action_exit_code_(ErrorCode::kError) {}
+
+  virtual void ProcessingDone(const ActionProcessor* processor,
+                              ErrorCode code) {
+    EXPECT_EQ(processor_, processor);
+    EXPECT_FALSE(processing_done_called_);
+    processing_done_called_ = true;
+  }
+  virtual void ProcessingStopped(const ActionProcessor* processor) {
+    EXPECT_EQ(processor_, processor);
+    EXPECT_FALSE(processing_stopped_called_);
+    processing_stopped_called_ = true;
+  }
+  virtual void ActionCompleted(ActionProcessor* processor,
+                               AbstractAction* action,
+                               ErrorCode code) {
+    EXPECT_EQ(processor_, processor);
+    EXPECT_FALSE(action_completed_called_);
+    action_completed_called_ = true;
+    action_exit_code_ = code;
+  }
+
+  const ActionProcessor* processor_;
+  bool processing_done_called_;
+  bool processing_stopped_called_;
+  bool action_completed_called_;
+  ErrorCode action_exit_code_;
+};
+}  // namespace
+
+TEST(ActionProcessorTest, DelegateTest) {
+  ActionProcessorTestAction action;
+  ActionProcessor action_processor;
+  MyActionProcessorDelegate delegate(&action_processor);
+  action_processor.set_delegate(&delegate);
+
+  action_processor.EnqueueAction(&action);
+  action_processor.StartProcessing();
+  action.CompleteAction();
+  action_processor.set_delegate(nullptr);
+  EXPECT_TRUE(delegate.processing_done_called_);
+  EXPECT_TRUE(delegate.action_completed_called_);
+}
+
+TEST(ActionProcessorTest, StopProcessingTest) {
+  ActionProcessorTestAction action;
+  ActionProcessor action_processor;
+  MyActionProcessorDelegate delegate(&action_processor);
+  action_processor.set_delegate(&delegate);
+
+  action_processor.EnqueueAction(&action);
+  action_processor.StartProcessing();
+  action_processor.StopProcessing();
+  action_processor.set_delegate(nullptr);
+  EXPECT_TRUE(delegate.processing_stopped_called_);
+  EXPECT_FALSE(delegate.action_completed_called_);
+  EXPECT_FALSE(action_processor.IsRunning());
+  EXPECT_EQ(nullptr, action_processor.current_action());
+}
+
+TEST(ActionProcessorTest, ChainActionsTest) {
+  ActionProcessorTestAction action1, action2;
+  ActionProcessor action_processor;
+  action_processor.EnqueueAction(&action1);
+  action_processor.EnqueueAction(&action2);
+  action_processor.StartProcessing();
+  EXPECT_EQ(&action1, action_processor.current_action());
+  EXPECT_TRUE(action_processor.IsRunning());
+  action1.CompleteAction();
+  EXPECT_EQ(&action2, action_processor.current_action());
+  EXPECT_TRUE(action_processor.IsRunning());
+  action2.CompleteAction();
+  EXPECT_EQ(nullptr, action_processor.current_action());
+  EXPECT_FALSE(action_processor.IsRunning());
+}
+
+TEST(ActionProcessorTest, DtorTest) {
+  ActionProcessorTestAction action1, action2;
+  {
+    ActionProcessor action_processor;
+    action_processor.EnqueueAction(&action1);
+    action_processor.EnqueueAction(&action2);
+    action_processor.StartProcessing();
+  }
+  EXPECT_EQ(nullptr, action1.processor());
+  EXPECT_FALSE(action1.IsRunning());
+  EXPECT_EQ(nullptr, action2.processor());
+  EXPECT_FALSE(action2.IsRunning());
+}
+
+TEST(ActionProcessorTest, DefaultDelegateTest) {
+  // Just make sure it doesn't crash
+  ActionProcessorTestAction action;
+  ActionProcessor action_processor;
+  ActionProcessorDelegate delegate;
+  action_processor.set_delegate(&delegate);
+
+  action_processor.EnqueueAction(&action);
+  action_processor.StartProcessing();
+  action.CompleteAction();
+
+  action_processor.EnqueueAction(&action);
+  action_processor.StartProcessing();
+  action_processor.StopProcessing();
+
+  action_processor.set_delegate(nullptr);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/action_unittest.cc b/common/action_unittest.cc
new file mode 100644
index 0000000..dcdce17
--- /dev/null
+++ b/common/action_unittest.cc
@@ -0,0 +1,76 @@
+//
+// 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/common/action.h"
+
+#include <gtest/gtest.h>
+#include <string>
+#include "update_engine/common/action_processor.h"
+
+using std::string;
+
+namespace chromeos_update_engine {
+
+using chromeos_update_engine::ActionPipe;
+
+class ActionTestAction;
+
+template<>
+class ActionTraits<ActionTestAction> {
+ public:
+  typedef string OutputObjectType;
+  typedef string InputObjectType;
+};
+
+// This is a simple Action class for testing.
+class ActionTestAction : public Action<ActionTestAction> {
+ public:
+  typedef string InputObjectType;
+  typedef string OutputObjectType;
+  ActionPipe<string>* in_pipe() { return in_pipe_.get(); }
+  ActionPipe<string>* out_pipe() { return out_pipe_.get(); }
+  ActionProcessor* processor() { return processor_; }
+  void PerformAction() {}
+  void CompleteAction() {
+    ASSERT_TRUE(processor());
+    processor()->ActionComplete(this, ErrorCode::kSuccess);
+  }
+  string Type() const { return "ActionTestAction"; }
+};
+
+class ActionTest : public ::testing::Test { };
+
+// This test creates two simple Actions and sends a message via an ActionPipe
+// from one to the other.
+TEST(ActionTest, SimpleTest) {
+  ActionTestAction action;
+
+  EXPECT_FALSE(action.in_pipe());
+  EXPECT_FALSE(action.out_pipe());
+  EXPECT_FALSE(action.processor());
+  EXPECT_FALSE(action.IsRunning());
+
+  ActionProcessor action_processor;
+  action_processor.EnqueueAction(&action);
+  EXPECT_EQ(&action_processor, action.processor());
+
+  action_processor.StartProcessing();
+  EXPECT_TRUE(action.IsRunning());
+  action.CompleteAction();
+  EXPECT_FALSE(action.IsRunning());
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/boot_control.h b/common/boot_control.h
new file mode 100644
index 0000000..1463015
--- /dev/null
+++ b/common/boot_control.h
@@ -0,0 +1,35 @@
+//
+// 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_COMMON_BOOT_CONTROL_H_
+#define UPDATE_ENGINE_COMMON_BOOT_CONTROL_H_
+
+#include <memory>
+
+#include "update_engine/common/boot_control_interface.h"
+
+namespace chromeos_update_engine {
+namespace boot_control {
+
+// The real BootControlInterface is platform-specific. This factory function
+// creates a new BootControlInterface instance for the current platform. If
+// this fails nullptr is returned.
+std::unique_ptr<BootControlInterface> CreateBootControl();
+
+}  // namespace boot_control
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_BOOT_CONTROL_H_
diff --git a/common/boot_control_chromeos_unittest.cc b/common/boot_control_chromeos_unittest.cc
new file mode 100644
index 0000000..4c218c0
--- /dev/null
+++ b/common/boot_control_chromeos_unittest.cc
@@ -0,0 +1,70 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/common/boot_control_chromeos.h"
+
+#include <gtest/gtest.h>
+
+namespace chromeos_update_engine {
+
+class BootControlChromeOSTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    // We don't run Init() for bootctl_, we set its internal values instead.
+    bootctl_.num_slots_ = 2;
+    bootctl_.current_slot_ = 0;
+    bootctl_.boot_disk_name_ = "/dev/null";
+  }
+
+  BootControlChromeOS bootctl_;  // BootControlChromeOS under test.
+};
+
+TEST_F(BootControlChromeOSTest, SysfsBlockDeviceTest) {
+  EXPECT_EQ("/sys/block/sda", bootctl_.SysfsBlockDevice("/dev/sda"));
+  EXPECT_EQ("", bootctl_.SysfsBlockDevice("/foo/sda"));
+  EXPECT_EQ("", bootctl_.SysfsBlockDevice("/dev/foo/bar"));
+  EXPECT_EQ("", bootctl_.SysfsBlockDevice("/"));
+  EXPECT_EQ("", bootctl_.SysfsBlockDevice("./"));
+  EXPECT_EQ("", bootctl_.SysfsBlockDevice(""));
+}
+
+TEST_F(BootControlChromeOSTest, GetPartitionNumberTest) {
+  // The partition name should not be case-sensitive.
+  EXPECT_EQ(2, bootctl_.GetPartitionNumber("kernel", 0));
+  EXPECT_EQ(2, bootctl_.GetPartitionNumber("boot", 0));
+  EXPECT_EQ(2, bootctl_.GetPartitionNumber("KERNEL", 0));
+  EXPECT_EQ(2, bootctl_.GetPartitionNumber("BOOT", 0));
+
+  EXPECT_EQ(3, bootctl_.GetPartitionNumber("ROOT", 0));
+  EXPECT_EQ(3, bootctl_.GetPartitionNumber("system", 0));
+
+  EXPECT_EQ(3, bootctl_.GetPartitionNumber("ROOT", 0));
+  EXPECT_EQ(3, bootctl_.GetPartitionNumber("system", 0));
+
+  // Slot B.
+  EXPECT_EQ(4, bootctl_.GetPartitionNumber("KERNEL", 1));
+  EXPECT_EQ(5, bootctl_.GetPartitionNumber("ROOT", 1));
+
+  // Slot C doesn't exists.
+  EXPECT_EQ(-1, bootctl_.GetPartitionNumber("KERNEL", 2));
+  EXPECT_EQ(-1, bootctl_.GetPartitionNumber("ROOT", 2));
+
+  // Non A/B partitions are ignored.
+  EXPECT_EQ(-1, bootctl_.GetPartitionNumber("OEM", 0));
+  EXPECT_EQ(-1, bootctl_.GetPartitionNumber("A little panda", 0));
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/boot_control_interface.h b/common/boot_control_interface.h
new file mode 100644
index 0000000..659b388
--- /dev/null
+++ b/common/boot_control_interface.h
@@ -0,0 +1,98 @@
+//
+// 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_COMMON_BOOT_CONTROL_INTERFACE_H_
+#define UPDATE_ENGINE_COMMON_BOOT_CONTROL_INTERFACE_H_
+
+#include <climits>
+#include <string>
+
+#include <base/callback.h>
+#include <base/macros.h>
+
+namespace chromeos_update_engine {
+
+// The abstract boot control interface defines the interaction with the
+// platform's bootloader hiding vendor-specific details from the rest of
+// update_engine. This interface is used for controlling where the device should
+// boot from.
+class BootControlInterface {
+ public:
+  using Slot = unsigned int;
+
+  static const Slot kInvalidSlot = UINT_MAX;
+
+  virtual ~BootControlInterface() = default;
+
+  // Return the number of update slots in the system. A system will normally
+  // have two slots, named "A" and "B" in the documentation, but sometimes
+  // images running from other media can have only one slot, like some USB
+  // image. Systems with only one slot won't be able to update.
+  virtual unsigned int GetNumSlots() const = 0;
+
+  // Return the slot where we are running the system from. On success, the
+  // result is a number between 0 and GetNumSlots() - 1. Otherwise, log an error
+  // and return kInvalidSlot.
+  virtual Slot GetCurrentSlot() const = 0;
+
+  // Determines the block device for the given partition name and slot number.
+  // The |slot| number must be between 0 and GetNumSlots() - 1 and the
+  // |partition_name| is a platform-specific name that identifies a partition on
+  // every slot. 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;
+
+  // Returns whether the passed |slot| is marked as bootable. Returns false if
+  // the slot is invalid.
+  virtual bool IsSlotBootable(Slot slot) const = 0;
+
+  // Mark the specified slot unbootable. No other slot flags are modified.
+  // Returns true on success.
+  virtual bool MarkSlotUnbootable(Slot slot) = 0;
+
+  // Set the passed |slot| as the preferred boot slot. Returns whether it
+  // succeeded setting the active slot. If succeeded, on next boot the
+  // bootloader will attempt to load the |slot| marked as active. Note that this
+  // method doesn't change the value of GetCurrentSlot() on the current boot.
+  virtual bool SetActiveBootSlot(Slot slot) = 0;
+
+  // Mark the current slot as successfully booted asynchronously. No other slot
+  // flags are modified. Returns false if it was not able to schedule the
+  // operation, otherwise, returns true and calls the |callback| with the result
+  // of the operation.
+  virtual bool MarkBootSuccessfulAsync(base::Callback<void(bool)> callback) = 0;
+
+  // Return a human-readable slot name used for logging.
+  static std::string SlotName(Slot slot) {
+    if (slot == kInvalidSlot)
+      return "INVALID";
+    if (slot < 26)
+      return std::string(1, 'A' + slot);
+    return "TOO_BIG";
+  }
+
+ protected:
+  BootControlInterface() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BootControlInterface);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_BOOT_CONTROL_INTERFACE_H_
diff --git a/common/boot_control_stub.cc b/common/boot_control_stub.cc
new file mode 100644
index 0000000..2de0c82
--- /dev/null
+++ b/common/boot_control_stub.cc
@@ -0,0 +1,62 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/common/boot_control_stub.h"
+
+#include <base/logging.h>
+
+using std::string;
+
+namespace chromeos_update_engine {
+
+unsigned int BootControlStub::GetNumSlots() const {
+  return 0;
+}
+
+BootControlInterface::Slot BootControlStub::GetCurrentSlot() const {
+  LOG(ERROR) << __FUNCTION__ << " should never be called.";
+  return 0;
+}
+
+bool BootControlStub::GetPartitionDevice(const string& partition_name,
+                                         Slot slot,
+                                         string* device) const {
+  LOG(ERROR) << __FUNCTION__ << " should never be called.";
+  return false;
+}
+
+bool BootControlStub::IsSlotBootable(Slot slot) const {
+  LOG(ERROR) << __FUNCTION__ << " should never be called.";
+  return false;
+}
+
+bool BootControlStub::MarkSlotUnbootable(Slot slot) {
+  LOG(ERROR) << __FUNCTION__ << " should never be called.";
+  return false;
+}
+
+bool BootControlStub::SetActiveBootSlot(Slot slot) {
+  LOG(ERROR) << __FUNCTION__ << " should never be called.";
+  return false;
+}
+
+bool BootControlStub::MarkBootSuccessfulAsync(
+    base::Callback<void(bool)> callback) {
+  // This is expected to be called on update_engine startup.
+  return false;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/boot_control_stub.h b/common/boot_control_stub.h
new file mode 100644
index 0000000..7832adc
--- /dev/null
+++ b/common/boot_control_stub.h
@@ -0,0 +1,55 @@
+//
+// 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_COMMON_BOOT_CONTROL_STUB_H_
+#define UPDATE_ENGINE_COMMON_BOOT_CONTROL_STUB_H_
+
+#include <string>
+
+#include "update_engine/common/boot_control_interface.h"
+
+namespace chromeos_update_engine {
+
+// An implementation of the BootControlInterface that does nothing,
+// typically used when e.g. an underlying HAL implementation cannot be
+// loaded or doesn't exist.
+//
+// You are gauranteed that the implementation of GetNumSlots() method
+// always returns 0. This can be used to identify that this
+// implementation is in use.
+class BootControlStub : public BootControlInterface {
+ public:
+  BootControlStub() = default;
+  ~BootControlStub() = default;
+
+  // BootControlInterface overrides.
+  unsigned int GetNumSlots() const override;
+  BootControlInterface::Slot GetCurrentSlot() const override;
+  bool GetPartitionDevice(const std::string& partition_name,
+                          BootControlInterface::Slot slot,
+                          std::string* device) const override;
+  bool IsSlotBootable(BootControlInterface::Slot slot) const override;
+  bool MarkSlotUnbootable(BootControlInterface::Slot slot) override;
+  bool SetActiveBootSlot(BootControlInterface::Slot slot) override;
+  bool MarkBootSuccessfulAsync(base::Callback<void(bool)> callback) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BootControlStub);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_BOOT_CONTROL_STUB_H_
diff --git a/common/certificate_checker.cc b/common/certificate_checker.cc
new file mode 100644
index 0000000..86df950
--- /dev/null
+++ b/common/certificate_checker.cc
@@ -0,0 +1,204 @@
+//
+// 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/common/certificate_checker.h"
+
+#include <string>
+
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <curl/curl.h>
+#include <openssl/evp.h>
+#include <openssl/ssl.h>
+
+#include "update_engine/common/constants.h"
+#include "update_engine/common/prefs_interface.h"
+#include "update_engine/common/utils.h"
+
+using std::string;
+
+namespace chromeos_update_engine {
+
+bool OpenSSLWrapper::GetCertificateDigest(X509_STORE_CTX* x509_ctx,
+                                          int* out_depth,
+                                          unsigned int* out_digest_length,
+                                          uint8_t* out_digest) const {
+  TEST_AND_RETURN_FALSE(out_digest);
+  X509* certificate = X509_STORE_CTX_get_current_cert(x509_ctx);
+  TEST_AND_RETURN_FALSE(certificate);
+  int depth = X509_STORE_CTX_get_error_depth(x509_ctx);
+  if (out_depth)
+    *out_depth = depth;
+
+  unsigned int len;
+  const EVP_MD* digest_function = EVP_sha256();
+  bool success = X509_digest(certificate, digest_function, out_digest, &len);
+
+  if (success && out_digest_length)
+    *out_digest_length = len;
+  return success;
+}
+
+// static
+CertificateChecker* CertificateChecker::cert_checker_singleton_ = nullptr;
+
+CertificateChecker::CertificateChecker(PrefsInterface* prefs,
+                                       OpenSSLWrapper* openssl_wrapper)
+    : prefs_(prefs), openssl_wrapper_(openssl_wrapper) {
+}
+
+CertificateChecker::~CertificateChecker() {
+  if (cert_checker_singleton_ == this)
+    cert_checker_singleton_ = nullptr;
+}
+
+void CertificateChecker::Init() {
+  CHECK(cert_checker_singleton_ == nullptr);
+  cert_checker_singleton_ = this;
+}
+
+// static
+CURLcode CertificateChecker::ProcessSSLContext(CURL* curl_handle,
+                                               SSL_CTX* ssl_ctx,
+                                               void* ptr) {
+  ServerToCheck* server_to_check = reinterpret_cast<ServerToCheck*>(ptr);
+
+  if (!cert_checker_singleton_) {
+    DLOG(WARNING) << "No CertificateChecker singleton initialized.";
+    return CURLE_FAILED_INIT;
+  }
+
+  // From here we set the SSL_CTX to another callback, from the openssl library,
+  // which will be called after each server certificate is validated. However,
+  // since openssl does not allow us to pass our own data pointer to the
+  // callback, the certificate check will have to be done statically. Since we
+  // need to know which update server we are using in order to check the
+  // certificate, we hardcode Chrome OS's two known update servers here, and
+  // define a different static callback for each. Since this code should only
+  // run in official builds, this should not be a problem. However, if an update
+  // server different from the ones listed here is used, the check will not
+  // take place.
+  int (*verify_callback)(int, X509_STORE_CTX*);
+  switch (*server_to_check) {
+    case ServerToCheck::kDownload:
+      verify_callback = &CertificateChecker::VerifySSLCallbackDownload;
+      break;
+    case ServerToCheck::kUpdate:
+      verify_callback = &CertificateChecker::VerifySSLCallbackUpdate;
+      break;
+    case ServerToCheck::kNone:
+      verify_callback = nullptr;
+      break;
+  }
+
+  SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, verify_callback);
+  return CURLE_OK;
+}
+
+// static
+int CertificateChecker::VerifySSLCallbackDownload(int preverify_ok,
+                                                  X509_STORE_CTX* x509_ctx) {
+  return VerifySSLCallback(preverify_ok, x509_ctx, ServerToCheck::kDownload);
+}
+
+// static
+int CertificateChecker::VerifySSLCallbackUpdate(int preverify_ok,
+                                                X509_STORE_CTX* x509_ctx) {
+  return VerifySSLCallback(preverify_ok, x509_ctx, ServerToCheck::kUpdate);
+}
+
+// static
+int CertificateChecker::VerifySSLCallback(int preverify_ok,
+                                          X509_STORE_CTX* x509_ctx,
+                                          ServerToCheck server_to_check) {
+  CHECK(cert_checker_singleton_ != nullptr);
+  return cert_checker_singleton_->CheckCertificateChange(
+      preverify_ok, x509_ctx, server_to_check) ? 1 : 0;
+}
+
+bool CertificateChecker::CheckCertificateChange(int preverify_ok,
+                                                X509_STORE_CTX* x509_ctx,
+                                                ServerToCheck server_to_check) {
+  TEST_AND_RETURN_FALSE(prefs_ != nullptr);
+
+  // If pre-verification failed, we are not interested in the current
+  // certificate. We store a report to UMA and just propagate the fail result.
+  if (!preverify_ok) {
+    NotifyCertificateChecked(server_to_check, CertificateCheckResult::kFailed);
+    return false;
+  }
+
+  int depth;
+  unsigned int digest_length;
+  uint8_t digest[EVP_MAX_MD_SIZE];
+
+  if (!openssl_wrapper_->GetCertificateDigest(x509_ctx,
+                                              &depth,
+                                              &digest_length,
+                                              digest)) {
+    LOG(WARNING) << "Failed to generate digest of X509 certificate "
+                 << "from update server.";
+    NotifyCertificateChecked(server_to_check, CertificateCheckResult::kValid);
+    return true;
+  }
+
+  // We convert the raw bytes of the digest to an hex string, for storage in
+  // prefs.
+  string digest_string = base::HexEncode(digest, digest_length);
+
+  string storage_key =
+      base::StringPrintf("%s-%d-%d", kPrefsUpdateServerCertificate,
+                         static_cast<int>(server_to_check), depth);
+  string stored_digest;
+  // If there's no stored certificate, we just store the current one and return.
+  if (!prefs_->GetString(storage_key, &stored_digest)) {
+    if (!prefs_->SetString(storage_key, digest_string)) {
+      LOG(WARNING) << "Failed to store server certificate on storage key "
+                   << storage_key;
+    }
+    NotifyCertificateChecked(server_to_check, CertificateCheckResult::kValid);
+    return true;
+  }
+
+  // Certificate changed, we store a report to UMA and store the most recent
+  // certificate.
+  if (stored_digest != digest_string) {
+    if (!prefs_->SetString(storage_key, digest_string)) {
+      LOG(WARNING) << "Failed to store server certificate on storage key "
+                   << storage_key;
+    }
+    LOG(INFO) << "Certificate changed from " << stored_digest << " to "
+              << digest_string << ".";
+    NotifyCertificateChecked(server_to_check,
+                             CertificateCheckResult::kValidChanged);
+    return true;
+  }
+
+  NotifyCertificateChecked(server_to_check, CertificateCheckResult::kValid);
+  // Since we don't perform actual SSL verification, we return success.
+  return true;
+}
+
+void CertificateChecker::NotifyCertificateChecked(
+    ServerToCheck server_to_check,
+    CertificateCheckResult result) {
+  if (observer_)
+    observer_->CertificateChecked(server_to_check, result);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/certificate_checker.h b/common/certificate_checker.h
new file mode 100644
index 0000000..c785192
--- /dev/null
+++ b/common/certificate_checker.h
@@ -0,0 +1,175 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_COMMON_CERTIFICATE_CHECKER_H_
+#define UPDATE_ENGINE_COMMON_CERTIFICATE_CHECKER_H_
+
+#include <curl/curl.h>
+#include <openssl/ssl.h>
+
+#include <string>
+
+#include <base/macros.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+namespace chromeos_update_engine {
+
+class PrefsInterface;
+
+// Wrapper for openssl operations with the certificates.
+class OpenSSLWrapper {
+ public:
+  OpenSSLWrapper() = default;
+  virtual ~OpenSSLWrapper() = default;
+
+  // Takes an openssl X509_STORE_CTX, extracts the corresponding certificate
+  // from it and calculates its fingerprint (SHA256 digest). Returns true on
+  // success and false otherwise.
+  //
+  // |x509_ctx| is the pointer to the openssl object that holds the certificate.
+  // |out_depth| is the depth of the current certificate, in the certificate
+  // chain.
+  // |out_digest_length| is the length of the generated digest.
+  // |out_digest| is the byte array where the digest itself will be written.
+  // It should be big enough to hold a SHA1 digest (e.g. EVP_MAX_MD_SIZE).
+  virtual bool GetCertificateDigest(X509_STORE_CTX* x509_ctx,
+                                    int* out_depth,
+                                    unsigned int* out_digest_length,
+                                    uint8_t* out_digest) const;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(OpenSSLWrapper);
+};
+
+// The values in this enum are replicated in the metrics server. See metrics.h
+// for instructions on how to update these values in the server side.
+enum class CertificateCheckResult {
+  // The certificate is valid and the same as seen before or the first time we
+  // see a certificate.
+  kValid,
+  // The certificate is valid, but is different than a previously seen
+  // certificate for the selected server.
+  kValidChanged,
+  // The certificate validation failed.
+  kFailed,
+
+  // This value must be the last entry.
+  kNumConstants
+};
+
+// These values are used to generate the keys of files persisted via prefs.
+// This means that changing these will cause loss of information on metrics
+// reporting, during the transition. These values are also mapped to a metric
+// name in metrics.cc, so adding values here requires to assign a new metric
+// name in that file.
+enum class ServerToCheck {
+  kUpdate = 0,
+  kDownload,
+
+  // Ignore this server.
+  kNone,
+};
+
+// Responsible for checking whether update server certificates change, and
+// reporting to UMA when this happens. Since all state information is persisted,
+// and openssl forces us to use a static callback with no data pointer, this
+// class is entirely static.
+class CertificateChecker {
+ public:
+  class Observer {
+   public:
+    virtual ~Observer() = default;
+
+    // Called whenever a certificate is checked for the server |server_to_check|
+    // passing the result of said certificate check.
+    virtual void CertificateChecked(ServerToCheck server_to_check,
+                                    CertificateCheckResult result) = 0;
+  };
+
+  CertificateChecker(PrefsInterface* prefs, OpenSSLWrapper* openssl_wrapper);
+  ~CertificateChecker();
+
+  // This callback is called by libcurl just before the initialization of an
+  // SSL connection after having processed all other SSL related options. Used
+  // to check if server certificates change. |cert_checker| is expected to be a
+  // pointer to the CertificateChecker instance.
+  static CURLcode ProcessSSLContext(CURL* curl_handle,
+                                    SSL_CTX* ssl_ctx,
+                                    void* cert_checker);
+
+  // Initialize this class as the singleton instance. Only one instance can be
+  // initialized at a time and it should be initialized before other methods
+  // can be used.
+  void Init();
+
+  // Set the certificate observer to the passed instance. To remove the
+  // observer, pass a nullptr. The |observer| instance must be valid while this
+  // CertificateChecker verifies certificates.
+  void SetObserver(Observer* observer) { observer_ = observer; }
+
+ private:
+  FRIEND_TEST(CertificateCheckerTest, NewCertificate);
+  FRIEND_TEST(CertificateCheckerTest, SameCertificate);
+  FRIEND_TEST(CertificateCheckerTest, ChangedCertificate);
+  FRIEND_TEST(CertificateCheckerTest, FailedCertificate);
+
+  // These callbacks are asynchronously called by openssl after initial SSL
+  // verification. They are used to perform any additional security verification
+  // on the connection, but we use them here to get hold of the server
+  // certificate, in order to determine if it has changed since the last
+  // connection. Since openssl forces us to do this statically, we define two
+  // different callbacks for the two different official update servers, and only
+  // assign the correspondent one. The assigned callback is then called once per
+  // each certificate on the server and returns 1 for success and 0 for failure.
+  static int VerifySSLCallbackDownload(int preverify_ok,
+                                       X509_STORE_CTX* x509_ctx);
+  static int VerifySSLCallbackUpdate(int preverify_ok,
+                                     X509_STORE_CTX* x509_ctx);
+  static int VerifySSLCallback(int preverify_ok,
+                               X509_STORE_CTX* x509_ctx,
+                               ServerToCheck server_to_check);
+
+  // Checks if server certificate stored in |x509_ctx| has changed since last
+  // connection to that same server, specified by |server_to_check|.
+  // This is called by the callbacks defined above. The result of the
+  // certificate check is passed to the observer, if any. Returns true on
+  // success and false otherwise.
+  bool CheckCertificateChange(int preverify_ok,
+                              X509_STORE_CTX* x509_ctx,
+                              ServerToCheck server_to_check);
+
+  // Notifies the observer, if any, of a certificate checking.
+  void NotifyCertificateChecked(ServerToCheck server_to_check,
+                                CertificateCheckResult result);
+
+  // The CertificateChecker singleton instance.
+  static CertificateChecker* cert_checker_singleton_;
+
+  // Prefs instance used to store the certificates seen in the past.
+  PrefsInterface* prefs_;
+
+  // The wrapper for openssl operations.
+  OpenSSLWrapper* openssl_wrapper_;
+
+  // The observer called whenever a certificate is checked, if not null.
+  Observer* observer_{nullptr};
+
+  DISALLOW_COPY_AND_ASSIGN(CertificateChecker);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_CERTIFICATE_CHECKER_H_
diff --git a/common/certificate_checker_unittest.cc b/common/certificate_checker_unittest.cc
new file mode 100644
index 0000000..c30acc5
--- /dev/null
+++ b/common/certificate_checker_unittest.cc
@@ -0,0 +1,140 @@
+//
+// 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/common/certificate_checker.h"
+
+#include <string>
+
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/constants.h"
+#include "update_engine/common/mock_certificate_checker.h"
+#include "update_engine/common/mock_prefs.h"
+
+using ::testing::DoAll;
+using ::testing::Return;
+using ::testing::SetArgumentPointee;
+using ::testing::SetArrayArgument;
+using ::testing::_;
+using std::string;
+
+namespace chromeos_update_engine {
+
+class MockCertificateCheckObserver : public CertificateChecker::Observer {
+ public:
+  MOCK_METHOD2(CertificateChecked,
+               void(ServerToCheck server_to_check,
+                    CertificateCheckResult result));
+};
+
+class CertificateCheckerTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    cert_key_ = base::StringPrintf("%s-%d-%d",
+                                   cert_key_prefix_.c_str(),
+                                   static_cast<int>(server_to_check_),
+                                   depth_);
+    cert_checker.Init();
+    cert_checker.SetObserver(&observer_);
+  }
+
+  void TearDown() override {
+    cert_checker.SetObserver(nullptr);
+  }
+
+  MockPrefs prefs_;
+  MockOpenSSLWrapper openssl_wrapper_;
+  // Parameters of our mock certificate digest.
+  int depth_{0};
+  unsigned int length_{4};
+  uint8_t digest_[4]{0x17, 0x7D, 0x07, 0x5F};
+  string digest_hex_{"177D075F"};
+  string diff_digest_hex_{"1234ABCD"};
+  string cert_key_prefix_{kPrefsUpdateServerCertificate};
+  ServerToCheck server_to_check_{ServerToCheck::kUpdate};
+  string cert_key_;
+
+  testing::StrictMock<MockCertificateCheckObserver> observer_;
+  CertificateChecker cert_checker{&prefs_, &openssl_wrapper_};
+};
+
+// check certificate change, new
+TEST_F(CertificateCheckerTest, NewCertificate) {
+  EXPECT_CALL(openssl_wrapper_, GetCertificateDigest(nullptr, _, _, _))
+      .WillOnce(DoAll(
+          SetArgumentPointee<1>(depth_),
+          SetArgumentPointee<2>(length_),
+          SetArrayArgument<3>(digest_, digest_ + 4),
+          Return(true)));
+  EXPECT_CALL(prefs_, GetString(cert_key_, _)).WillOnce(Return(false));
+  EXPECT_CALL(prefs_, SetString(cert_key_, digest_hex_)).WillOnce(Return(true));
+  EXPECT_CALL(observer_,
+              CertificateChecked(server_to_check_,
+                                 CertificateCheckResult::kValid));
+  ASSERT_TRUE(
+      cert_checker.CheckCertificateChange(1, nullptr, server_to_check_));
+}
+
+// check certificate change, unchanged
+TEST_F(CertificateCheckerTest, SameCertificate) {
+  EXPECT_CALL(openssl_wrapper_, GetCertificateDigest(nullptr, _, _, _))
+      .WillOnce(DoAll(
+          SetArgumentPointee<1>(depth_),
+          SetArgumentPointee<2>(length_),
+          SetArrayArgument<3>(digest_, digest_ + 4),
+          Return(true)));
+  EXPECT_CALL(prefs_, GetString(cert_key_, _))
+      .WillOnce(DoAll(SetArgumentPointee<1>(digest_hex_), Return(true)));
+  EXPECT_CALL(prefs_, SetString(_, _)).Times(0);
+  EXPECT_CALL(observer_,
+              CertificateChecked(server_to_check_,
+                                 CertificateCheckResult::kValid));
+  ASSERT_TRUE(
+      cert_checker.CheckCertificateChange(1, nullptr, server_to_check_));
+}
+
+// check certificate change, changed
+TEST_F(CertificateCheckerTest, ChangedCertificate) {
+  EXPECT_CALL(openssl_wrapper_, GetCertificateDigest(nullptr, _, _, _))
+      .WillOnce(DoAll(
+          SetArgumentPointee<1>(depth_),
+          SetArgumentPointee<2>(length_),
+          SetArrayArgument<3>(digest_, digest_ + 4),
+          Return(true)));
+  EXPECT_CALL(prefs_, GetString(cert_key_, _))
+      .WillOnce(DoAll(SetArgumentPointee<1>(diff_digest_hex_), Return(true)));
+  EXPECT_CALL(observer_,
+              CertificateChecked(server_to_check_,
+                                 CertificateCheckResult::kValidChanged));
+  EXPECT_CALL(prefs_, SetString(cert_key_, digest_hex_)).WillOnce(Return(true));
+  ASSERT_TRUE(
+      cert_checker.CheckCertificateChange(1, nullptr, server_to_check_));
+}
+
+// check certificate change, failed
+TEST_F(CertificateCheckerTest, FailedCertificate) {
+  EXPECT_CALL(observer_, CertificateChecked(server_to_check_,
+                                            CertificateCheckResult::kFailed));
+  EXPECT_CALL(prefs_, GetString(_, _)).Times(0);
+  EXPECT_CALL(openssl_wrapper_, GetCertificateDigest(_, _, _, _)).Times(0);
+  ASSERT_FALSE(
+      cert_checker.CheckCertificateChange(0, nullptr, server_to_check_));
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/clock.cc b/common/clock.cc
new file mode 100644
index 0000000..f0eff44
--- /dev/null
+++ b/common/clock.cc
@@ -0,0 +1,58 @@
+//
+// 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.
+//
+
+#include "update_engine/common/clock.h"
+
+#include <time.h>
+
+namespace chromeos_update_engine {
+
+base::Time Clock::GetWallclockTime() {
+  return base::Time::Now();
+}
+
+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
+    // often and we don't want to fill up the disk. Note that this
+    // only fails if running on ancient kernels (CLOCK_MONOTONIC_RAW
+    // was added in Linux 2.6.28) so it never fails on a ChromeOS
+    // device.
+    return base::Time();
+  }
+  struct timeval now_tv;
+  now_tv.tv_sec = now_ts.tv_sec;
+  now_tv.tv_usec = now_ts.tv_nsec/base::Time::kNanosecondsPerMicrosecond;
+  return base::Time::FromTimeVal(now_tv);
+}
+
+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
+    // often and we don't want to fill up the disk. Note that this
+    // only fails if running on ancient kernels (CLOCK_BOOTTIME was
+    // added in Linux 2.6.39) so it never fails on a ChromeOS device.
+    return base::Time();
+  }
+  struct timeval now_tv;
+  now_tv.tv_sec = now_ts.tv_sec;
+  now_tv.tv_usec = now_ts.tv_nsec/base::Time::kNanosecondsPerMicrosecond;
+  return base::Time::FromTimeVal(now_tv);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/clock.h b/common/clock.h
new file mode 100644
index 0000000..2f373a7
--- /dev/null
+++ b/common/clock.h
@@ -0,0 +1,41 @@
+//
+// 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_COMMON_CLOCK_H_
+#define UPDATE_ENGINE_COMMON_CLOCK_H_
+
+#include "update_engine/common/clock_interface.h"
+
+namespace chromeos_update_engine {
+
+// Implements a clock.
+class Clock : public ClockInterface {
+ public:
+  Clock() {}
+
+  base::Time GetWallclockTime() override;
+
+  base::Time GetMonotonicTime() override;
+
+  base::Time GetBootTime() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Clock);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_CLOCK_H_
diff --git a/common/clock_interface.h b/common/clock_interface.h
new file mode 100644
index 0000000..2228983
--- /dev/null
+++ b/common/clock_interface.h
@@ -0,0 +1,54 @@
+//
+// 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_COMMON_CLOCK_INTERFACE_H_
+#define UPDATE_ENGINE_COMMON_CLOCK_INTERFACE_H_
+
+#include <string>
+
+#include <base/time/time.h>
+
+namespace chromeos_update_engine {
+
+// The clock interface allows access to various system clocks. The
+// sole reason for providing this as an interface is unit testing.
+// Additionally, the sole reason for the methods not being static
+// is also unit testing.
+class ClockInterface {
+ public:
+  virtual ~ClockInterface() = default;
+
+  // Gets the current time e.g. similar to base::Time::Now().
+  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() = 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() = 0;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_CLOCK_INTERFACE_H_
diff --git a/common/constants.cc b/common/constants.cc
new file mode 100644
index 0000000..fe4e643
--- /dev/null
+++ b/common/constants.cc
@@ -0,0 +1,90 @@
+//
+// 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.
+//
+
+#include "update_engine/common/constants.h"
+
+namespace chromeos_update_engine {
+
+const char kPowerwashMarkerFile[] =
+    "/mnt/stateful_partition/factory_install_reset";
+
+const char kPowerwashCommand[] = "safe fast keepimg reason=update_engine\n";
+
+const char kPowerwashSafePrefsSubDirectory[] = "update_engine/prefs";
+
+const char kPrefsSubDirectory[] = "prefs";
+
+const char kStatefulPartition[] = "/mnt/stateful_partition";
+
+// Constants defining keys for the persisted state of update engine.
+const char kPrefsAttemptInProgress[] = "attempt-in-progress";
+const char kPrefsBackoffExpiryTime[] = "backoff-expiry-time";
+const char kPrefsBootId[] = "boot-id";
+const char kPrefsCurrentBytesDownloaded[] = "current-bytes-downloaded";
+const char kPrefsCurrentResponseSignature[] = "current-response-signature";
+const char kPrefsCurrentUrlFailureCount[] = "current-url-failure-count";
+const char kPrefsCurrentUrlIndex[] = "current-url-index";
+const char kPrefsDailyMetricsLastReportedAt[] =
+    "daily-metrics-last-reported-at";
+const char kPrefsDeltaUpdateFailures[] = "delta-update-failures";
+const char kPrefsFullPayloadAttemptNumber[] = "full-payload-attempt-number";
+const char kPrefsInstallDateDays[] = "install-date-days";
+const char kPrefsLastActivePingDay[] = "last-active-ping-day";
+const char kPrefsLastRollCallPingDay[] = "last-roll-call-ping-day";
+const char kPrefsManifestMetadataSize[] = "manifest-metadata-size";
+const char kPrefsMetricsAttemptLastReportingTime[] =
+    "metrics-attempt-last-reporting-time";
+const char kPrefsMetricsCheckLastReportingTime[] =
+    "metrics-check-last-reporting-time";
+const char kPrefsNumReboots[] = "num-reboots";
+const char kPrefsNumResponsesSeen[] = "num-responses-seen";
+const char kPrefsOmahaCohort[] = "omaha-cohort";
+const char kPrefsOmahaCohortHint[] = "omaha-cohort-hint";
+const char kPrefsOmahaCohortName[] = "omaha-cohort-name";
+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 kPrefsPreviousVersion[] = "previous-version";
+const char kPrefsResumedUpdateFailures[] = "resumed-update-failures";
+const char kPrefsRollbackVersion[] = "rollback-version";
+const char kPrefsChannelOnSlotPrefix[] = "channel-on-slot-";
+const char kPrefsSystemUpdatedMarker[] = "system-updated-marker";
+const char kPrefsTargetVersionAttempt[] = "target-version-attempt";
+const char kPrefsTargetVersionInstalledFrom[] = "target-version-installed-from";
+const char kPrefsTargetVersionUniqueId[] = "target-version-unique-id";
+const char kPrefsTotalBytesDownloaded[] = "total-bytes-downloaded";
+const char kPrefsUpdateCheckCount[] = "update-check-count";
+const char kPrefsUpdateCheckResponseHash[] = "update-check-response-hash";
+const char kPrefsUpdateCompletedBootTime[] = "update-completed-boot-time";
+const char kPrefsUpdateCompletedOnBootId[] = "update-completed-on-boot-id";
+const char kPrefsUpdateDurationUptime[] = "update-duration-uptime";
+const char kPrefsUpdateFirstSeenAt[] = "update-first-seen-at";
+const char kPrefsUpdateOverCellularPermission[] =
+    "update-over-cellular-permission";
+const char kPrefsUpdateServerCertificate[] = "update-server-cert";
+const char kPrefsUpdateStateNextDataLength[] = "update-state-next-data-length";
+const char kPrefsUpdateStateNextDataOffset[] = "update-state-next-data-offset";
+const char kPrefsUpdateStateNextOperation[] = "update-state-next-operation";
+const char kPrefsUpdateStateSHA256Context[] = "update-state-sha-256-context";
+const char kPrefsUpdateStateSignatureBlob[] = "update-state-signature-blob";
+const char kPrefsUpdateStateSignedSHA256Context[] =
+    "update-state-signed-sha-256-context";
+const char kPrefsUpdateTimestampStart[] = "update-timestamp-start";
+const char kPrefsUrlSwitchCount[] = "url-switch-count";
+const char kPrefsWallClockWaitPeriod[] = "wall-clock-wait-period";
+
+}  // namespace chromeos_update_engine
diff --git a/common/constants.h b/common/constants.h
new file mode 100644
index 0000000..f20de8e
--- /dev/null
+++ b/common/constants.h
@@ -0,0 +1,180 @@
+//
+// 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_COMMON_CONSTANTS_H_
+#define UPDATE_ENGINE_COMMON_CONSTANTS_H_
+
+namespace chromeos_update_engine {
+
+// The name of the marker file used to trigger powerwash when post-install
+// completes successfully so that the device is powerwashed on next reboot.
+extern const char kPowerwashMarkerFile[];
+
+// The contents of the powerwash marker file.
+extern const char kPowerwashCommand[];
+
+// Directory for AU prefs that are preserved across powerwash.
+extern const char kPowerwashSafePrefsSubDirectory[];
+
+// The location where we store the AU preferences (state etc).
+extern const char kPrefsSubDirectory[];
+
+// Path to the stateful partition on the root filesystem.
+extern const char kStatefulPartition[];
+
+// Constants related to preferences.
+extern const char kPrefsAttemptInProgress[];
+extern const char kPrefsBackoffExpiryTime[];
+extern const char kPrefsBootId[];
+extern const char kPrefsCurrentBytesDownloaded[];
+extern const char kPrefsCurrentResponseSignature[];
+extern const char kPrefsCurrentUrlFailureCount[];
+extern const char kPrefsCurrentUrlIndex[];
+extern const char kPrefsDailyMetricsLastReportedAt[];
+extern const char kPrefsDeltaUpdateFailures[];
+extern const char kPrefsFullPayloadAttemptNumber[];
+extern const char kPrefsInstallDateDays[];
+extern const char kPrefsLastActivePingDay[];
+extern const char kPrefsLastRollCallPingDay[];
+extern const char kPrefsManifestMetadataSize[];
+extern const char kPrefsMetricsAttemptLastReportingTime[];
+extern const char kPrefsMetricsCheckLastReportingTime[];
+extern const char kPrefsNumReboots[];
+extern const char kPrefsNumResponsesSeen[];
+extern const char kPrefsOmahaCohort[];
+extern const char kPrefsOmahaCohortHint[];
+extern const char kPrefsOmahaCohortName[];
+extern const char kPrefsP2PEnabled[];
+extern const char kPrefsP2PFirstAttemptTimestamp[];
+extern const char kPrefsP2PNumAttempts[];
+extern const char kPrefsPayloadAttemptNumber[];
+extern const char kPrefsPreviousVersion[];
+extern const char kPrefsResumedUpdateFailures[];
+extern const char kPrefsRollbackVersion[];
+extern const char kPrefsChannelOnSlotPrefix[];
+extern const char kPrefsSystemUpdatedMarker[];
+extern const char kPrefsTargetVersionAttempt[];
+extern const char kPrefsTargetVersionInstalledFrom[];
+extern const char kPrefsTargetVersionUniqueId[];
+extern const char kPrefsTotalBytesDownloaded[];
+extern const char kPrefsUpdateCheckCount[];
+extern const char kPrefsUpdateCheckResponseHash[];
+extern const char kPrefsUpdateCompletedBootTime[];
+extern const char kPrefsUpdateCompletedOnBootId[];
+extern const char kPrefsUpdateDurationUptime[];
+extern const char kPrefsUpdateFirstSeenAt[];
+extern const char kPrefsUpdateOverCellularPermission[];
+extern const char kPrefsUpdateServerCertificate[];
+extern const char kPrefsUpdateStateNextDataLength[];
+extern const char kPrefsUpdateStateNextDataOffset[];
+extern const char kPrefsUpdateStateNextOperation[];
+extern const char kPrefsUpdateStateSHA256Context[];
+extern const char kPrefsUpdateStateSignatureBlob[];
+extern const char kPrefsUpdateStateSignedSHA256Context[];
+extern const char kPrefsUpdateTimestampStart[];
+extern const char kPrefsUrlSwitchCount[];
+extern const char kPrefsWallClockWaitPeriod[];
+
+// 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.
+typedef enum {
+  kDownloadSourceHttpsServer,  // UMA Binary representation: 0001
+  kDownloadSourceHttpServer,   // UMA Binary representation: 0010
+  kDownloadSourceHttpPeer,     // UMA Binary representation: 0100
+
+  // Note: Add new sources only above this line.
+  kNumDownloadSources
+} DownloadSource;
+
+// A payload can be a Full or Delta payload. In some cases, a Full payload is
+// used even when a Delta payload was available for the update, called here
+// ForcedFull. The PayloadType enum is only used to send UMA metrics about the
+// successfully applied payload.
+typedef enum {
+  kPayloadTypeFull,
+  kPayloadTypeDelta,
+  kPayloadTypeForcedFull,
+
+  // Note: Add new payload types only above this line.
+  kNumPayloadTypes
+} PayloadType;
+
+// Maximum number of times we'll allow using p2p for the same update payload.
+const int kMaxP2PAttempts = 10;
+
+// Maximum wallclock time we allow attempting to update using p2p for
+// the same update payload - five days.
+const int kMaxP2PAttemptTimeSeconds = 5 * 24 * 60 * 60;
+
+// The maximum amount of time to spend waiting for p2p-client(1) to
+// return while waiting in line to use the LAN - six hours.
+const int kMaxP2PNetworkWaitTimeSeconds = 6 * 60 * 60;
+
+// The maximum number of payload files to keep in /var/cache/p2p.
+const int kMaxP2PFilesToKeep = 3;
+
+// The maximum number of days to keep a p2p file;
+const int kMaxP2PFileAgeDays = 5;
+
+// The default number of UMA buckets for metrics.
+const int kNumDefaultUmaBuckets = 50;
+
+// General constants
+const int kNumBytesInOneMiB = 1024 * 1024;
+
+// Number of redirects allowed when downloading.
+const int kDownloadMaxRedirects = 10;
+
+// The minimum average speed that downloads must sustain...
+//
+// This is set low because some devices may have very poor
+// connectivity and we want to make as much forward progress as
+// possible. For p2p this is high (25 kB/second) since we can assume
+// high bandwidth (same LAN) and we want to fail fast.
+const int kDownloadLowSpeedLimitBps = 1;
+const int kDownloadP2PLowSpeedLimitBps = 25 * 1000;
+
+// ... measured over this period.
+//
+// For non-official builds (e.g. typically built on a developer's
+// workstation and served via devserver) bump this since it takes time
+// for the workstation to generate the payload. For p2p, make this
+// relatively low since we want to fail fast.
+const int kDownloadLowSpeedTimeSeconds = 90;
+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.
+const int kDownloadMaxRetryCount = 20;
+const int kDownloadMaxRetryCountOobeNotComplete = 3;
+const int kDownloadP2PMaxRetryCount = 5;
+
+// The connect timeout, in seconds.
+//
+// This is set high because some devices may have very poor
+// connectivity and we may be using HTTPS which involves complicated
+// multi-roundtrip setup. For p2p, this is set low because we can
+// the server is on the same LAN and we want to fail fast.
+const int kDownloadConnectTimeoutSeconds = 30;
+const int kDownloadP2PConnectTimeoutSeconds = 5;
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_CONSTANTS_H_
diff --git a/common/error_code.h b/common/error_code.h
new file mode 100644
index 0000000..2bbdcfa
--- /dev/null
+++ b/common/error_code.h
@@ -0,0 +1,135 @@
+//
+// 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_COMMON_ERROR_CODE_H_
+#define UPDATE_ENGINE_COMMON_ERROR_CODE_H_
+
+#include <ostream>  // NOLINT(readability/streams)
+
+namespace chromeos_update_engine {
+
+// Action exit codes.
+enum class ErrorCode : int {
+  kSuccess = 0,
+  kError = 1,
+  kOmahaRequestError = 2,
+  kOmahaResponseHandlerError = 3,
+  kFilesystemCopierError = 4,
+  kPostinstallRunnerError = 5,
+  kPayloadMismatchedType = 6,
+  kInstallDeviceOpenError = 7,
+  kKernelDeviceOpenError = 8,
+  kDownloadTransferError = 9,
+  kPayloadHashMismatchError = 10,
+  kPayloadSizeMismatchError = 11,
+  kDownloadPayloadVerificationError = 12,
+  kDownloadNewPartitionInfoError = 13,
+  kDownloadWriteError = 14,
+  kNewRootfsVerificationError = 15,
+  kNewKernelVerificationError = 16,
+  kSignedDeltaPayloadExpectedError = 17,
+  kDownloadPayloadPubKeyVerificationError = 18,
+  kPostinstallBootedFromFirmwareB = 19,
+  kDownloadStateInitializationError = 20,
+  kDownloadInvalidMetadataMagicString = 21,
+  kDownloadSignatureMissingInManifest = 22,
+  kDownloadManifestParseError = 23,
+  kDownloadMetadataSignatureError = 24,
+  kDownloadMetadataSignatureVerificationError = 25,
+  kDownloadMetadataSignatureMismatch = 26,
+  kDownloadOperationHashVerificationError = 27,
+  kDownloadOperationExecutionError = 28,
+  kDownloadOperationHashMismatch = 29,
+  kOmahaRequestEmptyResponseError = 30,
+  kOmahaRequestXMLParseError = 31,
+  kDownloadInvalidMetadataSize = 32,
+  kDownloadInvalidMetadataSignature = 33,
+  kOmahaResponseInvalid = 34,
+  kOmahaUpdateIgnoredPerPolicy = 35,
+  kOmahaUpdateDeferredPerPolicy = 36,
+  kOmahaErrorInHTTPResponse = 37,
+  kDownloadOperationHashMissingError = 38,
+  kDownloadMetadataSignatureMissingError = 39,
+  kOmahaUpdateDeferredForBackoff = 40,
+  kPostinstallPowerwashError = 41,
+  kUpdateCanceledByChannelChange = 42,
+  kPostinstallFirmwareRONotUpdatable = 43,
+  kUnsupportedMajorPayloadVersion = 44,
+  kUnsupportedMinorPayloadVersion = 45,
+  kOmahaRequestXMLHasEntityDecl = 46,
+  kFilesystemVerifierError = 47,
+
+  // VERY IMPORTANT! When adding new error codes:
+  //
+  // 1) Update tools/metrics/histograms/histograms.xml in Chrome.
+  //
+  // 2) Update the assorted switch statements in update_engine which won't
+  //    build until this case is added.
+
+  // Any code above this is sent to both Omaha and UMA as-is, except
+  // kOmahaErrorInHTTPResponse (see error code 2000 for more details).
+  // Codes/flags below this line is sent only to Omaha and not to UMA.
+
+  // kUmaReportedMax is not an error code per se, it's just the count
+  // of the number of enums above.  Add any new errors above this line if you
+  // want them to show up on UMA. Stuff below this line will not be sent to UMA
+  // but is used for other errors that are sent to Omaha. We don't assign any
+  // particular value for this enum so that it's just one more than the last
+  // one above and thus always represents the correct count of UMA metrics
+  // buckets, even when new enums are added above this line in future. See
+  // metrics::ReportUpdateAttemptMetrics() on how this enum is used.
+  kUmaReportedMax,
+
+  // use the 2xxx range to encode HTTP errors. These errors are available in
+  // Dremel with the individual granularity. But for UMA purposes, all these
+  // errors are aggregated into one: kOmahaErrorInHTTPResponse.
+  kOmahaRequestHTTPResponseBase = 2000,  // + HTTP response code
+
+  // TODO(jaysri): Move out all the bit masks into separate constants
+  // outside the enum as part of fixing bug 34369.
+  // Bit flags. Remember to update the mask below for new bits.
+
+  // Set if boot mode not normal.
+  // TODO(garnold) This is very debatable value to use, knowing that the
+  // underlying type is a signed int (often, 32-bit). However, at this point
+  // there are parts of the ecosystem that expect this to be a negative value,
+  // so we preserve this semantics. This should be reconsidered if/when we
+  // modify the implementation of ErrorCode into a properly encapsulated class.
+  kDevModeFlag = 1 << 31,
+
+  // Set if resuming an interruped update.
+  kResumedFlag = 1 << 30,
+
+  // Set if using a dev/test image as opposed to an MP-signed image.
+  kTestImageFlag = 1 << 29,
+
+  // Set if using devserver or Omaha sandbox (using crosh autest).
+  kTestOmahaUrlFlag = 1 << 28,
+
+  // Mask that indicates bit positions that are used to indicate special flags
+  // that are embedded in the error code to provide additional context about
+  // the system in which the error was encountered.
+  kSpecialFlags = (kDevModeFlag | kResumedFlag | kTestImageFlag |
+                   kTestOmahaUrlFlag)
+};
+
+inline std::ostream& operator<<(std::ostream& os, ErrorCode val) {
+  return os << static_cast<int>(val);
+}
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_ERROR_CODE_H_
diff --git a/common/fake_boot_control.h b/common/fake_boot_control.h
new file mode 100644
index 0000000..5c6c160
--- /dev/null
+++ b/common/fake_boot_control.h
@@ -0,0 +1,112 @@
+//
+// 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_COMMON_FAKE_BOOT_CONTROL_H_
+#define UPDATE_ENGINE_COMMON_FAKE_BOOT_CONTROL_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <base/time/time.h>
+
+#include "update_engine/common/boot_control_interface.h"
+
+namespace chromeos_update_engine {
+
+// Implements a fake bootloader control interface used for testing.
+class FakeBootControl : public BootControlInterface {
+ public:
+  FakeBootControl() {
+    SetNumSlots(num_slots_);
+    // The current slot should be bootable.
+    is_bootable_[current_slot_] = true;
+  }
+
+  // BootControlInterface overrides.
+  unsigned int GetNumSlots() const override { return num_slots_; }
+  BootControlInterface::Slot GetCurrentSlot() const override {
+    return current_slot_;
+  }
+
+  bool GetPartitionDevice(const std::string& partition_name,
+                          BootControlInterface::Slot slot,
+                          std::string* device) const override {
+    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 {
+    return slot < num_slots_ && is_bootable_[slot];
+  }
+
+  bool MarkSlotUnbootable(BootControlInterface::Slot slot) override {
+    if (slot >= num_slots_)
+      return false;
+    is_bootable_[slot] = false;
+    return true;
+  }
+
+  bool SetActiveBootSlot(Slot slot) override { return true; }
+
+  bool MarkBootSuccessfulAsync(base::Callback<void(bool)> callback) override {
+    // We run the callback directly from here to avoid having to setup a message
+    // loop in the test environment.
+    callback.Run(true);
+    return true;
+  }
+
+  // Setters
+  void SetNumSlots(unsigned int num_slots) {
+    num_slots_ = num_slots;
+    is_bootable_.resize(num_slots_, false);
+    devices_.resize(num_slots_);
+  }
+
+  void SetCurrentSlot(BootControlInterface::Slot slot) {
+    current_slot_ = slot;
+  }
+
+  void SetPartitionDevice(const std::string partition_name,
+                          BootControlInterface::Slot slot,
+                          const std::string device) {
+    DCHECK(slot < num_slots_);
+    devices_[slot][partition_name] = device;
+  }
+
+  void SetSlotBootable(BootControlInterface::Slot slot, bool bootable) {
+    DCHECK(slot < num_slots_);
+    is_bootable_[slot] = bootable;
+  }
+
+ private:
+  BootControlInterface::Slot num_slots_{2};
+  BootControlInterface::Slot current_slot_{0};
+
+  std::vector<bool> is_bootable_;
+  std::vector<std::map<std::string, std::string>> devices_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeBootControl);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_FAKE_BOOT_CONTROL_H_
diff --git a/common/fake_clock.h b/common/fake_clock.h
new file mode 100644
index 0000000..3d3bad8
--- /dev/null
+++ b/common/fake_clock.h
@@ -0,0 +1,63 @@
+//
+// 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_COMMON_FAKE_CLOCK_H_
+#define UPDATE_ENGINE_COMMON_FAKE_CLOCK_H_
+
+#include "update_engine/common/clock_interface.h"
+
+namespace chromeos_update_engine {
+
+// Implements a clock that can be made to tell any time you want.
+class FakeClock : public ClockInterface {
+ public:
+  FakeClock() {}
+
+  base::Time GetWallclockTime() override {
+    return wallclock_time_;
+  }
+
+  base::Time GetMonotonicTime() override {
+    return monotonic_time_;
+  }
+
+  base::Time GetBootTime() override {
+    return boot_time_;
+  }
+
+  void SetWallclockTime(const base::Time &time) {
+    wallclock_time_ = time;
+  }
+
+  void SetMonotonicTime(const base::Time &time) {
+    monotonic_time_ = time;
+  }
+
+  void SetBootTime(const base::Time &time) {
+    boot_time_ = time;
+  }
+
+ private:
+  base::Time wallclock_time_;
+  base::Time monotonic_time_;
+  base::Time boot_time_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeClock);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_FAKE_CLOCK_H_
diff --git a/common/fake_hardware.h b/common/fake_hardware.h
new file mode 100644
index 0000000..23d6498
--- /dev/null
+++ b/common/fake_hardware.h
@@ -0,0 +1,124 @@
+//
+// 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_COMMON_FAKE_HARDWARE_H_
+#define UPDATE_ENGINE_COMMON_FAKE_HARDWARE_H_
+
+#include <map>
+#include <string>
+
+#include <base/time/time.h>
+
+#include "update_engine/common/hardware_interface.h"
+
+namespace chromeos_update_engine {
+
+// Implements a fake hardware interface used for testing.
+class FakeHardware : public HardwareInterface {
+ public:
+  // Value used to signal that the powerwash_count file is not present. When
+  // this value is used in SetPowerwashCount(), GetPowerwashCount() will return
+  // false.
+  static const int kPowerwashCountNotSet = -1;
+
+  FakeHardware()
+      : is_official_build_(true),
+        is_normal_boot_mode_(true),
+        is_oobe_complete_(false),
+        hardware_class_("Fake HWID BLAH-1234"),
+        firmware_version_("Fake Firmware v1.0.1"),
+        ec_version_("Fake EC v1.0a"),
+        powerwash_count_(kPowerwashCountNotSet) {}
+
+  // HardwareInterface methods.
+  bool IsOfficialBuild() const override { return is_official_build_; }
+
+  bool IsNormalBootMode() const override { return is_normal_boot_mode_; }
+
+  bool IsOOBEComplete(base::Time* out_time_of_oobe) const override {
+    if (out_time_of_oobe != nullptr)
+      *out_time_of_oobe = oobe_timestamp_;
+    return is_oobe_complete_;
+  }
+
+  std::string GetHardwareClass() const override { return hardware_class_; }
+
+  std::string GetFirmwareVersion() const override { return firmware_version_; }
+
+  std::string GetECVersion() const override { return ec_version_; }
+
+  int GetPowerwashCount() const override { return powerwash_count_; }
+
+  bool GetNonVolatileDirectory(base::FilePath* path) const override {
+    return false;
+  }
+
+  bool GetPowerwashSafeDirectory(base::FilePath* path) const override {
+    return false;
+  }
+
+  // Setters
+  void SetIsOfficialBuild(bool is_official_build) {
+    is_official_build_ = is_official_build;
+  }
+
+  void SetIsNormalBootMode(bool is_normal_boot_mode) {
+    is_normal_boot_mode_ = is_normal_boot_mode;
+  }
+
+  // Sets the IsOOBEComplete to True with the given timestamp.
+  void SetIsOOBEComplete(base::Time oobe_timestamp) {
+    is_oobe_complete_ = true;
+    oobe_timestamp_ = oobe_timestamp;
+  }
+
+  // Sets the IsOOBEComplete to False.
+  void UnsetIsOOBEComplete() {
+    is_oobe_complete_ = false;
+  }
+
+  void SetHardwareClass(std::string hardware_class) {
+    hardware_class_ = hardware_class;
+  }
+
+  void SetFirmwareVersion(std::string firmware_version) {
+    firmware_version_ = firmware_version;
+  }
+
+  void SetECVersion(std::string ec_version) {
+    ec_version_ = ec_version;
+  }
+
+  void SetPowerwashCount(int powerwash_count) {
+    powerwash_count_ = powerwash_count;
+  }
+
+ private:
+  bool is_official_build_;
+  bool is_normal_boot_mode_;
+  bool is_oobe_complete_;
+  base::Time oobe_timestamp_;
+  std::string hardware_class_;
+  std::string firmware_version_;
+  std::string ec_version_;
+  int powerwash_count_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeHardware);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_FAKE_HARDWARE_H_
diff --git a/common/fake_prefs.cc b/common/fake_prefs.cc
new file mode 100644
index 0000000..5a0a3af
--- /dev/null
+++ b/common/fake_prefs.cc
@@ -0,0 +1,168 @@
+//
+// 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/common/fake_prefs.h"
+
+#include <algorithm>
+
+#include <gtest/gtest.h>
+
+using std::string;
+
+using chromeos_update_engine::FakePrefs;
+
+namespace {
+
+void CheckNotNull(const string& key, void* ptr) {
+  EXPECT_NE(nullptr, ptr)
+      << "Called Get*() for key \"" << key << "\" with a null parameter.";
+}
+
+}  // namespace
+
+namespace chromeos_update_engine {
+
+FakePrefs::~FakePrefs() {
+  EXPECT_TRUE(observers_.empty());
+}
+
+// Compile-time type-dependent constants definitions.
+template<>
+FakePrefs::PrefType const FakePrefs::PrefConsts<string>::type =
+    FakePrefs::PrefType::kString;
+template<>
+string FakePrefs::PrefValue::* const  // NOLINT(runtime/string), not static str.
+    FakePrefs::PrefConsts<string>::member = &FakePrefs::PrefValue::as_str;
+
+template<>
+FakePrefs::PrefType const FakePrefs::PrefConsts<int64_t>::type =
+    FakePrefs::PrefType::kInt64;
+template<>
+int64_t FakePrefs::PrefValue::* const FakePrefs::PrefConsts<int64_t>::member =
+    &FakePrefs::PrefValue::as_int64;
+
+template<>
+FakePrefs::PrefType const FakePrefs::PrefConsts<bool>::type =
+    FakePrefs::PrefType::kBool;
+template<>
+bool FakePrefs::PrefValue::* const FakePrefs::PrefConsts<bool>::member =
+    &FakePrefs::PrefValue::as_bool;
+
+bool FakePrefs::GetString(const string& key, string* value) const {
+  return GetValue(key, value);
+}
+
+bool FakePrefs::SetString(const string& key, const string& value) {
+  SetValue(key, value);
+  return true;
+}
+
+bool FakePrefs::GetInt64(const string& key, int64_t* value) const {
+  return GetValue(key, value);
+}
+
+bool FakePrefs::SetInt64(const string& key, const int64_t value) {
+  SetValue(key, value);
+  return true;
+}
+
+bool FakePrefs::GetBoolean(const string& key, bool* value) const {
+  return GetValue(key, value);
+}
+
+bool FakePrefs::SetBoolean(const string& key, const bool value) {
+  SetValue(key, value);
+  return true;
+}
+
+bool FakePrefs::Exists(const string& key) const {
+  return values_.find(key) != values_.end();
+}
+
+bool FakePrefs::Delete(const string& key) {
+  if (values_.find(key) == values_.end())
+    return false;
+  values_.erase(key);
+  const auto observers_for_key = observers_.find(key);
+  if (observers_for_key != observers_.end()) {
+    std::vector<ObserverInterface*> copy_observers(observers_for_key->second);
+    for (ObserverInterface* observer : copy_observers)
+      observer->OnPrefDeleted(key);
+  }
+  return true;
+}
+
+string FakePrefs::GetTypeName(PrefType type) {
+  switch (type) {
+    case PrefType::kString:
+      return "string";
+    case PrefType::kInt64:
+      return "int64_t";
+    case PrefType::kBool:
+      return "bool";
+  }
+  return "Unknown";
+}
+
+void FakePrefs::CheckKeyType(const string& key, PrefType type) const {
+  auto it = values_.find(key);
+  EXPECT_TRUE(it == values_.end() || it->second.type == type)
+      << "Key \"" << key << "\" if defined as " << GetTypeName(it->second.type)
+      << " but is accessed as a " << GetTypeName(type);
+}
+
+template<typename T>
+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) = value;
+  const auto observers_for_key = observers_.find(key);
+  if (observers_for_key != observers_.end()) {
+    std::vector<ObserverInterface*> copy_observers(observers_for_key->second);
+    for (ObserverInterface* observer : copy_observers)
+      observer->OnPrefSet(key);
+  }
+}
+
+template<typename T>
+bool FakePrefs::GetValue(const string& key, T* value) const {
+  CheckKeyType(key, PrefConsts<T>::type);
+  auto it = values_.find(key);
+  if (it == values_.end())
+    return false;
+  CheckNotNull(key, value);
+  *value = it->second.value.*(PrefConsts<T>::member);
+  return true;
+}
+
+void FakePrefs::AddObserver(const string& key, ObserverInterface* observer) {
+  observers_[key].push_back(observer);
+}
+
+void FakePrefs::RemoveObserver(const string& key, ObserverInterface* observer) {
+  std::vector<ObserverInterface*>& observers_for_key = observers_[key];
+  auto observer_it =
+      std::find(observers_for_key.begin(), observers_for_key.end(), observer);
+  EXPECT_NE(observer_it, observers_for_key.end())
+      << "Trying to remove an observer instance not watching the key "
+      << key;
+  if (observer_it != observers_for_key.end())
+    observers_for_key.erase(observer_it);
+  if (observers_for_key.empty())
+    observers_.erase(key);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/fake_prefs.h b/common/fake_prefs.h
new file mode 100644
index 0000000..d194060
--- /dev/null
+++ b/common/fake_prefs.h
@@ -0,0 +1,113 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_COMMON_FAKE_PREFS_H_
+#define UPDATE_ENGINE_COMMON_FAKE_PREFS_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+
+#include "update_engine/common/prefs_interface.h"
+
+namespace chromeos_update_engine {
+
+// Implements a fake preference store by storing the value associated with
+// a key in a std::map, suitable for testing. It doesn't allow to set a value on
+// a key with a different type than the previously set type. This enforces the
+// type of a given key to be fixed. Also the class checks that the Get*()
+// methods aren't called on a key set with a different type.
+
+class FakePrefs : public PrefsInterface {
+ public:
+  FakePrefs() = default;
+  ~FakePrefs();
+
+  // PrefsInterface methods.
+  bool GetString(const std::string& key, std::string* value) const 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;
+  bool SetBoolean(const std::string& key, const bool value) override;
+
+  bool Exists(const std::string& key) const override;
+  bool Delete(const std::string& key) override;
+
+  void AddObserver(const std::string& key,
+                   ObserverInterface* observer) override;
+  void RemoveObserver(const std::string& key,
+                      ObserverInterface* observer) override;
+
+ private:
+  enum class PrefType {
+    kString,
+    kInt64,
+    kBool,
+  };
+  struct PrefValue {
+    std::string as_str;
+    int64_t as_int64;
+    bool as_bool;
+  };
+
+  struct PrefTypeValue {
+    PrefType type;
+    PrefValue value;
+  };
+
+  // Class to store compile-time type-dependent constants.
+  template<typename T>
+  class PrefConsts {
+   public:
+    // The PrefType associated with T.
+    static FakePrefs::PrefType const type;
+
+    // The data member pointer to PrefValue associated with T.
+    static T FakePrefs::PrefValue::* const member;
+  };
+
+  // Returns a string representation of the PrefType useful for logging.
+  static std::string GetTypeName(PrefType type);
+
+  // Checks that the |key| is either not present or has the given |type|.
+  void CheckKeyType(const std::string& key, PrefType type) const;
+
+  // 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, 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
+  // different type. Returns whether the get succeeded.
+  template<typename T>
+  bool GetValue(const std::string& key, T* value) const;
+
+  // Container for all the key/value pairs.
+  std::map<std::string, PrefTypeValue> values_;
+
+  // The registered observers watching for changes.
+  std::map<std::string, std::vector<ObserverInterface*>> observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakePrefs);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_FAKE_PREFS_H_
diff --git a/common/hardware.h b/common/hardware.h
new file mode 100644
index 0000000..f1365e0
--- /dev/null
+++ b/common/hardware.h
@@ -0,0 +1,34 @@
+//
+// 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_COMMON_HARDWARE_H_
+#define UPDATE_ENGINE_COMMON_HARDWARE_H_
+
+#include <memory>
+
+#include "update_engine/common/hardware_interface.h"
+
+namespace chromeos_update_engine {
+namespace hardware {
+
+// The real HardwareInterface is platform-specific. This factory function
+// creates a new HardwareInterface instance for the current platform.
+std::unique_ptr<HardwareInterface> CreateHardware();
+
+}  // namespace hardware
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_HARDWARE_H_
diff --git a/common/hardware_interface.h b/common/hardware_interface.h
new file mode 100644
index 0000000..17ce694
--- /dev/null
+++ b/common/hardware_interface.h
@@ -0,0 +1,81 @@
+//
+// 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_COMMON_HARDWARE_INTERFACE_H_
+#define UPDATE_ENGINE_COMMON_HARDWARE_INTERFACE_H_
+
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+#include <base/time/time.h>
+
+namespace chromeos_update_engine {
+
+// The hardware interface allows access to the crossystem exposed properties,
+// such as the firmware version, hwid, verified boot mode.
+// These stateless functions are tied together in this interface to facilitate
+// unit testing.
+class HardwareInterface {
+ public:
+  virtual ~HardwareInterface() {}
+
+  // Returns whether this is an official build. Official build means that the
+  // server maintains and updates the build, so update_engine should run and
+  // periodically check for updates.
+  virtual bool IsOfficialBuild() const = 0;
+
+  // Returns true if the boot mode is normal or if it's unable to
+  // determine the boot mode. Returns false if the boot mode is
+  // developer. A dev-mode boot will allow the user to access developer-only
+  // features.
+  virtual bool IsNormalBootMode() const = 0;
+
+  // Returns true if the OOBE process has been completed and EULA accepted,
+  // False otherwise. If True is returned, and |out_time_of_oobe| isn't null,
+  // the time-stamp of when OOBE happened is stored at |out_time_of_oobe|.
+  virtual bool IsOOBEComplete(base::Time* out_time_of_oobe) const = 0;
+
+  // Returns the HWID or an empty string on error.
+  virtual std::string GetHardwareClass() const = 0;
+
+  // Returns the firmware version or an empty string if the system is
+  // not running chrome os firmware.
+  virtual std::string GetFirmwareVersion() const = 0;
+
+  // Returns the ec version or an empty string if the system is not
+  // running a custom chrome os ec.
+  virtual std::string GetECVersion() const = 0;
+
+  // Returns the powerwash_count from the stateful. If the file is not found
+  // or is invalid, returns -1. Brand new machines out of the factory or after
+  // recovery don't have this value set.
+  virtual int GetPowerwashCount() const = 0;
+
+  // Store in |path| the path to a non-volatile directory (persisted across
+  // reboots) available for this daemon. In case of an error, such as no
+  // directory available, returns false.
+  virtual bool GetNonVolatileDirectory(base::FilePath* path) const = 0;
+
+  // Store in |path| the path to a non-volatile directory persisted across
+  // powerwash cycles. In case of an error, such as no directory available,
+  // returns false.
+  virtual bool GetPowerwashSafeDirectory(base::FilePath* path) const = 0;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_HARDWARE_INTERFACE_H_
diff --git a/common/hash_calculator.cc b/common/hash_calculator.cc
new file mode 100644
index 0000000..de6e0f9
--- /dev/null
+++ b/common/hash_calculator.cc
@@ -0,0 +1,143 @@
+//
+// 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/common/hash_calculator.h"
+
+#include <fcntl.h>
+
+#include <base/logging.h>
+#include <base/posix/eintr_wrapper.h>
+#include <brillo/data_encoding.h>
+
+#include "update_engine/common/utils.h"
+
+using std::string;
+
+namespace chromeos_update_engine {
+
+HashCalculator::HashCalculator() : valid_(false) {
+  valid_ = (SHA256_Init(&ctx_) == 1);
+  LOG_IF(ERROR, !valid_) << "SHA256_Init failed";
+}
+
+// Update is called with all of the data that should be hashed in order.
+// Mostly just passes the data through to OpenSSL's SHA256_Update()
+bool HashCalculator::Update(const void* data, size_t length) {
+  TEST_AND_RETURN_FALSE(valid_);
+  TEST_AND_RETURN_FALSE(hash_.empty());
+  static_assert(sizeof(size_t) <= sizeof(unsigned long),  // NOLINT(runtime/int)
+                "length param may be truncated in SHA256_Update");
+  TEST_AND_RETURN_FALSE(SHA256_Update(&ctx_, data, length) == 1);
+  return true;
+}
+
+off_t HashCalculator::UpdateFile(const string& name, off_t length) {
+  int fd = HANDLE_EINTR(open(name.c_str(), O_RDONLY));
+  if (fd < 0) {
+    return -1;
+  }
+
+  const int kBufferSize = 128 * 1024;  // 128 KiB
+  brillo::Blob buffer(kBufferSize);
+  off_t bytes_processed = 0;
+  while (length < 0 || bytes_processed < length) {
+    off_t bytes_to_read = buffer.size();
+    if (length >= 0 && bytes_to_read > length - bytes_processed) {
+      bytes_to_read = length - bytes_processed;
+    }
+    ssize_t rc = HANDLE_EINTR(read(fd, buffer.data(), bytes_to_read));
+    if (rc == 0) {  // EOF
+      break;
+    }
+    if (rc < 0 || !Update(buffer.data(), rc)) {
+      bytes_processed = -1;
+      break;
+    }
+    bytes_processed += rc;
+  }
+  IGNORE_EINTR(close(fd));
+  return bytes_processed;
+}
+
+// Call Finalize() when all data has been passed in. This mostly just
+// calls OpenSSL's SHA256_Final() and then base64 encodes the hash.
+bool HashCalculator::Finalize() {
+  TEST_AND_RETURN_FALSE(hash_.empty());
+  TEST_AND_RETURN_FALSE(raw_hash_.empty());
+  raw_hash_.resize(SHA256_DIGEST_LENGTH);
+  TEST_AND_RETURN_FALSE(SHA256_Final(raw_hash_.data(), &ctx_) == 1);
+
+  // Convert raw_hash_ to base64 encoding and store it in hash_.
+  hash_ = brillo::data_encoding::Base64Encode(raw_hash_.data(),
+                                              raw_hash_.size());
+  return true;
+}
+
+bool HashCalculator::RawHashOfBytes(const void* data,
+                                    size_t length,
+                                    brillo::Blob* out_hash) {
+  HashCalculator calc;
+  TEST_AND_RETURN_FALSE(calc.Update(data, length));
+  TEST_AND_RETURN_FALSE(calc.Finalize());
+  *out_hash = calc.raw_hash();
+  return true;
+}
+
+bool HashCalculator::RawHashOfData(const brillo::Blob& data,
+                                   brillo::Blob* out_hash) {
+  return RawHashOfBytes(data.data(), data.size(), out_hash);
+}
+
+off_t HashCalculator::RawHashOfFile(const string& name, off_t length,
+                                    brillo::Blob* out_hash) {
+  HashCalculator calc;
+  off_t res = calc.UpdateFile(name, length);
+  if (res < 0) {
+    return res;
+  }
+  if (!calc.Finalize()) {
+    return -1;
+  }
+  *out_hash = calc.raw_hash();
+  return res;
+}
+
+string HashCalculator::HashOfBytes(const void* data, size_t length) {
+  HashCalculator calc;
+  calc.Update(data, length);
+  calc.Finalize();
+  return calc.hash();
+}
+
+string HashCalculator::HashOfString(const string& str) {
+  return HashOfBytes(str.data(), str.size());
+}
+
+string HashCalculator::HashOfData(const brillo::Blob& data) {
+  return HashOfBytes(data.data(), data.size());
+}
+
+string HashCalculator::GetContext() const {
+  return string(reinterpret_cast<const char*>(&ctx_), sizeof(ctx_));
+}
+
+bool HashCalculator::SetContext(const string& context) {
+  TEST_AND_RETURN_FALSE(context.size() == sizeof(ctx_));
+  memcpy(&ctx_, context.data(), sizeof(ctx_));
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/hash_calculator.h b/common/hash_calculator.h
new file mode 100644
index 0000000..f749585
--- /dev/null
+++ b/common/hash_calculator.h
@@ -0,0 +1,107 @@
+//
+// 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_COMMON_HASH_CALCULATOR_H_
+#define UPDATE_ENGINE_COMMON_HASH_CALCULATOR_H_
+
+#include <openssl/sha.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/macros.h>
+#include <brillo/secure_blob.h>
+
+// Omaha uses base64 encoded SHA-256 as the hash. This class provides a simple
+// wrapper around OpenSSL providing such a formatted hash of data passed in.
+// The methods of this class must be called in a very specific order: First the
+// ctor (of course), then 0 or more calls to Update(), then Finalize(), then 0
+// or more calls to hash().
+
+namespace chromeos_update_engine {
+
+class HashCalculator {
+ public:
+  HashCalculator();
+
+  // Update is called with all of the data that should be hashed in order.
+  // Update will read |length| bytes of |data|.
+  // Returns true on success.
+  bool Update(const void* data, size_t length);
+
+  // Updates the hash with up to |length| bytes of data from |file|. If |length|
+  // is negative, reads in and updates with the whole file. Returns the number
+  // of bytes that the hash was updated with, or -1 on error.
+  off_t UpdateFile(const std::string& name, off_t length);
+
+  // Call Finalize() when all data has been passed in. This method tells
+  // OpenSSl that no more data will come in and base64 encodes the resulting
+  // hash.
+  // Returns true on success.
+  bool Finalize();
+
+  // Gets the hash. Finalize() must have been called.
+  const std::string& hash() const {
+    DCHECK(!hash_.empty()) << "Call Finalize() first";
+    return hash_;
+  }
+
+  const brillo::Blob& raw_hash() const {
+    DCHECK(!raw_hash_.empty()) << "Call Finalize() first";
+    return raw_hash_;
+  }
+
+  // Gets the current hash context. Note that the string will contain binary
+  // data (including \0 characters).
+  std::string GetContext() const;
+
+  // Sets the current hash context. |context| must the string returned by a
+  // previous HashCalculator::GetContext method call. Returns true on success,
+  // and false otherwise.
+  bool SetContext(const std::string& context);
+
+  static bool RawHashOfBytes(const void* data,
+                             size_t length,
+                             brillo::Blob* out_hash);
+  static bool RawHashOfData(const brillo::Blob& data,
+                            brillo::Blob* out_hash);
+  static off_t RawHashOfFile(const std::string& name, off_t length,
+                             brillo::Blob* out_hash);
+
+  // Used by tests
+  static std::string HashOfBytes(const void* data, size_t length);
+  static std::string HashOfString(const std::string& str);
+  static std::string HashOfData(const brillo::Blob& data);
+
+ private:
+  // If non-empty, the final base64 encoded hash and the raw hash. Will only be
+  // set to non-empty when Finalize is called.
+  std::string hash_;
+  brillo::Blob raw_hash_;
+
+  // Init success
+  bool valid_;
+
+  // The hash state used by OpenSSL
+  SHA256_CTX ctx_;
+  DISALLOW_COPY_AND_ASSIGN(HashCalculator);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_HASH_CALCULATOR_H_
diff --git a/common/hash_calculator_unittest.cc b/common/hash_calculator_unittest.cc
new file mode 100644
index 0000000..27dbc56
--- /dev/null
+++ b/common/hash_calculator_unittest.cc
@@ -0,0 +1,174 @@
+//
+// 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/common/hash_calculator.h"
+
+#include <math.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <brillo/secure_blob.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/libcurl_http_fetcher.h"
+#include "update_engine/common/utils.h"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+// Generated by running this on a linux shell:
+// $ echo -n hi | openssl dgst -sha256 -binary | openssl base64
+static const char kExpectedHash[] =
+    "j0NDRmSPa5bfid2pAcUXaxCm2Dlh3TwayItZstwyeqQ=";
+static const uint8_t kExpectedRawHash[] = {
+  0x8f, 0x43, 0x43, 0x46, 0x64, 0x8f, 0x6b, 0x96,
+  0xdf, 0x89, 0xdd, 0xa9, 0x01, 0xc5, 0x17, 0x6b,
+  0x10, 0xa6, 0xd8, 0x39, 0x61, 0xdd, 0x3c, 0x1a,
+  0xc8, 0x8b, 0x59, 0xb2, 0xdc, 0x32, 0x7a, 0xa4
+};
+
+class HashCalculatorTest : public ::testing::Test {
+ public:
+  HashCalculatorTest() {}
+};
+
+TEST_F(HashCalculatorTest, SimpleTest) {
+  HashCalculator calc;
+  calc.Update("hi", 2);
+  calc.Finalize();
+  EXPECT_EQ(kExpectedHash, calc.hash());
+  brillo::Blob raw_hash(std::begin(kExpectedRawHash),
+                        std::end(kExpectedRawHash));
+  EXPECT_TRUE(raw_hash == calc.raw_hash());
+}
+
+TEST_F(HashCalculatorTest, MultiUpdateTest) {
+  HashCalculator calc;
+  calc.Update("h", 1);
+  calc.Update("i", 1);
+  calc.Finalize();
+  EXPECT_EQ(kExpectedHash, calc.hash());
+  brillo::Blob raw_hash(std::begin(kExpectedRawHash),
+                        std::end(kExpectedRawHash));
+  EXPECT_TRUE(raw_hash == calc.raw_hash());
+}
+
+TEST_F(HashCalculatorTest, ContextTest) {
+  HashCalculator calc;
+  calc.Update("h", 1);
+  string calc_context = calc.GetContext();
+  calc.Finalize();
+  HashCalculator calc_next;
+  calc_next.SetContext(calc_context);
+  calc_next.Update("i", 1);
+  calc_next.Finalize();
+  EXPECT_EQ(kExpectedHash, calc_next.hash());
+  brillo::Blob raw_hash(std::begin(kExpectedRawHash),
+                        std::end(kExpectedRawHash));
+  EXPECT_TRUE(raw_hash == calc_next.raw_hash());
+}
+
+TEST_F(HashCalculatorTest, BigTest) {
+  HashCalculator calc;
+
+  int digit_count = 1;
+  int next_overflow = 10;
+  for (int i = 0; i < 1000000; i++) {
+    char buf[8];
+    if (i == next_overflow) {
+      next_overflow *= 10;
+      digit_count++;
+    }
+    ASSERT_EQ(digit_count, snprintf(buf, sizeof(buf), "%d", i)) << " i = " << i;
+    calc.Update(buf, strlen(buf));
+  }
+  calc.Finalize();
+
+  // Hash constant generated by running this on a linux shell:
+  // $ C=0
+  // $ while [ $C -lt 1000000 ]; do
+  //     echo -n $C
+  //     let C=C+1
+  //   done | openssl dgst -sha256 -binary | openssl base64
+  EXPECT_EQ("NZf8k6SPBkYMvhaX8YgzuMgbkLP1XZ+neM8K5wcSsf8=", calc.hash());
+}
+
+TEST_F(HashCalculatorTest, UpdateFileSimpleTest) {
+  string data_path;
+  ASSERT_TRUE(
+      utils::MakeTempFile("data.XXXXXX", &data_path, nullptr));
+  ScopedPathUnlinker data_path_unlinker(data_path);
+  ASSERT_TRUE(utils::WriteFile(data_path.c_str(), "hi", 2));
+
+  static const int kLengths[] = { -1, 2, 10 };
+  for (size_t i = 0; i < arraysize(kLengths); i++) {
+    HashCalculator calc;
+    EXPECT_EQ(2, calc.UpdateFile(data_path, kLengths[i]));
+    EXPECT_TRUE(calc.Finalize());
+    EXPECT_EQ(kExpectedHash, calc.hash());
+    brillo::Blob raw_hash(std::begin(kExpectedRawHash),
+                          std::end(kExpectedRawHash));
+    EXPECT_TRUE(raw_hash == calc.raw_hash());
+  }
+
+  HashCalculator calc;
+  EXPECT_EQ(0, calc.UpdateFile(data_path, 0));
+  EXPECT_EQ(1, calc.UpdateFile(data_path, 1));
+  EXPECT_TRUE(calc.Finalize());
+  // echo -n h | openssl dgst -sha256 -binary | openssl base64
+  EXPECT_EQ("qqlAJmTxpB9A67xSyZk+tmrrNmYClY/fqig7ceZNsSM=", calc.hash());
+}
+
+TEST_F(HashCalculatorTest, RawHashOfFileSimpleTest) {
+  string data_path;
+  ASSERT_TRUE(
+      utils::MakeTempFile("data.XXXXXX", &data_path, nullptr));
+  ScopedPathUnlinker data_path_unlinker(data_path);
+  ASSERT_TRUE(utils::WriteFile(data_path.c_str(), "hi", 2));
+
+  static const int kLengths[] = { -1, 2, 10 };
+  for (size_t i = 0; i < arraysize(kLengths); i++) {
+    brillo::Blob exp_raw_hash(std::begin(kExpectedRawHash),
+                              std::end(kExpectedRawHash));
+    brillo::Blob raw_hash;
+    EXPECT_EQ(2, HashCalculator::RawHashOfFile(data_path,
+                                               kLengths[i],
+                                               &raw_hash));
+    EXPECT_TRUE(exp_raw_hash == raw_hash);
+  }
+}
+
+TEST_F(HashCalculatorTest, UpdateFileNonexistentTest) {
+  HashCalculator calc;
+  EXPECT_EQ(-1, calc.UpdateFile("/some/non-existent/file", -1));
+}
+
+TEST_F(HashCalculatorTest, AbortTest) {
+  // Just make sure we don't crash and valgrind doesn't detect memory leaks
+  {
+    HashCalculator calc;
+  }
+  {
+    HashCalculator calc;
+    calc.Update("h", 1);
+  }
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/http_common.cc b/common/http_common.cc
new file mode 100644
index 0000000..7d98889
--- /dev/null
+++ b/common/http_common.cc
@@ -0,0 +1,86 @@
+//
+// 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.
+//
+
+// Implementation of common HTTP related functions.
+
+#include "update_engine/common/http_common.h"
+
+#include <base/macros.h>
+
+namespace chromeos_update_engine {
+
+const char *GetHttpResponseDescription(HttpResponseCode code) {
+  static const struct {
+    HttpResponseCode code;
+    const char* description;
+  } http_response_table[] = {
+    { kHttpResponseOk,                  "OK" },
+    { kHttpResponseCreated,             "Created" },
+    { kHttpResponseAccepted,            "Accepted" },
+    { kHttpResponseNonAuthInfo,         "Non-Authoritative Information" },
+    { kHttpResponseNoContent,           "No Content" },
+    { kHttpResponseResetContent,        "Reset Content" },
+    { kHttpResponsePartialContent,      "Partial Content" },
+    { kHttpResponseMultipleChoices,     "Multiple Choices" },
+    { kHttpResponseMovedPermanently,    "Moved Permanently" },
+    { kHttpResponseFound,               "Found" },
+    { kHttpResponseSeeOther,            "See Other" },
+    { kHttpResponseNotModified,         "Not Modified" },
+    { kHttpResponseUseProxy,            "Use Proxy" },
+    { kHttpResponseTempRedirect,        "Temporary Redirect" },
+    { kHttpResponseBadRequest,          "Bad Request" },
+    { kHttpResponseUnauth,              "Unauthorized" },
+    { kHttpResponseForbidden,           "Forbidden" },
+    { kHttpResponseNotFound,            "Not Found" },
+    { kHttpResponseRequestTimeout,      "Request Timeout" },
+    { kHttpResponseInternalServerError, "Internal Server Error" },
+    { kHttpResponseNotImplemented,      "Not Implemented" },
+    { kHttpResponseServiceUnavailable,  "Service Unavailable" },
+    { kHttpResponseVersionNotSupported, "HTTP Version Not Supported" },
+  };
+
+  bool is_found = false;
+  size_t i;
+  for (i = 0; i < arraysize(http_response_table); i++)
+    if ((is_found = (http_response_table[i].code == code)))
+      break;
+
+  return (is_found ? http_response_table[i].description : "(unsupported)");
+}
+
+HttpResponseCode StringToHttpResponseCode(const char *s) {
+  return static_cast<HttpResponseCode>(strtoul(s, nullptr, 10));
+}
+
+
+const char *GetHttpContentTypeString(HttpContentType type) {
+  static const struct {
+    HttpContentType type;
+    const char* str;
+  } http_content_type_table[] = {
+    { kHttpContentTypeTextXml, "text/xml" },
+  };
+
+  bool is_found = false;
+  size_t i;
+  for (i = 0; i < arraysize(http_content_type_table); i++)
+    if ((is_found = (http_content_type_table[i].type == type)))
+      break;
+
+  return (is_found ? http_content_type_table[i].str : nullptr);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/http_common.h b/common/http_common.h
new file mode 100644
index 0000000..041cad5
--- /dev/null
+++ b/common/http_common.h
@@ -0,0 +1,74 @@
+//
+// 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.
+//
+
+// This file contains general definitions used in implementing, testing and
+// emulating communication over HTTP.
+
+#ifndef UPDATE_ENGINE_COMMON_HTTP_COMMON_H_
+#define UPDATE_ENGINE_COMMON_HTTP_COMMON_H_
+
+#include <cstdlib>
+
+namespace chromeos_update_engine {
+
+// Enumeration type for HTTP response codes.
+enum HttpResponseCode {
+  kHttpResponseUndefined           = 0,
+  kHttpResponseOk                  = 200,
+  kHttpResponseCreated             = 201,
+  kHttpResponseAccepted            = 202,
+  kHttpResponseNonAuthInfo         = 203,
+  kHttpResponseNoContent           = 204,
+  kHttpResponseResetContent        = 205,
+  kHttpResponsePartialContent      = 206,
+  kHttpResponseMultipleChoices     = 300,
+  kHttpResponseMovedPermanently    = 301,
+  kHttpResponseFound               = 302,
+  kHttpResponseSeeOther            = 303,
+  kHttpResponseNotModified         = 304,
+  kHttpResponseUseProxy            = 305,
+  kHttpResponseTempRedirect        = 307,
+  kHttpResponseBadRequest          = 400,
+  kHttpResponseUnauth              = 401,
+  kHttpResponseForbidden           = 403,
+  kHttpResponseNotFound            = 404,
+  kHttpResponseRequestTimeout      = 408,
+  kHttpResponseReqRangeNotSat      = 416,
+  kHttpResponseInternalServerError = 500,
+  kHttpResponseNotImplemented      = 501,
+  kHttpResponseServiceUnavailable  = 503,
+  kHttpResponseVersionNotSupported = 505,
+};
+
+// Returns a standard HTTP status line string for a given response code.
+const char *GetHttpResponseDescription(HttpResponseCode code);
+
+// Converts a string beginning with an HTTP error code into numerical value.
+HttpResponseCode StringToHttpResponseCode(const char *s);
+
+
+// Enumeration type for HTTP Content-Type.
+enum HttpContentType {
+  kHttpContentTypeUnspecified = 0,
+  kHttpContentTypeTextXml,
+};
+
+// Returns a standard HTTP Content-Type string.
+const char *GetHttpContentTypeString(HttpContentType type);
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_HTTP_COMMON_H_
diff --git a/common/http_fetcher.cc b/common/http_fetcher.cc
new file mode 100644
index 0000000..400b43c
--- /dev/null
+++ b/common/http_fetcher.cc
@@ -0,0 +1,82 @@
+//
+// 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/common/http_fetcher.h"
+
+#include <base/bind.h>
+
+using base::Closure;
+using brillo::MessageLoop;
+using std::deque;
+using std::string;
+
+namespace chromeos_update_engine {
+
+HttpFetcher::~HttpFetcher() {
+  if (no_resolver_idle_id_ != MessageLoop::kTaskIdNull) {
+    MessageLoop::current()->CancelTask(no_resolver_idle_id_);
+    no_resolver_idle_id_ = MessageLoop::kTaskIdNull;
+  }
+}
+
+void HttpFetcher::SetPostData(const void* data, size_t size,
+                              HttpContentType type) {
+  post_data_set_ = true;
+  post_data_.clear();
+  const char* char_data = reinterpret_cast<const char*>(data);
+  post_data_.insert(post_data_.end(), char_data, char_data + size);
+  post_content_type_ = type;
+}
+
+void HttpFetcher::SetPostData(const void* data, size_t size) {
+  SetPostData(data, size, kHttpContentTypeUnspecified);
+}
+
+// Proxy methods to set the proxies, then to pop them off.
+bool HttpFetcher::ResolveProxiesForUrl(const string& url,
+                                       const Closure& callback) {
+  CHECK_EQ(static_cast<Closure*>(nullptr), callback_.get());
+  callback_.reset(new Closure(callback));
+
+  if (!proxy_resolver_) {
+    LOG(INFO) << "Not resolving proxies (no proxy resolver).";
+    no_resolver_idle_id_ = MessageLoop::current()->PostTask(
+        FROM_HERE,
+        base::Bind(&HttpFetcher::NoProxyResolverCallback,
+                   base::Unretained(this)));
+    return true;
+  }
+  return proxy_resolver_->GetProxiesForUrl(url,
+                                           &HttpFetcher::StaticProxiesResolved,
+                                           this);
+}
+
+void HttpFetcher::NoProxyResolverCallback() {
+  ProxiesResolved(deque<string>());
+}
+
+void HttpFetcher::ProxiesResolved(const deque<string>& proxies) {
+  no_resolver_idle_id_ = MessageLoop::kTaskIdNull;
+  if (!proxies.empty())
+    SetProxies(proxies);
+  CHECK_NE(static_cast<Closure*>(nullptr), callback_.get());
+  Closure* callback = callback_.release();
+  // This may indirectly call back into ResolveProxiesForUrl():
+  callback->Run();
+  delete callback;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/http_fetcher.h b/common/http_fetcher.h
new file mode 100644
index 0000000..11e8e9f
--- /dev/null
+++ b/common/http_fetcher.h
@@ -0,0 +1,197 @@
+//
+// 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_COMMON_HTTP_FETCHER_H_
+#define UPDATE_ENGINE_COMMON_HTTP_FETCHER_H_
+
+#include <deque>
+#include <string>
+#include <vector>
+
+#include <base/callback.h>
+#include <base/logging.h>
+#include <base/macros.h>
+#include <brillo/message_loops/message_loop.h>
+
+#include "update_engine/common/http_common.h"
+#include "update_engine/proxy_resolver.h"
+
+// This class is a simple wrapper around an HTTP library (libcurl). We can
+// easily mock out this interface for testing.
+
+// Implementations of this class should use asynchronous i/o. They can access
+// the MessageLoop to request callbacks when timers or file descriptors change.
+
+namespace chromeos_update_engine {
+
+class HttpFetcherDelegate;
+
+class HttpFetcher {
+ public:
+  // |proxy_resolver| is the resolver that will be consulted for proxy
+  // settings. It may be null, in which case direct connections will
+  // be used. Does not take ownership of the resolver.
+  HttpFetcher(ProxyResolver* proxy_resolver)
+      : post_data_set_(false),
+        http_response_code_(0),
+        delegate_(nullptr),
+        proxies_(1, kNoProxy),
+        proxy_resolver_(proxy_resolver),
+        callback_(nullptr) {}
+  virtual ~HttpFetcher();
+
+  void set_delegate(HttpFetcherDelegate* delegate) { delegate_ = delegate; }
+  HttpFetcherDelegate* delegate() const { return delegate_; }
+  int http_response_code() const { return http_response_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.
+  void SetPostData(const void* data, size_t size, HttpContentType type);
+
+  // Same without a specified Content-Type.
+  void SetPostData(const void* data, size_t size);
+
+  // Proxy methods to set the proxies, then to pop them off.
+  // Returns true on success.
+  bool ResolveProxiesForUrl(const std::string& url,
+                            const base::Closure& callback);
+
+  void SetProxies(const std::deque<std::string>& proxies) {
+    proxies_ = proxies;
+  }
+  const std::string& GetCurrentProxy() const {
+    return proxies_.front();
+  }
+  bool HasProxy() const { return !proxies_.empty(); }
+  void PopProxy() { proxies_.pop_front(); }
+
+  // Downloading should resume from this offset
+  virtual void SetOffset(off_t offset) = 0;
+
+  // Set/unset the length of the range to be downloaded.
+  virtual void SetLength(size_t length) = 0;
+  virtual void UnsetLength() = 0;
+
+  // Begins the transfer to the specified URL. This fetcher instance should not
+  // be destroyed until either TransferComplete, or TransferTerminated is
+  // called.
+  virtual void BeginTransfer(const std::string& url) = 0;
+
+  // Aborts the transfer. The transfer may not abort right away -- delegate's
+  // TransferTerminated() will be called when the transfer is actually done.
+  virtual void TerminateTransfer() = 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.
+  virtual void Pause() = 0;
+
+  // Used to unpause an HttpFetcher and let the bytes stream in again.
+  // If a delegate is set, ReceivedBytes() may be called on it before
+  // Unpause() returns
+  virtual void Unpause() = 0;
+
+  // These two function are overloaded in LibcurlHttp fetcher to speed
+  // testing.
+  virtual void set_idle_seconds(int seconds) {}
+  virtual void set_retry_seconds(int seconds) {}
+
+  // Sets the values used to time out the connection if the transfer
+  // rate is less than |low_speed_bps| bytes/sec for more than
+  // |low_speed_sec| seconds.
+  virtual void set_low_speed_limit(int low_speed_bps, int low_speed_sec) = 0;
+
+  // Sets the connect timeout, e.g. the maximum amount of time willing
+  // to wait for establishing a connection to the server.
+  virtual void set_connect_timeout(int connect_timeout_seconds) = 0;
+
+  // Sets the number of allowed retries.
+  virtual void set_max_retry_count(int max_retry_count) = 0;
+
+  // Get the total number of bytes downloaded by fetcher.
+  virtual size_t GetBytesDownloaded() = 0;
+
+  ProxyResolver* proxy_resolver() const { return proxy_resolver_; }
+
+ protected:
+  // The URL we're actively fetching from
+  std::string url_;
+
+  // POST data for the transfer, and whether or not it was ever set
+  bool post_data_set_;
+  brillo::Blob post_data_;
+  HttpContentType post_content_type_;
+
+  // The server's HTTP response code from the last transfer. This
+  // field should be set to 0 when a new transfer is initiated, and
+  // set to the response code when the transfer is complete.
+  int http_response_code_;
+
+  // The delegate; may be null.
+  HttpFetcherDelegate* delegate_;
+
+  // Proxy servers
+  std::deque<std::string> proxies_;
+
+  ProxyResolver* const proxy_resolver_;
+
+  // The ID of the idle callback, used when we have no proxy resolver.
+  brillo::MessageLoop::TaskId no_resolver_idle_id_{
+      brillo::MessageLoop::kTaskIdNull};
+
+  // Callback for when we are resolving proxies
+  std::unique_ptr<base::Closure> callback_;
+
+ private:
+  // Callback from the proxy resolver
+  void ProxiesResolved(const std::deque<std::string>& proxies);
+  static void StaticProxiesResolved(const std::deque<std::string>& proxies,
+                                    void* data) {
+    reinterpret_cast<HttpFetcher*>(data)->ProxiesResolved(proxies);
+  }
+
+  // Callback used to run the proxy resolver callback when there is no
+  // |proxy_resolver_|.
+  void NoProxyResolverCallback();
+
+  DISALLOW_COPY_AND_ASSIGN(HttpFetcher);
+};
+
+// Interface for delegates
+class HttpFetcherDelegate {
+ public:
+  virtual ~HttpFetcherDelegate() = default;
+
+  // Called every time bytes are received.
+  virtual void ReceivedBytes(HttpFetcher* fetcher,
+                             const void* bytes,
+                             size_t length) = 0;
+
+  // Called if the fetcher seeks to a particular offset.
+  virtual void SeekToOffset(off_t offset) {}
+
+  // When a transfer has completed, exactly one of these two methods will be
+  // called. TransferTerminated is called when the transfer has been aborted
+  // through TerminateTransfer. TransferComplete is called in all other
+  // situations. It's OK to destroy the |fetcher| object in this callback.
+  virtual void TransferComplete(HttpFetcher* fetcher, bool successful) = 0;
+  virtual void TransferTerminated(HttpFetcher* fetcher) {}
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_HTTP_FETCHER_H_
diff --git a/common/http_fetcher_unittest.cc b/common/http_fetcher_unittest.cc
new file mode 100644
index 0000000..17e360e
--- /dev/null
+++ b/common/http_fetcher_unittest.cc
@@ -0,0 +1,1138 @@
+//
+// 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 <netinet/in.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/location.h>
+#include <base/logging.h>
+#include <base/message_loop/message_loop.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/bind_lambda.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 <brillo/process.h>
+#include <brillo/streams/file_stream.h>
+#include <brillo/streams/stream.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/fake_hardware.h"
+#include "update_engine/common/http_common.h"
+#include "update_engine/common/libcurl_http_fetcher.h"
+#include "update_engine/common/mock_http_fetcher.h"
+#include "update_engine/common/multi_range_http_fetcher.h"
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/proxy_resolver.h"
+
+using brillo::MessageLoop;
+using std::make_pair;
+using std::pair;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+namespace {
+
+const int kBigLength           = 100000;
+const int kMediumLength        = 1000;
+const int kFlakyTruncateLength = 29000;
+const int kFlakySleepEvery     = 3;
+const int kFlakySleepSecs      = 10;
+
+}  // namespace
+
+namespace chromeos_update_engine {
+
+static const char *kUnusedUrl = "unused://unused";
+
+static inline string LocalServerUrlForPath(in_port_t port,
+                                           const string& path) {
+  string port_str = (port ? base::StringPrintf(":%hu", port) : "");
+  return base::StringPrintf("http://127.0.0.1%s%s", port_str.c_str(),
+                            path.c_str());
+}
+
+//
+// Class hierarchy for HTTP server implementations.
+//
+
+class HttpServer {
+ public:
+  // This makes it an abstract class (dirty but works).
+  virtual ~HttpServer() = 0;
+
+  virtual in_port_t GetPort() const {
+    return 0;
+  }
+
+  bool started_;
+};
+
+HttpServer::~HttpServer() {}
+
+
+class NullHttpServer : public HttpServer {
+ public:
+  NullHttpServer() {
+    started_ = true;
+  }
+};
+
+
+class PythonHttpServer : public HttpServer {
+ public:
+  PythonHttpServer() : port_(0) {
+    started_ = false;
+
+    // Spawn the server process.
+    unique_ptr<brillo::Process> http_server(new brillo::ProcessImpl());
+    base::FilePath test_server_path =
+        test_utils::GetBuildArtifactsPath().Append("test_http_server");
+    http_server->AddArg(test_server_path.value());
+    http_server->RedirectUsingPipe(STDOUT_FILENO, false);
+
+    if (!http_server->Start()) {
+      ADD_FAILURE() << "failed to spawn http server process";
+      return;
+    }
+    LOG(INFO) << "started http server with pid " << http_server->pid();
+
+    // Wait for server to begin accepting connections, obtain its port.
+    brillo::StreamPtr stdout = brillo::FileStream::FromFileDescriptor(
+        http_server->GetPipe(STDOUT_FILENO), false /* own */, nullptr);
+    if (!stdout)
+      return;
+
+    vector<char> buf(128);
+    string line;
+    while (line.find('\n') == string::npos) {
+      size_t read;
+      if (!stdout->ReadBlocking(buf.data(), buf.size(), &read, nullptr)) {
+        ADD_FAILURE() << "error reading http server stdout";
+        return;
+      }
+      line.append(buf.data(), read);
+      if (read == 0)
+        break;
+    }
+    // Parse the port from the output line.
+    const size_t listening_msg_prefix_len = strlen(kServerListeningMsgPrefix);
+    if (line.size() < listening_msg_prefix_len) {
+      ADD_FAILURE() << "server output too short";
+      return;
+    }
+
+    EXPECT_EQ(kServerListeningMsgPrefix,
+              line.substr(0, listening_msg_prefix_len));
+    string port_str = line.substr(listening_msg_prefix_len);
+    port_str.resize(port_str.find('\n'));
+    EXPECT_TRUE(base::StringToUint(port_str, &port_));
+
+    started_ = true;
+    LOG(INFO) << "server running, listening on port " << port_;
+
+    // Any failure before this point will SIGKILL the test server if started
+    // when the |http_server| goes out of scope.
+    http_server_ = std::move(http_server);
+  }
+
+  ~PythonHttpServer() {
+    // If there's no process, do nothing.
+    if (!http_server_)
+      return;
+    // Wait up to 10 seconds for the process to finish. Destroying the process
+    // will kill it with a SIGKILL otherwise.
+    http_server_->Kill(SIGTERM, 10);
+  }
+
+  in_port_t GetPort() const override {
+    return port_;
+  }
+
+ private:
+  static const char* kServerListeningMsgPrefix;
+
+  unique_ptr<brillo::Process> http_server_;
+  unsigned int port_;
+};
+
+const char* PythonHttpServer::kServerListeningMsgPrefix = "listening on port ";
+
+//
+// Class hierarchy for HTTP fetcher test wrappers.
+//
+
+class AnyHttpFetcherTest {
+ public:
+  AnyHttpFetcherTest() {}
+  virtual ~AnyHttpFetcherTest() {}
+
+  virtual HttpFetcher* NewLargeFetcher(size_t num_proxies) = 0;
+  HttpFetcher* NewLargeFetcher() {
+    return NewLargeFetcher(1);
+  }
+
+  virtual HttpFetcher* NewSmallFetcher(size_t num_proxies) = 0;
+  HttpFetcher* NewSmallFetcher() {
+    return NewSmallFetcher(1);
+  }
+
+  virtual string BigUrl(in_port_t port) const { return kUnusedUrl; }
+  virtual string SmallUrl(in_port_t port) const { return kUnusedUrl; }
+  virtual string ErrorUrl(in_port_t port) const { return kUnusedUrl; }
+
+  virtual bool IsMock() const = 0;
+  virtual bool IsMulti() const = 0;
+
+  virtual void IgnoreServerAborting(HttpServer* server) const {}
+
+  virtual HttpServer* CreateServer() = 0;
+
+  FakeHardware* fake_hardware() {
+    return &fake_hardware_;
+  }
+
+ protected:
+  DirectProxyResolver proxy_resolver_;
+  FakeHardware fake_hardware_;
+};
+
+class MockHttpFetcherTest : public AnyHttpFetcherTest {
+ public:
+  // Necessary to unhide the definition in the base class.
+  using AnyHttpFetcherTest::NewLargeFetcher;
+  HttpFetcher* NewLargeFetcher(size_t num_proxies) override {
+    brillo::Blob big_data(1000000);
+    CHECK_GT(num_proxies, 0u);
+    proxy_resolver_.set_num_proxies(num_proxies);
+    return new MockHttpFetcher(
+        big_data.data(),
+        big_data.size(),
+        reinterpret_cast<ProxyResolver*>(&proxy_resolver_));
+  }
+
+  // Necessary to unhide the definition in the base class.
+  using AnyHttpFetcherTest::NewSmallFetcher;
+  HttpFetcher* NewSmallFetcher(size_t num_proxies) override {
+    CHECK_GT(num_proxies, 0u);
+    proxy_resolver_.set_num_proxies(num_proxies);
+    return new MockHttpFetcher(
+        "x",
+        1,
+        reinterpret_cast<ProxyResolver*>(&proxy_resolver_));
+  }
+
+  bool IsMock() const override { return true; }
+  bool IsMulti() const override { return false; }
+
+  HttpServer* CreateServer() override {
+    return new NullHttpServer;
+  }
+};
+
+class LibcurlHttpFetcherTest : public AnyHttpFetcherTest {
+ public:
+  // Necessary to unhide the definition in the base class.
+  using AnyHttpFetcherTest::NewLargeFetcher;
+  HttpFetcher* NewLargeFetcher(size_t num_proxies) override {
+    CHECK_GT(num_proxies, 0u);
+    proxy_resolver_.set_num_proxies(num_proxies);
+    LibcurlHttpFetcher *ret = new
+        LibcurlHttpFetcher(reinterpret_cast<ProxyResolver*>(&proxy_resolver_),
+                           &fake_hardware_);
+    // Speed up test execution.
+    ret->set_idle_seconds(1);
+    ret->set_retry_seconds(1);
+    fake_hardware_.SetIsOfficialBuild(false);
+    return ret;
+  }
+
+  // Necessary to unhide the definition in the base class.
+  using AnyHttpFetcherTest::NewSmallFetcher;
+  HttpFetcher* NewSmallFetcher(size_t num_proxies) override {
+    return NewLargeFetcher(num_proxies);
+  }
+
+  string BigUrl(in_port_t port) const override {
+    return LocalServerUrlForPath(port,
+                                 base::StringPrintf("/download/%d",
+                                                    kBigLength));
+  }
+  string SmallUrl(in_port_t port) const override {
+    return LocalServerUrlForPath(port, "/foo");
+  }
+  string ErrorUrl(in_port_t port) const override {
+    return LocalServerUrlForPath(port, "/error");
+  }
+
+  bool IsMock() const override { return false; }
+  bool IsMulti() const override { return false; }
+
+  void IgnoreServerAborting(HttpServer* server) const override {
+    // Nothing to do.
+  }
+
+  HttpServer* CreateServer() override {
+    return new PythonHttpServer;
+  }
+};
+
+class MultiRangeHttpFetcherTest : public LibcurlHttpFetcherTest {
+ public:
+  // Necessary to unhide the definition in the base class.
+  using AnyHttpFetcherTest::NewLargeFetcher;
+  HttpFetcher* NewLargeFetcher(size_t num_proxies) override {
+    CHECK_GT(num_proxies, 0u);
+    proxy_resolver_.set_num_proxies(num_proxies);
+    ProxyResolver* resolver =
+        reinterpret_cast<ProxyResolver*>(&proxy_resolver_);
+    MultiRangeHttpFetcher *ret =
+        new MultiRangeHttpFetcher(
+            new LibcurlHttpFetcher(resolver, &fake_hardware_));
+    ret->ClearRanges();
+    ret->AddRange(0);
+    // Speed up test execution.
+    ret->set_idle_seconds(1);
+    ret->set_retry_seconds(1);
+    fake_hardware_.SetIsOfficialBuild(false);
+    return ret;
+  }
+
+  // Necessary to unhide the definition in the base class.
+  using AnyHttpFetcherTest::NewSmallFetcher;
+  HttpFetcher* NewSmallFetcher(size_t num_proxies) override {
+    return NewLargeFetcher(num_proxies);
+  }
+
+  bool IsMulti() const override { return true; }
+};
+
+
+//
+// Infrastructure for type tests of HTTP fetcher.
+// See: http://code.google.com/p/googletest/wiki/AdvancedGuide#Typed_Tests
+//
+
+// Fixture class template. We use an explicit constraint to guarantee that it
+// can only be instantiated with an AnyHttpFetcherTest type, see:
+// http://www2.research.att.com/~bs/bs_faq2.html#constraints
+template <typename T>
+class HttpFetcherTest : public ::testing::Test {
+ public:
+  base::MessageLoopForIO base_loop_;
+  brillo::BaseMessageLoop loop_{&base_loop_};
+
+  T test_;
+
+ protected:
+  HttpFetcherTest() {
+    loop_.SetAsCurrent();
+  }
+
+  void TearDown() override {
+    EXPECT_EQ(0, brillo::MessageLoopRunMaxIterations(&loop_, 1));
+  }
+
+ private:
+  static void TypeConstraint(T* a) {
+    AnyHttpFetcherTest *b = a;
+    if (b == 0)  // Silence compiler warning of unused variable.
+      *b = a;
+  }
+};
+
+// Test case types list.
+typedef ::testing::Types<LibcurlHttpFetcherTest,
+                         MockHttpFetcherTest,
+                         MultiRangeHttpFetcherTest> HttpFetcherTestTypes;
+TYPED_TEST_CASE(HttpFetcherTest, HttpFetcherTestTypes);
+
+
+namespace {
+class HttpFetcherTestDelegate : public HttpFetcherDelegate {
+ public:
+  HttpFetcherTestDelegate() :
+      is_expect_error_(false), times_transfer_complete_called_(0),
+      times_transfer_terminated_called_(0), times_received_bytes_called_(0) {}
+
+  void ReceivedBytes(HttpFetcher* /* fetcher */,
+                     const void* /* bytes */, size_t /* length */) override {
+    // Update counters
+    times_received_bytes_called_++;
+  }
+
+  void TransferComplete(HttpFetcher* fetcher, bool successful) override {
+    if (is_expect_error_)
+      EXPECT_EQ(kHttpResponseNotFound, fetcher->http_response_code());
+    else
+      EXPECT_EQ(kHttpResponseOk, fetcher->http_response_code());
+    MessageLoop::current()->BreakLoop();
+
+    // Update counter
+    times_transfer_complete_called_++;
+  }
+
+  void TransferTerminated(HttpFetcher* fetcher) override {
+    ADD_FAILURE();
+    times_transfer_terminated_called_++;
+  }
+
+  // Are we expecting an error response? (default: no)
+  bool is_expect_error_;
+
+  // Counters for callback invocations.
+  int times_transfer_complete_called_;
+  int times_transfer_terminated_called_;
+  int times_received_bytes_called_;
+};
+
+
+void StartTransfer(HttpFetcher* http_fetcher, const string& url) {
+  http_fetcher->BeginTransfer(url);
+}
+}  // namespace
+
+TYPED_TEST(HttpFetcherTest, SimpleTest) {
+  HttpFetcherTestDelegate delegate;
+  unique_ptr<HttpFetcher> fetcher(this->test_.NewSmallFetcher());
+  fetcher->set_delegate(&delegate);
+
+  unique_ptr<HttpServer> server(this->test_.CreateServer());
+  ASSERT_TRUE(server->started_);
+
+  this->loop_.PostTask(FROM_HERE, base::Bind(
+      StartTransfer,
+      fetcher.get(),
+      this->test_.SmallUrl(server->GetPort())));
+  this->loop_.Run();
+}
+
+TYPED_TEST(HttpFetcherTest, SimpleBigTest) {
+  HttpFetcherTestDelegate delegate;
+  unique_ptr<HttpFetcher> fetcher(this->test_.NewLargeFetcher());
+  fetcher->set_delegate(&delegate);
+
+  unique_ptr<HttpServer> server(this->test_.CreateServer());
+  ASSERT_TRUE(server->started_);
+
+  this->loop_.PostTask(FROM_HERE, base::Bind(
+      StartTransfer,
+      fetcher.get(),
+      this->test_.BigUrl(server->GetPort())));
+  this->loop_.Run();
+}
+
+// Issue #9648: when server returns an error HTTP response, the fetcher needs to
+// terminate transfer prematurely, rather than try to process the error payload.
+TYPED_TEST(HttpFetcherTest, ErrorTest) {
+  if (this->test_.IsMock() || this->test_.IsMulti())
+    return;
+  HttpFetcherTestDelegate delegate;
+
+  // Delegate should expect an error response.
+  delegate.is_expect_error_ = true;
+
+  unique_ptr<HttpFetcher> fetcher(this->test_.NewSmallFetcher());
+  fetcher->set_delegate(&delegate);
+
+  unique_ptr<HttpServer> server(this->test_.CreateServer());
+  ASSERT_TRUE(server->started_);
+
+  this->loop_.PostTask(FROM_HERE, base::Bind(
+      StartTransfer,
+      fetcher.get(),
+      this->test_.ErrorUrl(server->GetPort())));
+  this->loop_.Run();
+
+  // Make sure that no bytes were received.
+  CHECK_EQ(delegate.times_received_bytes_called_, 0);
+  CHECK_EQ(fetcher->GetBytesDownloaded(), static_cast<size_t>(0));
+
+  // Make sure that transfer completion was signaled once, and no termination
+  // was signaled.
+  CHECK_EQ(delegate.times_transfer_complete_called_, 1);
+  CHECK_EQ(delegate.times_transfer_terminated_called_, 0);
+}
+
+namespace {
+class PausingHttpFetcherTestDelegate : public HttpFetcherDelegate {
+ public:
+  void ReceivedBytes(HttpFetcher* fetcher,
+                     const void* /* bytes */, size_t /* length */) override {
+    CHECK(!paused_);
+    paused_ = true;
+    fetcher->Pause();
+  }
+  void TransferComplete(HttpFetcher* fetcher, bool successful) override {
+    MessageLoop::current()->BreakLoop();
+  }
+  void TransferTerminated(HttpFetcher* fetcher) override {
+    ADD_FAILURE();
+  }
+  void Unpause() {
+    CHECK(paused_);
+    paused_ = false;
+    fetcher_->Unpause();
+  }
+  bool paused_;
+  HttpFetcher* fetcher_;
+};
+
+void UnpausingTimeoutCallback(PausingHttpFetcherTestDelegate* delegate,
+                              MessageLoop::TaskId* my_id) {
+  if (delegate->paused_)
+    delegate->Unpause();
+  // Update the task id with the new scheduled callback.
+  *my_id = MessageLoop::current()->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&UnpausingTimeoutCallback, delegate, my_id),
+      base::TimeDelta::FromMilliseconds(200));
+}
+}  // namespace
+
+TYPED_TEST(HttpFetcherTest, PauseTest) {
+  {
+    PausingHttpFetcherTestDelegate delegate;
+    unique_ptr<HttpFetcher> fetcher(this->test_.NewLargeFetcher());
+    delegate.paused_ = false;
+    delegate.fetcher_ = fetcher.get();
+    fetcher->set_delegate(&delegate);
+
+    unique_ptr<HttpServer> server(this->test_.CreateServer());
+    ASSERT_TRUE(server->started_);
+
+    MessageLoop::TaskId callback_id;
+    callback_id = this->loop_.PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&UnpausingTimeoutCallback, &delegate, &callback_id),
+        base::TimeDelta::FromMilliseconds(200));
+    fetcher->BeginTransfer(this->test_.BigUrl(server->GetPort()));
+
+    this->loop_.Run();
+    EXPECT_TRUE(this->loop_.CancelTask(callback_id));
+  }
+}
+
+namespace {
+class AbortingHttpFetcherTestDelegate : public HttpFetcherDelegate {
+ public:
+  void ReceivedBytes(HttpFetcher* fetcher,
+                     const void* bytes, size_t length) override {}
+  void TransferComplete(HttpFetcher* fetcher, bool successful) override {
+    ADD_FAILURE();  // We should never get here
+    MessageLoop::current()->BreakLoop();
+  }
+  void TransferTerminated(HttpFetcher* fetcher) override {
+    EXPECT_EQ(fetcher, fetcher_.get());
+    EXPECT_FALSE(once_);
+    EXPECT_TRUE(callback_once_);
+    callback_once_ = false;
+    // The fetcher could have a callback scheduled on the ProxyResolver that
+    // can fire after this callback. We wait until the end of the test to
+    // delete the fetcher.
+  }
+  void TerminateTransfer() {
+    CHECK(once_);
+    once_ = false;
+    fetcher_->TerminateTransfer();
+  }
+  void EndLoop() {
+    MessageLoop::current()->BreakLoop();
+  }
+  bool once_;
+  bool callback_once_;
+  unique_ptr<HttpFetcher> fetcher_;
+};
+
+void AbortingTimeoutCallback(AbortingHttpFetcherTestDelegate* delegate,
+                             MessageLoop::TaskId* my_id) {
+  if (delegate->once_) {
+    delegate->TerminateTransfer();
+    *my_id = MessageLoop::current()->PostTask(
+        FROM_HERE,
+        base::Bind(AbortingTimeoutCallback, delegate, my_id));
+  } else {
+    delegate->EndLoop();
+    *my_id = MessageLoop::kTaskIdNull;
+  }
+}
+}  // namespace
+
+TYPED_TEST(HttpFetcherTest, AbortTest) {
+  AbortingHttpFetcherTestDelegate delegate;
+  delegate.fetcher_.reset(this->test_.NewLargeFetcher());
+  delegate.once_ = true;
+  delegate.callback_once_ = true;
+  delegate.fetcher_->set_delegate(&delegate);
+
+  unique_ptr<HttpServer> server(this->test_.CreateServer());
+  this->test_.IgnoreServerAborting(server.get());
+  ASSERT_TRUE(server->started_);
+
+  MessageLoop::TaskId task_id = MessageLoop::kTaskIdNull;
+
+  task_id = this->loop_.PostTask(
+      FROM_HERE,
+      base::Bind(AbortingTimeoutCallback, &delegate, &task_id));
+  delegate.fetcher_->BeginTransfer(this->test_.BigUrl(server->GetPort()));
+
+  this->loop_.Run();
+  CHECK(!delegate.once_);
+  CHECK(!delegate.callback_once_);
+  this->loop_.CancelTask(task_id);
+}
+
+namespace {
+class FlakyHttpFetcherTestDelegate : public HttpFetcherDelegate {
+ public:
+  void ReceivedBytes(HttpFetcher* fetcher,
+                     const void* bytes, size_t length) override {
+    data.append(reinterpret_cast<const char*>(bytes), length);
+  }
+  void TransferComplete(HttpFetcher* fetcher, bool successful) override {
+    EXPECT_TRUE(successful);
+    EXPECT_EQ(kHttpResponsePartialContent, fetcher->http_response_code());
+    MessageLoop::current()->BreakLoop();
+  }
+  void TransferTerminated(HttpFetcher* fetcher) override {
+    ADD_FAILURE();
+  }
+  string data;
+};
+}  // namespace
+
+TYPED_TEST(HttpFetcherTest, FlakyTest) {
+  if (this->test_.IsMock())
+    return;
+  {
+    FlakyHttpFetcherTestDelegate delegate;
+    unique_ptr<HttpFetcher> fetcher(this->test_.NewSmallFetcher());
+    fetcher->set_delegate(&delegate);
+
+    unique_ptr<HttpServer> server(this->test_.CreateServer());
+    ASSERT_TRUE(server->started_);
+
+    this->loop_.PostTask(FROM_HERE, base::Bind(
+        &StartTransfer,
+        fetcher.get(),
+        LocalServerUrlForPath(server->GetPort(),
+                              base::StringPrintf("/flaky/%d/%d/%d/%d",
+                                                 kBigLength,
+                                                 kFlakyTruncateLength,
+                                                 kFlakySleepEvery,
+                                                 kFlakySleepSecs))));
+    this->loop_.Run();
+
+    // verify the data we get back
+    ASSERT_EQ(kBigLength, delegate.data.size());
+    for (int i = 0; i < kBigLength; i += 10) {
+      // Assert so that we don't flood the screen w/ EXPECT errors on failure.
+      ASSERT_EQ(delegate.data.substr(i, 10), "abcdefghij");
+    }
+  }
+}
+
+namespace {
+// This delegate kills the server attached to it after receiving any bytes.
+// This can be used for testing what happens when you try to fetch data and
+// the server dies.
+class FailureHttpFetcherTestDelegate : public HttpFetcherDelegate {
+ public:
+  explicit FailureHttpFetcherTestDelegate(PythonHttpServer* server)
+      : server_(server) {}
+
+  ~FailureHttpFetcherTestDelegate() override {
+    if (server_) {
+      LOG(INFO) << "Stopping server in destructor";
+      delete server_;
+      LOG(INFO) << "server stopped";
+    }
+  }
+
+  void ReceivedBytes(HttpFetcher* fetcher,
+                     const void* bytes, size_t length) override {
+    if (server_) {
+      LOG(INFO) << "Stopping server in ReceivedBytes";
+      delete server_;
+      LOG(INFO) << "server stopped";
+      server_ = nullptr;
+    }
+  }
+  void TransferComplete(HttpFetcher* fetcher, bool successful) override {
+    EXPECT_FALSE(successful);
+    EXPECT_EQ(0, fetcher->http_response_code());
+    MessageLoop::current()->BreakLoop();
+  }
+  void TransferTerminated(HttpFetcher* fetcher) override {
+    ADD_FAILURE();
+  }
+  PythonHttpServer* server_;
+};
+}  // namespace
+
+
+TYPED_TEST(HttpFetcherTest, FailureTest) {
+  // This test ensures that a fetcher responds correctly when a server isn't
+  // available at all.
+  if (this->test_.IsMock())
+    return;
+  {
+    FailureHttpFetcherTestDelegate delegate(nullptr);
+    unique_ptr<HttpFetcher> fetcher(this->test_.NewSmallFetcher());
+    fetcher->set_delegate(&delegate);
+
+    this->loop_.PostTask(FROM_HERE,
+                         base::Bind(StartTransfer,
+                                    fetcher.get(),
+                                    "http://host_doesnt_exist99999999"));
+    this->loop_.Run();
+
+    // Exiting and testing happens in the delegate
+  }
+}
+
+TYPED_TEST(HttpFetcherTest, NoResponseTest) {
+  // This test starts a new http server but the server doesn't respond and just
+  // closes the connection.
+  if (this->test_.IsMock())
+    return;
+
+  PythonHttpServer* server = new PythonHttpServer();
+  int port = server->GetPort();
+  ASSERT_TRUE(server->started_);
+
+  // Handles destruction and claims ownership.
+  FailureHttpFetcherTestDelegate delegate(server);
+  unique_ptr<HttpFetcher> fetcher(this->test_.NewSmallFetcher());
+  fetcher->set_delegate(&delegate);
+  // The server will not reply at all, so we can limit the execution time of the
+  // test by reducing the low-speed timeout to something small. The test will
+  // finish once the TimeoutCallback() triggers (every second) and the timeout
+  // expired.
+  fetcher->set_low_speed_limit(kDownloadLowSpeedLimitBps, 1);
+
+  this->loop_.PostTask(FROM_HERE, base::Bind(
+      StartTransfer,
+      fetcher.get(),
+      LocalServerUrlForPath(port, "/hang")));
+  this->loop_.Run();
+
+  // Check that no other callback runs in the next two seconds. That would
+  // indicate a leaked callback.
+  bool timeout = false;
+  auto callback = base::Bind([&timeout]{ timeout = true;});
+  this->loop_.PostDelayedTask(FROM_HERE, callback,
+                              base::TimeDelta::FromSeconds(2));
+  EXPECT_TRUE(this->loop_.RunOnce(true));
+  EXPECT_TRUE(timeout);
+}
+
+TYPED_TEST(HttpFetcherTest, ServerDiesTest) {
+  // This test starts a new http server and kills it after receiving its first
+  // set of bytes. It test whether or not our fetcher eventually gives up on
+  // retries and aborts correctly.
+  if (this->test_.IsMock())
+    return;
+  {
+    PythonHttpServer* server = new PythonHttpServer();
+    int port = server->GetPort();
+    ASSERT_TRUE(server->started_);
+
+    // Handles destruction and claims ownership.
+    FailureHttpFetcherTestDelegate delegate(server);
+    unique_ptr<HttpFetcher> fetcher(this->test_.NewSmallFetcher());
+    fetcher->set_delegate(&delegate);
+
+    this->loop_.PostTask(FROM_HERE, base::Bind(
+        StartTransfer,
+        fetcher.get(),
+        LocalServerUrlForPath(port,
+                              base::StringPrintf("/flaky/%d/%d/%d/%d",
+                                                 kBigLength,
+                                                 kFlakyTruncateLength,
+                                                 kFlakySleepEvery,
+                                                 kFlakySleepSecs))));
+    this->loop_.Run();
+
+    // Exiting and testing happens in the delegate
+  }
+}
+
+namespace {
+const HttpResponseCode kRedirectCodes[] = {
+  kHttpResponseMovedPermanently, kHttpResponseFound, kHttpResponseSeeOther,
+  kHttpResponseTempRedirect
+};
+
+class RedirectHttpFetcherTestDelegate : public HttpFetcherDelegate {
+ public:
+  explicit RedirectHttpFetcherTestDelegate(bool expected_successful)
+      : expected_successful_(expected_successful) {}
+  void ReceivedBytes(HttpFetcher* fetcher,
+                     const void* bytes, size_t length) override {
+    data.append(reinterpret_cast<const char*>(bytes), length);
+  }
+  void TransferComplete(HttpFetcher* fetcher, bool successful) override {
+    EXPECT_EQ(expected_successful_, successful);
+    if (expected_successful_) {
+      EXPECT_EQ(kHttpResponseOk, fetcher->http_response_code());
+    } else {
+      EXPECT_GE(fetcher->http_response_code(), kHttpResponseMovedPermanently);
+      EXPECT_LE(fetcher->http_response_code(), kHttpResponseTempRedirect);
+    }
+    MessageLoop::current()->BreakLoop();
+  }
+  void TransferTerminated(HttpFetcher* fetcher) override {
+    ADD_FAILURE();
+  }
+  bool expected_successful_;
+  string data;
+};
+
+// RedirectTest takes ownership of |http_fetcher|.
+void RedirectTest(const HttpServer* server,
+                  bool expected_successful,
+                  const string& url,
+                  HttpFetcher* http_fetcher) {
+  RedirectHttpFetcherTestDelegate delegate(expected_successful);
+  unique_ptr<HttpFetcher> fetcher(http_fetcher);
+  fetcher->set_delegate(&delegate);
+
+  MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+      StartTransfer,
+      fetcher.get(),
+      LocalServerUrlForPath(server->GetPort(), url)));
+  MessageLoop::current()->Run();
+  if (expected_successful) {
+    // verify the data we get back
+    ASSERT_EQ(kMediumLength, delegate.data.size());
+    for (int i = 0; i < kMediumLength; i += 10) {
+      // Assert so that we don't flood the screen w/ EXPECT errors on failure.
+      ASSERT_EQ(delegate.data.substr(i, 10), "abcdefghij");
+    }
+  }
+}
+}  // namespace
+
+TYPED_TEST(HttpFetcherTest, SimpleRedirectTest) {
+  if (this->test_.IsMock())
+    return;
+
+  unique_ptr<HttpServer> server(this->test_.CreateServer());
+  ASSERT_TRUE(server->started_);
+
+  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());
+  }
+}
+
+TYPED_TEST(HttpFetcherTest, MaxRedirectTest) {
+  if (this->test_.IsMock())
+    return;
+
+  unique_ptr<HttpServer> server(this->test_.CreateServer());
+  ASSERT_TRUE(server->started_);
+
+  string url;
+  for (int r = 0; r < kDownloadMaxRedirects; r++) {
+    url += base::StringPrintf("/redirect/%d",
+                              kRedirectCodes[r % arraysize(kRedirectCodes)]);
+  }
+  url += base::StringPrintf("/download/%d", kMediumLength);
+  RedirectTest(server.get(), true, url, this->test_.NewLargeFetcher());
+}
+
+TYPED_TEST(HttpFetcherTest, BeyondMaxRedirectTest) {
+  if (this->test_.IsMock())
+    return;
+
+  unique_ptr<HttpServer> server(this->test_.CreateServer());
+  ASSERT_TRUE(server->started_);
+
+  string url;
+  for (int r = 0; r < kDownloadMaxRedirects + 1; r++) {
+    url += base::StringPrintf("/redirect/%d",
+                              kRedirectCodes[r % arraysize(kRedirectCodes)]);
+  }
+  url += base::StringPrintf("/download/%d", kMediumLength);
+  RedirectTest(server.get(), false, url, this->test_.NewLargeFetcher());
+}
+
+namespace {
+class MultiHttpFetcherTestDelegate : public HttpFetcherDelegate {
+ public:
+  explicit MultiHttpFetcherTestDelegate(int expected_response_code)
+      : expected_response_code_(expected_response_code) {}
+
+  void ReceivedBytes(HttpFetcher* fetcher,
+                     const void* bytes, size_t length) override {
+    EXPECT_EQ(fetcher, fetcher_.get());
+    data.append(reinterpret_cast<const char*>(bytes), length);
+  }
+
+  void TransferComplete(HttpFetcher* fetcher, bool successful) override {
+    EXPECT_EQ(fetcher, fetcher_.get());
+    EXPECT_EQ(expected_response_code_ != kHttpResponseUndefined, successful);
+    if (expected_response_code_ != 0)
+      EXPECT_EQ(expected_response_code_, fetcher->http_response_code());
+    // Destroy the fetcher (because we're allowed to).
+    fetcher_.reset(nullptr);
+    MessageLoop::current()->BreakLoop();
+  }
+
+  void TransferTerminated(HttpFetcher* fetcher) override {
+    ADD_FAILURE();
+  }
+
+  unique_ptr<HttpFetcher> fetcher_;
+  int expected_response_code_;
+  string data;
+};
+
+void MultiTest(HttpFetcher* fetcher_in,
+               FakeHardware* fake_hardware,
+               const string& url,
+               const vector<pair<off_t, off_t>>& ranges,
+               const string& expected_prefix,
+               off_t expected_size,
+               HttpResponseCode expected_response_code) {
+  MultiHttpFetcherTestDelegate delegate(expected_response_code);
+  delegate.fetcher_.reset(fetcher_in);
+
+  MultiRangeHttpFetcher* multi_fetcher =
+      dynamic_cast<MultiRangeHttpFetcher*>(fetcher_in);
+  ASSERT_TRUE(multi_fetcher);
+  multi_fetcher->ClearRanges();
+  for (vector<pair<off_t, off_t>>::const_iterator it = ranges.begin(),
+           e = ranges.end(); it != e; ++it) {
+    string tmp_str = base::StringPrintf("%jd+", it->first);
+    if (it->second > 0) {
+      base::StringAppendF(&tmp_str, "%jd", it->second);
+      multi_fetcher->AddRange(it->first, it->second);
+    } else {
+      base::StringAppendF(&tmp_str, "?");
+      multi_fetcher->AddRange(it->first);
+    }
+    LOG(INFO) << "added range: " << tmp_str;
+  }
+  fake_hardware->SetIsOfficialBuild(false);
+  multi_fetcher->set_delegate(&delegate);
+
+  MessageLoop::current()->PostTask(
+      FROM_HERE,
+      base::Bind(StartTransfer, multi_fetcher, url));
+  MessageLoop::current()->Run();
+
+  EXPECT_EQ(expected_size, delegate.data.size());
+  EXPECT_EQ(expected_prefix,
+            string(delegate.data.data(), expected_prefix.size()));
+}
+}  // namespace
+
+TYPED_TEST(HttpFetcherTest, MultiHttpFetcherSimpleTest) {
+  if (!this->test_.IsMulti())
+    return;
+
+  unique_ptr<HttpServer> server(this->test_.CreateServer());
+  ASSERT_TRUE(server->started_);
+
+  vector<pair<off_t, off_t>> ranges;
+  ranges.push_back(make_pair(0, 25));
+  ranges.push_back(make_pair(99, 0));
+  MultiTest(this->test_.NewLargeFetcher(),
+            this->test_.fake_hardware(),
+            this->test_.BigUrl(server->GetPort()),
+            ranges,
+            "abcdefghijabcdefghijabcdejabcdefghijabcdef",
+            kBigLength - (99 - 25),
+            kHttpResponsePartialContent);
+}
+
+TYPED_TEST(HttpFetcherTest, MultiHttpFetcherLengthLimitTest) {
+  if (!this->test_.IsMulti())
+    return;
+
+  unique_ptr<HttpServer> server(this->test_.CreateServer());
+  ASSERT_TRUE(server->started_);
+
+  vector<pair<off_t, off_t>> ranges;
+  ranges.push_back(make_pair(0, 24));
+  MultiTest(this->test_.NewLargeFetcher(),
+            this->test_.fake_hardware(),
+            this->test_.BigUrl(server->GetPort()),
+            ranges,
+            "abcdefghijabcdefghijabcd",
+            24,
+            kHttpResponsePartialContent);
+}
+
+TYPED_TEST(HttpFetcherTest, MultiHttpFetcherMultiEndTest) {
+  if (!this->test_.IsMulti())
+    return;
+
+  unique_ptr<HttpServer> server(this->test_.CreateServer());
+  ASSERT_TRUE(server->started_);
+
+  vector<pair<off_t, off_t>> ranges;
+  ranges.push_back(make_pair(kBigLength - 2, 0));
+  ranges.push_back(make_pair(kBigLength - 3, 0));
+  MultiTest(this->test_.NewLargeFetcher(),
+            this->test_.fake_hardware(),
+            this->test_.BigUrl(server->GetPort()),
+            ranges,
+            "ijhij",
+            5,
+            kHttpResponsePartialContent);
+}
+
+TYPED_TEST(HttpFetcherTest, MultiHttpFetcherInsufficientTest) {
+  if (!this->test_.IsMulti())
+    return;
+
+  unique_ptr<HttpServer> server(this->test_.CreateServer());
+  ASSERT_TRUE(server->started_);
+
+  vector<pair<off_t, off_t>> ranges;
+  ranges.push_back(make_pair(kBigLength - 2, 4));
+  for (int i = 0; i < 2; ++i) {
+    LOG(INFO) << "i = " << i;
+    MultiTest(this->test_.NewLargeFetcher(),
+              this->test_.fake_hardware(),
+              this->test_.BigUrl(server->GetPort()),
+              ranges,
+              "ij",
+              2,
+              kHttpResponseUndefined);
+    ranges.push_back(make_pair(0, 5));
+  }
+}
+
+// Issue #18143: when a fetch of a secondary chunk out of a chain, then it
+// should retry with other proxies listed before giving up.
+//
+// (1) successful recovery: The offset fetch will fail twice but succeed with
+// the third proxy.
+TYPED_TEST(HttpFetcherTest, MultiHttpFetcherErrorIfOffsetRecoverableTest) {
+  if (!this->test_.IsMulti())
+    return;
+
+  unique_ptr<HttpServer> server(this->test_.CreateServer());
+  ASSERT_TRUE(server->started_);
+
+  vector<pair<off_t, off_t>> ranges;
+  ranges.push_back(make_pair(0, 25));
+  ranges.push_back(make_pair(99, 0));
+  MultiTest(this->test_.NewLargeFetcher(3),
+            this->test_.fake_hardware(),
+            LocalServerUrlForPath(server->GetPort(),
+                                  base::StringPrintf("/error-if-offset/%d/2",
+                                                     kBigLength)),
+            ranges,
+            "abcdefghijabcdefghijabcdejabcdefghijabcdef",
+            kBigLength - (99 - 25),
+            kHttpResponsePartialContent);
+}
+
+// (2) unsuccessful recovery: The offset fetch will fail repeatedly.  The
+// fetcher will signal a (failed) completed transfer to the delegate.
+TYPED_TEST(HttpFetcherTest, MultiHttpFetcherErrorIfOffsetUnrecoverableTest) {
+  if (!this->test_.IsMulti())
+    return;
+
+  unique_ptr<HttpServer> server(this->test_.CreateServer());
+  ASSERT_TRUE(server->started_);
+
+  vector<pair<off_t, off_t>> ranges;
+  ranges.push_back(make_pair(0, 25));
+  ranges.push_back(make_pair(99, 0));
+  MultiTest(this->test_.NewLargeFetcher(2),
+            this->test_.fake_hardware(),
+            LocalServerUrlForPath(server->GetPort(),
+                                  base::StringPrintf("/error-if-offset/%d/3",
+                                                     kBigLength)),
+            ranges,
+            "abcdefghijabcdefghijabcde",  // only received the first chunk
+            25,
+            kHttpResponseUndefined);
+}
+
+
+
+namespace {
+class BlockedTransferTestDelegate : public HttpFetcherDelegate {
+ public:
+  void ReceivedBytes(HttpFetcher* fetcher,
+                     const void* bytes, size_t length) override {
+    ADD_FAILURE();
+  }
+  void TransferComplete(HttpFetcher* fetcher, bool successful) override {
+    EXPECT_FALSE(successful);
+    MessageLoop::current()->BreakLoop();
+  }
+  void TransferTerminated(HttpFetcher* fetcher) override {
+    ADD_FAILURE();
+  }
+};
+
+void BlockedTransferTestHelper(AnyHttpFetcherTest* fetcher_test,
+                               bool is_official_build) {
+  if (fetcher_test->IsMock() || fetcher_test->IsMulti())
+    return;
+
+  unique_ptr<HttpServer> server(fetcher_test->CreateServer());
+  ASSERT_TRUE(server->started_);
+
+  BlockedTransferTestDelegate delegate;
+  unique_ptr<HttpFetcher> fetcher(fetcher_test->NewLargeFetcher());
+  LOG(INFO) << "is_official_build: " << is_official_build;
+  // NewLargeFetcher creates the HttpFetcher* with a FakeSystemState.
+  fetcher_test->fake_hardware()->SetIsOfficialBuild(is_official_build);
+  fetcher->set_delegate(&delegate);
+
+  MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+      StartTransfer,
+      fetcher.get(),
+      LocalServerUrlForPath(server->GetPort(),
+                            fetcher_test->SmallUrl(server->GetPort()))));
+  MessageLoop::current()->Run();
+}
+}  // namespace
+
+TYPED_TEST(HttpFetcherTest, BlockedTransferTest) {
+  BlockedTransferTestHelper(&this->test_, false);
+}
+
+TYPED_TEST(HttpFetcherTest, BlockedTransferOfficialBuildTest) {
+  BlockedTransferTestHelper(&this->test_, true);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/hwid_override.cc b/common/hwid_override.cc
new file mode 100644
index 0000000..8800e94
--- /dev/null
+++ b/common/hwid_override.cc
@@ -0,0 +1,46 @@
+//
+// 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.
+//
+
+#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 {
+
+const char HwidOverride::kHwidOverrideKey[] = "HWID_OVERRIDE";
+
+HwidOverride::HwidOverride() {}
+
+HwidOverride::~HwidOverride() {}
+
+string HwidOverride::Read(const base::FilePath& root) {
+  brillo::KeyValueStore lsb_release;
+  lsb_release.Load(base::FilePath(root.value() + "/etc/lsb-release"));
+  string result;
+  if (lsb_release.GetString(kHwidOverrideKey, &result))
+    return result;
+  return "";
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/hwid_override.h b/common/hwid_override.h
new file mode 100644
index 0000000..d39b572
--- /dev/null
+++ b/common/hwid_override.h
@@ -0,0 +1,46 @@
+//
+// 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_COMMON_HWID_OVERRIDE_H_
+#define UPDATE_ENGINE_COMMON_HWID_OVERRIDE_H_
+
+#include <map>
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/macros.h>
+
+namespace chromeos_update_engine {
+
+// Class that allows HWID to be read from <root>/etc/lsb-release.
+class HwidOverride {
+ public:
+  HwidOverride();
+  ~HwidOverride();
+
+  // Read HWID from an /etc/lsb-release file under given root.
+  // An empty string is returned if there is any error.
+  static std::string Read(const base::FilePath& root);
+
+  static const char kHwidOverrideKey[];
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HwidOverride);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_HWID_OVERRIDE_H_
diff --git a/common/hwid_override_unittest.cc b/common/hwid_override_unittest.cc
new file mode 100644
index 0000000..fff64bc
--- /dev/null
+++ b/common/hwid_override_unittest.cc
@@ -0,0 +1,67 @@
+//
+// 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/common/hwid_override.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>
+
+namespace chromeos_update_engine {
+
+class HwidOverrideTest : public ::testing::Test {
+ public:
+  HwidOverrideTest() {}
+  ~HwidOverrideTest() override = default;
+
+  void SetUp() override {
+    ASSERT_TRUE(tempdir_.CreateUniqueTempDir());
+    ASSERT_TRUE(base::CreateDirectory(tempdir_.path().Append("etc")));
+  }
+
+ protected:
+  base::ScopedTempDir tempdir_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HwidOverrideTest);
+};
+
+TEST_F(HwidOverrideTest, ReadGood) {
+  std::string expected_hwid("expected");
+  std::string keyval(HwidOverride::kHwidOverrideKey);
+  keyval += ("=" + expected_hwid);
+  ASSERT_EQ(base::WriteFile(tempdir_.path().Append("etc/lsb-release"),
+                            keyval.c_str(), keyval.length()),
+            keyval.length());
+  EXPECT_EQ(expected_hwid, HwidOverride::Read(tempdir_.path()));
+}
+
+TEST_F(HwidOverrideTest, ReadNothing) {
+  std::string keyval("SOMETHING_ELSE=UNINTERESTING");
+  ASSERT_EQ(base::WriteFile(tempdir_.path().Append("etc/lsb-release"),
+                            keyval.c_str(), keyval.length()),
+            keyval.length());
+  EXPECT_EQ(std::string(), HwidOverride::Read(tempdir_.path()));
+}
+
+TEST_F(HwidOverrideTest, ReadFailure) {
+  EXPECT_EQ(std::string(), HwidOverride::Read(tempdir_.path()));
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/libcurl_http_fetcher.cc b/common/libcurl_http_fetcher.cc
new file mode 100644
index 0000000..13784fa
--- /dev/null
+++ b/common/libcurl_http_fetcher.cc
@@ -0,0 +1,587 @@
+//
+// 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/common/libcurl_http_fetcher.h"
+
+#include <algorithm>
+#include <string>
+
+#include <base/bind.h>
+#include <base/format_macros.h>
+#include <base/location.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+
+#include "update_engine/common/certificate_checker.h"
+#include "update_engine/common/hardware_interface.h"
+#include "update_engine/common/platform_constants.h"
+
+using base::TimeDelta;
+using brillo::MessageLoop;
+using std::max;
+using std::string;
+
+// This is a concrete implementation of HttpFetcher that uses libcurl to do the
+// http work.
+
+namespace chromeos_update_engine {
+
+namespace {
+const int kNoNetworkRetrySeconds = 10;
+}  // namespace
+
+LibcurlHttpFetcher::LibcurlHttpFetcher(ProxyResolver* proxy_resolver,
+                                       HardwareInterface* hardware)
+    : HttpFetcher(proxy_resolver), hardware_(hardware) {
+  // Dev users want a longer timeout (180 seconds) because they may
+  // be waiting on the dev server to build an image.
+  if (!hardware_->IsOfficialBuild())
+    low_speed_time_seconds_ = kDownloadDevModeLowSpeedTimeSeconds;
+  if (!hardware_->IsOOBEComplete(nullptr))
+    max_retry_count_ = kDownloadMaxRetryCountOobeNotComplete;
+}
+
+LibcurlHttpFetcher::~LibcurlHttpFetcher() {
+  LOG_IF(ERROR, transfer_in_progress_)
+      << "Destroying the fetcher while a transfer is in progress.";
+  CleanUp();
+}
+
+bool LibcurlHttpFetcher::GetProxyType(const string& proxy,
+                                      curl_proxytype* out_type) {
+  if (base::StartsWithASCII(proxy, "socks5://", true) ||
+      base::StartsWithASCII(proxy, "socks://", true)) {
+    *out_type = CURLPROXY_SOCKS5_HOSTNAME;
+    return true;
+  }
+  if (base::StartsWithASCII(proxy, "socks4://", true)) {
+    *out_type = CURLPROXY_SOCKS4A;
+    return true;
+  }
+  if (base::StartsWithASCII(proxy, "http://", true) ||
+      base::StartsWithASCII(proxy, "https://", true)) {
+    *out_type = CURLPROXY_HTTP;
+    return true;
+  }
+  if (base::StartsWithASCII(proxy, kNoProxy, true)) {
+    // known failure case. don't log.
+    return false;
+  }
+  LOG(INFO) << "Unknown proxy type: " << proxy;
+  return false;
+}
+
+void LibcurlHttpFetcher::ResumeTransfer(const string& url) {
+  LOG(INFO) << "Starting/Resuming transfer";
+  CHECK(!transfer_in_progress_);
+  url_ = url;
+  curl_multi_handle_ = curl_multi_init();
+  CHECK(curl_multi_handle_);
+
+  curl_handle_ = curl_easy_init();
+  CHECK(curl_handle_);
+
+  CHECK(HasProxy());
+  bool is_direct = (GetCurrentProxy() == kNoProxy);
+  LOG(INFO) << "Using proxy: " << (is_direct ? "no" : "yes");
+  if (is_direct) {
+    CHECK_EQ(curl_easy_setopt(curl_handle_,
+                              CURLOPT_PROXY,
+                              ""), CURLE_OK);
+  } else {
+    CHECK_EQ(curl_easy_setopt(curl_handle_,
+                              CURLOPT_PROXY,
+                              GetCurrentProxy().c_str()), CURLE_OK);
+    // Curl seems to require us to set the protocol
+    curl_proxytype type;
+    if (GetProxyType(GetCurrentProxy(), &type)) {
+      CHECK_EQ(curl_easy_setopt(curl_handle_,
+                                CURLOPT_PROXYTYPE,
+                                type), CURLE_OK);
+    }
+  }
+
+  if (post_data_set_) {
+    CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_POST, 1), CURLE_OK);
+    CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_POSTFIELDS,
+                              post_data_.data()),
+             CURLE_OK);
+    CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_POSTFIELDSIZE,
+                              post_data_.size()),
+             CURLE_OK);
+
+    // Set the Content-Type HTTP header, if one was specifically set.
+    CHECK(!curl_http_headers_);
+    if (post_content_type_ != kHttpContentTypeUnspecified) {
+      const string content_type_attr =
+        base::StringPrintf("Content-Type: %s",
+                           GetHttpContentTypeString(post_content_type_));
+      curl_http_headers_ = curl_slist_append(nullptr,
+                                             content_type_attr.c_str());
+      CHECK(curl_http_headers_);
+      CHECK_EQ(
+          curl_easy_setopt(curl_handle_, CURLOPT_HTTPHEADER,
+                           curl_http_headers_),
+          CURLE_OK);
+    } else {
+      LOG(WARNING) << "no content type set, using libcurl default";
+    }
+  }
+
+  if (bytes_downloaded_ > 0 || download_length_) {
+    // Resume from where we left off.
+    resume_offset_ = bytes_downloaded_;
+    CHECK_GE(resume_offset_, 0);
+
+    // Compute end offset, if one is specified. As per HTTP specification, this
+    // is an inclusive boundary. Make sure it doesn't overflow.
+    size_t end_offset = 0;
+    if (download_length_) {
+      end_offset = static_cast<size_t>(resume_offset_) + download_length_ - 1;
+      CHECK_LE((size_t) resume_offset_, end_offset);
+    }
+
+    // Create a string representation of the desired range.
+    string range_str = base::StringPrintf(
+        "%" PRIu64 "-", static_cast<uint64_t>(resume_offset_));
+    if (end_offset)
+      range_str += std::to_string(end_offset);
+    CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_RANGE, range_str.c_str()),
+             CURLE_OK);
+  }
+
+  CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_WRITEDATA, this), CURLE_OK);
+  CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_WRITEFUNCTION,
+                            StaticLibcurlWrite), CURLE_OK);
+  CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_URL, url_.c_str()),
+           CURLE_OK);
+
+  // If the connection drops under |low_speed_limit_bps_| (10
+  // bytes/sec by default) for |low_speed_time_seconds_| (90 seconds,
+  // 180 on non-official builds), reconnect.
+  CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_LOW_SPEED_LIMIT,
+                            low_speed_limit_bps_),
+           CURLE_OK);
+  CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_LOW_SPEED_TIME,
+                            low_speed_time_seconds_),
+           CURLE_OK);
+  CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_CONNECTTIMEOUT,
+                            connect_timeout_seconds_),
+           CURLE_OK);
+
+  // By default, libcurl doesn't follow redirections. Allow up to
+  // |kDownloadMaxRedirects| redirections.
+  CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_FOLLOWLOCATION, 1), CURLE_OK);
+  CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_MAXREDIRS,
+                            kDownloadMaxRedirects),
+           CURLE_OK);
+
+  // Lock down the appropriate curl options for HTTP or HTTPS depending on
+  // the url.
+  if (hardware_->IsOfficialBuild()) {
+    if (base::StartsWithASCII(url_, "http://", false))
+      SetCurlOptionsForHttp();
+    else
+      SetCurlOptionsForHttps();
+  } else {
+    LOG(INFO) << "Not setting http(s) curl options because we are "
+              << "running a dev/test image";
+  }
+
+  CHECK_EQ(curl_multi_add_handle(curl_multi_handle_, curl_handle_), CURLM_OK);
+  transfer_in_progress_ = true;
+}
+
+// Lock down only the protocol in case of HTTP.
+void LibcurlHttpFetcher::SetCurlOptionsForHttp() {
+  LOG(INFO) << "Setting up curl options for HTTP";
+  CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_PROTOCOLS, CURLPROTO_HTTP),
+           CURLE_OK);
+  CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_REDIR_PROTOCOLS,
+                            CURLPROTO_HTTP),
+           CURLE_OK);
+}
+
+// Security lock-down in official builds: makes sure that peer certificate
+// verification is enabled, restricts the set of trusted certificates,
+// restricts protocols to HTTPS, restricts ciphers to HIGH.
+void LibcurlHttpFetcher::SetCurlOptionsForHttps() {
+  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_CAPATH,
+                            constants::kCACertificatesPath),
+           CURLE_OK);
+  CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS),
+           CURLE_OK);
+  CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_REDIR_PROTOCOLS,
+                            CURLPROTO_HTTPS),
+           CURLE_OK);
+  CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_SSL_CIPHER_LIST, "HIGH:!ADH"),
+           CURLE_OK);
+  if (server_to_check_ != ServerToCheck::kNone) {
+    CHECK_EQ(
+        curl_easy_setopt(curl_handle_, CURLOPT_SSL_CTX_DATA, &server_to_check_),
+        CURLE_OK);
+    CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_SSL_CTX_FUNCTION,
+                              CertificateChecker::ProcessSSLContext),
+             CURLE_OK);
+  }
+}
+
+
+// Begins the transfer, which must not have already been started.
+void LibcurlHttpFetcher::BeginTransfer(const string& url) {
+  CHECK(!transfer_in_progress_);
+  url_ = url;
+  auto closure = base::Bind(&LibcurlHttpFetcher::ProxiesResolved,
+                            base::Unretained(this));
+  if (!ResolveProxiesForUrl(url_, closure)) {
+    LOG(ERROR) << "Couldn't resolve proxies";
+    if (delegate_)
+      delegate_->TransferComplete(this, false);
+  }
+}
+
+void LibcurlHttpFetcher::ProxiesResolved() {
+  transfer_size_ = -1;
+  resume_offset_ = 0;
+  retry_count_ = 0;
+  no_network_retry_count_ = 0;
+  http_response_code_ = 0;
+  terminate_requested_ = false;
+  sent_byte_ = false;
+  ResumeTransfer(url_);
+  CurlPerformOnce();
+}
+
+void LibcurlHttpFetcher::ForceTransferTermination() {
+  CleanUp();
+  if (delegate_) {
+    // Note that after the callback returns this object may be destroyed.
+    delegate_->TransferTerminated(this);
+  }
+}
+
+void LibcurlHttpFetcher::TerminateTransfer() {
+  if (in_write_callback_) {
+    terminate_requested_ = true;
+  } else {
+    ForceTransferTermination();
+  }
+}
+
+void LibcurlHttpFetcher::CurlPerformOnce() {
+  CHECK(transfer_in_progress_);
+  int running_handles = 0;
+  CURLMcode retcode = CURLM_CALL_MULTI_PERFORM;
+
+  // libcurl may request that we immediately call curl_multi_perform after it
+  // returns, so we do. libcurl promises that curl_multi_perform will not block.
+  while (CURLM_CALL_MULTI_PERFORM == retcode) {
+    retcode = curl_multi_perform(curl_multi_handle_, &running_handles);
+    if (terminate_requested_) {
+      ForceTransferTermination();
+      return;
+    }
+  }
+  if (0 == running_handles) {
+    GetHttpResponseCode();
+    if (http_response_code_) {
+      LOG(INFO) << "HTTP response code: " << http_response_code_;
+      no_network_retry_count_ = 0;
+    } else {
+      LOG(ERROR) << "Unable to get http response code.";
+    }
+
+    // we're done!
+    CleanUp();
+
+    // 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
+    // is online/offline and return an appropriate error code.
+    if (!sent_byte_ &&
+        http_response_code_ == 0 &&
+        no_network_retry_count_ < no_network_max_retries_) {
+      no_network_retry_count_++;
+      MessageLoop::current()->PostDelayedTask(
+          FROM_HERE,
+          base::Bind(&LibcurlHttpFetcher::RetryTimeoutCallback,
+                     base::Unretained(this)),
+          TimeDelta::FromSeconds(kNoNetworkRetrySeconds));
+      LOG(INFO) << "No HTTP response, retry " << no_network_retry_count_;
+      return;
+    }
+
+    if ((!sent_byte_ && !IsHttpResponseSuccess()) || IsHttpResponseError()) {
+      // The transfer completed w/ error and we didn't get any bytes.
+      // If we have another proxy to try, try that.
+      //
+      // TODO(garnold) in fact there are two separate cases here: one case is an
+      // other-than-success return code (including no return code) and no
+      // received bytes, which is necessary due to the way callbacks are
+      // currently processing error conditions;  the second is an explicit HTTP
+      // error code, where some data may have been received (as in the case of a
+      // semi-successful multi-chunk fetch).  This is a confusing behavior and
+      // should be unified into a complete, coherent interface.
+      LOG(INFO) << "Transfer resulted in an error (" << http_response_code_
+                << "), " << bytes_downloaded_ << " bytes downloaded";
+
+      PopProxy();  // Delete the proxy we just gave up on.
+
+      if (HasProxy()) {
+        // We have another proxy. Retry immediately.
+        LOG(INFO) << "Retrying with next proxy setting";
+        MessageLoop::current()->PostTask(
+            FROM_HERE,
+            base::Bind(&LibcurlHttpFetcher::RetryTimeoutCallback,
+                       base::Unretained(this)));
+      } else {
+        // Out of proxies. Give up.
+        LOG(INFO) << "No further proxies, indicating transfer complete";
+        if (delegate_)
+          delegate_->TransferComplete(this, false);  // signal fail
+      }
+    } else if ((transfer_size_ >= 0) && (bytes_downloaded_ < transfer_size_)) {
+      retry_count_++;
+      LOG(INFO) << "Transfer interrupted after downloading "
+                << bytes_downloaded_ << " of " << transfer_size_ << " bytes. "
+                << transfer_size_ - bytes_downloaded_ << " bytes remaining "
+                << "after " << retry_count_ << " attempt(s)";
+
+      if (retry_count_ > max_retry_count_) {
+        LOG(INFO) << "Reached max attempts (" << retry_count_ << ")";
+        if (delegate_)
+          delegate_->TransferComplete(this, false);  // signal fail
+      } else {
+        // Need to restart transfer
+        LOG(INFO) << "Restarting transfer to download the remaining bytes";
+        MessageLoop::current()->PostDelayedTask(
+            FROM_HERE,
+            base::Bind(&LibcurlHttpFetcher::RetryTimeoutCallback,
+                       base::Unretained(this)),
+            TimeDelta::FromSeconds(retry_seconds_));
+      }
+    } else {
+      LOG(INFO) << "Transfer completed (" << http_response_code_
+                << "), " << bytes_downloaded_ << " bytes downloaded";
+      if (delegate_) {
+        bool success = IsHttpResponseSuccess();
+        delegate_->TransferComplete(this, success);
+      }
+    }
+  } else {
+    // set up callback
+    SetupMessageLoopSources();
+  }
+}
+
+size_t LibcurlHttpFetcher::LibcurlWrite(void *ptr, size_t size, size_t nmemb) {
+  // Update HTTP response first.
+  GetHttpResponseCode();
+  const size_t payload_size = size * nmemb;
+
+  // Do nothing if no payload or HTTP response is an error.
+  if (payload_size == 0 || !IsHttpResponseSuccess()) {
+    LOG(INFO) << "HTTP response unsuccessful (" << http_response_code_
+              << ") or no payload (" << payload_size << "), nothing to do";
+    return 0;
+  }
+
+  sent_byte_ = true;
+  {
+    double transfer_size_double;
+    CHECK_EQ(curl_easy_getinfo(curl_handle_,
+                               CURLINFO_CONTENT_LENGTH_DOWNLOAD,
+                               &transfer_size_double), CURLE_OK);
+    off_t new_transfer_size = static_cast<off_t>(transfer_size_double);
+    if (new_transfer_size > 0) {
+      transfer_size_ = resume_offset_ + new_transfer_size;
+    }
+  }
+  bytes_downloaded_ += payload_size;
+  in_write_callback_ = true;
+  if (delegate_)
+    delegate_->ReceivedBytes(this, ptr, payload_size);
+  in_write_callback_ = false;
+  return payload_size;
+}
+
+void LibcurlHttpFetcher::Pause() {
+  CHECK(curl_handle_);
+  CHECK(transfer_in_progress_);
+  CHECK_EQ(curl_easy_pause(curl_handle_, CURLPAUSE_ALL), CURLE_OK);
+}
+
+void LibcurlHttpFetcher::Unpause() {
+  CHECK(curl_handle_);
+  CHECK(transfer_in_progress_);
+  CHECK_EQ(curl_easy_pause(curl_handle_, CURLPAUSE_CONT), CURLE_OK);
+}
+
+// This method sets up callbacks with the MessageLoop.
+void LibcurlHttpFetcher::SetupMessageLoopSources() {
+  fd_set fd_read;
+  fd_set fd_write;
+  fd_set fd_exc;
+
+  FD_ZERO(&fd_read);
+  FD_ZERO(&fd_write);
+  FD_ZERO(&fd_exc);
+
+  int fd_max = 0;
+
+  // Ask libcurl for the set of file descriptors we should track on its
+  // behalf.
+  CHECK_EQ(curl_multi_fdset(curl_multi_handle_, &fd_read, &fd_write,
+                            &fd_exc, &fd_max), CURLM_OK);
+
+  // 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 < 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_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.
+    bool is_exc = FD_ISSET(fd, &fd_exc) != 0;
+    bool must_track[2] = {
+      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 < 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.
+        if (tracked) {
+          MessageLoop::current()->CancelTask(fd_task_it->second);
+          fd_task_maps_[t].erase(fd_task_it);
+        }
+        continue;
+      }
+
+      // If we are already tracking this fd, continue -- nothing to do.
+      if (tracked)
+        continue;
+
+      // Track a new fd.
+      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) {
+        LOG(INFO) << "io_counter = " << io_counter;
+      }
+    }
+  }
+
+  // Set up a timeout callback for libcurl.
+  if (timeout_id_ == MessageLoop::kTaskIdNull) {
+    LOG(INFO) << "Setting up timeout source: " << idle_seconds_ << " seconds.";
+    timeout_id_ = MessageLoop::current()->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&LibcurlHttpFetcher::TimeoutCallback,
+                   base::Unretained(this)),
+        TimeDelta::FromSeconds(idle_seconds_));
+  }
+}
+
+void LibcurlHttpFetcher::RetryTimeoutCallback() {
+  ResumeTransfer(url_);
+  CurlPerformOnce();
+}
+
+void LibcurlHttpFetcher::TimeoutCallback() {
+  // We always re-schedule the callback, even if we don't want to be called
+  // anymore. We will remove the event source separately if we don't want to
+  // be called back.
+  timeout_id_ = MessageLoop::current()->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&LibcurlHttpFetcher::TimeoutCallback, base::Unretained(this)),
+      TimeDelta::FromSeconds(idle_seconds_));
+
+  // CurlPerformOnce() may call CleanUp(), so we need to schedule our callback
+  // first, since it could be canceled by this call.
+  if (transfer_in_progress_)
+    CurlPerformOnce();
+}
+
+void LibcurlHttpFetcher::CleanUp() {
+  MessageLoop::current()->CancelTask(timeout_id_);
+  timeout_id_ = MessageLoop::kTaskIdNull;
+
+  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_) {
+    curl_slist_free_all(curl_http_headers_);
+    curl_http_headers_ = nullptr;
+  }
+  if (curl_handle_) {
+    if (curl_multi_handle_) {
+      CHECK_EQ(curl_multi_remove_handle(curl_multi_handle_, curl_handle_),
+               CURLM_OK);
+    }
+    curl_easy_cleanup(curl_handle_);
+    curl_handle_ = nullptr;
+  }
+  if (curl_multi_handle_) {
+    CHECK_EQ(curl_multi_cleanup(curl_multi_handle_), CURLM_OK);
+    curl_multi_handle_ = nullptr;
+  }
+  transfer_in_progress_ = false;
+}
+
+void LibcurlHttpFetcher::GetHttpResponseCode() {
+  long http_response_code = 0;  // NOLINT(runtime/int) - curl needs long.
+  if (curl_easy_getinfo(curl_handle_,
+                        CURLINFO_RESPONSE_CODE,
+                        &http_response_code) == CURLE_OK) {
+    http_response_code_ = static_cast<int>(http_response_code);
+  }
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/libcurl_http_fetcher.h b/common/libcurl_http_fetcher.h
new file mode 100644
index 0000000..900c973
--- /dev/null
+++ b/common/libcurl_http_fetcher.h
@@ -0,0 +1,249 @@
+//
+// 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_COMMON_LIBCURL_HTTP_FETCHER_H_
+#define UPDATE_ENGINE_COMMON_LIBCURL_HTTP_FETCHER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <curl/curl.h>
+
+#include <base/logging.h>
+#include <base/macros.h>
+#include <brillo/message_loops/message_loop.h>
+
+#include "update_engine/common/certificate_checker.h"
+#include "update_engine/common/hardware_interface.h"
+#include "update_engine/common/http_fetcher.h"
+
+// This is a concrete implementation of HttpFetcher that uses libcurl to do the
+// http work.
+
+namespace chromeos_update_engine {
+
+class LibcurlHttpFetcher : public HttpFetcher {
+ public:
+  LibcurlHttpFetcher(ProxyResolver* proxy_resolver,
+                     HardwareInterface* hardware);
+
+  // Cleans up all internal state. Does not notify delegate
+  ~LibcurlHttpFetcher() override;
+
+  void SetOffset(off_t offset) override { bytes_downloaded_ = offset; }
+
+  void SetLength(size_t length) override { download_length_ = length; }
+  void UnsetLength() override { SetLength(0); }
+
+  // Begins the transfer if it hasn't already begun.
+  void BeginTransfer(const std::string& url) override;
+
+  // If the transfer is in progress, aborts the transfer early. The transfer
+  // cannot be resumed.
+  void TerminateTransfer() override;
+
+  // Suspend the transfer by calling curl_easy_pause(CURLPAUSE_ALL).
+  void Pause() override;
+
+  // Resume the transfer by calling curl_easy_pause(CURLPAUSE_CONT).
+  void Unpause() override;
+
+  // Libcurl sometimes asks to be called back after some time while
+  // leaving that time unspecified. In that case, we pick a reasonable
+  // default of one second, but it can be overridden here. This is
+  // primarily useful for testing.
+  // From http://curl.haxx.se/libcurl/c/curl_multi_timeout.html:
+  //     if libcurl returns a -1 timeout here, it just means that libcurl
+  //     currently has no stored timeout value. You must not wait too long
+  //     (more than a few seconds perhaps) before you call
+  //     curl_multi_perform() again.
+  void set_idle_seconds(int seconds) override { idle_seconds_ = seconds; }
+
+  // Sets the retry timeout. Useful for testing.
+  void set_retry_seconds(int seconds) override { retry_seconds_ = seconds; }
+
+  void set_no_network_max_retries(int retries) {
+    no_network_max_retries_ = retries;
+  }
+
+  void set_server_to_check(ServerToCheck server_to_check) {
+    server_to_check_ = server_to_check;
+  }
+
+  size_t GetBytesDownloaded() override {
+    return static_cast<size_t>(bytes_downloaded_);
+  }
+
+  void set_low_speed_limit(int low_speed_bps, int low_speed_sec) override {
+    low_speed_limit_bps_ = low_speed_bps;
+    low_speed_time_seconds_ = low_speed_sec;
+  }
+
+  void set_connect_timeout(int connect_timeout_seconds) override {
+    connect_timeout_seconds_ = connect_timeout_seconds;
+  }
+
+  void set_max_retry_count(int max_retry_count) override {
+    max_retry_count_ = max_retry_count;
+  }
+
+ private:
+  // Callback for when proxy resolution has completed. This begins the
+  // transfer.
+  void ProxiesResolved();
+
+  // Asks libcurl for the http response code and stores it in the object.
+  void GetHttpResponseCode();
+
+  // Checks whether stored HTTP response is within the success range.
+  inline bool IsHttpResponseSuccess() {
+    return (http_response_code_ >= 200 && http_response_code_ < 300);
+  }
+
+  // Checks whether stored HTTP response is within the error range. This
+  // includes both errors with the request (4xx) and server errors (5xx).
+  inline bool IsHttpResponseError() {
+    return (http_response_code_ >= 400 && http_response_code_ < 600);
+  }
+
+  // Resumes a transfer where it left off. This will use the
+  // HTTP Range: header to make a new connection from where the last
+  // left off.
+  virtual void ResumeTransfer(const std::string& url);
+
+  void TimeoutCallback();
+  void RetryTimeoutCallback();
+
+  // Calls into curl_multi_perform to let libcurl do its work. Returns after
+  // curl_multi_perform is finished, which may actually be after more than
+  // one call to curl_multi_perform. This method will set up the message
+  // loop with sources for future work that libcurl will do.
+  // This method will not block.
+  // Returns true if we should resume immediately after this call.
+  void CurlPerformOnce();
+
+  // Sets up message loop sources as needed by libcurl. This is generally
+  // the file descriptor of the socket and a timer in case nothing happens
+  // on the fds.
+  void SetupMessageLoopSources();
+
+  // Callback called by libcurl when new data has arrived on the transfer
+  size_t LibcurlWrite(void *ptr, size_t size, size_t nmemb);
+  static size_t StaticLibcurlWrite(void *ptr, size_t size,
+                                   size_t nmemb, void *stream) {
+    return reinterpret_cast<LibcurlHttpFetcher*>(stream)->
+        LibcurlWrite(ptr, size, nmemb);
+  }
+
+  // Cleans up the following if they are non-null:
+  // curl(m) handles, fd_task_maps_, timeout_id_.
+  void CleanUp();
+
+  // Force terminate the transfer. This will invoke the delegate's (if any)
+  // TransferTerminated callback so, after returning, this fetcher instance may
+  // be destroyed.
+  void ForceTransferTermination();
+
+  // Sets the curl options for HTTP URL.
+  void SetCurlOptionsForHttp();
+
+  // Sets the curl options for HTTPS URL.
+  void SetCurlOptionsForHttps();
+
+  // Convert a proxy URL into a curl proxy type, if applicable. Returns true iff
+  // conversion was successful, false otherwise (in which case nothing is
+  // written to |out_type|).
+  bool GetProxyType(const std::string& proxy, curl_proxytype* out_type);
+
+  // Hardware interface used to query dev-mode and official build settings.
+  HardwareInterface* hardware_;
+
+  // Handles for the libcurl library
+  CURLM* curl_multi_handle_{nullptr};
+  CURL* curl_handle_{nullptr};
+  struct curl_slist* curl_http_headers_{nullptr};
+
+  // Lists of all read(0)/write(1) file descriptors that we're waiting on from
+  // 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, brillo::MessageLoop::TaskId> fd_task_maps_[2];
+
+  // The TaskId of the timer we're waiting on. kTaskIdNull if we are not waiting
+  // on it.
+  brillo::MessageLoop::TaskId timeout_id_{brillo::MessageLoop::kTaskIdNull};
+
+  bool transfer_in_progress_{false};
+
+  // The transfer size. -1 if not known.
+  off_t transfer_size_{0};
+
+  // How many bytes have been downloaded and sent to the delegate.
+  off_t bytes_downloaded_{0};
+
+  // The remaining maximum number of bytes to download. Zero represents an
+  // unspecified length.
+  size_t download_length_{0};
+
+  // If we resumed an earlier transfer, data offset that we used for the
+  // new connection.  0 otherwise.
+  // In this class, resume refers to resuming a dropped HTTP connection,
+  // not to resuming an interrupted download.
+  off_t resume_offset_{0};
+
+  // Number of resumes performed so far and the max allowed.
+  int retry_count_{0};
+  int max_retry_count_{kDownloadMaxRetryCount};
+
+  // Seconds to wait before retrying a resume.
+  int retry_seconds_{20};
+
+  // Number of resumes due to no network (e.g., HTTP response code 0).
+  int no_network_retry_count_{0};
+  int no_network_max_retries_{0};
+
+  // Seconds to wait before asking libcurl to "perform".
+  int idle_seconds_{1};
+
+  // If true, we are currently performing a write callback on the delegate.
+  bool in_write_callback_{false};
+
+  // If true, we have returned at least one byte in the write callback
+  // to the delegate.
+  bool sent_byte_{false};
+
+  // We can't clean everything up while we're in a write callback, so
+  // if we get a terminate request, queue it until we can handle it.
+  bool terminate_requested_{false};
+
+  // The ServerToCheck used when checking this connection's certificate. If no
+  // certificate check needs to be performed, this should be set to
+  // ServerToCheck::kNone.
+  ServerToCheck server_to_check_{ServerToCheck::kNone};
+
+  int low_speed_limit_bps_{kDownloadLowSpeedLimitBps};
+  int low_speed_time_seconds_{kDownloadLowSpeedTimeSeconds};
+  int connect_timeout_seconds_{kDownloadConnectTimeoutSeconds};
+  int num_max_retries_;
+
+  DISALLOW_COPY_AND_ASSIGN(LibcurlHttpFetcher);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_LIBCURL_HTTP_FETCHER_H_
diff --git a/common/mock_certificate_checker.h b/common/mock_certificate_checker.h
new file mode 100644
index 0000000..1f55ca1
--- /dev/null
+++ b/common/mock_certificate_checker.h
@@ -0,0 +1,38 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_COMMON_MOCK_CERTIFICATE_CHECKER_H_
+#define UPDATE_ENGINE_COMMON_MOCK_CERTIFICATE_CHECKER_H_
+
+#include <gmock/gmock.h>
+#include <openssl/ssl.h>
+
+#include "update_engine/common/certificate_checker.h"
+
+namespace chromeos_update_engine {
+
+class MockOpenSSLWrapper : public OpenSSLWrapper {
+ public:
+  MOCK_CONST_METHOD4(GetCertificateDigest,
+                     bool(X509_STORE_CTX* x509_ctx,
+                          int* out_depth,
+                          unsigned int* out_digest_length,
+                          uint8_t* out_digest));
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_MOCK_CERTIFICATE_CHECKER_H_
diff --git a/common/mock_hardware.h b/common/mock_hardware.h
new file mode 100644
index 0000000..451af91
--- /dev/null
+++ b/common/mock_hardware.h
@@ -0,0 +1,90 @@
+//
+// 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_COMMON_MOCK_HARDWARE_H_
+#define UPDATE_ENGINE_COMMON_MOCK_HARDWARE_H_
+
+#include <string>
+
+#include "update_engine/common/fake_hardware.h"
+
+#include <gmock/gmock.h>
+
+namespace chromeos_update_engine {
+
+// A mocked, fake implementation of HardwareInterface.
+class MockHardware : public HardwareInterface {
+ public:
+  MockHardware() {
+    // Delegate all calls to the fake instance
+    ON_CALL(*this, IsOfficialBuild())
+      .WillByDefault(testing::Invoke(&fake_,
+            &FakeHardware::IsOfficialBuild));
+    ON_CALL(*this, IsNormalBootMode())
+      .WillByDefault(testing::Invoke(&fake_,
+            &FakeHardware::IsNormalBootMode));
+    ON_CALL(*this, IsOOBEComplete(testing::_))
+      .WillByDefault(testing::Invoke(&fake_,
+            &FakeHardware::IsOOBEComplete));
+    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, GetPowerwashCount())
+      .WillByDefault(testing::Invoke(&fake_,
+            &FakeHardware::GetPowerwashCount));
+    ON_CALL(*this, GetNonVolatileDirectory(testing::_))
+      .WillByDefault(testing::Invoke(&fake_,
+            &FakeHardware::GetNonVolatileDirectory));
+    ON_CALL(*this, GetPowerwashSafeDirectory(testing::_))
+      .WillByDefault(testing::Invoke(&fake_,
+            &FakeHardware::GetPowerwashSafeDirectory));
+  }
+
+  ~MockHardware() override = default;
+
+  // Hardware overrides.
+  MOCK_CONST_METHOD0(IsOfficialBuild, bool());
+  MOCK_CONST_METHOD0(IsNormalBootMode, 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(GetPowerwashCount, int());
+  MOCK_CONST_METHOD1(GetNonVolatileDirectory, bool(base::FilePath*));
+  MOCK_CONST_METHOD1(GetPowerwashSafeDirectory, bool(base::FilePath*));
+
+  // Returns a reference to the underlying FakeHardware.
+  FakeHardware& fake() {
+    return fake_;
+  }
+
+ private:
+  // The underlying FakeHardware.
+  FakeHardware fake_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockHardware);
+};
+
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_MOCK_HARDWARE_H_
diff --git a/common/mock_http_fetcher.cc b/common/mock_http_fetcher.cc
new file mode 100644
index 0000000..f3fa70d
--- /dev/null
+++ b/common/mock_http_fetcher.cc
@@ -0,0 +1,149 @@
+//
+// 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/common/mock_http_fetcher.h"
+
+#include <algorithm>
+
+#include <base/bind.h>
+#include <base/logging.h>
+#include <base/time/time.h>
+#include <gtest/gtest.h>
+
+// This is a mock implementation of HttpFetcher which is useful for testing.
+
+using brillo::MessageLoop;
+using std::min;
+
+namespace chromeos_update_engine {
+
+MockHttpFetcher::~MockHttpFetcher() {
+  CHECK(timeout_id_ == MessageLoop::kTaskIdNull) <<
+      "Call TerminateTransfer() before dtor.";
+}
+
+void MockHttpFetcher::BeginTransfer(const std::string& url) {
+  EXPECT_FALSE(never_use_);
+  if (fail_transfer_ || data_.empty()) {
+    // No data to send, just notify of completion..
+    SignalTransferComplete();
+    return;
+  }
+  if (sent_size_ < data_.size())
+    SendData(true);
+}
+
+// Returns false on one condition: If timeout_id_ was already set
+// and it needs to be deleted by the caller. If timeout_id_ is null
+// when this function is called, this function will always return true.
+bool MockHttpFetcher::SendData(bool skip_delivery) {
+  if (fail_transfer_) {
+    SignalTransferComplete();
+    return timeout_id_ != MessageLoop::kTaskIdNull;
+  }
+
+  CHECK_LT(sent_size_, data_.size());
+  if (!skip_delivery) {
+    const size_t chunk_size = min(kMockHttpFetcherChunkSize,
+                                  data_.size() - sent_size_);
+    CHECK(delegate_);
+    delegate_->ReceivedBytes(this, &data_[sent_size_], chunk_size);
+    // We may get terminated in the callback.
+    if (sent_size_ == data_.size()) {
+      LOG(INFO) << "Terminated in the ReceivedBytes callback.";
+      return timeout_id_ != MessageLoop::kTaskIdNull;
+    }
+    sent_size_ += chunk_size;
+    CHECK_LE(sent_size_, data_.size());
+    if (sent_size_ == data_.size()) {
+      // We've sent all the data. Notify of success.
+      SignalTransferComplete();
+    }
+  }
+
+  if (paused_) {
+    // If we're paused, we should return true if timeout_id_ is set,
+    // since we need the caller to delete it.
+    return timeout_id_ != MessageLoop::kTaskIdNull;
+  }
+
+  if (timeout_id_ != MessageLoop::kTaskIdNull) {
+    // we still need a timeout if there's more data to send
+    return sent_size_ < data_.size();
+  } else if (sent_size_ < data_.size()) {
+    // we don't have a timeout source and we need one
+    timeout_id_ = MessageLoop::current()->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&MockHttpFetcher::TimeoutCallback, base::Unretained(this)),
+        base::TimeDelta::FromMilliseconds(10));
+  }
+  return true;
+}
+
+void MockHttpFetcher::TimeoutCallback() {
+  CHECK(!paused_);
+  if (SendData(false)) {
+    // We need to re-schedule the timeout.
+    timeout_id_ = MessageLoop::current()->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&MockHttpFetcher::TimeoutCallback, base::Unretained(this)),
+        base::TimeDelta::FromMilliseconds(10));
+  } else {
+    timeout_id_ = MessageLoop::kTaskIdNull;
+  }
+}
+
+// If the transfer is in progress, aborts the transfer early.
+// The transfer cannot be resumed.
+void MockHttpFetcher::TerminateTransfer() {
+  LOG(INFO) << "Terminating transfer.";
+  sent_size_ = data_.size();
+  // Kill any timeout, it is ok to call with kTaskIdNull.
+  MessageLoop::current()->CancelTask(timeout_id_);
+  timeout_id_ = MessageLoop::kTaskIdNull;
+  delegate_->TransferTerminated(this);
+}
+
+void MockHttpFetcher::Pause() {
+  CHECK(!paused_);
+  paused_ = true;
+  MessageLoop::current()->CancelTask(timeout_id_);
+  timeout_id_ = MessageLoop::kTaskIdNull;
+}
+
+void MockHttpFetcher::Unpause() {
+  CHECK(paused_) << "You must pause before unpause.";
+  paused_ = false;
+  if (sent_size_ < data_.size()) {
+    SendData(false);
+  }
+}
+
+void MockHttpFetcher::FailTransfer(int http_response_code) {
+  fail_transfer_ = true;
+  http_response_code_ = http_response_code;
+}
+
+void MockHttpFetcher::SignalTransferComplete() {
+  // If the transfer has been failed, the HTTP response code should be set
+  // already.
+  if (!fail_transfer_) {
+    http_response_code_ = 200;
+  }
+  delegate_->TransferComplete(this, !fail_transfer_);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/mock_http_fetcher.h b/common/mock_http_fetcher.h
new file mode 100644
index 0000000..90d34dd
--- /dev/null
+++ b/common/mock_http_fetcher.h
@@ -0,0 +1,146 @@
+//
+// 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_COMMON_MOCK_HTTP_FETCHER_H_
+#define UPDATE_ENGINE_COMMON_MOCK_HTTP_FETCHER_H_
+
+#include <string>
+#include <vector>
+
+#include <base/logging.h>
+#include <brillo/message_loops/message_loop.h>
+
+#include "update_engine/common/http_fetcher.h"
+
+// This is a mock implementation of HttpFetcher which is useful for testing.
+// All data must be passed into the ctor. When started, MockHttpFetcher will
+// deliver the data in chunks of size kMockHttpFetcherChunkSize. To simulate
+// a network failure, you can call FailTransfer().
+
+namespace chromeos_update_engine {
+
+// MockHttpFetcher will send a chunk of data down in each call to BeginTransfer
+// and Unpause. For the other chunks of data, a callback is put on the run
+// loop and when that's called, another chunk is sent down.
+const size_t kMockHttpFetcherChunkSize(65536);
+
+class MockHttpFetcher : public HttpFetcher {
+ public:
+  // The data passed in here is copied and then passed to the delegate after
+  // the transfer begins.
+  MockHttpFetcher(const uint8_t* data,
+                  size_t size,
+                  ProxyResolver* proxy_resolver)
+      : HttpFetcher(proxy_resolver),
+        sent_size_(0),
+        timeout_id_(brillo::MessageLoop::kTaskIdNull),
+        paused_(false),
+        fail_transfer_(false),
+        never_use_(false) {
+    data_.insert(data_.end(), data, data + size);
+  }
+
+  // Constructor overload for string data.
+  MockHttpFetcher(const char* data, size_t size, ProxyResolver* proxy_resolver)
+      : MockHttpFetcher(reinterpret_cast<const uint8_t*>(data), size,
+                        proxy_resolver) {}
+
+  // Cleans up all internal state. Does not notify delegate
+  ~MockHttpFetcher() override;
+
+  // Ignores this.
+  void SetOffset(off_t offset) override {
+    sent_size_ = offset;
+    if (delegate_)
+      delegate_->SeekToOffset(offset);
+  }
+
+  // Do nothing.
+  void SetLength(size_t length) override {}
+  void UnsetLength() override {}
+  void set_low_speed_limit(int low_speed_bps, int low_speed_sec) override {}
+  void set_connect_timeout(int connect_timeout_seconds) override {}
+  void set_max_retry_count(int max_retry_count) override {}
+
+  // 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;
+
+  // If the transfer is in progress, aborts the transfer early.
+  // The transfer cannot be resumed.
+  void TerminateTransfer() override;
+
+  // Suspend the mock transfer.
+  void Pause() override;
+
+  // Resume the mock transfer.
+  void Unpause() override;
+
+  // Fail the transfer. This simulates a network failure.
+  void FailTransfer(int http_response_code);
+
+  // If set to true, this will EXPECT fail on BeginTransfer
+  void set_never_use(bool never_use) { never_use_ = never_use; }
+
+  const brillo::Blob& post_data() const {
+    return post_data_;
+  }
+
+ private:
+  // Sends data to the delegate and sets up a timeout callback if needed.
+  // There must be a delegate and there must be data to send. If there is
+  // already a timeout callback, and it should be deleted by the caller,
+  // this will return false; otherwise true is returned.
+  // If skip_delivery is true, no bytes will be delivered, but the callbacks
+  // still be set if needed.
+  bool SendData(bool skip_delivery);
+
+  // Callback for when our message loop timeout expires.
+  void TimeoutCallback();
+
+  // Sets the HTTP response code and signals to the delegate that the transfer
+  // is complete.
+  void SignalTransferComplete();
+
+  // A full copy of the data we'll return to the delegate
+  brillo::Blob data_;
+
+  // The number of bytes we've sent so far
+  size_t sent_size_;
+
+  // The TaskId of the timeout callback. After each chunk of data sent, we
+  // time out for 0s just to make sure that run loop services other clients.
+  brillo::MessageLoop::TaskId timeout_id_;
+
+  // True iff the fetcher is paused.
+  bool paused_;
+
+  // Set to true if the transfer should fail.
+  bool fail_transfer_;
+
+  // Set to true if BeginTransfer should EXPECT fail.
+  bool never_use_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockHttpFetcher);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_MOCK_HTTP_FETCHER_H_
diff --git a/common/mock_prefs.h b/common/mock_prefs.h
new file mode 100644
index 0000000..0e639a2
--- /dev/null
+++ b/common/mock_prefs.h
@@ -0,0 +1,51 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_COMMON_MOCK_PREFS_H_
+#define UPDATE_ENGINE_COMMON_MOCK_PREFS_H_
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include "update_engine/common/constants.h"
+#include "update_engine/common/prefs_interface.h"
+
+namespace chromeos_update_engine {
+
+class MockPrefs : public PrefsInterface {
+ public:
+  MOCK_CONST_METHOD2(GetString,
+                     bool(const std::string& key, std::string* 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));
+
+  MOCK_CONST_METHOD2(GetBoolean, bool(const std::string& key, bool* value));
+  MOCK_METHOD2(SetBoolean, bool(const std::string& key, const bool value));
+
+  MOCK_CONST_METHOD1(Exists, bool(const std::string& key));
+  MOCK_METHOD1(Delete, bool(const std::string& key));
+
+  MOCK_METHOD2(AddObserver, void(const std::string& key, ObserverInterface*));
+  MOCK_METHOD2(RemoveObserver,
+               void(const std::string& key, ObserverInterface*));
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_MOCK_PREFS_H_
diff --git a/common/multi_range_http_fetcher.cc b/common/multi_range_http_fetcher.cc
new file mode 100644
index 0000000..0a97b6e
--- /dev/null
+++ b/common/multi_range_http_fetcher.cc
@@ -0,0 +1,190 @@
+//
+// 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/common/multi_range_http_fetcher.h"
+
+#include <base/strings/stringprintf.h>
+
+#include <algorithm>
+#include <string>
+
+#include "update_engine/common/utils.h"
+
+namespace chromeos_update_engine {
+
+// Begins the transfer to the specified URL.
+// State change: Stopped -> Downloading
+// (corner case: Stopped -> Stopped for an empty request)
+void MultiRangeHttpFetcher::BeginTransfer(const std::string& url) {
+  CHECK(!base_fetcher_active_) << "BeginTransfer but already active.";
+  CHECK(!pending_transfer_ended_) << "BeginTransfer but pending.";
+  CHECK(!terminating_) << "BeginTransfer but terminating.";
+
+  if (ranges_.empty()) {
+    // Note that after the callback returns this object may be destroyed.
+    if (delegate_)
+      delegate_->TransferComplete(this, true);
+    return;
+  }
+  url_ = url;
+  current_index_ = 0;
+  bytes_received_this_range_ = 0;
+  LOG(INFO) << "starting first transfer";
+  base_fetcher_->set_delegate(this);
+  StartTransfer();
+}
+
+// State change: Downloading -> Pending transfer ended
+void MultiRangeHttpFetcher::TerminateTransfer() {
+  if (!base_fetcher_active_) {
+    LOG(INFO) << "Called TerminateTransfer but not active.";
+    // Note that after the callback returns this object may be destroyed.
+    if (delegate_)
+      delegate_->TransferTerminated(this);
+    return;
+  }
+  terminating_ = true;
+
+  if (!pending_transfer_ended_) {
+    base_fetcher_->TerminateTransfer();
+  }
+}
+
+// State change: Stopped or Downloading -> Downloading
+void MultiRangeHttpFetcher::StartTransfer() {
+  if (current_index_ >= ranges_.size()) {
+    return;
+  }
+
+  Range range = ranges_[current_index_];
+  LOG(INFO) << "starting transfer of range " << range.ToString();
+
+  bytes_received_this_range_ = 0;
+  base_fetcher_->SetOffset(range.offset());
+  if (range.HasLength())
+    base_fetcher_->SetLength(range.length());
+  else
+    base_fetcher_->UnsetLength();
+  if (delegate_)
+    delegate_->SeekToOffset(range.offset());
+  base_fetcher_active_ = true;
+  base_fetcher_->BeginTransfer(url_);
+}
+
+// State change: Downloading -> Downloading or Pending transfer ended
+void MultiRangeHttpFetcher::ReceivedBytes(HttpFetcher* fetcher,
+                                          const void* bytes,
+                                          size_t length) {
+  CHECK_LT(current_index_, ranges_.size());
+  CHECK_EQ(fetcher, base_fetcher_.get());
+  CHECK(!pending_transfer_ended_);
+  size_t next_size = length;
+  Range range = ranges_[current_index_];
+  if (range.HasLength()) {
+    next_size = std::min(next_size,
+                         range.length() - bytes_received_this_range_);
+  }
+  LOG_IF(WARNING, next_size <= 0) << "Asked to write length <= 0";
+  if (delegate_) {
+    delegate_->ReceivedBytes(this, bytes, next_size);
+  }
+  bytes_received_this_range_ += length;
+  if (range.HasLength() && bytes_received_this_range_ >= range.length()) {
+    // Terminates the current fetcher. Waits for its TransferTerminated
+    // callback before starting the next range so that we don't end up
+    // signalling the delegate that the whole multi-transfer is complete
+    // before all fetchers are really done and cleaned up.
+    pending_transfer_ended_ = true;
+    LOG(INFO) << "terminating transfer";
+    fetcher->TerminateTransfer();
+  }
+}
+
+// State change: Downloading or Pending transfer ended -> Stopped
+void MultiRangeHttpFetcher::TransferEnded(HttpFetcher* fetcher,
+                                          bool successful) {
+  CHECK(base_fetcher_active_) << "Transfer ended unexpectedly.";
+  CHECK_EQ(fetcher, base_fetcher_.get());
+  pending_transfer_ended_ = false;
+  http_response_code_ = fetcher->http_response_code();
+  LOG(INFO) << "TransferEnded w/ code " << http_response_code_;
+  if (terminating_) {
+    LOG(INFO) << "Terminating.";
+    Reset();
+    // Note that after the callback returns this object may be destroyed.
+    if (delegate_)
+      delegate_->TransferTerminated(this);
+    return;
+  }
+
+  // If we didn't get enough bytes, it's failure
+  Range range = ranges_[current_index_];
+  if (range.HasLength()) {
+    if (bytes_received_this_range_ < range.length()) {
+      // Failure
+      LOG(INFO) << "Didn't get enough bytes. Ending w/ failure.";
+      Reset();
+      // Note that after the callback returns this object may be destroyed.
+      if (delegate_)
+        delegate_->TransferComplete(this, false);
+      return;
+    }
+    // We got enough bytes and there were bytes specified, so this is success.
+    successful = true;
+  }
+
+  // If we have another transfer, do that.
+  if (current_index_ + 1 < ranges_.size()) {
+    current_index_++;
+    LOG(INFO) << "Starting next transfer (" << current_index_ << ").";
+    StartTransfer();
+    return;
+  }
+
+  LOG(INFO) << "Done w/ all transfers";
+  Reset();
+  // Note that after the callback returns this object may be destroyed.
+  if (delegate_)
+    delegate_->TransferComplete(this, successful);
+}
+
+void MultiRangeHttpFetcher::TransferComplete(HttpFetcher* fetcher,
+                                             bool successful) {
+  LOG(INFO) << "Received transfer complete.";
+  TransferEnded(fetcher, successful);
+}
+
+void MultiRangeHttpFetcher::TransferTerminated(HttpFetcher* fetcher) {
+  LOG(INFO) << "Received transfer terminated.";
+  TransferEnded(fetcher, false);
+}
+
+void MultiRangeHttpFetcher::Reset() {
+  base_fetcher_active_ = pending_transfer_ended_ = terminating_ = false;
+  current_index_ = 0;
+  bytes_received_this_range_ = 0;
+}
+
+std::string MultiRangeHttpFetcher::Range::ToString() const {
+  std::string range_str = base::StringPrintf("%jd+", offset());
+  if (HasLength())
+    range_str += std::to_string(length());
+  else
+    range_str += "?";
+  return range_str;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/multi_range_http_fetcher.h b/common/multi_range_http_fetcher.h
new file mode 100644
index 0000000..8158a22
--- /dev/null
+++ b/common/multi_range_http_fetcher.h
@@ -0,0 +1,179 @@
+//
+// 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_COMMON_MULTI_RANGE_HTTP_FETCHER_H_
+#define UPDATE_ENGINE_COMMON_MULTI_RANGE_HTTP_FETCHER_H_
+
+#include <deque>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "update_engine/common/http_fetcher.h"
+
+// This class is a simple wrapper around an HttpFetcher. The client
+// specifies a vector of byte ranges. MultiRangeHttpFetcher will fetch bytes
+// from those offsets, using the same bash fetcher for all ranges. Thus, the
+// fetcher must support beginning a transfer after one has stopped. Pass -1
+// as a length to specify unlimited length. It really only would make sense
+// for the last range specified to have unlimited length, tho it is legal for
+// other entries to have unlimited length.
+
+// There are three states a MultiRangeHttpFetcher object will be in:
+// - Stopped (start state)
+// - Downloading
+// - Pending transfer ended
+// Various functions below that might change state indicate possible
+// state changes.
+
+namespace chromeos_update_engine {
+
+class MultiRangeHttpFetcher : public HttpFetcher, public HttpFetcherDelegate {
+ public:
+  // Takes ownership of the passed in fetcher.
+  explicit MultiRangeHttpFetcher(HttpFetcher* base_fetcher)
+      : HttpFetcher(base_fetcher->proxy_resolver()),
+        base_fetcher_(base_fetcher),
+        base_fetcher_active_(false),
+        pending_transfer_ended_(false),
+        terminating_(false),
+        current_index_(0),
+        bytes_received_this_range_(0) {}
+  ~MultiRangeHttpFetcher() override {}
+
+  void ClearRanges() { ranges_.clear(); }
+
+  void AddRange(off_t offset, size_t size) {
+    CHECK_GT(size, static_cast<size_t>(0));
+    ranges_.push_back(Range(offset, size));
+  }
+
+  void AddRange(off_t offset) {
+    ranges_.push_back(Range(offset));
+  }
+
+  // HttpFetcher overrides.
+  void SetOffset(off_t offset) override {}  // for now, doesn't support this
+
+  void SetLength(size_t length) override {}  // unsupported
+  void UnsetLength() override {}
+
+  // Begins the transfer to the specified URL.
+  // State change: Stopped -> Downloading
+  // (corner case: Stopped -> Stopped for an empty request)
+  void BeginTransfer(const std::string& url) override;
+
+  // State change: Downloading -> Pending transfer ended
+  void TerminateTransfer() override;
+
+  void Pause() override { base_fetcher_->Pause(); }
+
+  void Unpause() override { base_fetcher_->Unpause(); }
+
+  // These functions are overloaded in LibcurlHttp fetcher for testing purposes.
+  void set_idle_seconds(int seconds) override {
+    base_fetcher_->set_idle_seconds(seconds);
+  }
+  void set_retry_seconds(int seconds) override {
+    base_fetcher_->set_retry_seconds(seconds);
+  }
+  // TODO(deymo): Determine if this method should be virtual in HttpFetcher so
+  // this call is sent to the base_fetcher_.
+  virtual void SetProxies(const std::deque<std::string>& proxies) {
+    base_fetcher_->SetProxies(proxies);
+  }
+
+  inline size_t GetBytesDownloaded() override {
+    return base_fetcher_->GetBytesDownloaded();
+  }
+
+  void set_low_speed_limit(int low_speed_bps, int low_speed_sec) override {
+    base_fetcher_->set_low_speed_limit(low_speed_bps, low_speed_sec);
+  }
+
+  void set_connect_timeout(int connect_timeout_seconds) override {
+    base_fetcher_->set_connect_timeout(connect_timeout_seconds);
+  }
+
+  void set_max_retry_count(int max_retry_count) override {
+    base_fetcher_->set_max_retry_count(max_retry_count);
+  }
+
+ private:
+  // A range object defining the offset and length of a download chunk.  Zero
+  // length indicates an unspecified end offset (note that it is impossible to
+  // request a zero-length range in HTTP).
+  class Range {
+   public:
+    Range(off_t offset, size_t length) : offset_(offset), length_(length) {}
+    explicit Range(off_t offset) : offset_(offset), length_(0) {}
+
+    inline off_t offset() const { return offset_; }
+    inline size_t length() const { return length_; }
+
+    inline bool HasLength() const { return (length_ > 0); }
+
+    std::string ToString() const;
+
+   private:
+    off_t offset_;
+    size_t length_;
+  };
+
+  typedef std::vector<Range> RangesVect;
+
+  // State change: Stopped or Downloading -> Downloading
+  void StartTransfer();
+
+  // HttpFetcherDelegate overrides.
+  // State change: Downloading -> Downloading or Pending transfer ended
+  void ReceivedBytes(HttpFetcher* fetcher,
+                     const void* bytes,
+                     size_t length) override;
+
+  // State change: Pending transfer ended -> Stopped
+  void TransferEnded(HttpFetcher* fetcher, bool successful);
+  // These two call TransferEnded():
+  void TransferComplete(HttpFetcher* fetcher, bool successful) override;
+  void TransferTerminated(HttpFetcher* fetcher) override;
+
+  void Reset();
+
+  std::unique_ptr<HttpFetcher> base_fetcher_;
+
+  // If true, do not send any more data or TransferComplete to the delegate.
+  bool base_fetcher_active_;
+
+  // If true, the next fetcher needs to be started when TransferTerminated is
+  // received from the current fetcher.
+  bool pending_transfer_ended_;
+
+  // True if we are waiting for base fetcher to terminate b/c we are
+  // ourselves terminating.
+  bool terminating_;
+
+  RangesVect ranges_;
+
+  RangesVect::size_type current_index_;  // index into ranges_
+  size_t bytes_received_this_range_;
+
+  DISALLOW_COPY_AND_ASSIGN(MultiRangeHttpFetcher);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_MULTI_RANGE_HTTP_FETCHER_H_
diff --git a/common/platform_constants.h b/common/platform_constants.h
new file mode 100644
index 0000000..d1786ff
--- /dev/null
+++ b/common/platform_constants.h
@@ -0,0 +1,56 @@
+//
+// 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_COMMON_PLATFORM_CONSTANTS_H_
+#define UPDATE_ENGINE_COMMON_PLATFORM_CONSTANTS_H_
+
+namespace chromeos_update_engine {
+namespace constants {
+
+// The default URL used by all products when running in normal mode. The AUTest
+// URL is used when testing normal images against the alternative AUTest server.
+// Note that the URL can be override in run-time in certain cases.
+extern const char kOmahaDefaultProductionURL[];
+extern const char kOmahaDefaultAUTestURL[];
+
+// Our product name used in Omaha. This value must match the one configured in
+// the server side and is sent on every request.
+extern const char kOmahaUpdaterID[];
+
+// The name of the platform as sent to Omaha.
+extern const char kOmahaPlatformName[];
+
+// Path to the location of the public half of the payload key. The payload key
+// is used to sign the contents of the payload binary file: the manifest and the
+// whole payload.
+extern const char kUpdatePayloadPublicKeyPath[];
+
+// Path to the directory containing all the SSL certificates accepted by
+// update_engine when sending requests to Omaha and the download server (if
+// HTTPS is used for that as well).
+extern const char kCACertificatesPath[];
+
+// Path to the file used to notify chrome about the deadline of the last omaha
+// response. Empty if not supported.
+extern const char kOmahaResponseDeadlineFile[];
+
+// The stateful directory used by update_engine.
+extern const char kNonVolatileDirectory[];
+
+}  // namespace constants
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_PLATFORM_CONSTANTS_H_
diff --git a/common/platform_constants_android.cc b/common/platform_constants_android.cc
new file mode 100644
index 0000000..4f55106
--- /dev/null
+++ b/common/platform_constants_android.cc
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/common/platform_constants.h"
+
+namespace chromeos_update_engine {
+namespace constants {
+
+const char kOmahaDefaultProductionURL[] =
+    "https://clients2.google.com/service/update2/brillo";
+const char kOmahaDefaultAUTestURL[] =
+    "https://clients2.google.com/service/update2/brillo";
+const char kOmahaUpdaterID[] = "Brillo";
+const char kOmahaPlatformName[] = "Brillo";
+const char kUpdatePayloadPublicKeyPath[] =
+    "/etc/update_engine/update-payload-key.pub.pem";
+const char kCACertificatesPath[] = "/system/etc/security/cacerts_google";
+// No deadline file API support on Android.
+const char kOmahaResponseDeadlineFile[] = "";
+const char kNonVolatileDirectory[] = "/data/misc/update_engine";
+
+}  // namespace constants
+}  // namespace chromeos_update_engine
diff --git a/common/platform_constants_chromeos.cc b/common/platform_constants_chromeos.cc
new file mode 100644
index 0000000..d8587ca
--- /dev/null
+++ b/common/platform_constants_chromeos.cc
@@ -0,0 +1,37 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/common/platform_constants.h"
+
+namespace chromeos_update_engine {
+namespace constants {
+
+const char kOmahaDefaultProductionURL[] =
+    "https://tools.google.com/service/update2";
+const char kOmahaDefaultAUTestURL[] =
+    "https://omaha.sandbox.google.com/service/update2";
+const char kOmahaUpdaterID[] = "ChromeOSUpdateEngine";
+const char kOmahaPlatformName[] = "Chrome OS";
+const char kUpdatePayloadPublicKeyPath[] =
+    "/usr/share/update_engine/update-payload-key.pub.pem";
+const char kCACertificatesPath[] = "/usr/share/chromeos-ca-certificates";
+const char kOmahaResponseDeadlineFile[] =
+    "/tmp/update-check-response-deadline";
+// This directory is wiped during powerwash.
+const char kNonVolatileDirectory[] = "/var/lib/update_engine";
+
+}  // namespace constants
+}  // namespace chromeos_update_engine
diff --git a/common/prefs.cc b/common/prefs.cc
new file mode 100644
index 0000000..9d3a30f
--- /dev/null
+++ b/common/prefs.cc
@@ -0,0 +1,143 @@
+//
+// 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/common/prefs.h"
+
+#include <algorithm>
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
+
+#include "update_engine/common/utils.h"
+
+using std::string;
+
+namespace chromeos_update_engine {
+
+bool Prefs::Init(const base::FilePath& prefs_dir) {
+  prefs_dir_ = prefs_dir;
+  return true;
+}
+
+bool Prefs::GetString(const string& key, string* value) const {
+  base::FilePath filename;
+  TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
+  if (!base::ReadFileToString(filename, value)) {
+    LOG(INFO) << key << " not present in " << prefs_dir_.value();
+    return false;
+  }
+  return true;
+}
+
+bool Prefs::SetString(const string& key, const string& value) {
+  base::FilePath filename;
+  TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
+  if (!base::DirectoryExists(filename.DirName())) {
+    // Only attempt to create the directory if it doesn't exist to avoid calls
+    // to parent directories where we might not have permission to write to.
+    TEST_AND_RETURN_FALSE(base::CreateDirectory(filename.DirName()));
+  }
+  TEST_AND_RETURN_FALSE(base::WriteFile(filename, value.data(), value.size()) ==
+                        static_cast<int>(value.size()));
+  const auto observers_for_key = observers_.find(key);
+  if (observers_for_key != observers_.end()) {
+    std::vector<ObserverInterface*> copy_observers(observers_for_key->second);
+    for (ObserverInterface* observer : copy_observers)
+      observer->OnPrefSet(key);
+  }
+  return true;
+}
+
+bool Prefs::GetInt64(const string& key, int64_t* value) const {
+  string str_value;
+  if (!GetString(key, &str_value))
+    return false;
+  base::TrimWhitespaceASCII(str_value, base::TRIM_ALL, &str_value);
+  TEST_AND_RETURN_FALSE(base::StringToInt64(str_value, value));
+  return true;
+}
+
+bool Prefs::SetInt64(const string& key, const int64_t value) {
+  return SetString(key, base::Int64ToString(value));
+}
+
+bool Prefs::GetBoolean(const string& key, bool* value) const {
+  string str_value;
+  if (!GetString(key, &str_value))
+    return false;
+  base::TrimWhitespaceASCII(str_value, base::TRIM_ALL, &str_value);
+  if (str_value == "false") {
+    *value = false;
+    return true;
+  }
+  if (str_value == "true") {
+    *value = true;
+    return true;
+  }
+  return false;
+}
+
+bool Prefs::SetBoolean(const string& key, const bool value) {
+  return SetString(key, value ? "true" : "false");
+}
+
+bool Prefs::Exists(const string& key) const {
+  base::FilePath filename;
+  TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
+  return base::PathExists(filename);
+}
+
+bool Prefs::Delete(const string& key) {
+  base::FilePath filename;
+  TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
+  TEST_AND_RETURN_FALSE(base::DeleteFile(filename, false));
+  const auto observers_for_key = observers_.find(key);
+  if (observers_for_key != observers_.end()) {
+    std::vector<ObserverInterface*> copy_observers(observers_for_key->second);
+    for (ObserverInterface* observer : copy_observers)
+      observer->OnPrefDeleted(key);
+  }
+  return true;
+}
+
+void Prefs::AddObserver(const string& key, ObserverInterface* observer) {
+  observers_[key].push_back(observer);
+}
+
+void Prefs::RemoveObserver(const string& key, ObserverInterface* observer) {
+  std::vector<ObserverInterface*>& observers_for_key = observers_[key];
+  auto observer_it =
+      std::find(observers_for_key.begin(), observers_for_key.end(), observer);
+  if (observer_it != observers_for_key.end())
+    observers_for_key.erase(observer_it);
+}
+
+bool Prefs::GetFileNameForKey(const string& key,
+                              base::FilePath* filename) const {
+  // Allows only non-empty keys containing [A-Za-z0-9_-].
+  TEST_AND_RETURN_FALSE(!key.empty());
+  for (size_t i = 0; i < key.size(); ++i) {
+    char c = key.at(i);
+    TEST_AND_RETURN_FALSE(IsAsciiAlpha(c) || IsAsciiDigit(c) ||
+                          c == '_' || c == '-');
+  }
+  *filename = prefs_dir_.Append(key);
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/prefs.h b/common/prefs.h
new file mode 100644
index 0000000..f11abc3
--- /dev/null
+++ b/common/prefs.h
@@ -0,0 +1,81 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_COMMON_PREFS_H_
+#define UPDATE_ENGINE_COMMON_PREFS_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+
+#include "gtest/gtest_prod.h"  // for FRIEND_TEST
+#include "update_engine/common/prefs_interface.h"
+
+namespace chromeos_update_engine {
+
+// Implements a preference store by storing the value associated with
+// a key in a separate file named after the key under a preference
+// store directory.
+
+class Prefs : public PrefsInterface {
+ public:
+  Prefs() = default;
+
+  // Initializes the store by associating this object with |prefs_dir|
+  // as the preference store directory. Returns true on success, false
+  // otherwise.
+  bool Init(const base::FilePath& prefs_dir);
+
+  // PrefsInterface methods.
+  bool GetString(const std::string& key, std::string* value) const 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;
+  bool SetBoolean(const std::string& key, const bool value) override;
+
+  bool Exists(const std::string& key) const override;
+  bool Delete(const std::string& key) override;
+
+  void AddObserver(const std::string& key,
+                   ObserverInterface* observer) override;
+  void RemoveObserver(const std::string& key,
+                      ObserverInterface* observer) override;
+
+ private:
+  FRIEND_TEST(PrefsTest, GetFileNameForKey);
+  FRIEND_TEST(PrefsTest, GetFileNameForKeyBadCharacter);
+  FRIEND_TEST(PrefsTest, GetFileNameForKeyEmpty);
+
+  // Sets |filename| to the full path to the file containing the data
+  // associated with |key|. Returns true on success, false otherwise.
+  bool GetFileNameForKey(const std::string& key,
+                         base::FilePath* filename) const;
+
+  // Preference store directory.
+  base::FilePath prefs_dir_;
+
+  // The registered observers watching for changes.
+  std::map<std::string, std::vector<ObserverInterface*>> observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(Prefs);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_PREFS_H_
diff --git a/common/prefs_interface.h b/common/prefs_interface.h
new file mode 100644
index 0000000..03ae3ec
--- /dev/null
+++ b/common/prefs_interface.h
@@ -0,0 +1,97 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_COMMON_PREFS_INTERFACE_H_
+#define UPDATE_ENGINE_COMMON_PREFS_INTERFACE_H_
+
+#include <stdint.h>
+
+#include <string>
+
+namespace chromeos_update_engine {
+
+// The prefs interface allows access to a persistent preferences
+// store. The two reasons for providing this as an interface are
+// testing as well as easier switching to a new implementation in the
+// future, if necessary.
+
+class PrefsInterface {
+ public:
+  // Observer class to be notified about key value changes.
+  class ObserverInterface {
+   public:
+    virtual ~ObserverInterface() = default;
+
+    // Called when the value is set for the observed |key|.
+    virtual void OnPrefSet(const std::string& key) = 0;
+
+    // Called when the observed |key| is deleted.
+    virtual void OnPrefDeleted(const std::string& key) = 0;
+  };
+
+  virtual ~PrefsInterface() = default;
+
+  // Gets a string |value| associated with |key|. Returns true on
+  // success, false on failure (including when the |key| is not
+  // present in the store).
+  virtual bool GetString(const std::string& key, std::string* value) const = 0;
+
+  // Associates |key| with a string |value|. Returns true on success,
+  // false otherwise.
+  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
+  // present in the store).
+  virtual bool GetInt64(const std::string& key, int64_t* value) const = 0;
+
+  // Associates |key| with an int64_t |value|. Returns true on success,
+  // false otherwise.
+  virtual bool SetInt64(const std::string& key, const int64_t value) = 0;
+
+  // Gets a boolean |value| associated with |key|. Returns true on
+  // success, false on failure (including when the |key| is not
+  // present in the store).
+  virtual bool GetBoolean(const std::string& key, bool* value) const = 0;
+
+  // Associates |key| with a boolean |value|. Returns true on success,
+  // false otherwise.
+  virtual bool SetBoolean(const std::string& key, const bool value) = 0;
+
+  // Returns true if the setting exists (i.e. a file with the given key
+  // exists in the prefs directory)
+  virtual bool Exists(const std::string& key) const = 0;
+
+  // Returns true if successfully deleted the file corresponding to
+  // this key. Calling with non-existent keys does nothing.
+  virtual bool Delete(const std::string& key) = 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,
+  // respectively.
+  virtual void AddObserver(const std::string& key,
+                           ObserverInterface* observer) = 0;
+
+  // Remove an observer added with AddObserver(). The observer won't be called
+  // anymore for future Set*() and Delete() method calls.
+  virtual void RemoveObserver(const std::string& key,
+                              ObserverInterface* observer) = 0;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_PREFS_INTERFACE_H_
diff --git a/common/prefs_unittest.cc b/common/prefs_unittest.cc
new file mode 100644
index 0000000..354b05b
--- /dev/null
+++ b/common/prefs_unittest.cc
@@ -0,0 +1,337 @@
+//
+// 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/common/prefs.h"
+
+#include <inttypes.h>
+
+#include <string>
+
+#include <base/files/file_util.h>
+#include <base/macros.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using std::string;
+using testing::Eq;
+using testing::_;
+
+namespace {
+// Test key used along the tests.
+const char kKey[] = "test-key";
+}
+
+namespace chromeos_update_engine {
+
+class PrefsTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ASSERT_TRUE(base::CreateNewTempDirectory("auprefs", &prefs_dir_));
+    ASSERT_TRUE(prefs_.Init(prefs_dir_));
+  }
+
+  void TearDown() override {
+    base::DeleteFile(prefs_dir_, true);  // recursive
+  }
+
+  bool SetValue(const string& key, const string& value) {
+    return base::WriteFile(prefs_dir_.Append(key), value.data(),
+                           value.length()) == static_cast<int>(value.length());
+  }
+
+  base::FilePath prefs_dir_;
+  Prefs prefs_;
+};
+
+TEST_F(PrefsTest, GetFileNameForKey) {
+  const char kAllvalidCharsKey[] =
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-";
+  base::FilePath path;
+  EXPECT_TRUE(prefs_.GetFileNameForKey(kAllvalidCharsKey, &path));
+  EXPECT_EQ(prefs_dir_.Append(kAllvalidCharsKey).value(), path.value());
+}
+
+TEST_F(PrefsTest, GetFileNameForKeyBadCharacter) {
+  base::FilePath path;
+  EXPECT_FALSE(prefs_.GetFileNameForKey("ABC abc", &path));
+}
+
+TEST_F(PrefsTest, GetFileNameForKeyEmpty) {
+  base::FilePath path;
+  EXPECT_FALSE(prefs_.GetFileNameForKey("", &path));
+}
+
+TEST_F(PrefsTest, GetString) {
+  const string test_data = "test data";
+  ASSERT_TRUE(SetValue(kKey, test_data));
+  string value;
+  EXPECT_TRUE(prefs_.GetString(kKey, &value));
+  EXPECT_EQ(test_data, value);
+}
+
+TEST_F(PrefsTest, GetStringBadKey) {
+  string value;
+  EXPECT_FALSE(prefs_.GetString(",bad", &value));
+}
+
+TEST_F(PrefsTest, GetStringNonExistentKey) {
+  string value;
+  EXPECT_FALSE(prefs_.GetString("non-existent-key", &value));
+}
+
+TEST_F(PrefsTest, SetString) {
+  const char kValue[] = "some test value\non 2 lines";
+  EXPECT_TRUE(prefs_.SetString(kKey, kValue));
+  string value;
+  EXPECT_TRUE(base::ReadFileToString(prefs_dir_.Append(kKey), &value));
+  EXPECT_EQ(kValue, value);
+}
+
+TEST_F(PrefsTest, SetStringBadKey) {
+  const char kKeyWithDots[] = ".no-dots";
+  EXPECT_FALSE(prefs_.SetString(kKeyWithDots, "some value"));
+  EXPECT_FALSE(base::PathExists(prefs_dir_.Append(kKeyWithDots)));
+}
+
+TEST_F(PrefsTest, SetStringCreateDir) {
+  const char kValue[] = "test value";
+  base::FilePath subdir = prefs_dir_.Append("subdir1").Append("subdir2");
+  EXPECT_TRUE(prefs_.Init(subdir));
+  EXPECT_TRUE(prefs_.SetString(kKey, kValue));
+  string value;
+  EXPECT_TRUE(base::ReadFileToString(subdir.Append(kKey), &value));
+  EXPECT_EQ(kValue, value);
+}
+
+TEST_F(PrefsTest, SetStringDirCreationFailure) {
+  EXPECT_TRUE(prefs_.Init(base::FilePath("/dev/null")));
+  EXPECT_FALSE(prefs_.SetString(kKey, "test value"));
+}
+
+TEST_F(PrefsTest, SetStringFileCreationFailure) {
+  base::CreateDirectory(prefs_dir_.Append(kKey));
+  EXPECT_FALSE(prefs_.SetString(kKey, "test value"));
+  EXPECT_TRUE(base::DirectoryExists(prefs_dir_.Append(kKey)));
+}
+
+TEST_F(PrefsTest, GetInt64) {
+  ASSERT_TRUE(SetValue(kKey, " \n 25 \t "));
+  int64_t value;
+  EXPECT_TRUE(prefs_.GetInt64(kKey, &value));
+  EXPECT_EQ(25, value);
+}
+
+TEST_F(PrefsTest, GetInt64BadValue) {
+  ASSERT_TRUE(SetValue(kKey, "30a"));
+  int64_t value;
+  EXPECT_FALSE(prefs_.GetInt64(kKey, &value));
+}
+
+TEST_F(PrefsTest, GetInt64Max) {
+  ASSERT_TRUE(SetValue(kKey, base::StringPrintf("%" PRIi64, kint64max)));
+  int64_t value;
+  EXPECT_TRUE(prefs_.GetInt64(kKey, &value));
+  EXPECT_EQ(kint64max, value);
+}
+
+TEST_F(PrefsTest, GetInt64Min) {
+  ASSERT_TRUE(SetValue(kKey, base::StringPrintf("%" PRIi64, kint64min)));
+  int64_t value;
+  EXPECT_TRUE(prefs_.GetInt64(kKey, &value));
+  EXPECT_EQ(kint64min, value);
+}
+
+TEST_F(PrefsTest, GetInt64Negative) {
+  ASSERT_TRUE(SetValue(kKey, " \t -100 \n "));
+  int64_t value;
+  EXPECT_TRUE(prefs_.GetInt64(kKey, &value));
+  EXPECT_EQ(-100, value);
+}
+
+TEST_F(PrefsTest, GetInt64NonExistentKey) {
+  int64_t value;
+  EXPECT_FALSE(prefs_.GetInt64("random-key", &value));
+}
+
+TEST_F(PrefsTest, SetInt64) {
+  EXPECT_TRUE(prefs_.SetInt64(kKey, -123));
+  string value;
+  EXPECT_TRUE(base::ReadFileToString(prefs_dir_.Append(kKey), &value));
+  EXPECT_EQ("-123", value);
+}
+
+TEST_F(PrefsTest, SetInt64BadKey) {
+  const char kKeyWithSpaces[] = "s p a c e s";
+  EXPECT_FALSE(prefs_.SetInt64(kKeyWithSpaces, 20));
+  EXPECT_FALSE(base::PathExists(prefs_dir_.Append(kKeyWithSpaces)));
+}
+
+TEST_F(PrefsTest, SetInt64Max) {
+  EXPECT_TRUE(prefs_.SetInt64(kKey, kint64max));
+  string value;
+  EXPECT_TRUE(base::ReadFileToString(prefs_dir_.Append(kKey), &value));
+  EXPECT_EQ(base::StringPrintf("%" PRIi64, kint64max), value);
+}
+
+TEST_F(PrefsTest, SetInt64Min) {
+  EXPECT_TRUE(prefs_.SetInt64(kKey, kint64min));
+  string value;
+  EXPECT_TRUE(base::ReadFileToString(prefs_dir_.Append(kKey), &value));
+  EXPECT_EQ(base::StringPrintf("%" PRIi64, kint64min), value);
+}
+
+TEST_F(PrefsTest, GetBooleanFalse) {
+  ASSERT_TRUE(SetValue(kKey, " \n false \t "));
+  bool value;
+  EXPECT_TRUE(prefs_.GetBoolean(kKey, &value));
+  EXPECT_FALSE(value);
+}
+
+TEST_F(PrefsTest, GetBooleanTrue) {
+  const char kKey[] = "test-key";
+  ASSERT_TRUE(SetValue(kKey, " \t true \n "));
+  bool value;
+  EXPECT_TRUE(prefs_.GetBoolean(kKey, &value));
+  EXPECT_TRUE(value);
+}
+
+TEST_F(PrefsTest, GetBooleanBadValue) {
+  const char kKey[] = "test-key";
+  ASSERT_TRUE(SetValue(kKey, "1"));
+  bool value;
+  EXPECT_FALSE(prefs_.GetBoolean(kKey, &value));
+}
+
+TEST_F(PrefsTest, GetBooleanBadEmptyValue) {
+  const char kKey[] = "test-key";
+  ASSERT_TRUE(SetValue(kKey, ""));
+  bool value;
+  EXPECT_FALSE(prefs_.GetBoolean(kKey, &value));
+}
+
+TEST_F(PrefsTest, GetBooleanNonExistentKey) {
+  bool value;
+  EXPECT_FALSE(prefs_.GetBoolean("random-key", &value));
+}
+
+TEST_F(PrefsTest, SetBooleanTrue) {
+  const char kKey[] = "test-bool";
+  EXPECT_TRUE(prefs_.SetBoolean(kKey, true));
+  string value;
+  EXPECT_TRUE(base::ReadFileToString(prefs_dir_.Append(kKey), &value));
+  EXPECT_EQ("true", value);
+}
+
+TEST_F(PrefsTest, SetBooleanFalse) {
+  const char kKey[] = "test-bool";
+  EXPECT_TRUE(prefs_.SetBoolean(kKey, false));
+  string value;
+  EXPECT_TRUE(base::ReadFileToString(prefs_dir_.Append(kKey), &value));
+  EXPECT_EQ("false", value);
+}
+
+TEST_F(PrefsTest, SetBooleanBadKey) {
+  const char kKey[] = "s p a c e s";
+  EXPECT_FALSE(prefs_.SetBoolean(kKey, true));
+  EXPECT_FALSE(base::PathExists(prefs_dir_.Append(kKey)));
+}
+
+TEST_F(PrefsTest, ExistsWorks) {
+  // test that the key doesn't exist before we set it.
+  EXPECT_FALSE(prefs_.Exists(kKey));
+
+  // test that the key exists after we set it.
+  ASSERT_TRUE(prefs_.SetInt64(kKey, 8));
+  EXPECT_TRUE(prefs_.Exists(kKey));
+}
+
+TEST_F(PrefsTest, DeleteWorks) {
+  // test that it's alright to delete a non-existent key.
+  EXPECT_TRUE(prefs_.Delete(kKey));
+
+  // delete the key after we set it.
+  ASSERT_TRUE(prefs_.SetInt64(kKey, 0));
+  EXPECT_TRUE(prefs_.Delete(kKey));
+
+  // make sure it doesn't exist anymore.
+  EXPECT_FALSE(prefs_.Exists(kKey));
+}
+
+class MockPrefsObserver : public PrefsInterface::ObserverInterface {
+ public:
+  MOCK_METHOD1(OnPrefSet, void(const string&));
+  MOCK_METHOD1(OnPrefDeleted, void(const string& key));
+};
+
+TEST_F(PrefsTest, ObserversCalled) {
+  MockPrefsObserver mock_obserser;
+  prefs_.AddObserver(kKey, &mock_obserser);
+
+  EXPECT_CALL(mock_obserser, OnPrefSet(Eq(kKey)));
+  EXPECT_CALL(mock_obserser, OnPrefDeleted(_)).Times(0);
+  prefs_.SetString(kKey, "value");
+  testing::Mock::VerifyAndClearExpectations(&mock_obserser);
+
+  EXPECT_CALL(mock_obserser, OnPrefSet(_)).Times(0);
+  EXPECT_CALL(mock_obserser, OnPrefDeleted(Eq(kKey)));
+  prefs_.Delete(kKey);
+  testing::Mock::VerifyAndClearExpectations(&mock_obserser);
+
+  prefs_.RemoveObserver(kKey, &mock_obserser);
+}
+
+TEST_F(PrefsTest, OnlyCalledOnObservedKeys) {
+  MockPrefsObserver mock_obserser;
+  const char kUnusedKey[] = "unused-key";
+  prefs_.AddObserver(kUnusedKey, &mock_obserser);
+
+  EXPECT_CALL(mock_obserser, OnPrefSet(_)).Times(0);
+  EXPECT_CALL(mock_obserser, OnPrefDeleted(_)).Times(0);
+  prefs_.SetString(kKey, "value");
+  prefs_.Delete(kKey);
+
+  prefs_.RemoveObserver(kUnusedKey, &mock_obserser);
+}
+
+TEST_F(PrefsTest, RemovedObserversNotCalled) {
+  MockPrefsObserver mock_obserser_a, mock_obserser_b;
+  prefs_.AddObserver(kKey, &mock_obserser_a);
+  prefs_.AddObserver(kKey, &mock_obserser_b);
+  EXPECT_CALL(mock_obserser_a, OnPrefSet(_)).Times(2);
+  EXPECT_CALL(mock_obserser_b, OnPrefSet(_)).Times(1);
+  EXPECT_TRUE(prefs_.SetString(kKey, "value"));
+  prefs_.RemoveObserver(kKey, &mock_obserser_b);
+  EXPECT_TRUE(prefs_.SetString(kKey, "other value"));
+  prefs_.RemoveObserver(kKey, &mock_obserser_a);
+  EXPECT_TRUE(prefs_.SetString(kKey, "yet another value"));
+}
+
+TEST_F(PrefsTest, UnsuccessfulCallsNotObserved) {
+  MockPrefsObserver mock_obserser;
+  const char kInvalidKey[] = "no spaces or .";
+  prefs_.AddObserver(kInvalidKey, &mock_obserser);
+
+  EXPECT_CALL(mock_obserser, OnPrefSet(_)).Times(0);
+  EXPECT_CALL(mock_obserser, OnPrefDeleted(_)).Times(0);
+  EXPECT_FALSE(prefs_.SetString(kInvalidKey, "value"));
+  EXPECT_FALSE(prefs_.Delete(kInvalidKey));
+
+  prefs_.RemoveObserver(kInvalidKey, &mock_obserser);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/subprocess.cc b/common/subprocess.cc
new file mode 100644
index 0000000..f43aaac
--- /dev/null
+++ b/common/subprocess.cc
@@ -0,0 +1,271 @@
+//
+// 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/common/subprocess.h"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/logging.h>
+#include <base/posix/eintr_wrapper.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/process.h>
+#include <brillo/secure_blob.h>
+
+using brillo::MessageLoop;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+bool SetupChild(const std::map<string, string>& env, uint32_t flags) {
+  // Setup the environment variables.
+  clearenv();
+  for (const auto& key_value : env) {
+    setenv(key_value.first.c_str(), key_value.second.c_str(), 0);
+  }
+
+  if ((flags & Subprocess::kRedirectStderrToStdout) != 0) {
+    if (HANDLE_EINTR(dup2(STDOUT_FILENO, STDERR_FILENO)) != STDERR_FILENO)
+      return false;
+  }
+
+  int fd = HANDLE_EINTR(open("/dev/null", O_RDONLY));
+  if (fd < 0)
+    return false;
+  if (HANDLE_EINTR(dup2(fd, STDIN_FILENO)) != STDIN_FILENO)
+    return false;
+  IGNORE_EINTR(close(fd));
+
+  return true;
+}
+
+// Helper function to launch a process with the given Subprocess::Flags.
+// This function only sets up and starts the process according to the |flags|.
+// The caller is responsible for watching the termination of the subprocess.
+// Return whether the process was successfully launched and fills in the |proc|
+// Process.
+bool LaunchProcess(const vector<string>& cmd,
+                   uint32_t flags,
+                   brillo::Process* proc) {
+  for (const string& arg : cmd)
+    proc->AddArg(arg);
+  proc->SetSearchPath((flags & Subprocess::kSearchPath) != 0);
+
+  // Create an environment for the child process with just the required PATHs.
+  std::map<string, string> env;
+  for (const char* key : {"LD_LIBRARY_PATH", "PATH"}) {
+    const char* value = getenv(key);
+    if (value)
+      env.emplace(key, value);
+  }
+
+  proc->RedirectUsingPipe(STDOUT_FILENO, false);
+  proc->SetPreExecCallback(base::Bind(&SetupChild, env, flags));
+
+  return proc->Start();
+}
+
+}  // namespace
+
+void Subprocess::Init(
+      brillo::AsynchronousSignalHandlerInterface* async_signal_handler) {
+  if (subprocess_singleton_ == this)
+    return;
+  CHECK(subprocess_singleton_ == nullptr);
+  subprocess_singleton_ = this;
+
+  process_reaper_.Register(async_signal_handler);
+}
+
+Subprocess::~Subprocess() {
+  if (subprocess_singleton_ == this)
+    subprocess_singleton_ = nullptr;
+}
+
+void Subprocess::OnStdoutReady(SubprocessRecord* record) {
+  char buf[1024];
+  ssize_t rc = 0;
+  do {
+    rc = HANDLE_EINTR(read(record->stdout_fd, buf, arraysize(buf)));
+    if (rc < 0) {
+      // EAGAIN and EWOULDBLOCK are normal return values when there's no more
+      // input as we are in non-blocking mode.
+      if (errno != EWOULDBLOCK && errno != EAGAIN) {
+        PLOG(ERROR) << "Error reading fd " << record->stdout_fd;
+        MessageLoop::current()->CancelTask(record->stdout_task_id);
+        record->stdout_task_id = MessageLoop::kTaskIdNull;
+      }
+    } else if (rc == 0) {
+      // A value of 0 means that the child closed its end of the pipe and there
+      // is nothing else to read from stdout.
+      MessageLoop::current()->CancelTask(record->stdout_task_id);
+      record->stdout_task_id = MessageLoop::kTaskIdNull;
+    } else {
+      record->stdout.append(buf, rc);
+    }
+  } while (rc > 0);
+}
+
+void Subprocess::ChildExitedCallback(const siginfo_t& info) {
+  auto pid_record = subprocess_records_.find(info.si_pid);
+  if (pid_record == subprocess_records_.end())
+    return;
+  SubprocessRecord* record = pid_record->second.get();
+
+  // Make sure we read any remaining process output and then close the pipe.
+  OnStdoutReady(record);
+
+  MessageLoop::current()->CancelTask(record->stdout_task_id);
+  record->stdout_task_id = MessageLoop::kTaskIdNull;
+
+  // Release and close all the pipes now.
+  record->proc.Release();
+  record->proc.Reset(0);
+
+  // Don't print any log if the subprocess exited with exit code 0.
+  if (info.si_code != CLD_EXITED) {
+    LOG(INFO) << "Subprocess terminated with si_code " << info.si_code;
+  } else if (info.si_status != 0) {
+    LOG(INFO) << "Subprocess exited with si_status: " << info.si_status;
+  }
+
+  if (!record->stdout.empty()) {
+    LOG(INFO) << "Subprocess output:\n" << record->stdout;
+  }
+  if (!record->callback.is_null()) {
+    record->callback.Run(info.si_status, record->stdout);
+  }
+  subprocess_records_.erase(pid_record);
+}
+
+pid_t Subprocess::Exec(const vector<string>& cmd,
+                       const ExecCallback& callback) {
+  return ExecFlags(cmd, kRedirectStderrToStdout, callback);
+}
+
+pid_t Subprocess::ExecFlags(const vector<string>& cmd,
+                            uint32_t flags,
+                            const ExecCallback& callback) {
+  unique_ptr<SubprocessRecord> record(new SubprocessRecord(callback));
+
+  if (!LaunchProcess(cmd, flags, &record->proc)) {
+    LOG(ERROR) << "Failed to launch subprocess";
+    return 0;
+  }
+
+  pid_t pid = record->proc.pid();
+  CHECK(process_reaper_.WatchForChild(FROM_HERE, pid, base::Bind(
+      &Subprocess::ChildExitedCallback,
+      base::Unretained(this))));
+
+  record->stdout_fd = record->proc.GetPipe(STDOUT_FILENO);
+  // Capture the subprocess output. Make our end of the pipe non-blocking.
+  int fd_flags = fcntl(record->stdout_fd, F_GETFL, 0) | O_NONBLOCK;
+  if (HANDLE_EINTR(fcntl(record->stdout_fd, F_SETFL, fd_flags)) < 0) {
+    LOG(ERROR) << "Unable to set non-blocking I/O mode on fd "
+               << record->stdout_fd << ".";
+  }
+
+  record->stdout_task_id = MessageLoop::current()->WatchFileDescriptor(
+      FROM_HERE,
+      record->stdout_fd,
+      MessageLoop::WatchMode::kWatchRead,
+      true,
+      base::Bind(&Subprocess::OnStdoutReady, record.get()));
+
+  subprocess_records_[pid].reset(record.release());
+  return pid;
+}
+
+void Subprocess::KillExec(pid_t pid) {
+  auto pid_record = subprocess_records_.find(pid);
+  if (pid_record == subprocess_records_.end())
+    return;
+  pid_record->second->callback.Reset();
+  kill(pid, SIGTERM);
+}
+
+bool Subprocess::SynchronousExec(const vector<string>& cmd,
+                                 int* return_code,
+                                 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) {
+  brillo::ProcessImpl proc;
+  if (!LaunchProcess(cmd, flags, &proc)) {
+    LOG(ERROR) << "Failed to launch subprocess";
+    return false;
+  }
+
+  if (stdout) {
+    stdout->clear();
+  }
+
+  int fd = proc.GetPipe(STDOUT_FILENO);
+  vector<char> buffer(32 * 1024);
+  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);
+    }
+  }
+  // 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();
+  if (return_code)
+    *return_code = proc_return_code;
+  return proc_return_code != brillo::Process::kErrorExitStatus;
+}
+
+bool Subprocess::SubprocessInFlight() {
+  for (const auto& pid_record : subprocess_records_) {
+    if (!pid_record.second->callback.is_null())
+      return true;
+  }
+  return false;
+}
+
+Subprocess* Subprocess::subprocess_singleton_ = nullptr;
+
+}  // namespace chromeos_update_engine
diff --git a/common/subprocess.h b/common/subprocess.h
new file mode 100644
index 0000000..6b952dc
--- /dev/null
+++ b/common/subprocess.h
@@ -0,0 +1,141 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_COMMON_SUBPROCESS_H_
+#define UPDATE_ENGINE_COMMON_SUBPROCESS_H_
+
+#include <unistd.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/callback.h>
+#include <base/logging.h>
+#include <base/macros.h>
+#include <brillo/asynchronous_signal_handler_interface.h>
+#include <brillo/message_loops/message_loop.h>
+#include <brillo/process.h>
+#include <brillo/process_reaper.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
+// be saved and used to cancel the callback request and kill your process. If
+// you know you won't call KillExec(), you may safely lose the return value
+// from Exec().
+
+// To create the Subprocess singleton just instantiate it with and call Init().
+// You can't have two Subprocess instances initialized at the same time.
+
+namespace chromeos_update_engine {
+
+class Subprocess {
+ public:
+  enum Flags {
+    kSearchPath = 1 << 0,
+    kRedirectStderrToStdout = 1 << 1,
+  };
+
+  // Callback type used when an async process terminates. It receives the exit
+  // code and the stdout output (and stderr if redirected).
+  using ExecCallback = base::Callback<void(int, const std::string&)>;
+
+  Subprocess() = default;
+
+  // Destroy and unregister the Subprocess singleton.
+  ~Subprocess();
+
+  // Initialize and register the Subprocess singleton.
+  void Init(brillo::AsynchronousSignalHandlerInterface* async_signal_handler);
+
+  // Launches a process in the background and calls the passed |callback| when
+  // the process exits.
+  // Returns the process id of the new launched process or 0 in case of failure.
+  pid_t Exec(const std::vector<std::string>& cmd, const ExecCallback& callback);
+  pid_t ExecFlags(const std::vector<std::string>& cmd,
+                  uint32_t flags,
+                  const ExecCallback& callback);
+
+  // Kills the running process with SIGTERM and ignores the callback.
+  void KillExec(pid_t tag);
+
+  // 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. Note that stderr is redirected to stdout.
+  static bool SynchronousExec(const std::vector<std::string>& cmd,
+                              int* return_code,
+                              std::string* stdout);
+  static bool SynchronousExecFlags(const std::vector<std::string>& cmd,
+                                   uint32_t flags,
+                                   int* return_code,
+                                   std::string* stdout);
+
+  // Gets the one instance.
+  static Subprocess& Get() {
+    return *subprocess_singleton_;
+  }
+
+  // Returns true iff there is at least one subprocess we're waiting on.
+  bool SubprocessInFlight();
+
+ private:
+  FRIEND_TEST(SubprocessTest, CancelTest);
+
+  struct SubprocessRecord {
+    explicit SubprocessRecord(const ExecCallback& callback)
+      : callback(callback) {}
+
+    // The callback supplied by the caller.
+    ExecCallback callback;
+
+    // The ProcessImpl instance managing the child process. Destroying this
+    // will close our end of the pipes we have open.
+    brillo::ProcessImpl proc;
+
+    // These are used to monitor the stdout of the running process, including
+    // the stderr if it was redirected.
+    brillo::MessageLoop::TaskId stdout_task_id{
+        brillo::MessageLoop::kTaskIdNull};
+    int stdout_fd{-1};
+    std::string stdout;
+  };
+
+  // Callback which runs whenever there is input available on the subprocess
+  // stdout pipe.
+  static void OnStdoutReady(SubprocessRecord* record);
+
+  // Callback for when any subprocess terminates. This calls the user
+  // requested callback.
+  void ChildExitedCallback(const siginfo_t& info);
+
+  // The global instance.
+  static Subprocess* subprocess_singleton_;
+
+  // A map from the asynchronous subprocess tag (see Exec) to the subprocess
+  // record structure for all active asynchronous subprocesses.
+  std::map<pid_t, std::unique_ptr<SubprocessRecord>> subprocess_records_;
+
+  // Used to watch for child processes.
+  brillo::ProcessReaper process_reaper_;
+
+  DISALLOW_COPY_AND_ASSIGN(Subprocess);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_SUBPROCESS_H_
diff --git a/common/subprocess_unittest.cc b/common/subprocess_unittest.cc
new file mode 100644
index 0000000..b37dc91
--- /dev/null
+++ b/common/subprocess_unittest.cc
@@ -0,0 +1,263 @@
+//
+// 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/common/subprocess.h"
+
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/location.h>
+#include <base/message_loop/message_loop.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <base/time/time.h>
+#include <brillo/bind_lambda.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 <brillo/strings/string_utils.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+
+using base::TimeDelta;
+using brillo::MessageLoop;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+class SubprocessTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    loop_.SetAsCurrent();
+    async_signal_handler_.Init();
+    subprocess_.Init(&async_signal_handler_);
+  }
+
+  base::MessageLoopForIO base_loop_;
+  brillo::BaseMessageLoop loop_{&base_loop_};
+  brillo::AsynchronousSignalHandler async_signal_handler_;
+  Subprocess subprocess_;
+};
+
+namespace {
+
+int local_server_port = 0;
+
+void ExpectedResults(int expected_return_code, const string& expected_output,
+                     int return_code, const string& output) {
+  EXPECT_EQ(expected_return_code, return_code);
+  EXPECT_EQ(expected_output, output);
+  MessageLoop::current()->BreakLoop();
+}
+
+void ExpectedEnvVars(int return_code, const string& output) {
+  EXPECT_EQ(0, return_code);
+  const std::set<string> allowed_envs = {"LD_LIBRARY_PATH", "PATH"};
+  for (string key_value : brillo::string_utils::Split(output, "\n")) {
+    auto key_value_pair = brillo::string_utils::SplitAtFirst(
+        key_value, "=", true);
+    EXPECT_NE(allowed_envs.end(), allowed_envs.find(key_value_pair.first));
+  }
+  MessageLoop::current()->BreakLoop();
+}
+
+}  // namespace
+
+TEST_F(SubprocessTest, IsASingleton) {
+  EXPECT_EQ(&subprocess_, &Subprocess::Get());
+}
+
+TEST_F(SubprocessTest, InactiveInstancesDontChangeTheSingleton) {
+  std::unique_ptr<Subprocess> another_subprocess(new Subprocess());
+  EXPECT_EQ(&subprocess_, &Subprocess::Get());
+  another_subprocess.reset();
+  EXPECT_EQ(&subprocess_, &Subprocess::Get());
+}
+
+TEST_F(SubprocessTest, SimpleTest) {
+  EXPECT_TRUE(subprocess_.Exec({"/bin/false"},
+                               base::Bind(&ExpectedResults, 1, "")));
+  loop_.Run();
+}
+
+TEST_F(SubprocessTest, EchoTest) {
+  EXPECT_TRUE(subprocess_.Exec(
+      {"/bin/sh", "-c", "echo this is stdout; echo this is stderr >&2"},
+      base::Bind(&ExpectedResults, 0, "this is stdout\nthis is stderr\n")));
+  loop_.Run();
+}
+
+TEST_F(SubprocessTest, StderrNotIncludedInOutputTest) {
+  EXPECT_TRUE(subprocess_.ExecFlags(
+      {"/bin/sh", "-c", "echo on stdout; echo on stderr >&2"},
+      0,
+      base::Bind(&ExpectedResults, 0, "on stdout\n")));
+  loop_.Run();
+}
+
+TEST_F(SubprocessTest, EnvVarsAreFiltered) {
+  EXPECT_TRUE(subprocess_.Exec({"/usr/bin/env"}, base::Bind(&ExpectedEnvVars)));
+  loop_.Run();
+}
+
+TEST_F(SubprocessTest, SynchronousTrueSearchsOnPath) {
+  int rc = -1;
+  EXPECT_TRUE(Subprocess::SynchronousExecFlags(
+      {"true"}, Subprocess::kSearchPath, &rc, nullptr));
+  EXPECT_EQ(0, rc);
+}
+
+TEST_F(SubprocessTest, SynchronousEchoTest) {
+  vector<string> cmd = {
+    "/bin/sh",
+    "-c",
+    "echo -n stdout-here; echo -n stderr-there > /dev/stderr"};
+  int rc = -1;
+  string stdout;
+  ASSERT_TRUE(Subprocess::SynchronousExec(cmd, &rc, &stdout));
+  EXPECT_EQ(0, rc);
+  EXPECT_EQ("stdout-herestderr-there", stdout);
+}
+
+TEST_F(SubprocessTest, SynchronousEchoNoOutputTest) {
+  int rc = -1;
+  ASSERT_TRUE(Subprocess::SynchronousExec(
+      {"/bin/sh", "-c", "echo test"},
+      &rc, nullptr));
+  EXPECT_EQ(0, rc);
+}
+
+namespace {
+void CallbackBad(int return_code, const string& output) {
+  ADD_FAILURE() << "should never be called.";
+}
+
+// TODO(garnold) this test method uses test_http_server as a representative for
+// interactive processes that can be spawned/terminated at will. This causes us
+// to go through hoops when spawning this process (e.g. obtaining the port
+// number it uses so we can control it with wget). It would have been much
+// preferred to use something else and thus simplify both test_http_server
+// (doesn't have to be able to communicate through a temp file) and the test
+// code below; for example, it sounds like a brain dead sleep loop with proper
+// signal handlers could be used instead.
+void StartAndCancelInRunLoop(bool* spawned) {
+  // Create a temp file for test_http_server to communicate its port number.
+  char temp_file_name[] = "/tmp/subprocess_unittest-test_http_server-XXXXXX";
+  int temp_fd = mkstemp(temp_file_name);
+  CHECK_GE(temp_fd, 0);
+  int temp_flags = fcntl(temp_fd, F_GETFL, 0) | O_NONBLOCK;
+  CHECK_EQ(fcntl(temp_fd, F_SETFL, temp_flags), 0);
+
+  vector<string> cmd;
+  cmd.push_back("./test_http_server");
+  cmd.push_back(temp_file_name);
+  uint32_t tag = Subprocess::Get().Exec(cmd, base::Bind(&CallbackBad));
+  EXPECT_NE(0, tag);
+  *spawned = true;
+  printf("test http server spawned\n");
+  // Wait for server to be up and running
+  TimeDelta total_wait_time;
+  const TimeDelta kSleepTime = TimeDelta::FromMilliseconds(100);
+  const TimeDelta kMaxWaitTime = TimeDelta::FromSeconds(3);
+  local_server_port = 0;
+  static const char* kServerListeningMsgPrefix = "listening on port ";
+  while (total_wait_time.InMicroseconds() < kMaxWaitTime.InMicroseconds()) {
+    char line[80];
+    int line_len = read(temp_fd, line, sizeof(line) - 1);
+    if (line_len > 0) {
+      line[line_len] = '\0';
+      CHECK_EQ(strstr(line, kServerListeningMsgPrefix), line);
+      const char* listening_port_str =
+          line + strlen(kServerListeningMsgPrefix);
+      char* end_ptr;
+      long raw_port = strtol(listening_port_str,  // NOLINT(runtime/int)
+                             &end_ptr, 10);
+      CHECK(!*end_ptr || *end_ptr == '\n');
+      local_server_port = static_cast<in_port_t>(raw_port);
+      break;
+    } else if (line_len < 0 && errno != EAGAIN) {
+      LOG(INFO) << "error reading from " << temp_file_name << ": "
+                << strerror(errno);
+      break;
+    }
+    usleep(kSleepTime.InMicroseconds());
+    total_wait_time += kSleepTime;
+  }
+  close(temp_fd);
+  remove(temp_file_name);
+  CHECK_GT(local_server_port, 0);
+  LOG(INFO) << "server listening on port " << local_server_port;
+  Subprocess::Get().KillExec(tag);
+}
+
+void ExitWhenDone(bool* spawned) {
+  if (*spawned && !Subprocess::Get().SubprocessInFlight()) {
+    // tear down the sub process
+    printf("tear down time\n");
+    int status = test_utils::System(
+        base::StringPrintf("wget -O /dev/null http://127.0.0.1:%d/quitquitquit",
+                           local_server_port));
+    EXPECT_NE(-1, status) << "system() failed";
+    EXPECT_TRUE(WIFEXITED(status))
+        << "command failed to run or died abnormally";
+    MessageLoop::current()->BreakLoop();
+  } else {
+    // Re-run this callback again in 10 ms.
+    MessageLoop::current()->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&ExitWhenDone, spawned),
+        TimeDelta::FromMilliseconds(10));
+  }
+}
+
+}  // namespace
+
+TEST_F(SubprocessTest, CancelTest) {
+  bool spawned = false;
+  loop_.PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&StartAndCancelInRunLoop, &spawned),
+      TimeDelta::FromMilliseconds(100));
+  loop_.PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&ExitWhenDone, &spawned),
+      TimeDelta::FromMilliseconds(10));
+  loop_.Run();
+  // 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(10),
+      base::Bind([] {
+        return Subprocess::Get().subprocess_records_.empty();
+      }));
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/terminator.cc b/common/terminator.cc
new file mode 100644
index 0000000..62adafd
--- /dev/null
+++ b/common/terminator.cc
@@ -0,0 +1,56 @@
+//
+// 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/common/terminator.h"
+
+#include <cstdlib>
+
+namespace chromeos_update_engine {
+
+volatile sig_atomic_t Terminator::exit_status_ = 1;  // default exit status
+volatile sig_atomic_t Terminator::exit_blocked_ = 0;
+volatile sig_atomic_t Terminator::exit_requested_ = 0;
+
+void Terminator::Init() {
+  exit_blocked_ = 0;
+  exit_requested_ = 0;
+  signal(SIGTERM, HandleSignal);
+}
+
+void Terminator::Init(int exit_status) {
+  exit_status_ = exit_status;
+  Init();
+}
+
+void Terminator::Exit() {
+  exit(exit_status_);
+}
+
+void Terminator::HandleSignal(int signum) {
+  if (exit_blocked_ == 0) {
+    Exit();
+  }
+  exit_requested_ = 1;
+}
+
+ScopedTerminatorExitUnblocker::~ScopedTerminatorExitUnblocker() {
+  Terminator::set_exit_blocked(false);
+  if (Terminator::exit_requested()) {
+    Terminator::Exit();
+  }
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/terminator.h b/common/terminator.h
new file mode 100644
index 0000000..20616f6
--- /dev/null
+++ b/common/terminator.h
@@ -0,0 +1,65 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_COMMON_TERMINATOR_H_
+#define UPDATE_ENGINE_COMMON_TERMINATOR_H_
+
+#include <signal.h>
+
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+namespace chromeos_update_engine {
+
+// A class allowing graceful delayed exit.
+class Terminator {
+ public:
+  // Initializes the terminator and sets up signal handlers.
+  static void Init();
+  static void Init(int exit_status);
+
+  // Terminates the current process.
+  static void Exit();
+
+  // Set to true if the terminator should block termination requests in an
+  // attempt to block exiting.
+  static void set_exit_blocked(bool block) { exit_blocked_ = block ? 1 : 0; }
+  static bool exit_blocked() { return exit_blocked_ != 0; }
+
+  // Returns true if the system is trying to terminate the process, false
+  // otherwise. Returns true only if exit was blocked when the termination
+  // request arrived.
+  static bool exit_requested() { return exit_requested_ != 0; }
+
+ private:
+  FRIEND_TEST(TerminatorTest, HandleSignalTest);
+  FRIEND_TEST(TerminatorDeathTest, ScopedTerminatorExitUnblockerExitTest);
+
+  // The signal handler.
+  static void HandleSignal(int signum);
+
+  static volatile sig_atomic_t exit_status_;
+  static volatile sig_atomic_t exit_blocked_;
+  static volatile sig_atomic_t exit_requested_;
+};
+
+class ScopedTerminatorExitUnblocker {
+ public:
+  ~ScopedTerminatorExitUnblocker();
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_TERMINATOR_H_
diff --git a/common/terminator_unittest.cc b/common/terminator_unittest.cc
new file mode 100644
index 0000000..5e8302f
--- /dev/null
+++ b/common/terminator_unittest.cc
@@ -0,0 +1,85 @@
+//
+// 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/common/terminator.h"
+
+#include <gtest/gtest.h>
+#include <gtest/gtest-spi.h>
+
+using testing::ExitedWithCode;
+
+namespace chromeos_update_engine {
+
+class TerminatorTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    Terminator::Init();
+    ASSERT_FALSE(Terminator::exit_blocked());
+    ASSERT_FALSE(Terminator::exit_requested());
+  }
+  void TearDown() override {
+    // Makes sure subsequent non-Terminator tests don't get accidentally
+    // terminated.
+    Terminator::Init();
+  }
+};
+
+typedef TerminatorTest TerminatorDeathTest;
+
+namespace {
+void UnblockExitThroughUnblocker() {
+  ScopedTerminatorExitUnblocker unblocker = ScopedTerminatorExitUnblocker();
+}
+
+void RaiseSIGTERM() {
+  ASSERT_EXIT(raise(SIGTERM), ExitedWithCode(2), "");
+}
+}  // namespace
+
+TEST_F(TerminatorTest, HandleSignalTest) {
+  Terminator::set_exit_blocked(true);
+  Terminator::HandleSignal(SIGTERM);
+  ASSERT_TRUE(Terminator::exit_requested());
+}
+
+TEST_F(TerminatorTest, ScopedTerminatorExitUnblockerTest) {
+  Terminator::set_exit_blocked(true);
+  ASSERT_TRUE(Terminator::exit_blocked());
+  ASSERT_FALSE(Terminator::exit_requested());
+  UnblockExitThroughUnblocker();
+  ASSERT_FALSE(Terminator::exit_blocked());
+  ASSERT_FALSE(Terminator::exit_requested());
+}
+
+TEST_F(TerminatorDeathTest, ExitTest) {
+  ASSERT_EXIT(Terminator::Exit(), ExitedWithCode(2), "");
+  Terminator::set_exit_blocked(true);
+  ASSERT_EXIT(Terminator::Exit(), ExitedWithCode(2), "");
+}
+
+TEST_F(TerminatorDeathTest, RaiseSignalTest) {
+  RaiseSIGTERM();
+  Terminator::set_exit_blocked(true);
+  EXPECT_FATAL_FAILURE(RaiseSIGTERM(), "");
+}
+
+TEST_F(TerminatorDeathTest, ScopedTerminatorExitUnblockerExitTest) {
+  Terminator::set_exit_blocked(true);
+  Terminator::exit_requested_ = 1;
+  ASSERT_EXIT(UnblockExitThroughUnblocker(), ExitedWithCode(2), "");
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/test_utils.cc b/common/test_utils.cc
new file mode 100644
index 0000000..f89c448
--- /dev/null
+++ b/common/test_utils.cc
@@ -0,0 +1,268 @@
+//
+// 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/common/test_utils.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/format_macros.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/file_writer.h"
+
+using base::StringPrintf;
+using std::set;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+void PrintTo(const Extent& extent, ::std::ostream* os) {
+  *os << "(" << extent.start_block() << ", " << extent.num_blocks() << ")";
+}
+
+namespace test_utils {
+
+const char* const kMountPathTemplate = "UpdateEngineTests_mnt-XXXXXX";
+
+const uint8_t kRandomString[] = {
+  0xf2, 0xb7, 0x55, 0x92, 0xea, 0xa6, 0xc9, 0x57,
+  0xe0, 0xf8, 0xeb, 0x34, 0x93, 0xd9, 0xc4, 0x8f,
+  0xcb, 0x20, 0xfa, 0x37, 0x4b, 0x40, 0xcf, 0xdc,
+  0xa5, 0x08, 0x70, 0x89, 0x79, 0x35, 0xe2, 0x3d,
+  0x56, 0xa4, 0x75, 0x73, 0xa3, 0x6d, 0xd1, 0xd5,
+  0x26, 0xbb, 0x9c, 0x60, 0xbd, 0x2f, 0x5a, 0xfa,
+  0xb7, 0xd4, 0x3a, 0x50, 0xa7, 0x6b, 0x3e, 0xfd,
+  0x61, 0x2b, 0x3a, 0x31, 0x30, 0x13, 0x33, 0x53,
+  0xdb, 0xd0, 0x32, 0x71, 0x5c, 0x39, 0xed, 0xda,
+  0xb4, 0x84, 0xca, 0xbc, 0xbd, 0x78, 0x1c, 0x0c,
+  0xd8, 0x0b, 0x41, 0xe8, 0xe1, 0xe0, 0x41, 0xad,
+  0x03, 0x12, 0xd3, 0x3d, 0xb8, 0x75, 0x9b, 0xe6,
+  0xd9, 0x01, 0xd0, 0x87, 0xf4, 0x36, 0xfa, 0xa7,
+  0x0a, 0xfa, 0xc5, 0x87, 0x65, 0xab, 0x9a, 0x7b,
+  0xeb, 0x58, 0x23, 0xf0, 0xa8, 0x0a, 0xf2, 0x33,
+  0x3a, 0xe2, 0xe3, 0x35, 0x74, 0x95, 0xdd, 0x3c,
+  0x59, 0x5a, 0xd9, 0x52, 0x3a, 0x3c, 0xac, 0xe5,
+  0x15, 0x87, 0x6d, 0x82, 0xbc, 0xf8, 0x7d, 0xbe,
+  0xca, 0xd3, 0x2c, 0xd6, 0xec, 0x38, 0xeb, 0xe4,
+  0x53, 0xb0, 0x4c, 0x3f, 0x39, 0x29, 0xf7, 0xa4,
+  0x73, 0xa8, 0xcb, 0x32, 0x50, 0x05, 0x8c, 0x1c,
+  0x1c, 0xca, 0xc9, 0x76, 0x0b, 0x8f, 0x6b, 0x57,
+  0x1f, 0x24, 0x2b, 0xba, 0x82, 0xba, 0xed, 0x58,
+  0xd8, 0xbf, 0xec, 0x06, 0x64, 0x52, 0x6a, 0x3f,
+  0xe4, 0xad, 0xce, 0x84, 0xb4, 0x27, 0x55, 0x14,
+  0xe3, 0x75, 0x59, 0x73, 0x71, 0x51, 0xea, 0xe8,
+  0xcc, 0xda, 0x4f, 0x09, 0xaf, 0xa4, 0xbc, 0x0e,
+  0xa6, 0x1f, 0xe2, 0x3a, 0xf8, 0x96, 0x7d, 0x30,
+  0x23, 0xc5, 0x12, 0xb5, 0xd8, 0x73, 0x6b, 0x71,
+  0xab, 0xf1, 0xd7, 0x43, 0x58, 0xa7, 0xc9, 0xf0,
+  0xe4, 0x85, 0x1c, 0xd6, 0x92, 0x50, 0x2c, 0x98,
+  0x36, 0xfe, 0x87, 0xaf, 0x43, 0x8f, 0x8f, 0xf5,
+  0x88, 0x48, 0x18, 0x42, 0xcf, 0x42, 0xc1, 0xa8,
+  0xe8, 0x05, 0x08, 0xa1, 0x45, 0x70, 0x5b, 0x8c,
+  0x39, 0x28, 0xab, 0xe9, 0x6b, 0x51, 0xd2, 0xcb,
+  0x30, 0x04, 0xea, 0x7d, 0x2f, 0x6e, 0x6c, 0x3b,
+  0x5f, 0x82, 0xd9, 0x5b, 0x89, 0x37, 0x65, 0x65,
+  0xbe, 0x9f, 0xa3, 0x5d,
+};
+
+bool IsXAttrSupported(const base::FilePath& dir_path) {
+  char *path = strdup(dir_path.Append("xattr_test_XXXXXX").value().c_str());
+
+  int fd = mkstemp(path);
+  if (fd == -1) {
+    PLOG(ERROR) << "Error creating temporary file in " << dir_path.value();
+    free(path);
+    return false;
+  }
+
+  if (unlink(path) != 0) {
+    PLOG(ERROR) << "Error unlinking temporary file " << path;
+    close(fd);
+    free(path);
+    return false;
+  }
+
+  int xattr_res = fsetxattr(fd, "user.xattr-test", "value", strlen("value"), 0);
+  if (xattr_res != 0) {
+    if (errno == ENOTSUP) {
+      // Leave it to call-sites to warn about non-support.
+    } else {
+      PLOG(ERROR) << "Error setting xattr on " << path;
+    }
+  }
+  close(fd);
+  free(path);
+  return xattr_res == 0;
+}
+
+bool WriteFileVector(const string& path, const brillo::Blob& data) {
+  return utils::WriteFile(path.c_str(), data.data(), data.size());
+}
+
+bool WriteFileString(const string& path, const string& data) {
+  return utils::WriteFile(path.c_str(), data.data(), data.size());
+}
+
+// Binds provided |filename| to an unused loopback device, whose name is written
+// to the string pointed to by |lo_dev_name_p|. Returns true on success, false
+// otherwise (along with corresponding test failures), in which case the content
+// of |lo_dev_name_p| is unknown.
+bool BindToUnusedLoopDevice(const string& filename, string* lo_dev_name_p) {
+  CHECK(lo_dev_name_p);
+
+  // Bind to an unused loopback device, sanity check the device name.
+  lo_dev_name_p->clear();
+  if (!(utils::ReadPipe("losetup --show -f " + filename, lo_dev_name_p) &&
+        base::StartsWithASCII(*lo_dev_name_p, "/dev/loop", true))) {
+    ADD_FAILURE();
+    return false;
+  }
+
+  // Strip anything from the first newline char.
+  size_t newline_pos = lo_dev_name_p->find('\n');
+  if (newline_pos != string::npos)
+    lo_dev_name_p->erase(newline_pos);
+
+  return true;
+}
+
+bool ExpectVectorsEq(const brillo::Blob& expected,
+                     const brillo::Blob& actual) {
+  EXPECT_EQ(expected.size(), actual.size());
+  if (expected.size() != actual.size())
+    return false;
+  bool is_all_eq = true;
+  for (unsigned int i = 0; i < expected.size(); i++) {
+    EXPECT_EQ(expected[i], actual[i]) << "offset: " << i;
+    is_all_eq = is_all_eq && (expected[i] == actual[i]);
+  }
+  return is_all_eq;
+}
+
+void FillWithData(brillo::Blob* buffer) {
+  size_t input_counter = 0;
+  for (uint8_t& b : *buffer) {
+    b = kRandomString[input_counter];
+    input_counter++;
+    input_counter %= sizeof(kRandomString);
+  }
+}
+
+void CreateEmptyExtImageAtPath(const string& path,
+                               size_t size,
+                               int block_size) {
+  EXPECT_EQ(0, System(StringPrintf("dd if=/dev/zero of=%s"
+                                   " seek=%" PRIuS " bs=1 count=1 status=none",
+                                   path.c_str(), size)));
+  EXPECT_EQ(0, System(StringPrintf("mkfs.ext3 -q -b %d -F %s",
+                                   block_size, path.c_str())));
+}
+
+void CreateExtImageAtPath(const string& path, vector<string>* out_paths) {
+  // create 10MiB sparse file, mounted at a unique location.
+  string mount_path;
+  CHECK(utils::MakeTempDirectory(kMountPathTemplate, &mount_path));
+  ScopedDirRemover mount_path_unlinker(mount_path);
+
+  EXPECT_EQ(0, System(StringPrintf("dd if=/dev/zero of=%s"
+                                   " seek=10485759 bs=1 count=1 status=none",
+                                   path.c_str())));
+  EXPECT_EQ(0, System(StringPrintf("mkfs.ext3 -q -b 4096 -F %s",
+                                   path.c_str())));
+  EXPECT_EQ(0, System(StringPrintf("mount -o loop %s %s", path.c_str(),
+                                   mount_path.c_str())));
+  EXPECT_EQ(0, System(StringPrintf("echo hi > %s/hi", mount_path.c_str())));
+  EXPECT_EQ(0, System(StringPrintf("echo hello > %s/hello",
+                                   mount_path.c_str())));
+  EXPECT_EQ(0, System(StringPrintf("mkdir %s/some_dir", mount_path.c_str())));
+  EXPECT_EQ(0, System(StringPrintf("mkdir %s/some_dir/empty_dir",
+                                   mount_path.c_str())));
+  EXPECT_EQ(0, System(StringPrintf("mkdir %s/some_dir/mnt",
+                                   mount_path.c_str())));
+  EXPECT_EQ(0, System(StringPrintf("echo T > %s/some_dir/test",
+                                   mount_path.c_str())));
+  EXPECT_EQ(0, System(StringPrintf("mkfifo %s/some_dir/fifo",
+                                   mount_path.c_str())));
+  EXPECT_EQ(0, System(StringPrintf("mknod %s/cdev c 2 3", mount_path.c_str())));
+  EXPECT_EQ(0, System(StringPrintf("ln -s /some/target %s/sym",
+                                   mount_path.c_str())));
+  EXPECT_EQ(0, System(StringPrintf("ln %s/some_dir/test %s/testlink",
+                                   mount_path.c_str(), mount_path.c_str())));
+  EXPECT_EQ(0, System(StringPrintf("echo T > %s/srchardlink0",
+                                   mount_path.c_str())));
+  EXPECT_EQ(0, System(StringPrintf("ln %s/srchardlink0 %s/srchardlink1",
+                                   mount_path.c_str(), mount_path.c_str())));
+  EXPECT_EQ(0, System(StringPrintf("ln -s bogus %s/boguslink",
+                                   mount_path.c_str())));
+  EXPECT_TRUE(utils::UnmountFilesystem(mount_path.c_str()));
+
+  if (out_paths) {
+    out_paths->clear();
+    out_paths->push_back("");
+    out_paths->push_back("/hi");
+    out_paths->push_back("/boguslink");
+    out_paths->push_back("/hello");
+    out_paths->push_back("/some_dir");
+    out_paths->push_back("/some_dir/empty_dir");
+    out_paths->push_back("/some_dir/mnt");
+    out_paths->push_back("/some_dir/test");
+    out_paths->push_back("/some_dir/fifo");
+    out_paths->push_back("/cdev");
+    out_paths->push_back("/testlink");
+    out_paths->push_back("/sym");
+    out_paths->push_back("/srchardlink0");
+    out_paths->push_back("/srchardlink1");
+    out_paths->push_back("/lost+found");
+  }
+}
+
+ScopedLoopMounter::ScopedLoopMounter(const string& file_path,
+                                     string* mnt_path,
+                                     unsigned long flags) {  // NOLINT - long
+  EXPECT_TRUE(utils::MakeTempDirectory("mnt.XXXXXX", mnt_path));
+  dir_remover_.reset(new ScopedDirRemover(*mnt_path));
+
+  string loop_dev;
+  loop_binder_.reset(new ScopedLoopbackDeviceBinder(file_path, &loop_dev));
+
+  EXPECT_TRUE(utils::MountFilesystem(loop_dev, *mnt_path, flags));
+  unmounter_.reset(new ScopedFilesystemUnmounter(*mnt_path));
+}
+
+base::FilePath GetBuildArtifactsPath() {
+  base::FilePath exe_path;
+  base::ReadSymbolicLink(base::FilePath("/proc/self/exe"), &exe_path);
+  return exe_path.DirName();
+}
+
+}  // namespace test_utils
+}  // namespace chromeos_update_engine
diff --git a/common/test_utils.h b/common/test_utils.h
new file mode 100644
index 0000000..616bdd3
--- /dev/null
+++ b/common/test_utils.h
@@ -0,0 +1,271 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_COMMON_TEST_UTILS_H_
+#define UPDATE_ENGINE_COMMON_TEST_UTILS_H_
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+// Streams used for gtest's PrintTo() functions.
+#include <iostream>  // NOLINT(readability/streams)
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <base/callback.h>
+#include <base/files/file_path.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/action.h"
+#include "update_engine/common/subprocess.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/update_metadata.pb.h"
+
+// These are some handy functions for unittests.
+
+namespace chromeos_update_engine {
+
+// PrintTo() functions are used by gtest to log these objects. These PrintTo()
+// functions must be defined in the same namespace as the first argument.
+void PrintTo(const Extent& extent, ::std::ostream* os);
+
+namespace test_utils {
+
+// 300 byte pseudo-random string. Not null terminated.
+// This does not gzip compress well.
+extern const uint8_t kRandomString[300];
+
+// Writes the data passed to path. The file at path will be overwritten if it
+// exists. Returns true on success, false otherwise.
+bool WriteFileVector(const std::string& path, const brillo::Blob& data);
+bool WriteFileString(const std::string& path, const std::string& data);
+
+bool BindToUnusedLoopDevice(const std::string &filename,
+                            std::string* lo_dev_name_ptr);
+
+// Returns true iff a == b
+bool ExpectVectorsEq(const brillo::Blob& a, const brillo::Blob& b);
+
+inline int System(const std::string& cmd) {
+  return system(cmd.c_str());
+}
+
+inline int Symlink(const std::string& oldpath, const std::string& newpath) {
+  return symlink(oldpath.c_str(), newpath.c_str());
+}
+
+inline int Chmod(const std::string& path, mode_t mode) {
+  return chmod(path.c_str(), mode);
+}
+
+inline int Mkdir(const std::string& path, mode_t mode) {
+  return mkdir(path.c_str(), mode);
+}
+
+inline int Chdir(const std::string& path) {
+  return chdir(path.c_str());
+}
+
+// Checks if xattr is supported in the directory specified by
+// |dir_path| which must be writable. Returns true if the feature is
+// supported, false if not or if an error occurred.
+bool IsXAttrSupported(const base::FilePath& dir_path);
+
+void FillWithData(brillo::Blob* buffer);
+
+// Creates an empty ext image.
+void CreateEmptyExtImageAtPath(const std::string& path,
+                               size_t size,
+                               int block_size);
+
+// Creates an ext image with some files in it. The paths creates are
+// returned in out_paths.
+void CreateExtImageAtPath(const std::string& path,
+                          std::vector<std::string>* out_paths);
+
+// Class to unmount FS when object goes out of scope
+class ScopedFilesystemUnmounter {
+ public:
+  explicit ScopedFilesystemUnmounter(const std::string& mountpoint)
+      : mountpoint_(mountpoint),
+        should_unmount_(true) {}
+  ~ScopedFilesystemUnmounter() {
+    if (should_unmount_) {
+      utils::UnmountFilesystem(mountpoint_);
+    }
+  }
+  void set_should_unmount(bool unmount) { should_unmount_ = unmount; }
+ private:
+  const std::string mountpoint_;
+  bool should_unmount_;
+  DISALLOW_COPY_AND_ASSIGN(ScopedFilesystemUnmounter);
+};
+
+class ScopedLoopbackDeviceBinder {
+ public:
+  ScopedLoopbackDeviceBinder(const std::string& file, std::string* dev) {
+    is_bound_ = BindToUnusedLoopDevice(file, &dev_);
+    EXPECT_TRUE(is_bound_);
+
+    if (is_bound_ && dev)
+      *dev = dev_;
+  }
+
+  ~ScopedLoopbackDeviceBinder() {
+    if (!is_bound_)
+      return;
+
+    for (int retry = 0; retry < 5; retry++) {
+      std::vector<std::string> args;
+      args.push_back("/sbin/losetup");
+      args.push_back("-d");
+      args.push_back(dev_);
+      int return_code = 0;
+      EXPECT_TRUE(Subprocess::SynchronousExec(args, &return_code, nullptr));
+      if (return_code == 0) {
+        return;
+      }
+      sleep(1);
+    }
+    ADD_FAILURE();
+  }
+
+  const std::string &dev() {
+    EXPECT_TRUE(is_bound_);
+    return dev_;
+  }
+
+  bool is_bound() const { return is_bound_; }
+
+ private:
+  std::string dev_;
+  bool is_bound_;
+  DISALLOW_COPY_AND_ASSIGN(ScopedLoopbackDeviceBinder);
+};
+
+class ScopedTempFile {
+ public:
+  ScopedTempFile() {
+    EXPECT_TRUE(utils::MakeTempFile("update_engine_test_temp_file.XXXXXX",
+                                    &path_,
+                                    nullptr));
+    unlinker_.reset(new ScopedPathUnlinker(path_));
+  }
+  const std::string& GetPath() { return path_; }
+ private:
+  std::string path_;
+  std::unique_ptr<ScopedPathUnlinker> unlinker_;
+};
+
+class ScopedLoopMounter {
+ public:
+  explicit ScopedLoopMounter(const std::string& file_path,
+                             std::string* mnt_path,
+                             unsigned long flags);  // NOLINT(runtime/int)
+
+ private:
+  // These objects must be destructed in the following order:
+  //   ScopedFilesystemUnmounter (the file system must be unmounted first)
+  //   ScopedLoopbackDeviceBinder (then the loop device can be deleted)
+  //   ScopedDirRemover (then the mount point can be deleted)
+  std::unique_ptr<ScopedDirRemover> dir_remover_;
+  std::unique_ptr<ScopedLoopbackDeviceBinder> loop_binder_;
+  std::unique_ptr<ScopedFilesystemUnmounter> unmounter_;
+};
+
+// Returns the path where the build artifacts are stored. This is the directory
+// where the unittest executable is being run from.
+base::FilePath GetBuildArtifactsPath();
+
+}  // namespace test_utils
+
+// Useful actions for test. These need to be defined in the
+// chromeos_update_engine namespace.
+
+class NoneType;
+
+template<typename T>
+class ObjectFeederAction;
+
+template<typename T>
+class ActionTraits<ObjectFeederAction<T>> {
+ public:
+  typedef T OutputObjectType;
+  typedef NoneType InputObjectType;
+};
+
+// This is a simple Action class for testing. It feeds an object into
+// another action.
+template<typename T>
+class ObjectFeederAction : public Action<ObjectFeederAction<T>> {
+ public:
+  typedef NoneType InputObjectType;
+  typedef T OutputObjectType;
+  void PerformAction() {
+    LOG(INFO) << "feeder running!";
+    CHECK(this->processor_);
+    if (this->HasOutputPipe()) {
+      this->SetOutputObject(out_obj_);
+    }
+    this->processor_->ActionComplete(this, ErrorCode::kSuccess);
+  }
+  static std::string StaticType() { return "ObjectFeederAction"; }
+  std::string Type() const { return StaticType(); }
+  void set_obj(const T& out_obj) {
+    out_obj_ = out_obj;
+  }
+ private:
+  T out_obj_;
+};
+
+template<typename T>
+class ObjectCollectorAction;
+
+template<typename T>
+class ActionTraits<ObjectCollectorAction<T>> {
+ public:
+  typedef NoneType OutputObjectType;
+  typedef T InputObjectType;
+};
+
+// This is a simple Action class for testing. It receives an object from
+// another action.
+template<typename T>
+class ObjectCollectorAction : public Action<ObjectCollectorAction<T>> {
+ public:
+  typedef T InputObjectType;
+  typedef NoneType OutputObjectType;
+  void PerformAction() {
+    LOG(INFO) << "collector running!";
+    ASSERT_TRUE(this->processor_);
+    if (this->HasInputObject()) {
+      object_ = this->GetInputObject();
+    }
+    this->processor_->ActionComplete(this, ErrorCode::kSuccess);
+  }
+  static std::string StaticType() { return "ObjectCollectorAction"; }
+  std::string Type() const { return StaticType(); }
+  const T& object() const { return object_; }
+ private:
+  T object_;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_TEST_UTILS_H_
diff --git a/common/utils.cc b/common/utils.cc
new file mode 100644
index 0000000..f1a357b
--- /dev/null
+++ b/common/utils.cc
@@ -0,0 +1,1302 @@
+//
+// 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/common/utils.h"
+
+#include <stdint.h>
+
+#include <dirent.h>
+#include <elf.h>
+#include <endian.h>
+#include <errno.h>
+#include <ext2fs/ext2fs.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include <base/callback.h>
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_file.h>
+#include <base/format_macros.h>
+#include <base/location.h>
+#include <base/logging.h>
+#include <base/posix/eintr_wrapper.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 <brillo/data_encoding.h>
+#include <brillo/message_loops/message_loop.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"
+#include "update_engine/common/subprocess.h"
+#include "update_engine/payload_consumer/file_descriptor.h"
+#include "update_engine/payload_consumer/file_writer.h"
+
+using base::Time;
+using base::TimeDelta;
+using std::min;
+using std::pair;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+// The following constants control how UnmountFilesystem should retry if
+// umount() fails with an errno EBUSY, i.e. retry 5 times over the course of
+// one second.
+const int kUnmountMaxNumOfRetries = 5;
+const int kUnmountRetryIntervalInMicroseconds = 200 * 1000;  // 200 ms
+
+// Number of bytes to read from a file to attempt to detect its contents. Used
+// in GetFileFormat.
+const int kGetFileFormatMaxHeaderSize = 32;
+
+// 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::StartsWithASCII(disk_name, "/dev/ubi", true) ||
+         base::StartsWithASCII(disk_name, "/dev/mtd", true);
+}
+
+// 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
+// temporary path in |template_path| and returns true.
+bool GetTempName(const string& path, base::FilePath* template_path) {
+  if (path[0] == '/' || base::StartsWithASCII(path, "./", true) ||
+      base::StartsWithASCII(path, "../", true)) {
+    *template_path = base::FilePath(path);
+    return true;
+  }
+
+  base::FilePath temp_dir;
+#ifdef __ANDROID__
+  temp_dir = base::FilePath(constants::kNonVolatileDirectory).Append("tmp");
+  if (!base::PathExists(temp_dir))
+    TEST_AND_RETURN_FALSE(base::CreateDirectory(temp_dir));
+#else
+  TEST_AND_RETURN_FALSE(base::GetTempDir(&temp_dir));
+#endif  // __ANDROID__
+  *template_path = temp_dir.Append(path);
+  return true;
+}
+
+}  // namespace
+
+namespace utils {
+
+// Cgroup container is created in update-engine's upstart script located at
+// /etc/init/update-engine.conf.
+static const char kCGroupDir[] = "/sys/fs/cgroup/cpu/update-engine";
+
+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, int data_len) {
+  DirectFileWriter writer;
+  TEST_AND_RETURN_FALSE_ERRNO(0 == writer.Open(path,
+                                               O_WRONLY | O_CREAT | O_TRUNC,
+                                               0600));
+  ScopedFileWriterCloser closer(&writer);
+  TEST_AND_RETURN_FALSE_ERRNO(writer.Write(data, data_len));
+  return true;
+}
+
+bool WriteAll(int fd, const void* buf, size_t count) {
+  const char* c_buf = static_cast<const char*>(buf);
+  ssize_t bytes_written = 0;
+  while (bytes_written < static_cast<ssize_t>(count)) {
+    ssize_t rc = write(fd, c_buf + bytes_written, count - bytes_written);
+    TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
+    bytes_written += rc;
+  }
+  return true;
+}
+
+bool PWriteAll(int fd, const void* buf, size_t count, off_t offset) {
+  const char* c_buf = static_cast<const char*>(buf);
+  size_t bytes_written = 0;
+  int num_attempts = 0;
+  while (bytes_written < count) {
+    num_attempts++;
+    ssize_t rc = pwrite(fd, c_buf + bytes_written, count - bytes_written,
+                        offset + bytes_written);
+    // TODO(garnold) for debugging failure in chromium-os:31077; to be removed.
+    if (rc < 0) {
+      PLOG(ERROR) << "pwrite error; num_attempts=" << num_attempts
+                  << " bytes_written=" << bytes_written
+                  << " count=" << count << " offset=" << offset;
+    }
+    TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
+    bytes_written += rc;
+  }
+  return true;
+}
+
+bool WriteAll(FileDescriptorPtr fd, const void* buf, size_t count) {
+  const char* c_buf = static_cast<const char*>(buf);
+  ssize_t bytes_written = 0;
+  while (bytes_written < static_cast<ssize_t>(count)) {
+    ssize_t rc = fd->Write(c_buf + bytes_written, count - bytes_written);
+    TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
+    bytes_written += rc;
+  }
+  return true;
+}
+
+bool PWriteAll(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);
+}
+
+bool PReadAll(int fd, void* buf, size_t count, off_t offset,
+              ssize_t* out_bytes_read) {
+  char* c_buf = static_cast<char*>(buf);
+  ssize_t bytes_read = 0;
+  while (bytes_read < static_cast<ssize_t>(count)) {
+    ssize_t rc = pread(fd, c_buf + bytes_read, count - bytes_read,
+                       offset + bytes_read);
+    TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
+    if (rc == 0) {
+      break;
+    }
+    bytes_read += rc;
+  }
+  *out_bytes_read = bytes_read;
+  return true;
+}
+
+bool PReadAll(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);
+  ssize_t bytes_read = 0;
+  while (bytes_read < static_cast<ssize_t>(count)) {
+    ssize_t rc = fd->Read(c_buf + bytes_read, count - bytes_read);
+    TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
+    if (rc == 0) {
+      break;
+    }
+    bytes_read += rc;
+  }
+  *out_bytes_read = bytes_read;
+  return true;
+}
+
+// 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, size_t nbytes,
+                        brillo::Blob* vec_p) {
+  CHECK(buf);
+  CHECK(vec_p);
+  vec_p->insert(vec_p->end(), buf, buf + nbytes);
+}
+static void AppendBytes(const uint8_t* buf, size_t nbytes,
+                        string* str_p) {
+  CHECK(buf);
+  CHECK(str_p);
+  str_p->append(buf, buf + nbytes);
+}
+
+// Reads from an open file |fp|, appending the read content to the container
+// pointer to by |out_p|.  Returns true upon successful reading all of the
+// file's content, false otherwise. If |size| is not -1, reads up to |size|
+// bytes.
+template <class T>
+static bool Read(FILE* fp, off_t size, T* out_p) {
+  CHECK(fp);
+  CHECK(size == -1 || size >= 0);
+  uint8_t buf[1024];
+  while (size == -1 || size > 0) {
+    off_t bytes_to_read = sizeof(buf);
+    if (size > 0 && bytes_to_read > size) {
+      bytes_to_read = size;
+    }
+    size_t nbytes = fread(buf, 1, bytes_to_read, fp);
+    if (!nbytes) {
+      break;
+    }
+    AppendBytes(buf, nbytes, out_p);
+    if (size != -1) {
+      CHECK(size >= static_cast<off_t>(nbytes));
+      size -= nbytes;
+    }
+  }
+  if (ferror(fp)) {
+    return false;
+  }
+  return size == 0 || feof(fp);
+}
+
+// Opens a file |path| for reading and appends its the contents to a container
+// |out_p|. Starts reading the file from |offset|. If |offset| is beyond the end
+// of the file, returns success. If |size| is not -1, reads up to |size| bytes.
+template <class T>
+static bool ReadFileChunkAndAppend(const string& path,
+                                   off_t offset,
+                                   off_t size,
+                                   T* out_p) {
+  CHECK_GE(offset, 0);
+  CHECK(size == -1 || size >= 0);
+  base::ScopedFILE fp(fopen(path.c_str(), "r"));
+  if (!fp.get())
+    return false;
+  if (offset) {
+    // Return success without appending any data if a chunk beyond the end of
+    // the file is requested.
+    if (offset >= FileSize(path)) {
+      return true;
+    }
+    TEST_AND_RETURN_FALSE_ERRNO(fseek(fp.get(), offset, SEEK_SET) == 0);
+  }
+  return Read(fp.get(), size, out_p);
+}
+
+// TODO(deymo): This is only used in unittest, but requires the private
+// Read<string>() defined here. Expose Read<string>() or move to base/ version.
+bool ReadPipe(const string& cmd, string* out_p) {
+  FILE* fp = popen(cmd.c_str(), "r");
+  if (!fp)
+    return false;
+  bool success = Read(fp, -1, out_p);
+  return (success && pclose(fp) >= 0);
+}
+
+bool ReadFile(const string& path, brillo::Blob* out_p) {
+  return ReadFileChunkAndAppend(path, 0, -1, out_p);
+}
+
+bool ReadFile(const string& path, string* out_p) {
+  return ReadFileChunkAndAppend(path, 0, -1, out_p);
+}
+
+bool ReadFileChunk(const string& path, off_t offset, off_t size,
+                   brillo::Blob* out_p) {
+  return ReadFileChunkAndAppend(path, offset, size, out_p);
+}
+
+off_t BlockDevSize(int fd) {
+  uint64_t dev_size;
+  int rc = ioctl(fd, BLKGETSIZE64, &dev_size);
+  if (rc == -1) {
+    dev_size = -1;
+    PLOG(ERROR) << "Error running ioctl(BLKGETSIZE64) on " << fd;
+  }
+  return dev_size;
+}
+
+off_t FileSize(int fd) {
+  struct stat stbuf;
+  int rc = fstat(fd, &stbuf);
+  CHECK_EQ(rc, 0);
+  if (rc < 0) {
+    PLOG(ERROR) << "Error stat-ing " << fd;
+    return rc;
+  }
+  if (S_ISREG(stbuf.st_mode))
+    return stbuf.st_size;
+  if (S_ISBLK(stbuf.st_mode))
+    return BlockDevSize(fd);
+  LOG(ERROR) << "Couldn't determine the type of " << fd;
+  return -1;
+}
+
+off_t FileSize(const string& path) {
+  int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
+  if (fd == -1) {
+    PLOG(ERROR) << "Error opening " << path;
+    return fd;
+  }
+  off_t size = FileSize(fd);
+  if (size == -1)
+    PLOG(ERROR) << "Error getting file size of " << path;
+  close(fd);
+  return size;
+}
+
+void HexDumpArray(const uint8_t* const arr, const size_t length) {
+  LOG(INFO) << "Logging array of length: " << length;
+  const unsigned int bytes_per_line = 16;
+  for (uint32_t i = 0; i < length; i += bytes_per_line) {
+    const unsigned int bytes_remaining = length - i;
+    const unsigned int bytes_per_this_line = min(bytes_per_line,
+                                                 bytes_remaining);
+    char header[100];
+    int r = snprintf(header, sizeof(header), "0x%08x : ", i);
+    TEST_AND_RETURN(r == 13);
+    string line = header;
+    for (unsigned int j = 0; j < bytes_per_this_line; j++) {
+      char buf[20];
+      uint8_t c = arr[i + j];
+      r = snprintf(buf, sizeof(buf), "%02x ", static_cast<unsigned int>(c));
+      TEST_AND_RETURN(r == 3);
+      line += buf;
+    }
+    LOG(INFO) << line;
+  }
+}
+
+bool SplitPartitionName(const string& partition_name,
+                        string* out_disk_name,
+                        int* out_partition_num) {
+  if (!base::StartsWithASCII(partition_name, "/dev/", true)) {
+    LOG(ERROR) << "Invalid partition device name: " << partition_name;
+    return false;
+  }
+
+  size_t last_nondigit_pos = partition_name.find_last_not_of("0123456789");
+  if (last_nondigit_pos == string::npos ||
+      (last_nondigit_pos + 1) == partition_name.size()) {
+    LOG(ERROR) << "Unable to parse partition device name: " << partition_name;
+    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
+    size_t disk_name_len = last_nondigit_pos;
+    if (partition_name[last_nondigit_pos] != 'p' ||
+        last_nondigit_pos == 0 ||
+        !isdigit(partition_name[last_nondigit_pos - 1])) {
+      disk_name_len++;
+    }
+    *out_disk_name = partition_name.substr(0, disk_name_len);
+  }
+
+  if (out_partition_num) {
+    string partition_str = partition_name.substr(last_nondigit_pos + 1,
+                                                 partition_name_len);
+    *out_partition_num = atoi(partition_str.c_str());
+  }
+  return true;
+}
+
+string MakePartitionName(const string& disk_name, int partition_num) {
+  if (partition_num < 1) {
+    LOG(ERROR) << "Invalid partition number: " << partition_num;
+    return string();
+  }
+
+  if (!base::StartsWithASCII(disk_name, "/dev/", true)) {
+    LOG(ERROR) << "Invalid disk name: " << disk_name;
+    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.
+    // Add "p" to separate the disk name from partition number,
+    // e.g. "/dev/loop0p2"
+    partition_name += 'p';
+  }
+
+  partition_name += std::to_string(partition_num);
+
+  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';
+  return strerror_r(err, buf, sizeof(buf));
+}
+
+bool FileExists(const char* path) {
+  struct stat stbuf;
+  return 0 == lstat(path, &stbuf);
+}
+
+bool IsSymlink(const char* path) {
+  struct stat stbuf;
+  return lstat(path, &stbuf) == 0 && S_ISLNK(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,
+                  string* filename,
+                  int* fd) {
+  base::FilePath filename_template;
+  TEST_AND_RETURN_FALSE(
+      GetTempName(base_filename_template, &filename_template));
+  DCHECK(filename || fd);
+  vector<char> buf(filename_template.value().size() + 1);
+  memcpy(buf.data(), filename_template.value().data(),
+         filename_template.value().size());
+  buf[filename_template.value().size()] = '\0';
+
+  int mkstemp_fd = mkstemp(buf.data());
+  TEST_AND_RETURN_FALSE_ERRNO(mkstemp_fd >= 0);
+  if (filename) {
+    *filename = buf.data();
+  }
+  if (fd) {
+    *fd = mkstemp_fd;
+  } else {
+    close(mkstemp_fd);
+  }
+  return true;
+}
+
+bool MakeTempDirectory(const string& base_dirname_template,
+                       string* dirname) {
+  base::FilePath dirname_template;
+  TEST_AND_RETURN_FALSE(GetTempName(base_dirname_template, &dirname_template));
+  DCHECK(dirname);
+  vector<char> buf(dirname_template.value().size() + 1);
+  memcpy(buf.data(), dirname_template.value().data(),
+         dirname_template.value().size());
+  buf[dirname_template.value().size()] = '\0';
+
+  char* return_code = mkdtemp(buf.data());
+  TEST_AND_RETURN_FALSE_ERRNO(return_code != nullptr);
+  *dirname = buf.data();
+  return true;
+}
+
+bool MountFilesystem(const string& device,
+                     const string& mountpoint,
+                     unsigned long mountflags) {  // NOLINT(runtime/int)
+  // TODO(sosa): Remove "ext3" once crbug.com/208022 is resolved.
+  const vector<const char*> fstypes{"ext2", "ext3", "ext4", "squashfs"};
+  for (const char* fstype : fstypes) {
+    int rc = mount(device.c_str(), mountpoint.c_str(), fstype, mountflags,
+                   nullptr);
+    if (rc == 0)
+      return true;
+
+    PLOG(WARNING) << "Unable to mount destination device " << device
+                  << " on " << mountpoint << " as " << fstype;
+  }
+  LOG(ERROR) << "Unable to mount " << device << " with any supported type";
+  return false;
+}
+
+bool UnmountFilesystem(const string& mountpoint) {
+  for (int num_retries = 0; ; ++num_retries) {
+    if (umount(mountpoint.c_str()) == 0)
+      break;
+
+    TEST_AND_RETURN_FALSE_ERRNO(errno == EBUSY &&
+                                num_retries < kUnmountMaxNumOfRetries);
+    usleep(kUnmountRetryIntervalInMicroseconds);
+  }
+  return true;
+}
+
+bool GetFilesystemSize(const string& device,
+                       int* out_block_count,
+                       int* out_block_size) {
+  int fd = HANDLE_EINTR(open(device.c_str(), O_RDONLY));
+  TEST_AND_RETURN_FALSE_ERRNO(fd >= 0);
+  ScopedFdCloser fd_closer(&fd);
+  return GetFilesystemSizeFromFD(fd, out_block_count, out_block_size);
+}
+
+bool GetFilesystemSizeFromFD(int fd,
+                             int* out_block_count,
+                             int* out_block_size) {
+  TEST_AND_RETURN_FALSE(fd >= 0);
+
+  // Determine the filesystem size by directly reading the block count and
+  // block size information from the superblock. Supported FS are ext3 and
+  // squashfs.
+
+  // Read from the fd only once and detect in memory. The first 2 KiB is enough
+  // to read the ext2 superblock (located at offset 1024) and the squashfs
+  // superblock (located at offset 0).
+  const ssize_t kBufferSize = 2048;
+
+  uint8_t buffer[kBufferSize];
+  if (HANDLE_EINTR(pread(fd, buffer, kBufferSize, 0)) != kBufferSize) {
+    PLOG(ERROR) << "Unable to read the file system header:";
+    return false;
+  }
+
+  if (GetSquashfs4Size(buffer, kBufferSize, out_block_count, out_block_size))
+    return true;
+  if (GetExt3Size(buffer, kBufferSize, out_block_count, out_block_size))
+    return true;
+
+  LOG(ERROR) << "Unable to determine file system type.";
+  return false;
+}
+
+bool GetExt3Size(const uint8_t* buffer, size_t buffer_size,
+                 int* out_block_count,
+                 int* out_block_size) {
+  // See include/linux/ext2_fs.h for more details on the structure. We obtain
+  // ext2 constants from ext2fs/ext2fs.h header but we don't link with the
+  // library.
+  if (buffer_size < SUPERBLOCK_OFFSET + SUPERBLOCK_SIZE)
+    return false;
+
+  const uint8_t* superblock = buffer + SUPERBLOCK_OFFSET;
+
+  // ext3_fs.h: ext3_super_block.s_blocks_count
+  uint32_t block_count =
+      *reinterpret_cast<const uint32_t*>(superblock + 1 * sizeof(int32_t));
+
+  // ext3_fs.h: ext3_super_block.s_log_block_size
+  uint32_t log_block_size =
+      *reinterpret_cast<const uint32_t*>(superblock + 6 * sizeof(int32_t));
+
+  // ext3_fs.h: ext3_super_block.s_magic
+  uint16_t magic =
+      *reinterpret_cast<const uint16_t*>(superblock + 14 * sizeof(int32_t));
+
+  block_count = le32toh(block_count);
+  log_block_size = le32toh(log_block_size) + EXT2_MIN_BLOCK_LOG_SIZE;
+  magic = le16toh(magic);
+
+  // Sanity check the parameters.
+  TEST_AND_RETURN_FALSE(magic == EXT2_SUPER_MAGIC);
+  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);
+
+  if (out_block_count)
+    *out_block_count = block_count;
+  if (out_block_size)
+    *out_block_size = 1 << log_block_size;
+  return true;
+}
+
+bool GetSquashfs4Size(const uint8_t* buffer, size_t buffer_size,
+                      int* out_block_count,
+                      int* out_block_size) {
+  // See fs/squashfs/squashfs_fs.h for format details. We only support
+  // Squashfs 4.x little endian.
+
+  // sizeof(struct squashfs_super_block)
+  const size_t kSquashfsSuperBlockSize = 96;
+  if (buffer_size < kSquashfsSuperBlockSize)
+    return false;
+
+  // Check magic, squashfs_fs.h: SQUASHFS_MAGIC
+  if (memcmp(buffer, "hsqs", 4) != 0)
+    return false;  // Only little endian is supported.
+
+  // squashfs_fs.h: struct squashfs_super_block.s_major
+  uint16_t s_major = *reinterpret_cast<const uint16_t*>(
+      buffer + 5 * sizeof(uint32_t) + 4 * sizeof(uint16_t));
+
+  if (s_major != 4) {
+    LOG(ERROR) << "Found unsupported squashfs major version " << s_major;
+    return false;
+  }
+
+  // squashfs_fs.h: struct squashfs_super_block.bytes_used
+  uint64_t bytes_used = *reinterpret_cast<const int64_t*>(
+      buffer + 5 * sizeof(uint32_t) + 6 * sizeof(uint16_t) + sizeof(uint64_t));
+
+  const int block_size = 4096;
+
+  // The squashfs' bytes_used doesn't need to be aligned with the block boundary
+  // so we round up to the nearest blocksize.
+  if (out_block_count)
+    *out_block_count = (bytes_used + block_size - 1) / block_size;
+  if (out_block_size)
+    *out_block_size = block_size;
+  return true;
+}
+
+bool IsExtFilesystem(const string& device) {
+  brillo::Blob header;
+  // The first 2 KiB is enough to read the ext2 superblock (located at offset
+  // 1024).
+  if (!ReadFileChunk(device, 0, 2048, &header))
+    return false;
+  return GetExt3Size(header.data(), header.size(), nullptr, nullptr);
+}
+
+bool IsSquashfsFilesystem(const string& device) {
+  brillo::Blob header;
+  // The first 96 is enough to read the squashfs superblock.
+  const ssize_t kSquashfsSuperBlockSize = 96;
+  if (!ReadFileChunk(device, 0, kSquashfsSuperBlockSize, &header))
+    return false;
+  return GetSquashfs4Size(header.data(), header.size(), nullptr, nullptr);
+}
+
+// Tries to parse the header of an ELF file to obtain a human-readable
+// description of it on the |output| string.
+static bool GetFileFormatELF(const uint8_t* buffer, size_t size,
+                             string* output) {
+  // 0x00: EI_MAG - ELF magic header, 4 bytes.
+  if (size < SELFMAG || memcmp(buffer, ELFMAG, SELFMAG) != 0)
+    return false;
+  *output = "ELF";
+
+  // 0x04: EI_CLASS, 1 byte.
+  if (size < EI_CLASS + 1)
+    return true;
+  switch (buffer[EI_CLASS]) {
+    case ELFCLASS32:
+      *output += " 32-bit";
+      break;
+    case ELFCLASS64:
+      *output += " 64-bit";
+      break;
+    default:
+      *output += " ?-bit";
+  }
+
+  // 0x05: EI_DATA, endianness, 1 byte.
+  if (size < EI_DATA + 1)
+    return true;
+  uint8_t ei_data = buffer[EI_DATA];
+  switch (ei_data) {
+    case ELFDATA2LSB:
+      *output += " little-endian";
+      break;
+    case ELFDATA2MSB:
+      *output += " big-endian";
+      break;
+    default:
+      *output += " ?-endian";
+      // Don't parse anything after the 0x10 offset if endianness is unknown.
+      return true;
+  }
+
+  const Elf32_Ehdr* hdr = reinterpret_cast<const Elf32_Ehdr*>(buffer);
+  // 0x12: e_machine, 2 byte endianness based on ei_data. The position (0x12)
+  // and size is the same for both 32 and 64 bits.
+  if (size < offsetof(Elf32_Ehdr, e_machine) + sizeof(hdr->e_machine))
+    return true;
+  uint16_t e_machine;
+  // Fix endianess regardless of the host endianess.
+  if (ei_data == ELFDATA2LSB)
+    e_machine = le16toh(hdr->e_machine);
+  else
+    e_machine = be16toh(hdr->e_machine);
+
+  switch (e_machine) {
+    case EM_386:
+      *output += " x86";
+      break;
+    case EM_MIPS:
+      *output += " mips";
+      break;
+    case EM_ARM:
+      *output += " arm";
+      break;
+    case EM_X86_64:
+      *output += " x86-64";
+      break;
+    default:
+      *output += " unknown-arch";
+  }
+  return true;
+}
+
+string GetFileFormat(const string& path) {
+  brillo::Blob buffer;
+  if (!ReadFileChunkAndAppend(path, 0, kGetFileFormatMaxHeaderSize, &buffer))
+    return "File not found.";
+
+  string result;
+  if (GetFileFormatELF(buffer.data(), buffer.size(), &result))
+    return result;
+
+  return "data";
+}
+
+namespace {
+// Do the actual trigger. We do it as a main-loop callback to (try to) get a
+// consistent stack trace.
+void TriggerCrashReporterUpload() {
+  pid_t pid = fork();
+  CHECK_GE(pid, 0) << "fork failed";  // fork() failed. Something is very wrong.
+  if (pid == 0) {
+    // We are the child. Crash.
+    abort();  // never returns
+  }
+  // We are the parent. Wait for child to terminate.
+  pid_t result = waitpid(pid, nullptr, 0);
+  LOG_IF(ERROR, result < 0) << "waitpid() failed";
+}
+}  // namespace
+
+void ScheduleCrashReporterUpload() {
+  brillo::MessageLoop::current()->PostTask(
+      FROM_HERE,
+      base::Bind(&TriggerCrashReporterUpload));
+}
+
+bool SetCpuShares(CpuShares shares) {
+  string string_shares = base::IntToString(static_cast<int>(shares));
+  string cpu_shares_file = string(utils::kCGroupDir) + "/cpu.shares";
+  LOG(INFO) << "Setting cgroup cpu shares to  " << string_shares;
+  if (utils::WriteFile(cpu_shares_file.c_str(), string_shares.c_str(),
+                       string_shares.size())) {
+    return true;
+  } else {
+    LOG(ERROR) << "Failed to change cgroup cpu shares to "<< string_shares
+               << " using " << cpu_shares_file;
+    return false;
+  }
+}
+
+int FuzzInt(int value, unsigned int range) {
+  int min = value - range / 2;
+  int max = value + range - range / 2;
+  return base::RandInt(min, max);
+}
+
+string FormatSecs(unsigned secs) {
+  return FormatTimeDelta(TimeDelta::FromSeconds(secs));
+}
+
+string FormatTimeDelta(TimeDelta delta) {
+  string str;
+
+  // Handle negative durations by prefixing with a minus.
+  if (delta.ToInternalValue() < 0) {
+    delta *= -1;
+    str = "-";
+  }
+
+  // Canonicalize into days, hours, minutes, seconds and microseconds.
+  unsigned days = delta.InDays();
+  delta -= TimeDelta::FromDays(days);
+  unsigned hours = delta.InHours();
+  delta -= TimeDelta::FromHours(hours);
+  unsigned mins = delta.InMinutes();
+  delta -= TimeDelta::FromMinutes(mins);
+  unsigned secs = delta.InSeconds();
+  delta -= TimeDelta::FromSeconds(secs);
+  unsigned usecs = delta.InMicroseconds();
+
+  if (days)
+    base::StringAppendF(&str, "%ud", days);
+  if (days || hours)
+    base::StringAppendF(&str, "%uh", hours);
+  if (days || hours || mins)
+    base::StringAppendF(&str, "%um", mins);
+  base::StringAppendF(&str, "%u", secs);
+  if (usecs) {
+    int width = 6;
+    while ((usecs / 10) * 10 == usecs) {
+      usecs /= 10;
+      width--;
+    }
+    base::StringAppendF(&str, ".%0*u", width, usecs);
+  }
+  base::StringAppendF(&str, "s");
+  return str;
+}
+
+string ToString(const Time utc_time) {
+  Time::Exploded exp_time;
+  utc_time.UTCExplode(&exp_time);
+  return base::StringPrintf("%d/%d/%d %d:%02d:%02d GMT",
+                      exp_time.month,
+                      exp_time.day_of_month,
+                      exp_time.year,
+                      exp_time.hour,
+                      exp_time.minute,
+                      exp_time.second);
+}
+
+string ToString(bool b) {
+  return (b ? "true" : "false");
+}
+
+string ToString(DownloadSource source) {
+  switch (source) {
+    case kDownloadSourceHttpsServer: return "HttpsServer";
+    case kDownloadSourceHttpServer:  return "HttpServer";
+    case kDownloadSourceHttpPeer:    return "HttpPeer";
+    case kNumDownloadSources:        return "Unknown";
+    // Don't add a default case to let the compiler warn about newly added
+    // download sources which should be added here.
+  }
+
+  return "Unknown";
+}
+
+string ToString(PayloadType payload_type) {
+  switch (payload_type) {
+    case kPayloadTypeDelta:      return "Delta";
+    case kPayloadTypeFull:       return "Full";
+    case kPayloadTypeForcedFull: return "ForcedFull";
+    case kNumPayloadTypes:       return "Unknown";
+    // Don't add a default case to let the compiler warn about newly added
+    // payload types which should be added here.
+  }
+
+  return "Unknown";
+}
+
+ErrorCode GetBaseErrorCode(ErrorCode code) {
+  // Ignore the higher order bits in the code by applying the mask as
+  // we want the enumerations to be in the small contiguous range
+  // with values less than ErrorCode::kUmaReportedMax.
+  ErrorCode base_code = static_cast<ErrorCode>(
+      static_cast<int>(code) & ~static_cast<int>(ErrorCode::kSpecialFlags));
+
+  // Make additional adjustments required for UMA and error classification.
+  // TODO(jaysri): Move this logic to UeErrorCode.cc when we fix
+  // chromium-os:34369.
+  if (base_code >= ErrorCode::kOmahaRequestHTTPResponseBase) {
+    // Since we want to keep the enums to a small value, aggregate all HTTP
+    // errors into this one bucket for UMA and error classification purposes.
+    LOG(INFO) << "Converting error code " << base_code
+              << " to ErrorCode::kOmahaErrorInHTTPResponse";
+    base_code = ErrorCode::kOmahaErrorInHTTPResponse;
+  }
+
+  return base_code;
+}
+
+string CodeToString(ErrorCode code) {
+  // If the given code has both parts (i.e. the error code part and the flags
+  // part) then strip off the flags part since the switch statement below
+  // has case statements only for the base error code or a single flag but
+  // doesn't support any combinations of those.
+  if ((static_cast<int>(code) & static_cast<int>(ErrorCode::kSpecialFlags)) &&
+      (static_cast<int>(code) & ~static_cast<int>(ErrorCode::kSpecialFlags)))
+    code = static_cast<ErrorCode>(
+        static_cast<int>(code) & ~static_cast<int>(ErrorCode::kSpecialFlags));
+  switch (code) {
+    case ErrorCode::kSuccess: return "ErrorCode::kSuccess";
+    case ErrorCode::kError: return "ErrorCode::kError";
+    case ErrorCode::kOmahaRequestError: return "ErrorCode::kOmahaRequestError";
+    case ErrorCode::kOmahaResponseHandlerError:
+      return "ErrorCode::kOmahaResponseHandlerError";
+    case ErrorCode::kFilesystemCopierError:
+      return "ErrorCode::kFilesystemCopierError";
+    case ErrorCode::kPostinstallRunnerError:
+      return "ErrorCode::kPostinstallRunnerError";
+    case ErrorCode::kPayloadMismatchedType:
+      return "ErrorCode::kPayloadMismatchedType";
+    case ErrorCode::kInstallDeviceOpenError:
+      return "ErrorCode::kInstallDeviceOpenError";
+    case ErrorCode::kKernelDeviceOpenError:
+      return "ErrorCode::kKernelDeviceOpenError";
+    case ErrorCode::kDownloadTransferError:
+      return "ErrorCode::kDownloadTransferError";
+    case ErrorCode::kPayloadHashMismatchError:
+      return "ErrorCode::kPayloadHashMismatchError";
+    case ErrorCode::kPayloadSizeMismatchError:
+      return "ErrorCode::kPayloadSizeMismatchError";
+    case ErrorCode::kDownloadPayloadVerificationError:
+      return "ErrorCode::kDownloadPayloadVerificationError";
+    case ErrorCode::kDownloadNewPartitionInfoError:
+      return "ErrorCode::kDownloadNewPartitionInfoError";
+    case ErrorCode::kDownloadWriteError:
+      return "ErrorCode::kDownloadWriteError";
+    case ErrorCode::kNewRootfsVerificationError:
+      return "ErrorCode::kNewRootfsVerificationError";
+    case ErrorCode::kNewKernelVerificationError:
+      return "ErrorCode::kNewKernelVerificationError";
+    case ErrorCode::kSignedDeltaPayloadExpectedError:
+      return "ErrorCode::kSignedDeltaPayloadExpectedError";
+    case ErrorCode::kDownloadPayloadPubKeyVerificationError:
+      return "ErrorCode::kDownloadPayloadPubKeyVerificationError";
+    case ErrorCode::kPostinstallBootedFromFirmwareB:
+      return "ErrorCode::kPostinstallBootedFromFirmwareB";
+    case ErrorCode::kDownloadStateInitializationError:
+      return "ErrorCode::kDownloadStateInitializationError";
+    case ErrorCode::kDownloadInvalidMetadataMagicString:
+      return "ErrorCode::kDownloadInvalidMetadataMagicString";
+    case ErrorCode::kDownloadSignatureMissingInManifest:
+      return "ErrorCode::kDownloadSignatureMissingInManifest";
+    case ErrorCode::kDownloadManifestParseError:
+      return "ErrorCode::kDownloadManifestParseError";
+    case ErrorCode::kDownloadMetadataSignatureError:
+      return "ErrorCode::kDownloadMetadataSignatureError";
+    case ErrorCode::kDownloadMetadataSignatureVerificationError:
+      return "ErrorCode::kDownloadMetadataSignatureVerificationError";
+    case ErrorCode::kDownloadMetadataSignatureMismatch:
+      return "ErrorCode::kDownloadMetadataSignatureMismatch";
+    case ErrorCode::kDownloadOperationHashVerificationError:
+      return "ErrorCode::kDownloadOperationHashVerificationError";
+    case ErrorCode::kDownloadOperationExecutionError:
+      return "ErrorCode::kDownloadOperationExecutionError";
+    case ErrorCode::kDownloadOperationHashMismatch:
+      return "ErrorCode::kDownloadOperationHashMismatch";
+    case ErrorCode::kOmahaRequestEmptyResponseError:
+      return "ErrorCode::kOmahaRequestEmptyResponseError";
+    case ErrorCode::kOmahaRequestXMLParseError:
+      return "ErrorCode::kOmahaRequestXMLParseError";
+    case ErrorCode::kDownloadInvalidMetadataSize:
+      return "ErrorCode::kDownloadInvalidMetadataSize";
+    case ErrorCode::kDownloadInvalidMetadataSignature:
+      return "ErrorCode::kDownloadInvalidMetadataSignature";
+    case ErrorCode::kOmahaResponseInvalid:
+      return "ErrorCode::kOmahaResponseInvalid";
+    case ErrorCode::kOmahaUpdateIgnoredPerPolicy:
+      return "ErrorCode::kOmahaUpdateIgnoredPerPolicy";
+    case ErrorCode::kOmahaUpdateDeferredPerPolicy:
+      return "ErrorCode::kOmahaUpdateDeferredPerPolicy";
+    case ErrorCode::kOmahaErrorInHTTPResponse:
+      return "ErrorCode::kOmahaErrorInHTTPResponse";
+    case ErrorCode::kDownloadOperationHashMissingError:
+      return "ErrorCode::kDownloadOperationHashMissingError";
+    case ErrorCode::kDownloadMetadataSignatureMissingError:
+      return "ErrorCode::kDownloadMetadataSignatureMissingError";
+    case ErrorCode::kOmahaUpdateDeferredForBackoff:
+      return "ErrorCode::kOmahaUpdateDeferredForBackoff";
+    case ErrorCode::kPostinstallPowerwashError:
+      return "ErrorCode::kPostinstallPowerwashError";
+    case ErrorCode::kUpdateCanceledByChannelChange:
+      return "ErrorCode::kUpdateCanceledByChannelChange";
+    case ErrorCode::kUmaReportedMax:
+      return "ErrorCode::kUmaReportedMax";
+    case ErrorCode::kOmahaRequestHTTPResponseBase:
+      return "ErrorCode::kOmahaRequestHTTPResponseBase";
+    case ErrorCode::kResumedFlag:
+      return "Resumed";
+    case ErrorCode::kDevModeFlag:
+      return "DevMode";
+    case ErrorCode::kTestImageFlag:
+      return "TestImage";
+    case ErrorCode::kTestOmahaUrlFlag:
+      return "TestOmahaUrl";
+    case ErrorCode::kSpecialFlags:
+      return "ErrorCode::kSpecialFlags";
+    case ErrorCode::kPostinstallFirmwareRONotUpdatable:
+      return "ErrorCode::kPostinstallFirmwareRONotUpdatable";
+    case ErrorCode::kUnsupportedMajorPayloadVersion:
+      return "ErrorCode::kUnsupportedMajorPayloadVersion";
+    case ErrorCode::kUnsupportedMinorPayloadVersion:
+      return "ErrorCode::kUnsupportedMinorPayloadVersion";
+    case ErrorCode::kOmahaRequestXMLHasEntityDecl:
+      return "ErrorCode::kOmahaRequestXMLHasEntityDecl";
+    case ErrorCode::kFilesystemVerifierError:
+      return "ErrorCode::kFilesystemVerifierError";
+    // 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::UintToString(static_cast<unsigned>(code));
+}
+
+bool CreatePowerwashMarkerFile(const char* file_path) {
+  const char* marker_file = file_path ? file_path : kPowerwashMarkerFile;
+  bool result = utils::WriteFile(marker_file,
+                                 kPowerwashCommand,
+                                 strlen(kPowerwashCommand));
+  if (result) {
+    LOG(INFO) << "Created " << marker_file << " to powerwash on next reboot";
+  } else {
+    PLOG(ERROR) << "Error in creating powerwash marker file: " << marker_file;
+  }
+
+  return result;
+}
+
+bool DeletePowerwashMarkerFile(const char* file_path) {
+  const char* marker_file = file_path ? file_path : kPowerwashMarkerFile;
+  const base::FilePath kPowerwashMarkerPath(marker_file);
+  bool result = base::DeleteFile(kPowerwashMarkerPath, false);
+
+  if (result)
+    LOG(INFO) << "Successfully deleted the powerwash marker file : "
+              << marker_file;
+  else
+    PLOG(ERROR) << "Could not delete the powerwash marker file : "
+                << marker_file;
+
+  return result;
+}
+
+Time TimeFromStructTimespec(struct timespec *ts) {
+  int64_t us = static_cast<int64_t>(ts->tv_sec) * Time::kMicrosecondsPerSecond +
+      static_cast<int64_t>(ts->tv_nsec) / Time::kNanosecondsPerMicrosecond;
+  return Time::UnixEpoch() + TimeDelta::FromMicroseconds(us);
+}
+
+string StringVectorToString(const vector<string> &vec_str) {
+  string str = "[";
+  for (vector<string>::const_iterator i = vec_str.begin();
+       i != vec_str.end(); ++i) {
+    if (i != vec_str.begin())
+      str += ", ";
+    str += '"';
+    str += *i;
+    str += '"';
+  }
+  str += "]";
+  return str;
+}
+
+string CalculateP2PFileId(const string& payload_hash, size_t payload_size) {
+  string encoded_hash = brillo::data_encoding::Base64Encode(payload_hash);
+  return base::StringPrintf("cros_update_size_%" PRIuS "_hash_%s",
+                            payload_size,
+                            encoded_hash.c_str());
+}
+
+bool DecodeAndStoreBase64String(const string& base64_encoded,
+                                base::FilePath *out_path) {
+  brillo::Blob contents;
+
+  out_path->clear();
+
+  if (base64_encoded.size() == 0) {
+    LOG(ERROR) << "Can't decode empty string.";
+    return false;
+  }
+
+  if (!brillo::data_encoding::Base64Decode(base64_encoded, &contents) ||
+      contents.size() == 0) {
+    LOG(ERROR) << "Error decoding base64.";
+    return false;
+  }
+
+  FILE *file = base::CreateAndOpenTemporaryFile(out_path);
+  if (file == nullptr) {
+    LOG(ERROR) << "Error creating temporary file.";
+    return false;
+  }
+
+  if (fwrite(contents.data(), 1, contents.size(), file) != contents.size()) {
+    PLOG(ERROR) << "Error writing to temporary file.";
+    if (fclose(file) != 0)
+      PLOG(ERROR) << "Error closing temporary file.";
+    if (unlink(out_path->value().c_str()) != 0)
+      PLOG(ERROR) << "Error unlinking temporary file.";
+    out_path->clear();
+    return false;
+  }
+
+  if (fclose(file) != 0) {
+    PLOG(ERROR) << "Error closing temporary file.";
+    out_path->clear();
+    return false;
+  }
+
+  return true;
+}
+
+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;
+  const int64_t kNumSecondsPerWeek = 7*24*3600;
+  const int64_t kNumDaysPerWeek = 7;
+
+  time_t omaha_time = unix_time - kOmahaEpoch;
+
+  if (omaha_time < 0)
+    return false;
+
+  // Note, as per the comment in utils.h we are deliberately not
+  // handling DST correctly.
+
+  int64_t num_weeks_since_omaha_epoch = omaha_time / kNumSecondsPerWeek;
+  *out_num_days = num_weeks_since_omaha_epoch * kNumDaysPerWeek;
+
+  return true;
+}
+
+bool GetMinorVersion(const brillo::KeyValueStore& store,
+                     uint32_t* minor_version) {
+  string result;
+  if (store.GetString("PAYLOAD_MINOR_VERSION", &result)) {
+    if (!base::StringToUint(result, minor_version)) {
+      LOG(ERROR) << "StringToUint failed when parsing delta minor version.";
+      return false;
+    }
+    return true;
+  }
+  return false;
+}
+
+bool ReadExtents(const string& path, const vector<Extent>& extents,
+                 brillo::Blob* out_data, ssize_t out_data_size,
+                 size_t block_size) {
+  brillo::Blob data(out_data_size);
+  ssize_t bytes_read = 0;
+  int fd = open(path.c_str(), O_RDONLY);
+  TEST_AND_RETURN_FALSE_ERRNO(fd >= 0);
+  ScopedFdCloser fd_closer(&fd);
+
+  for (const Extent& extent : extents) {
+    ssize_t bytes_read_this_iteration = 0;
+    ssize_t bytes = extent.num_blocks() * block_size;
+    TEST_AND_RETURN_FALSE(bytes_read + bytes <= out_data_size);
+    TEST_AND_RETURN_FALSE(utils::PReadAll(fd,
+                                          &data[bytes_read],
+                                          bytes,
+                                          extent.start_block() * block_size,
+                                          &bytes_read_this_iteration));
+    TEST_AND_RETURN_FALSE(bytes_read_this_iteration == bytes);
+    bytes_read += bytes_read_this_iteration;
+  }
+  TEST_AND_RETURN_FALSE(out_data_size == bytes_read);
+  *out_data = data;
+  return true;
+}
+
+bool GetBootId(string* boot_id) {
+  TEST_AND_RETURN_FALSE(
+      base::ReadFileToString(base::FilePath(kBootIdPath), boot_id));
+  base::TrimWhitespaceASCII(*boot_id, base::TRIM_TRAILING, boot_id);
+  return true;
+}
+
+}  // namespace utils
+
+}  // namespace chromeos_update_engine
diff --git a/common/utils.h b/common/utils.h
new file mode 100644
index 0000000..0440dd8
--- /dev/null
+++ b/common/utils.h
@@ -0,0 +1,510 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_COMMON_UTILS_H_
+#define UPDATE_ENGINE_COMMON_UTILS_H_
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+#include <base/posix/eintr_wrapper.h>
+#include <base/time/time.h>
+#include <brillo/key_value_store.h>
+#include <brillo/secure_blob.h>
+
+#include "update_engine/common/action.h"
+#include "update_engine/common/action_processor.h"
+#include "update_engine/common/constants.h"
+#include "update_engine/payload_consumer/file_descriptor.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+namespace utils {
+
+// Converts a struct timespec representing a number of seconds since
+// the Unix epoch to a base::Time. Sub-microsecond time is rounded
+// down.
+base::Time TimeFromStructTimespec(struct timespec *ts);
+
+// Formats |vec_str| as a string of the form ["<elem1>", "<elem2>"].
+// Does no escaping, only use this for presentation in error messages.
+std::string StringVectorToString(const std::vector<std::string> &vec_str);
+
+// Calculates the p2p file id from payload hash and size
+std::string CalculateP2PFileId(const std::string& 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, int data_len);
+
+// Calls write() or pwrite() repeatedly until all count bytes at buf are
+// written to fd or an error occurs. Returns true on success.
+bool WriteAll(int fd, const void* buf, size_t count);
+bool PWriteAll(int fd, const void* buf, size_t count, off_t offset);
+
+bool WriteAll(FileDescriptorPtr fd, const void* buf, size_t count);
+bool PWriteAll(FileDescriptorPtr fd,
+               const void* buf,
+               size_t count,
+               off_t offset);
+
+// Calls pread() repeatedly until count bytes are read, or EOF is reached.
+// Returns number of bytes read in *bytes_read. Returns true on success.
+bool PReadAll(int fd, void* buf, size_t count, off_t offset,
+              ssize_t* out_bytes_read);
+
+bool PReadAll(FileDescriptorPtr fd, void* buf, size_t count, off_t offset,
+              ssize_t* out_bytes_read);
+
+// Opens |path| for reading and appends its entire content to the container
+// pointed to by |out_p|. Returns true upon successfully reading all of the
+// file's content, false otherwise, in which case the state of the output
+// container is unknown. ReadFileChunk starts reading the file from |offset|; if
+// |size| is not -1, only up to |size| bytes are read in.
+bool ReadFile(const std::string& path, brillo::Blob* out_p);
+bool ReadFile(const std::string& path, std::string* out_p);
+bool ReadFileChunk(const std::string& path, off_t offset, off_t size,
+                   brillo::Blob* out_p);
+
+// Invokes |cmd| in a pipe and appends its stdout to the container pointed to by
+// |out_p|. Returns true upon successfully reading all of the output, false
+// otherwise, in which case the state of the output container is unknown.
+bool ReadPipe(const std::string& cmd, std::string* out_p);
+
+// Returns the size of the block device at the file descriptor fd. If an error
+// occurs, -1 is returned.
+off_t BlockDevSize(int fd);
+
+// Returns the size of the file at path, or the file desciptor fd. If the file
+// is actually a block device, this function will automatically call
+// BlockDevSize. If the file doesn't exist or some error occurrs, -1 is
+// returned.
+off_t FileSize(const std::string& path);
+off_t FileSize(int fd);
+
+std::string ErrnoNumberAsString(int err);
+
+// Returns true if the file exists for sure. Returns false if it doesn't exist,
+// or an error occurs.
+bool FileExists(const char* path);
+
+// Returns true if |path| exists and is a symbolic link.
+bool IsSymlink(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
+// "../"), then it is prepended the system's temporary directory. On success,
+// stores the name of the new temporary file in |filename|. If |fd| is
+// non-null, the file descriptor returned by mkstemp is written to it and
+// kept open; otherwise, it is closed. The template must end with "XXXXXX".
+// Returns true on success.
+bool MakeTempFile(const std::string& base_filename_template,
+                  std::string* filename,
+                  int* fd);
+
+// If |base_dirname_template| is neither absolute (starts with "/") nor
+// explicitly relative to the current working directory (starts with "./" or
+// "../"), then it is prepended the system's temporary directory. On success,
+// stores the name of the new temporary directory in |dirname|. The template
+// must end with "XXXXXX". Returns true on success.
+bool MakeTempDirectory(const std::string& base_dirname_template,
+                       std::string* dirname);
+
+// Splits the partition device name into the block device name and partition
+// number. For example, "/dev/sda3" will be split into {"/dev/sda", 3} and
+// "/dev/mmcblk0p2" into {"/dev/mmcblk0", 2}
+// Returns false when malformed device name is passed in.
+// If both output parameters are omitted (null), can be used
+// just to test the validity of the device name. Note that the function
+// simply checks if the device name looks like a valid device, no other
+// checks are performed (i.e. it doesn't check if the device actually exists).
+bool SplitPartitionName(const std::string& partition_name,
+                        std::string* out_disk_name,
+                        int* out_partition_num);
+
+// Builds a partition device name from the block device name and partition
+// number. For example:
+// {"/dev/sda", 1} => "/dev/sda1"
+// {"/dev/mmcblk2", 12} => "/dev/mmcblk2p12"
+// 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);
+
+// Synchronously mount or unmount a filesystem. Return true on success.
+// When mounting, it will attempt to mount the the device as "ext3", "ext2" and
+// "squashfs", with the passed |flags| options.
+bool MountFilesystem(const std::string& device, const std::string& mountpoint,
+                     unsigned long flags);  // NOLINT(runtime/int)
+bool UnmountFilesystem(const std::string& mountpoint);
+
+// Returns the block count and the block byte size of the file system on
+// |device| (which may be a real device or a path to a filesystem image) or on
+// an opened file descriptor |fd|. The actual file-system size is |block_count|
+// * |block_size| bytes. Returns true on success, false otherwise.
+bool GetFilesystemSize(const std::string& device,
+                       int* out_block_count,
+                       int* out_block_size);
+bool GetFilesystemSizeFromFD(int fd,
+                             int* out_block_count,
+                             int* out_block_size);
+
+// Determines the block count and block size of the ext3 fs. At least 2048 bytes
+// are required to parse the first superblock. Returns whether the buffer
+// contains a valid ext3 filesystem and the values were parsed.
+bool GetExt3Size(const uint8_t* buffer, size_t buffer_size,
+                 int* out_block_count,
+                 int* out_block_size);
+
+// Determines the block count and block size of the squashfs v4 fs. At least 96
+// bytes are required to parse the header of the filesystem. Since squashfs
+// doesn't define a physical block size, a value of 4096 is used for the block
+// size, which is the default padding when creating the filesystem.
+// Returns whether the buffer contains a valid squashfs v4 header and the size
+// was parsed. Only little endian squashfs is supported.
+bool GetSquashfs4Size(const uint8_t* buffer, size_t buffer_size,
+                      int* out_block_count,
+                      int* out_block_size);
+
+// Returns whether the filesystem is an ext[234] filesystem. In case of failure,
+// such as if the file |device| doesn't exists or can't be read, it returns
+// false.
+bool IsExtFilesystem(const std::string& device);
+
+// Returns whether the filesystem is a squashfs filesystem. In case of failure,
+// such as if the file |device| doesn't exists or can't be read, it returns
+// false.
+bool IsSquashfsFilesystem(const std::string& device);
+
+// Returns a human-readable string with the file format based on magic constants
+// on the header of the file.
+std::string GetFileFormat(const std::string& path);
+
+// Returns the string representation of the given UTC time.
+// such as "11/14/2011 14:05:30 GMT".
+std::string ToString(const base::Time utc_time);
+
+// Returns true or false depending on the value of b.
+std::string ToString(bool b);
+
+// Returns a string representation of the given enum.
+std::string ToString(DownloadSource source);
+
+// Returns a string representation of the given enum.
+std::string ToString(PayloadType payload_type);
+
+// Schedules a Main Loop callback to trigger the crash reporter to perform an
+// upload as if this process had crashed.
+void ScheduleCrashReporterUpload();
+
+// Fuzzes an integer |value| randomly in the range:
+// [value - range / 2, value + range - range / 2]
+int FuzzInt(int value, unsigned int range);
+
+// Log a string in hex to LOG(INFO). Useful for debugging.
+void HexDumpArray(const uint8_t* const arr, const size_t length);
+inline void HexDumpString(const std::string& str) {
+  HexDumpArray(reinterpret_cast<const uint8_t*>(str.data()), str.size());
+}
+inline void HexDumpVector(const brillo::Blob& vect) {
+  HexDumpArray(vect.data(), vect.size());
+}
+
+template<typename KeyType, typename ValueType>
+bool MapContainsKey(const std::map<KeyType, ValueType>& m, const KeyType& k) {
+  return m.find(k) != m.end();
+}
+template<typename KeyType>
+bool SetContainsKey(const std::set<KeyType>& s, const KeyType& k) {
+  return s.find(k) != s.end();
+}
+
+template<typename T>
+bool VectorContainsValue(const std::vector<T>& vect, const T& value) {
+  return std::find(vect.begin(), vect.end(), value) != vect.end();
+}
+
+template<typename T>
+bool VectorIndexOf(const std::vector<T>& vect, const T& value,
+                   typename std::vector<T>::size_type* out_index) {
+  typename std::vector<T>::const_iterator it = std::find(vect.begin(),
+                                                         vect.end(),
+                                                         value);
+  if (it == vect.end()) {
+    return false;
+  } else {
+    *out_index = it - vect.begin();
+    return true;
+  }
+}
+
+// Cgroups cpu shares constants. 1024 is the default shares a standard process
+// gets and 2 is the minimum value. We set High as a value that gives the
+// update-engine 2x the cpu share of a standard process.
+enum CpuShares {
+  kCpuSharesHigh = 2048,
+  kCpuSharesNormal = 1024,
+  kCpuSharesLow = 2,
+};
+
+// Sets the current process shares to |shares|. Returns true on
+// success, false otherwise.
+bool SetCpuShares(CpuShares shares);
+
+// Converts seconds into human readable notation including days, hours, minutes
+// and seconds. For example, 185 will yield 3m5s, 4300 will yield 1h11m40s, and
+// 360000 will yield 4d4h0m0s.  Zero padding not applied. Seconds are always
+// shown in the result.
+std::string FormatSecs(unsigned secs);
+
+// Converts a TimeDelta into human readable notation including days, hours,
+// minutes, seconds and fractions of a second down to microsecond granularity,
+// as necessary; for example, an output of 5d2h0m15.053s means that the input
+// time was precise to the milliseconds only. Zero padding not applied, except
+// for fractions. Seconds are always shown, but fractions thereof are only shown
+// when applicable. If |delta| is negative, the output will have a leading '-'
+// followed by the absolute duration.
+std::string FormatTimeDelta(base::TimeDelta delta);
+
+// This method transforms the given error code to be suitable for UMA and
+// for error classification purposes by removing the higher order bits and
+// aggregating error codes beyond the enum range, etc. This method is
+// idempotent, i.e. if called with a value previously returned by this method,
+// it'll return the same value again.
+ErrorCode GetBaseErrorCode(ErrorCode code);
+
+// Returns a string representation of the ErrorCodes (either the base
+// error codes or the bit flags) for logging purposes.
+std::string CodeToString(ErrorCode code);
+
+// Creates the powerwash marker file with the appropriate commands in it.  Uses
+// |file_path| as the path to the marker file if non-null, otherwise uses the
+// global default. Returns true if successfully created.  False otherwise.
+bool CreatePowerwashMarkerFile(const char* file_path);
+
+// Deletes the marker file used to trigger Powerwash using clobber-state.  Uses
+// |file_path| as the path to the marker file if non-null, otherwise uses the
+// global default. Returns true if successfully deleted. False otherwise.
+bool DeletePowerwashMarkerFile(const char* file_path);
+
+// Decodes the data in |base64_encoded| and stores it in a temporary
+// file. Returns false if the given data is empty, not well-formed
+// base64 or if an error occurred. If true is returned, the decoded
+// data is stored in the file returned in |out_path|. The file should
+// be deleted when no longer needed.
+bool DecodeAndStoreBase64String(const std::string& base64_encoded,
+                                base::FilePath *out_path);
+
+// Converts |time| to an Omaha InstallDate which is defined as "the
+// number of PST8PDT calendar weeks since Jan 1st 2007 0:00 PST, times
+// seven" with PST8PDT defined as "Pacific Time" (e.g. UTC-07:00 if
+// daylight savings is observed and UTC-08:00 otherwise.)
+//
+// If the passed in |time| variable is before Monday January 1st 2007
+// 0:00 PST, False is returned and the value returned in
+// |out_num_days| is undefined. Otherwise the number of PST8PDT
+// calendar weeks since that date times seven is returned in
+// |out_num_days| and the function returns True.
+//
+// (NOTE: This function does not currently take daylight savings time
+// into account so the result may up to one hour off. This is because
+// the glibc date and timezone routines depend on the TZ environment
+// variable and changing environment variables is not thread-safe.
+bool ConvertToOmahaInstallDate(base::Time time, int *out_num_days);
+
+// Look for the minor version value in the passed |store| and set
+// |minor_version| to that value. Return whether the value was found and valid.
+bool GetMinorVersion(const brillo::KeyValueStore& store,
+                     uint32_t* minor_version);
+
+// This function reads the specified data in |extents| into |out_data|. The
+// extents are read from the file at |path|. |out_data_size| is the size of
+// |out_data|. Returns false if the number of bytes to read given in
+// |extents| does not equal |out_data_size|.
+bool ReadExtents(const std::string& path, const std::vector<Extent>& extents,
+                 brillo::Blob* out_data, ssize_t out_data_size,
+                 size_t block_size);
+
+// Read the current boot identifier and store it in |boot_id|. This identifier
+// is constants during the same boot of the kernel and is regenerated after
+// reboot. Returns whether it succeeded getting the boot_id.
+bool GetBootId(std::string* boot_id);
+
+}  // namespace utils
+
+
+// Utility class to close a file descriptor
+class ScopedFdCloser {
+ public:
+  explicit ScopedFdCloser(int* fd) : fd_(fd) {}
+  ~ScopedFdCloser() {
+    if (should_close_ && fd_ && (*fd_ >= 0) && !IGNORE_EINTR(close(*fd_)))
+      *fd_ = -1;
+  }
+  void set_should_close(bool should_close) { should_close_ = should_close; }
+ private:
+  int* fd_;
+  bool should_close_ = true;
+  DISALLOW_COPY_AND_ASSIGN(ScopedFdCloser);
+};
+
+// Utility class to delete a file when it goes out of scope.
+class ScopedPathUnlinker {
+ public:
+  explicit ScopedPathUnlinker(const std::string& path)
+      : path_(path),
+        should_remove_(true) {}
+  ~ScopedPathUnlinker() {
+    if (should_remove_ && unlink(path_.c_str()) < 0) {
+      PLOG(ERROR) << "Unable to unlink path " << path_;
+    }
+  }
+  void set_should_remove(bool should_remove) { should_remove_ = should_remove; }
+
+ private:
+  const std::string path_;
+  bool should_remove_;
+  DISALLOW_COPY_AND_ASSIGN(ScopedPathUnlinker);
+};
+
+// Utility class to delete an empty directory when it goes out of scope.
+class ScopedDirRemover {
+ public:
+  explicit ScopedDirRemover(const std::string& path)
+      : path_(path),
+        should_remove_(true) {}
+  ~ScopedDirRemover() {
+    if (should_remove_ && (rmdir(path_.c_str()) < 0)) {
+      PLOG(ERROR) << "Unable to remove dir " << path_;
+    }
+  }
+  void set_should_remove(bool should_remove) { should_remove_ = should_remove; }
+
+ protected:
+  const std::string path_;
+
+ private:
+  bool should_remove_;
+  DISALLOW_COPY_AND_ASSIGN(ScopedDirRemover);
+};
+
+// A little object to call ActionComplete on the ActionProcessor when
+// it's destructed.
+class ScopedActionCompleter {
+ public:
+  explicit ScopedActionCompleter(ActionProcessor* processor,
+                                 AbstractAction* action)
+      : processor_(processor),
+        action_(action),
+        code_(ErrorCode::kError),
+        should_complete_(true) {}
+  ~ScopedActionCompleter() {
+    if (should_complete_)
+      processor_->ActionComplete(action_, code_);
+  }
+  void set_code(ErrorCode code) { code_ = code; }
+  void set_should_complete(bool should_complete) {
+    should_complete_ = should_complete;
+  }
+  ErrorCode get_code() const { return code_; }
+
+ private:
+  ActionProcessor* processor_;
+  AbstractAction* action_;
+  ErrorCode code_;
+  bool should_complete_;
+  DISALLOW_COPY_AND_ASSIGN(ScopedActionCompleter);
+};
+
+}  // namespace chromeos_update_engine
+
+#define TEST_AND_RETURN_FALSE_ERRNO(_x)                                        \
+  do {                                                                         \
+    bool _success = static_cast<bool>(_x);                                     \
+    if (!_success) {                                                           \
+      std::string _msg =                                                       \
+          chromeos_update_engine::utils::ErrnoNumberAsString(errno);           \
+      LOG(ERROR) << #_x " failed: " << _msg;                                   \
+      return false;                                                            \
+    }                                                                          \
+  } while (0)
+
+#define TEST_AND_RETURN_FALSE(_x)                                              \
+  do {                                                                         \
+    bool _success = static_cast<bool>(_x);                                     \
+    if (!_success) {                                                           \
+      LOG(ERROR) << #_x " failed.";                                            \
+      return false;                                                            \
+    }                                                                          \
+  } while (0)
+
+#define TEST_AND_RETURN_ERRNO(_x)                                              \
+  do {                                                                         \
+    bool _success = static_cast<bool>(_x);                                     \
+    if (!_success) {                                                           \
+      std::string _msg =                                                       \
+          chromeos_update_engine::utils::ErrnoNumberAsString(errno);           \
+      LOG(ERROR) << #_x " failed: " << _msg;                                   \
+      return;                                                                  \
+    }                                                                          \
+  } while (0)
+
+#define TEST_AND_RETURN(_x)                                                    \
+  do {                                                                         \
+    bool _success = static_cast<bool>(_x);                                     \
+    if (!_success) {                                                           \
+      LOG(ERROR) << #_x " failed.";                                            \
+      return;                                                                  \
+    }                                                                          \
+  } while (0)
+
+#define TEST_AND_RETURN_FALSE_ERRCODE(_x)                                      \
+  do {                                                                         \
+    errcode_t _error = (_x);                                                   \
+    if (_error) {                                                              \
+      errno = _error;                                                          \
+      LOG(ERROR) << #_x " failed: " << _error;                                 \
+      return false;                                                            \
+    }                                                                          \
+  } while (0)
+
+#endif  // UPDATE_ENGINE_COMMON_UTILS_H_
diff --git a/common/utils_unittest.cc b/common/utils_unittest.cc
new file mode 100644
index 0000000..02f919e
--- /dev/null
+++ b/common/utils_unittest.cc
@@ -0,0 +1,569 @@
+//
+// 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/common/utils.h"
+
+#include <errno.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/message_loops/fake_message_loop.h>
+#include <brillo/message_loops/message_loop_utils.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/test_utils.h"
+
+using brillo::FakeMessageLoop;
+using std::map;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+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, ReadFileFailure) {
+  brillo::Blob empty;
+  EXPECT_FALSE(utils::ReadFile("/this/doesn't/exist", &empty));
+}
+
+TEST(UtilsTest, ReadFileChunk) {
+  base::FilePath file;
+  EXPECT_TRUE(base::CreateTemporaryFile(&file));
+  ScopedPathUnlinker unlinker(file.value());
+  brillo::Blob data;
+  const size_t kSize = 1024 * 1024;
+  for (size_t i = 0; i < kSize; i++) {
+    data.push_back(i % 255);
+  }
+  EXPECT_TRUE(utils::WriteFile(file.value().c_str(), data.data(), data.size()));
+  brillo::Blob in_data;
+  EXPECT_TRUE(utils::ReadFileChunk(file.value().c_str(), kSize, 10, &in_data));
+  EXPECT_TRUE(in_data.empty());
+  EXPECT_TRUE(utils::ReadFileChunk(file.value().c_str(), 0, -1, &in_data));
+  EXPECT_TRUE(data == in_data);
+  in_data.clear();
+  EXPECT_TRUE(utils::ReadFileChunk(file.value().c_str(), 10, 20, &in_data));
+  EXPECT_TRUE(brillo::Blob(data.begin() + 10, data.begin() + 10 + 20) ==
+              in_data);
+}
+
+TEST(UtilsTest, ErrnoNumberAsStringTest) {
+  EXPECT_EQ("No such file or directory", utils::ErrnoNumberAsString(ENOENT));
+}
+
+TEST(UtilsTest, IsSymlinkTest) {
+  string temp_dir;
+  EXPECT_TRUE(utils::MakeTempDirectory("symlink-test.XXXXXX", &temp_dir));
+  string temp_file = temp_dir + "/temp-file";
+  EXPECT_TRUE(utils::WriteFile(temp_file.c_str(), "", 0));
+  string temp_symlink = temp_dir + "/temp-symlink";
+  EXPECT_EQ(0, symlink(temp_file.c_str(), temp_symlink.c_str()));
+  EXPECT_FALSE(utils::IsSymlink(temp_dir.c_str()));
+  EXPECT_FALSE(utils::IsSymlink(temp_file.c_str()));
+  EXPECT_TRUE(utils::IsSymlink(temp_symlink.c_str()));
+  EXPECT_FALSE(utils::IsSymlink("/non/existent/path"));
+  EXPECT_TRUE(base::DeleteFile(base::FilePath(temp_dir), true));
+}
+
+TEST(UtilsTest, SplitPartitionNameTest) {
+  string disk;
+  int part_num;
+
+  EXPECT_TRUE(utils::SplitPartitionName("/dev/sda3", &disk, &part_num));
+  EXPECT_EQ("/dev/sda", disk);
+  EXPECT_EQ(3, part_num);
+
+  EXPECT_TRUE(utils::SplitPartitionName("/dev/sdp1234", &disk, &part_num));
+  EXPECT_EQ("/dev/sdp", disk);
+  EXPECT_EQ(1234, part_num);
+
+  EXPECT_TRUE(utils::SplitPartitionName("/dev/mmcblk0p3", &disk, &part_num));
+  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);
+
+  EXPECT_TRUE(utils::SplitPartitionName("/dev/loop28p11", &disk, &part_num));
+  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));
+  EXPECT_FALSE(utils::SplitPartitionName("/", &disk, &part_num));
+  EXPECT_FALSE(utils::SplitPartitionName("", &disk, &part_num));
+}
+
+TEST(UtilsTest, MakePartitionNameTest) {
+  EXPECT_EQ("/dev/sda4", utils::MakePartitionName("/dev/sda", 4));
+  EXPECT_EQ("/dev/sda123", utils::MakePartitionName("/dev/sda", 123));
+  EXPECT_EQ("/dev/mmcblk2", utils::MakePartitionName("/dev/mmcblk", 2));
+  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"));
+}
+
+namespace {
+// Compares cpu shares and returns an integer that is less
+// than, equal to or greater than 0 if |shares_lhs| is,
+// respectively, lower than, same as or higher than |shares_rhs|.
+int CompareCpuShares(utils::CpuShares shares_lhs,
+                     utils::CpuShares shares_rhs) {
+  return static_cast<int>(shares_lhs) - static_cast<int>(shares_rhs);
+}
+}  // namespace
+
+// Tests the CPU shares enum is in the order we expect it.
+TEST(UtilsTest, CompareCpuSharesTest) {
+  EXPECT_LT(CompareCpuShares(utils::kCpuSharesLow,
+                             utils::kCpuSharesNormal), 0);
+  EXPECT_GT(CompareCpuShares(utils::kCpuSharesNormal,
+                             utils::kCpuSharesLow), 0);
+  EXPECT_EQ(CompareCpuShares(utils::kCpuSharesNormal,
+                             utils::kCpuSharesNormal), 0);
+  EXPECT_GT(CompareCpuShares(utils::kCpuSharesHigh,
+                             utils::kCpuSharesNormal), 0);
+}
+
+TEST(UtilsTest, FuzzIntTest) {
+  static const unsigned int kRanges[] = { 0, 1, 2, 20 };
+  for (unsigned int range : kRanges) {
+    const int kValue = 50;
+    for (int tries = 0; tries < 100; ++tries) {
+      int value = utils::FuzzInt(kValue, range);
+      EXPECT_GE(value, kValue - range / 2);
+      EXPECT_LE(value, kValue + range - range / 2);
+    }
+  }
+}
+
+TEST(UtilsTest, RunAsRootGetFilesystemSizeTest) {
+  string img;
+  EXPECT_TRUE(utils::MakeTempFile("img.XXXXXX", &img, nullptr));
+  ScopedPathUnlinker img_unlinker(img);
+  test_utils::CreateExtImageAtPath(img, nullptr);
+  // Extend the "partition" holding the file system from 10MiB to 20MiB.
+  EXPECT_EQ(0, test_utils::System(base::StringPrintf(
+      "dd if=/dev/zero of=%s seek=20971519 bs=1 count=1 status=none",
+      img.c_str())));
+  EXPECT_EQ(20 * 1024 * 1024, utils::FileSize(img));
+  int block_count = 0;
+  int block_size = 0;
+  EXPECT_TRUE(utils::GetFilesystemSize(img, &block_count, &block_size));
+  EXPECT_EQ(4096, block_size);
+  EXPECT_EQ(10 * 1024 * 1024 / 4096, block_count);
+}
+
+// Squashfs example filesystem, generated with:
+//   echo hola>hola
+//   mksquashfs hola hola.sqfs -noappend -nopad
+//   hexdump hola.sqfs -e '16/1 "%02x, " "\n"'
+const uint8_t kSquashfsFile[] = {
+  0x68, 0x73, 0x71, 0x73, 0x02, 0x00, 0x00, 0x00,  // magic, inodes
+  0x3e, 0x49, 0x61, 0x54, 0x00, 0x00, 0x02, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x11, 0x00,
+  0xc0, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00,  // flags, noids, major, minor
+  0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // root_inode
+  0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // bytes_used
+  0xe7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0xd5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x68, 0x6f, 0x6c, 0x61, 0x0a, 0x2c, 0x00, 0x78,
+  0xda, 0x63, 0x62, 0x58, 0xc2, 0xc8, 0xc0, 0xc0,
+  0xc8, 0xd0, 0x6b, 0x91, 0x18, 0x02, 0x64, 0xa0,
+  0x00, 0x56, 0x06, 0x90, 0xcc, 0x7f, 0xb0, 0xbc,
+  0x9d, 0x67, 0x62, 0x08, 0x13, 0x54, 0x1c, 0x44,
+  0x4b, 0x03, 0x31, 0x33, 0x10, 0x03, 0x00, 0xb5,
+  0x87, 0x04, 0x89, 0x16, 0x00, 0x78, 0xda, 0x63,
+  0x60, 0x80, 0x00, 0x46, 0x28, 0xcd, 0xc4, 0xc0,
+  0xcc, 0x90, 0x91, 0x9f, 0x93, 0x08, 0x00, 0x04,
+  0x70, 0x01, 0xab, 0x10, 0x80, 0x60, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x78,
+  0xda, 0x63, 0x60, 0x80, 0x00, 0x05, 0x28, 0x0d,
+  0x00, 0x01, 0x10, 0x00, 0x21, 0xc5, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x80, 0x99,
+  0xcd, 0x02, 0x00, 0x88, 0x13, 0x00, 0x00, 0xdd,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+TEST(UtilsTest, GetSquashfs4Size) {
+  uint8_t buffer[sizeof(kSquashfsFile)];
+  memcpy(buffer, kSquashfsFile, sizeof(kSquashfsFile));
+
+  int block_count = -1;
+  int block_size = -1;
+  // Not enough bytes passed.
+  EXPECT_FALSE(utils::GetSquashfs4Size(buffer, 10, nullptr, nullptr));
+
+  // The whole file system is passed, which is enough for parsing.
+  EXPECT_TRUE(utils::GetSquashfs4Size(buffer, sizeof(kSquashfsFile),
+                                      &block_count, &block_size));
+  EXPECT_EQ(4096, block_size);
+  EXPECT_EQ(1, block_count);
+
+  // Modify the major version to 5.
+  uint16_t* s_major = reinterpret_cast<uint16_t*>(buffer + 0x1c);
+  *s_major = 5;
+  EXPECT_FALSE(utils::GetSquashfs4Size(buffer, 10, nullptr, nullptr));
+  memcpy(buffer, kSquashfsFile, sizeof(kSquashfsFile));
+
+  // Modify the bytes_used to have 6 blocks.
+  int64_t* bytes_used = reinterpret_cast<int64_t*>(buffer + 0x28);
+  *bytes_used = 4096 * 5 + 1;  // 6 "blocks".
+  EXPECT_TRUE(utils::GetSquashfs4Size(buffer, sizeof(kSquashfsFile),
+                                      &block_count, &block_size));
+  EXPECT_EQ(4096, block_size);
+  EXPECT_EQ(6, block_count);
+}
+
+namespace {
+void GetFileFormatTester(const string& expected,
+                         const vector<uint8_t>& contents) {
+  test_utils::ScopedTempFile file;
+  ASSERT_TRUE(utils::WriteFile(file.GetPath().c_str(),
+                               reinterpret_cast<const char*>(contents.data()),
+                               contents.size()));
+  EXPECT_EQ(expected, utils::GetFileFormat(file.GetPath()));
+}
+}  // namespace
+
+TEST(UtilsTest, GetFileFormatTest) {
+  EXPECT_EQ("File not found.", utils::GetFileFormat("/path/to/nowhere"));
+  GetFileFormatTester("data", vector<uint8_t>{1, 2, 3, 4, 5, 6, 7, 8});
+  GetFileFormatTester("ELF", vector<uint8_t>{0x7f, 0x45, 0x4c, 0x46});
+
+  // Real tests from cros_installer on different boards.
+  // ELF 32-bit LSB executable, Intel 80386
+  GetFileFormatTester(
+      "ELF 32-bit little-endian x86",
+      vector<uint8_t>{0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00,
+                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                      0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00,
+                      0x90, 0x83, 0x04, 0x08, 0x34, 0x00, 0x00, 0x00});
+
+  // ELF 32-bit LSB executable, MIPS
+  GetFileFormatTester(
+      "ELF 32-bit little-endian mips",
+      vector<uint8_t>{0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00,
+                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                      0x03, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00,
+                      0xc0, 0x12, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00});
+
+  // ELF 32-bit LSB executable, ARM
+  GetFileFormatTester(
+      "ELF 32-bit little-endian arm",
+      vector<uint8_t>{0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00,
+                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                      0x02, 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00,
+                      0x85, 0x8b, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00});
+
+  // ELF 64-bit LSB executable, x86-64
+  GetFileFormatTester(
+      "ELF 64-bit little-endian x86-64",
+      vector<uint8_t>{0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00,
+                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                      0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
+                      0xb0, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00});
+}
+
+TEST(UtilsTest, ScheduleCrashReporterUploadTest) {
+  // Not much to test. At least this tests for memory leaks, crashes,
+  // log errors.
+  FakeMessageLoop loop(nullptr);
+  loop.SetAsCurrent();
+  utils::ScheduleCrashReporterUpload();
+  // Test that we scheduled one callback from the crash reporter.
+  EXPECT_EQ(1, brillo::MessageLoopRunMaxIterations(&loop, 100));
+  EXPECT_FALSE(loop.PendingTasks());
+}
+
+TEST(UtilsTest, FormatTimeDeltaTest) {
+  // utils::FormatTimeDelta() is not locale-aware (it's only used for logging
+  // which is not localized) so we only need to test the C locale
+  EXPECT_EQ(utils::FormatTimeDelta(base::TimeDelta::FromMilliseconds(100)),
+            "0.1s");
+  EXPECT_EQ(utils::FormatTimeDelta(base::TimeDelta::FromSeconds(0)),
+            "0s");
+  EXPECT_EQ(utils::FormatTimeDelta(base::TimeDelta::FromSeconds(1)),
+            "1s");
+  EXPECT_EQ(utils::FormatTimeDelta(base::TimeDelta::FromSeconds(59)),
+            "59s");
+  EXPECT_EQ(utils::FormatTimeDelta(base::TimeDelta::FromSeconds(60)),
+            "1m0s");
+  EXPECT_EQ(utils::FormatTimeDelta(base::TimeDelta::FromSeconds(61)),
+            "1m1s");
+  EXPECT_EQ(utils::FormatTimeDelta(base::TimeDelta::FromSeconds(90)),
+            "1m30s");
+  EXPECT_EQ(utils::FormatTimeDelta(base::TimeDelta::FromSeconds(1205)),
+            "20m5s");
+  EXPECT_EQ(utils::FormatTimeDelta(base::TimeDelta::FromSeconds(3600)),
+            "1h0m0s");
+  EXPECT_EQ(utils::FormatTimeDelta(base::TimeDelta::FromSeconds(3601)),
+            "1h0m1s");
+  EXPECT_EQ(utils::FormatTimeDelta(base::TimeDelta::FromSeconds(3661)),
+            "1h1m1s");
+  EXPECT_EQ(utils::FormatTimeDelta(base::TimeDelta::FromSeconds(7261)),
+            "2h1m1s");
+  EXPECT_EQ(utils::FormatTimeDelta(base::TimeDelta::FromSeconds(86400)),
+            "1d0h0m0s");
+  EXPECT_EQ(utils::FormatTimeDelta(base::TimeDelta::FromSeconds(86401)),
+            "1d0h0m1s");
+  EXPECT_EQ(utils::FormatTimeDelta(base::TimeDelta::FromSeconds(200000)),
+            "2d7h33m20s");
+  EXPECT_EQ(utils::FormatTimeDelta(base::TimeDelta::FromSeconds(200000) +
+                                   base::TimeDelta::FromMilliseconds(1)),
+            "2d7h33m20.001s");
+  EXPECT_EQ(utils::FormatTimeDelta(base::TimeDelta::FromSeconds(-1)),
+            "-1s");
+}
+
+TEST(UtilsTest, TimeFromStructTimespecTest) {
+  struct timespec ts;
+
+  // Unix epoch (Thursday 00:00:00 UTC on Jan 1, 1970)
+  ts = (struct timespec) {.tv_sec = 0, .tv_nsec = 0};
+  EXPECT_EQ(base::Time::UnixEpoch(), utils::TimeFromStructTimespec(&ts));
+
+  // 42 ms after the Unix billennium (Sunday 01:46:40 UTC on September 9, 2001)
+  ts = (struct timespec) {.tv_sec = 1000 * 1000 * 1000,
+                          .tv_nsec = 42 * 1000 * 1000};
+  base::Time::Exploded exploded = (base::Time::Exploded) {
+    .year = 2001, .month = 9, .day_of_week = 0, .day_of_month = 9,
+    .hour = 1, .minute = 46, .second = 40, .millisecond = 42};
+  EXPECT_EQ(base::Time::FromUTCExploded(exploded),
+            utils::TimeFromStructTimespec(&ts));
+}
+
+TEST(UtilsTest, DecodeAndStoreBase64String) {
+  base::FilePath path;
+
+  // Ensure we return false on empty strings or invalid base64.
+  EXPECT_FALSE(utils::DecodeAndStoreBase64String("", &path));
+  EXPECT_FALSE(utils::DecodeAndStoreBase64String("not valid base64", &path));
+
+  // Pass known base64 and check that it matches. This string was generated
+  // the following way:
+  //
+  //   $ echo "Update Engine" | base64
+  //   VXBkYXRlIEVuZ2luZQo=
+  EXPECT_TRUE(utils::DecodeAndStoreBase64String("VXBkYXRlIEVuZ2luZQo=",
+                                                &path));
+  ScopedPathUnlinker unlinker(path.value());
+  string expected_contents = "Update Engine\n";
+  string contents;
+  EXPECT_TRUE(utils::ReadFile(path.value(), &contents));
+  EXPECT_EQ(contents, expected_contents);
+  EXPECT_EQ(utils::FileSize(path.value()), expected_contents.size());
+}
+
+TEST(UtilsTest, ConvertToOmahaInstallDate) {
+  // The Omaha Epoch starts at Jan 1, 2007 0:00 PST which is a
+  // Monday. In Unix time, this point in time is easily obtained via
+  // the date(1) command like this:
+  //
+  //  $ date +"%s" --date="Jan 1, 2007 0:00 PST"
+  const time_t omaha_epoch = 1167638400;
+  int value;
+
+  // Points in time *on and after* the Omaha epoch should not fail.
+  EXPECT_TRUE(utils::ConvertToOmahaInstallDate(
+      base::Time::FromTimeT(omaha_epoch), &value));
+  EXPECT_GE(value, 0);
+
+  // Anything before the Omaha epoch should fail. We test it for two points.
+  EXPECT_FALSE(utils::ConvertToOmahaInstallDate(
+      base::Time::FromTimeT(omaha_epoch - 1), &value));
+  EXPECT_FALSE(utils::ConvertToOmahaInstallDate(
+      base::Time::FromTimeT(omaha_epoch - 100*24*3600), &value));
+
+  // Check that we jump from 0 to 7 exactly on the one-week mark, e.g.
+  // on Jan 8, 2007 0:00 PST.
+  EXPECT_TRUE(utils::ConvertToOmahaInstallDate(
+      base::Time::FromTimeT(omaha_epoch + 7*24*3600 - 1), &value));
+  EXPECT_EQ(value, 0);
+  EXPECT_TRUE(utils::ConvertToOmahaInstallDate(
+      base::Time::FromTimeT(omaha_epoch + 7*24*3600), &value));
+  EXPECT_EQ(value, 7);
+
+  // Check a couple of more values.
+  EXPECT_TRUE(utils::ConvertToOmahaInstallDate(
+      base::Time::FromTimeT(omaha_epoch + 10*24*3600), &value));
+  EXPECT_EQ(value, 7);
+  EXPECT_TRUE(utils::ConvertToOmahaInstallDate(
+      base::Time::FromTimeT(omaha_epoch + 20*24*3600), &value));
+  EXPECT_EQ(value, 14);
+  EXPECT_TRUE(utils::ConvertToOmahaInstallDate(
+      base::Time::FromTimeT(omaha_epoch + 26*24*3600), &value));
+  EXPECT_EQ(value, 21);
+  EXPECT_TRUE(utils::ConvertToOmahaInstallDate(
+      base::Time::FromTimeT(omaha_epoch + 29*24*3600), &value));
+  EXPECT_EQ(value, 28);
+
+  // The date Jun 4, 2007 0:00 PDT is a Monday and is hence a point
+  // where the Omaha InstallDate jumps 7 days. Its unix time is
+  // 1180940400. Notably, this is a point in time where Daylight
+  // Savings Time (DST) was is in effect (e.g. it's PDT, not PST).
+  //
+  // Note that as utils::ConvertToOmahaInstallDate() _deliberately_
+  // ignores DST (as it's hard to implement in a thread-safe way using
+  // glibc, see comments in utils.h) we have to fudge by the DST
+  // offset which is one hour. Conveniently, if the function were
+  // someday modified to be DST aware, this test would have to be
+  // modified as well.
+  const time_t dst_time = 1180940400;  // Jun 4, 2007 0:00 PDT.
+  const time_t fudge = 3600;
+  int value1, value2;
+  EXPECT_TRUE(utils::ConvertToOmahaInstallDate(
+      base::Time::FromTimeT(dst_time + fudge - 1), &value1));
+  EXPECT_TRUE(utils::ConvertToOmahaInstallDate(
+      base::Time::FromTimeT(dst_time + fudge), &value2));
+  EXPECT_EQ(value1, value2 - 7);
+}
+
+TEST(UtilsTest, GetMinorVersion) {
+  // Test GetMinorVersion by verifying that it parses the conf file and returns
+  // the correct value.
+  uint32_t minor_version;
+
+  brillo::KeyValueStore store;
+  EXPECT_FALSE(utils::GetMinorVersion(store, &minor_version));
+
+  EXPECT_TRUE(store.LoadFromString("PAYLOAD_MINOR_VERSION=one-two-three\n"));
+  EXPECT_FALSE(utils::GetMinorVersion(store, &minor_version));
+
+  EXPECT_TRUE(store.LoadFromString("PAYLOAD_MINOR_VERSION=123\n"));
+  EXPECT_TRUE(utils::GetMinorVersion(store, &minor_version));
+  EXPECT_EQ(minor_version, 123);
+}
+
+static bool BoolMacroTestHelper() {
+  int i = 1;
+  unsigned int ui = 1;
+  bool b = 1;
+  std::unique_ptr<char> cptr(new char);
+
+  TEST_AND_RETURN_FALSE(i);
+  TEST_AND_RETURN_FALSE(ui);
+  TEST_AND_RETURN_FALSE(b);
+  TEST_AND_RETURN_FALSE(cptr);
+
+  TEST_AND_RETURN_FALSE_ERRNO(i);
+  TEST_AND_RETURN_FALSE_ERRNO(ui);
+  TEST_AND_RETURN_FALSE_ERRNO(b);
+  TEST_AND_RETURN_FALSE_ERRNO(cptr);
+
+  return true;
+}
+
+static void VoidMacroTestHelper(bool* ret) {
+  int i = 1;
+  unsigned int ui = 1;
+  bool b = 1;
+  std::unique_ptr<char> cptr(new char);
+
+  *ret = false;
+
+  TEST_AND_RETURN(i);
+  TEST_AND_RETURN(ui);
+  TEST_AND_RETURN(b);
+  TEST_AND_RETURN(cptr);
+
+  TEST_AND_RETURN_ERRNO(i);
+  TEST_AND_RETURN_ERRNO(ui);
+  TEST_AND_RETURN_ERRNO(b);
+  TEST_AND_RETURN_ERRNO(cptr);
+
+  *ret = true;
+}
+
+TEST(UtilsTest, TestMacros) {
+  bool void_test = false;
+  VoidMacroTestHelper(&void_test);
+  EXPECT_TRUE(void_test);
+
+  EXPECT_TRUE(BoolMacroTestHelper());
+}
+
+}  // namespace chromeos_update_engine
diff --git a/connection_manager.cc b/connection_manager.cc
new file mode 100644
index 0000000..778cba5
--- /dev/null
+++ b/connection_manager.cc
@@ -0,0 +1,245 @@
+//
+// 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/connection_manager.h"
+
+#include <set>
+#include <string>
+
+#include <base/stl_util.h>
+#include <base/strings/string_util.h>
+#include <policy/device_policy.h>
+#include <shill/dbus-constants.h>
+#include <shill/dbus-proxies.h>
+
+#include "update_engine/common/prefs.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/system_state.h"
+
+using org::chromium::flimflam::ManagerProxyInterface;
+using org::chromium::flimflam::ServiceProxyInterface;
+using std::set;
+using std::string;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+NetworkConnectionType ParseConnectionType(const string& type_str) {
+  if (type_str == shill::kTypeEthernet) {
+    return NetworkConnectionType::kEthernet;
+  } else if (type_str == shill::kTypeWifi) {
+    return NetworkConnectionType::kWifi;
+  } else if (type_str == shill::kTypeWimax) {
+    return NetworkConnectionType::kWimax;
+  } else if (type_str == shill::kTypeBluetooth) {
+    return NetworkConnectionType::kBluetooth;
+  } else if (type_str == shill::kTypeCellular) {
+    return NetworkConnectionType::kCellular;
+  }
+  return NetworkConnectionType::kUnknown;
+}
+
+NetworkTethering ParseTethering(const string& tethering_str) {
+  if (tethering_str == shill::kTetheringNotDetectedState) {
+    return NetworkTethering::kNotDetected;
+  } else if (tethering_str == shill::kTetheringSuspectedState) {
+    return NetworkTethering::kSuspected;
+  } else if (tethering_str == shill::kTetheringConfirmedState) {
+    return NetworkTethering::kConfirmed;
+  }
+  LOG(WARNING) << "Unknown Tethering value: " << tethering_str;
+  return NetworkTethering::kUnknown;
+}
+
+}  // namespace
+
+ConnectionManager::ConnectionManager(ShillProxyInterface* shill_proxy,
+                                     SystemState* system_state)
+    : shill_proxy_(shill_proxy), system_state_(system_state) {}
+
+bool ConnectionManager::IsUpdateAllowedOver(NetworkConnectionType type,
+                                            NetworkTethering tethering) const {
+  switch (type) {
+    case NetworkConnectionType::kBluetooth:
+      return false;
+
+    case NetworkConnectionType::kCellular: {
+      set<string> allowed_types;
+      const policy::DevicePolicy* device_policy =
+          system_state_->device_policy();
+
+      // A device_policy is loaded in a lazy way right before an update check,
+      // so the device_policy should be already loaded at this point. If it's
+      // not, return a safe value for this setting.
+      if (!device_policy) {
+        LOG(INFO) << "Disabling updates over cellular networks as there's no "
+                     "device policy loaded yet.";
+        return false;
+      }
+
+      if (device_policy->GetAllowedConnectionTypesForUpdate(&allowed_types)) {
+        // The update setting is enforced by the device policy.
+
+        if (!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;
+      } else {
+        // There's no update setting in the device policy, using the local user
+        // setting.
+        PrefsInterface* prefs = system_state_->prefs();
+
+        if (!prefs || !prefs->Exists(kPrefsUpdateOverCellularPermission)) {
+          LOG(INFO) << "Disabling updates over cellular connection as there's "
+                       "no device policy setting nor user preference present.";
+          return false;
+        }
+
+        bool stored_value;
+        if (!prefs->GetBoolean(kPrefsUpdateOverCellularPermission,
+                               &stored_value)) {
+          return false;
+        }
+
+        if (!stored_value) {
+          LOG(INFO) << "Disabling updates over cellular connection per user "
+                       "setting.";
+          return false;
+        }
+        LOG(INFO) << "Allowing updates over cellular per user setting.";
+        return true;
+      }
+    }
+
+    default:
+      if (tethering == NetworkTethering::kConfirmed) {
+        // Treat this connection as if it is a cellular connection.
+        LOG(INFO) << "Current connection is confirmed tethered, using Cellular "
+                     "setting.";
+        return IsUpdateAllowedOver(NetworkConnectionType::kCellular,
+                                   NetworkTethering::kUnknown);
+      }
+      return true;
+  }
+}
+
+// static
+const char* ConnectionManager::StringForConnectionType(
+    NetworkConnectionType type) {
+  switch (type) {
+    case NetworkConnectionType::kEthernet:
+      return shill::kTypeEthernet;
+    case NetworkConnectionType::kWifi:
+      return shill::kTypeWifi;
+    case NetworkConnectionType::kWimax:
+      return shill::kTypeWimax;
+    case NetworkConnectionType::kBluetooth:
+      return shill::kTypeBluetooth;
+    case NetworkConnectionType::kCellular:
+      return shill::kTypeCellular;
+    case NetworkConnectionType::kUnknown:
+      return "Unknown";
+  }
+  return "Unknown";
+}
+
+bool ConnectionManager::GetConnectionProperties(
+    NetworkConnectionType* out_type,
+    NetworkTethering* out_tethering) {
+  dbus::ObjectPath default_service_path;
+  TEST_AND_RETURN_FALSE(GetDefaultServicePath(&default_service_path));
+  if (!default_service_path.IsValid())
+    return false;
+  // Shill uses the "/" service path to indicate that it is not connected.
+  if (default_service_path.value() == "/")
+    return false;
+  TEST_AND_RETURN_FALSE(
+      GetServicePathProperties(default_service_path, out_type, out_tethering));
+  return true;
+}
+
+bool ConnectionManager::GetDefaultServicePath(dbus::ObjectPath* out_path) {
+  brillo::VariantDictionary properties;
+  brillo::ErrorPtr error;
+  ManagerProxyInterface* manager_proxy = shill_proxy_->GetManagerProxy();
+  if (!manager_proxy)
+    return false;
+  TEST_AND_RETURN_FALSE(manager_proxy->GetProperties(&properties, &error));
+
+  const auto& prop_default_service =
+      properties.find(shill::kDefaultServiceProperty);
+  if (prop_default_service == properties.end())
+    return false;
+
+  *out_path = prop_default_service->second.TryGet<dbus::ObjectPath>();
+  return out_path->IsValid();
+}
+
+bool ConnectionManager::GetServicePathProperties(
+    const dbus::ObjectPath& path,
+    NetworkConnectionType* out_type,
+    NetworkTethering* out_tethering) {
+  // We create and dispose the ServiceProxyInterface on every request.
+  std::unique_ptr<ServiceProxyInterface> service =
+      shill_proxy_->GetServiceForPath(path);
+
+  brillo::VariantDictionary properties;
+  brillo::ErrorPtr error;
+  TEST_AND_RETURN_FALSE(service->GetProperties(&properties, &error));
+
+  // Populate the out_tethering.
+  const auto& prop_tethering = properties.find(shill::kTetheringProperty);
+  if (prop_tethering == properties.end()) {
+    // Set to Unknown if not present.
+    *out_tethering = NetworkTethering::kUnknown;
+  } else {
+    // If the property doesn't contain a string value, the empty string will
+    // become kUnknown.
+    *out_tethering = ParseTethering(prop_tethering->second.TryGet<string>());
+  }
+
+  // Populate the out_type property.
+  const auto& prop_type = properties.find(shill::kTypeProperty);
+  if (prop_type == properties.end()) {
+    // Set to Unknown if not present.
+    *out_type = NetworkConnectionType::kUnknown;
+    return false;
+  }
+
+  string type_str = prop_type->second.TryGet<string>();
+  if (type_str == shill::kTypeVPN) {
+    const auto& prop_physical =
+        properties.find(shill::kPhysicalTechnologyProperty);
+    if (prop_physical == properties.end()) {
+      LOG(ERROR) << "No PhysicalTechnology property found for a VPN"
+                    " connection (service: "
+                 << path.value() << "). Returning default kUnknown value.";
+      *out_type = NetworkConnectionType::kUnknown;
+    } else {
+      *out_type = ParseConnectionType(prop_physical->second.TryGet<string>());
+    }
+  } else {
+    *out_type = ParseConnectionType(type_str);
+  }
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/connection_manager.h b/connection_manager.h
new file mode 100644
index 0000000..2057f3b
--- /dev/null
+++ b/connection_manager.h
@@ -0,0 +1,73 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_CONNECTION_MANAGER_H_
+#define UPDATE_ENGINE_CONNECTION_MANAGER_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <dbus/object_path.h>
+
+#include "update_engine/connection_manager_interface.h"
+#include "update_engine/shill_proxy_interface.h"
+
+namespace chromeos_update_engine {
+
+class SystemState;
+
+// This class implements the concrete class that talks with the connection
+// manager (shill) over DBus.
+// TODO(deymo): Remove this class and use ShillProvider from the UpdateManager.
+class ConnectionManager : public ConnectionManagerInterface {
+ public:
+  // Returns the string representation corresponding to the given
+  // connection type.
+  static const char* StringForConnectionType(NetworkConnectionType type);
+
+  // Constructs a new ConnectionManager object initialized with the
+  // given system state.
+  ConnectionManager(ShillProxyInterface* shill_proxy,
+                    SystemState* system_state);
+  ~ConnectionManager() override = default;
+
+  // ConnectionManagerInterface overrides.
+  bool GetConnectionProperties(NetworkConnectionType* out_type,
+                               NetworkTethering* out_tethering) override;
+  bool IsUpdateAllowedOver(NetworkConnectionType type,
+                           NetworkTethering tethering) const override;
+
+ private:
+  // Returns (via out_path) the default network path, or empty string if
+  // there's no network up. Returns true on success.
+  bool GetDefaultServicePath(dbus::ObjectPath* out_path);
+
+  bool GetServicePathProperties(const dbus::ObjectPath& path,
+                                NetworkConnectionType* out_type,
+                                NetworkTethering* out_tethering);
+
+  // The mockable interface to access the shill DBus proxies.
+  ShillProxyInterface* shill_proxy_;
+
+  // The global context for update_engine.
+  SystemState* system_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConnectionManager);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_CONNECTION_MANAGER_H_
diff --git a/connection_manager_interface.h b/connection_manager_interface.h
new file mode 100644
index 0000000..cb60a3c
--- /dev/null
+++ b/connection_manager_interface.h
@@ -0,0 +1,68 @@
+//
+// 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_CONNECTION_MANAGER_INTERFACE_H_
+#define UPDATE_ENGINE_CONNECTION_MANAGER_INTERFACE_H_
+
+#include <base/macros.h>
+
+namespace chromeos_update_engine {
+
+enum class NetworkConnectionType {
+  kEthernet,
+  kWifi,
+  kWimax,
+  kBluetooth,
+  kCellular,
+  kUnknown
+};
+
+enum class NetworkTethering {
+  kNotDetected,
+  kSuspected,
+  kConfirmed,
+  kUnknown
+};
+
+// This class exposes a generic interface to the connection manager
+// (e.g FlimFlam, Shill, etc.) to consolidate all connection-related
+// logic in update_engine.
+class ConnectionManagerInterface {
+ public:
+  virtual ~ConnectionManagerInterface() = default;
+
+  // Populates |out_type| with the type of the network connection
+  // that we are currently connected and |out_tethering| with the estimate of
+  // whether that network is being tethered.
+  virtual bool GetConnectionProperties(NetworkConnectionType* out_type,
+                                       NetworkTethering* out_tethering) = 0;
+
+  // Returns true if we're allowed to update the system when we're
+  // connected to the internet through the given network connection type and the
+  // given tethering state.
+  virtual bool IsUpdateAllowedOver(NetworkConnectionType type,
+                                   NetworkTethering tethering) const = 0;
+
+ protected:
+  ConnectionManagerInterface() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ConnectionManagerInterface);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_CONNECTION_MANAGER_INTERFACE_H_
diff --git a/connection_manager_unittest.cc b/connection_manager_unittest.cc
new file mode 100644
index 0000000..612929b
--- /dev/null
+++ b/connection_manager_unittest.cc
@@ -0,0 +1,409 @@
+//
+// 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/connection_manager.h"
+
+#include <set>
+#include <string>
+
+#include <base/logging.h>
+#include <brillo/any.h>
+#include <brillo/make_unique_ptr.h>
+#include <brillo/message_loops/fake_message_loop.h>
+#include <brillo/variant_dictionary.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <shill/dbus-constants.h>
+#include <shill/dbus-proxies.h>
+#include <shill/dbus-proxy-mocks.h>
+
+#include "update_engine/common/test_utils.h"
+#include "update_engine/fake_shill_proxy.h"
+#include "update_engine/fake_system_state.h"
+
+using org::chromium::flimflam::ManagerProxyMock;
+using org::chromium::flimflam::ServiceProxyMock;
+using std::set;
+using std::string;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::_;
+
+namespace chromeos_update_engine {
+
+class ConnectionManagerTest : public ::testing::Test {
+ public:
+  void SetUp() override {
+    loop_.SetAsCurrent();
+    fake_system_state_.set_connection_manager(&cmut_);
+  }
+
+  void TearDown() override { EXPECT_FALSE(loop_.PendingTasks()); }
+
+ protected:
+  // Sets the default_service object path in the response from the
+  // ManagerProxyMock instance.
+  void SetManagerReply(const char* default_service, bool reply_succeeds);
+
+  // Sets the |service_type|, |physical_technology| and |service_tethering|
+  // properties in the mocked service |service_path|. If any of the three
+  // const char* is a nullptr, the corresponding property will not be included
+  // in the response.
+  void SetServiceReply(const string& service_path,
+                       const char* service_type,
+                       const char* physical_technology,
+                       const char* service_tethering);
+
+  void TestWithServiceType(
+      const char* service_type,
+      const char* physical_technology,
+      NetworkConnectionType expected_type);
+  void TestWithServiceTethering(
+      const char* service_tethering,
+      NetworkTethering expected_tethering);
+
+  brillo::FakeMessageLoop loop_{nullptr};
+  FakeSystemState fake_system_state_;
+  FakeShillProxy fake_shill_proxy_;
+
+  // ConnectionManager under test.
+  ConnectionManager cmut_{&fake_shill_proxy_, &fake_system_state_};
+};
+
+void ConnectionManagerTest::SetManagerReply(const char* default_service,
+                                            bool reply_succeeds) {
+  ManagerProxyMock* manager_proxy_mock = fake_shill_proxy_.GetManagerProxy();
+  if (!reply_succeeds) {
+    EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _))
+        .WillOnce(Return(false));
+    return;
+  }
+
+  // Create a dictionary of properties and optionally include the default
+  // service.
+  brillo::VariantDictionary reply_dict;
+  reply_dict["SomeOtherProperty"] = 0xC0FFEE;
+
+  if (default_service) {
+    reply_dict[shill::kDefaultServiceProperty] =
+        dbus::ObjectPath(default_service);
+  }
+  EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _))
+      .WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true)));
+}
+
+void ConnectionManagerTest::SetServiceReply(const string& service_path,
+                                            const char* service_type,
+                                            const char* physical_technology,
+                                            const char* service_tethering) {
+  brillo::VariantDictionary reply_dict;
+  reply_dict["SomeOtherProperty"] = 0xC0FFEE;
+
+  if (service_type)
+    reply_dict[shill::kTypeProperty] = string(service_type);
+
+  if (physical_technology) {
+    reply_dict[shill::kPhysicalTechnologyProperty] =
+        string(physical_technology);
+  }
+
+  if (service_tethering)
+    reply_dict[shill::kTetheringProperty] = string(service_tethering);
+
+  std::unique_ptr<ServiceProxyMock> service_proxy_mock(new ServiceProxyMock());
+
+  // Plumb return value into mock object.
+  EXPECT_CALL(*service_proxy_mock.get(), GetProperties(_, _, _))
+      .WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true)));
+
+  fake_shill_proxy_.SetServiceForPath(dbus::ObjectPath(service_path),
+                                      std::move(service_proxy_mock));
+}
+
+void ConnectionManagerTest::TestWithServiceType(
+    const char* service_type,
+    const char* physical_technology,
+    NetworkConnectionType expected_type) {
+  SetManagerReply("/service/guest/network", true);
+  SetServiceReply("/service/guest/network",
+                  service_type,
+                  physical_technology,
+                  shill::kTetheringNotDetectedState);
+
+  NetworkConnectionType type;
+  NetworkTethering tethering;
+  EXPECT_TRUE(cmut_.GetConnectionProperties(&type, &tethering));
+  EXPECT_EQ(expected_type, type);
+  testing::Mock::VerifyAndClearExpectations(
+      fake_shill_proxy_.GetManagerProxy());
+}
+
+void ConnectionManagerTest::TestWithServiceTethering(
+    const char* service_tethering,
+    NetworkTethering expected_tethering) {
+  SetManagerReply("/service/guest/network", true);
+  SetServiceReply(
+      "/service/guest/network", shill::kTypeWifi, nullptr, service_tethering);
+
+  NetworkConnectionType type;
+  NetworkTethering tethering;
+  EXPECT_TRUE(cmut_.GetConnectionProperties(&type, &tethering));
+  EXPECT_EQ(expected_tethering, tethering);
+  testing::Mock::VerifyAndClearExpectations(
+      fake_shill_proxy_.GetManagerProxy());
+}
+
+TEST_F(ConnectionManagerTest, SimpleTest) {
+  TestWithServiceType(shill::kTypeEthernet, nullptr,
+                      NetworkConnectionType::kEthernet);
+  TestWithServiceType(shill::kTypeWifi, nullptr,
+                      NetworkConnectionType::kWifi);
+  TestWithServiceType(shill::kTypeWimax, nullptr,
+                      NetworkConnectionType::kWimax);
+  TestWithServiceType(shill::kTypeBluetooth, nullptr,
+                      NetworkConnectionType::kBluetooth);
+  TestWithServiceType(shill::kTypeCellular, nullptr,
+                      NetworkConnectionType::kCellular);
+}
+
+TEST_F(ConnectionManagerTest, PhysicalTechnologyTest) {
+  TestWithServiceType(shill::kTypeVPN, nullptr,
+                      NetworkConnectionType::kUnknown);
+  TestWithServiceType(shill::kTypeVPN, shill::kTypeVPN,
+                      NetworkConnectionType::kUnknown);
+  TestWithServiceType(shill::kTypeVPN, shill::kTypeWifi,
+                      NetworkConnectionType::kWifi);
+  TestWithServiceType(shill::kTypeVPN, shill::kTypeWimax,
+                      NetworkConnectionType::kWimax);
+}
+
+TEST_F(ConnectionManagerTest, TetheringTest) {
+  TestWithServiceTethering(shill::kTetheringConfirmedState,
+                           NetworkTethering::kConfirmed);
+  TestWithServiceTethering(shill::kTetheringNotDetectedState,
+                           NetworkTethering::kNotDetected);
+  TestWithServiceTethering(shill::kTetheringSuspectedState,
+                           NetworkTethering::kSuspected);
+  TestWithServiceTethering("I'm not a valid property value =)",
+                           NetworkTethering::kUnknown);
+}
+
+TEST_F(ConnectionManagerTest, UnknownTest) {
+  TestWithServiceType("foo", nullptr, NetworkConnectionType::kUnknown);
+}
+
+TEST_F(ConnectionManagerTest, AllowUpdatesOverEthernetTest) {
+  // Updates over Ethernet are allowed even if there's no policy.
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kEthernet,
+                                        NetworkTethering::kUnknown));
+}
+
+TEST_F(ConnectionManagerTest, AllowUpdatesOverWifiTest) {
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWifi,
+                                        NetworkTethering::kUnknown));
+}
+
+TEST_F(ConnectionManagerTest, AllowUpdatesOverWimaxTest) {
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWimax,
+                                        NetworkTethering::kUnknown));
+}
+
+TEST_F(ConnectionManagerTest, BlockUpdatesOverBluetoothTest) {
+  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kBluetooth,
+                                         NetworkTethering::kUnknown));
+}
+
+TEST_F(ConnectionManagerTest, AllowUpdatesOnlyOver3GPerPolicyTest) {
+  policy::MockDevicePolicy 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;
+  allowed_set.insert(
+      cmut_.StringForConnectionType(NetworkConnectionType::kCellular));
+
+  EXPECT_CALL(allow_3g_policy, GetAllowedConnectionTypesForUpdate(_))
+      .Times(1)
+      .WillOnce(DoAll(SetArgPointee<0>(allowed_set), Return(true)));
+
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
+                                        NetworkTethering::kUnknown));
+}
+
+TEST_F(ConnectionManagerTest, AllowUpdatesOver3GAndOtherTypesPerPolicyTest) {
+  policy::MockDevicePolicy 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, the others are ignored (see Bluetooth for example).
+  set<string> allowed_set;
+  allowed_set.insert(
+      cmut_.StringForConnectionType(NetworkConnectionType::kCellular));
+  allowed_set.insert(
+      cmut_.StringForConnectionType(NetworkConnectionType::kBluetooth));
+
+  EXPECT_CALL(allow_3g_policy, GetAllowedConnectionTypesForUpdate(_))
+      .Times(3)
+      .WillRepeatedly(DoAll(SetArgPointee<0>(allowed_set), Return(true)));
+
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kEthernet,
+                                        NetworkTethering::kUnknown));
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kEthernet,
+                                        NetworkTethering::kNotDetected));
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
+                                        NetworkTethering::kUnknown));
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWifi,
+                                        NetworkTethering::kUnknown));
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWimax,
+                                        NetworkTethering::kUnknown));
+  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kBluetooth,
+                                         NetworkTethering::kUnknown));
+
+  // Tethered networks are treated in the same way as Cellular networks and
+  // thus allowed.
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kEthernet,
+                                        NetworkTethering::kConfirmed));
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWifi,
+                                        NetworkTethering::kConfirmed));
+}
+
+TEST_F(ConnectionManagerTest, BlockUpdatesOverCellularByDefaultTest) {
+  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
+                                         NetworkTethering::kUnknown));
+}
+
+TEST_F(ConnectionManagerTest, BlockUpdatesOverTetheredNetworkByDefaultTest) {
+  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWifi,
+                                         NetworkTethering::kConfirmed));
+  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kEthernet,
+                                         NetworkTethering::kConfirmed));
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWifi,
+                                        NetworkTethering::kSuspected));
+}
+
+TEST_F(ConnectionManagerTest, BlockUpdatesOver3GPerPolicyTest) {
+  policy::MockDevicePolicy 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(
+      cmut_.StringForConnectionType(NetworkConnectionType::kEthernet));
+  allowed_set.insert(
+      cmut_.StringForConnectionType(NetworkConnectionType::kWifi));
+  allowed_set.insert(
+      cmut_.StringForConnectionType(NetworkConnectionType::kWimax));
+
+  EXPECT_CALL(block_3g_policy, GetAllowedConnectionTypesForUpdate(_))
+      .Times(1)
+      .WillOnce(DoAll(SetArgPointee<0>(allowed_set), Return(true)));
+
+  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
+                                         NetworkTethering::kUnknown));
+}
+
+TEST_F(ConnectionManagerTest, BlockUpdatesOver3GIfErrorInPolicyFetchTest) {
+  policy::MockDevicePolicy allow_3g_policy;
+
+  fake_system_state_.set_device_policy(&allow_3g_policy);
+
+  set<string> allowed_set;
+  allowed_set.insert(
+      cmut_.StringForConnectionType(NetworkConnectionType::kCellular));
+
+  // Return false for GetAllowedConnectionTypesForUpdate and see
+  // that updates are still blocked for 3G despite the value being in
+  // the string set above.
+  EXPECT_CALL(allow_3g_policy, GetAllowedConnectionTypesForUpdate(_))
+      .Times(1)
+      .WillOnce(DoAll(SetArgPointee<0>(allowed_set), Return(false)));
+
+  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
+                                         NetworkTethering::kUnknown));
+}
+
+TEST_F(ConnectionManagerTest, UseUserPrefForUpdatesOverCellularIfNoPolicyTest) {
+  policy::MockDevicePolicy no_policy;
+  testing::NiceMock<MockPrefs>* prefs = fake_system_state_.mock_prefs();
+
+  fake_system_state_.set_device_policy(&no_policy);
+
+  // No setting enforced by the device policy, user prefs should be used.
+  EXPECT_CALL(no_policy, GetAllowedConnectionTypesForUpdate(_))
+      .Times(3)
+      .WillRepeatedly(Return(false));
+
+  // No user pref: block.
+  EXPECT_CALL(*prefs, Exists(kPrefsUpdateOverCellularPermission))
+      .Times(1)
+      .WillOnce(Return(false));
+  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
+                                         NetworkTethering::kUnknown));
+
+  // Allow per user pref.
+  EXPECT_CALL(*prefs, Exists(kPrefsUpdateOverCellularPermission))
+      .Times(1)
+      .WillOnce(Return(true));
+  EXPECT_CALL(*prefs, GetBoolean(kPrefsUpdateOverCellularPermission, _))
+      .Times(1)
+      .WillOnce(DoAll(SetArgPointee<1>(true), Return(true)));
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
+                                        NetworkTethering::kUnknown));
+
+  // Block per user pref.
+  EXPECT_CALL(*prefs, Exists(kPrefsUpdateOverCellularPermission))
+      .Times(1)
+      .WillOnce(Return(true));
+  EXPECT_CALL(*prefs, GetBoolean(kPrefsUpdateOverCellularPermission, _))
+      .Times(1)
+      .WillOnce(DoAll(SetArgPointee<1>(false), Return(true)));
+  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
+                                         NetworkTethering::kUnknown));
+}
+
+TEST_F(ConnectionManagerTest, StringForConnectionTypeTest) {
+  EXPECT_STREQ(shill::kTypeEthernet,
+               cmut_.StringForConnectionType(NetworkConnectionType::kEthernet));
+  EXPECT_STREQ(shill::kTypeWifi,
+               cmut_.StringForConnectionType(NetworkConnectionType::kWifi));
+  EXPECT_STREQ(shill::kTypeWimax,
+               cmut_.StringForConnectionType(NetworkConnectionType::kWimax));
+  EXPECT_STREQ(shill::kTypeBluetooth,
+               cmut_.StringForConnectionType(
+                   NetworkConnectionType::kBluetooth));
+  EXPECT_STREQ(shill::kTypeCellular,
+               cmut_.StringForConnectionType(NetworkConnectionType::kCellular));
+  EXPECT_STREQ("Unknown",
+               cmut_.StringForConnectionType(NetworkConnectionType::kUnknown));
+  EXPECT_STREQ("Unknown",
+               cmut_.StringForConnectionType(
+                   static_cast<NetworkConnectionType>(999999)));
+}
+
+TEST_F(ConnectionManagerTest, MalformedServiceList) {
+  SetManagerReply("/service/guest/network", false);
+
+  NetworkConnectionType type;
+  NetworkTethering tethering;
+  EXPECT_FALSE(cmut_.GetConnectionProperties(&type, &tethering));
+}
+
+}  // namespace chromeos_update_engine
diff --git a/daemon.cc b/daemon.cc
new file mode 100644
index 0000000..0b13c18
--- /dev/null
+++ b/daemon.cc
@@ -0,0 +1,153 @@
+//
+// 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>
+#include <base/time/time.h>
+#include <brillo/message_loops/message_loop.h>
+
+#include "update_engine/common/clock.h"
+#include "update_engine/update_attempter.h"
+
+using brillo::MessageLoop;
+
+namespace {
+const int kDBusSystemMaxWaitSeconds = 2 * 60;
+}  // namespace
+
+namespace chromeos_update_engine {
+
+namespace {
+// Wait for passed |bus| DBus to be connected by attempting to connect it up to
+// |timeout| time. Returns whether the Connect() eventually succeeded.
+bool WaitForDBusSystem(dbus::Bus* bus, base::TimeDelta timeout) {
+  Clock clock;
+  base::Time deadline = clock.GetMonotonicTime() + timeout;
+
+  while (clock.GetMonotonicTime() < deadline) {
+    if (bus->Connect())
+      return true;
+    LOG(WARNING) << "Failed to get system bus, waiting.";
+    // Wait 1 second.
+    sleep(1);
+  }
+  LOG(ERROR) << "Failed to get system bus after " << timeout.InSeconds()
+             << " seconds.";
+  return false;
+}
+}  // namespace
+
+UpdateEngineDaemon::~UpdateEngineDaemon() {
+  UpdateAttempter* update_attempter = real_system_state_->update_attempter();
+  // Prevent any DBus communication from UpdateAttempter when shutting down the
+  // daemon.
+  if (update_attempter)
+    update_attempter->set_dbus_adaptor(nullptr);
+}
+
+int UpdateEngineDaemon::OnInit() {
+  // Register the |subprocess_| singleton with this Daemon as the signal
+  // handler.
+  subprocess_.Init(this);
+
+  // We use Daemon::OnInit() and not DBusDaemon::OnInit() to gracefully wait for
+  // the D-Bus connection for up two minutes to avoid re-spawning the daemon
+  // too fast causing thrashing if dbus-daemon is not running.
+  int exit_code = Daemon::OnInit();
+  if (exit_code != EX_OK)
+    return exit_code;
+
+  dbus::Bus::Options options;
+  options.bus_type = dbus::Bus::SYSTEM;
+  bus_ = new dbus::Bus(options);
+
+  // Wait for DBus to be ready and exit if it doesn't become available after
+  // the timeout.
+  if (!WaitForDBusSystem(
+          bus_.get(),
+          base::TimeDelta::FromSeconds(kDBusSystemMaxWaitSeconds))) {
+    // TODO(deymo): Make it possible to run update_engine even if dbus-daemon
+    // is not running or constantly crashing.
+    LOG(ERROR) << "Failed to initialize DBus, aborting.";
+    return 1;
+  }
+
+  CHECK(bus_->SetUpAsyncOperations());
+
+  // Initialize update engine global state but continue if something fails.
+  real_system_state_.reset(new RealSystemState(bus_));
+  LOG_IF(ERROR, !real_system_state_->Initialize())
+      << "Failed to initialize system state.";
+  UpdateAttempter* update_attempter = real_system_state_->update_attempter();
+  CHECK(update_attempter);
+
+  // Create the DBus service.
+  dbus_adaptor_.reset(new UpdateEngineAdaptor(real_system_state_.get(), bus_));
+  update_attempter->set_dbus_adaptor(dbus_adaptor_.get());
+
+  dbus_adaptor_->RegisterAsync(base::Bind(&UpdateEngineDaemon::OnDBusRegistered,
+                                          base::Unretained(this)));
+  LOG(INFO) << "Waiting for DBus object to be registered.";
+  return EX_OK;
+}
+
+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;
+  }
+
+  // Initiate update checks.
+  UpdateAttempter* update_attempter = real_system_state_->update_attempter();
+  update_attempter->ScheduleUpdates();
+
+  // Update boot flags after 45 seconds.
+  MessageLoop::current()->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&UpdateAttempter::UpdateBootFlags,
+                 base::Unretained(update_attempter)),
+      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)));
+
+  // Run the UpdateEngineStarted() method on |update_attempter|.
+  MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+      &UpdateAttempter::UpdateEngineStarted,
+      base::Unretained(update_attempter)));
+
+  LOG(INFO) << "Finished initialization. Now running the loop.";
+}
+
+}  // namespace chromeos_update_engine
diff --git a/daemon.h b/daemon.h
new file mode 100644
index 0000000..66841f6
--- /dev/null
+++ b/daemon.h
@@ -0,0 +1,60 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_DAEMON_H_
+#define UPDATE_ENGINE_DAEMON_H_
+
+#include <memory>
+#include <string>
+
+#include <brillo/daemons/dbus_daemon.h>
+
+#include "update_engine/common/subprocess.h"
+#include "update_engine/dbus_service.h"
+#include "update_engine/real_system_state.h"
+
+namespace chromeos_update_engine {
+
+class UpdateEngineDaemon : public brillo::DBusDaemon {
+ public:
+  UpdateEngineDaemon() = default;
+  ~UpdateEngineDaemon();
+
+ 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);
+
+  // 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_;
+
+  std::unique_ptr<UpdateEngineAdaptor> dbus_adaptor_;
+
+  // The RealSystemState uses the previous classes so it should be defined last.
+  std::unique_ptr<RealSystemState> real_system_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(UpdateEngineDaemon);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_DAEMON_H_
diff --git a/dbus_bindings/dbus-service-config.json b/dbus_bindings/dbus-service-config.json
new file mode 100644
index 0000000..fdae3ba
--- /dev/null
+++ b/dbus_bindings/dbus-service-config.json
@@ -0,0 +1,3 @@
+{
+  "service_name": "org.chromium.UpdateEngine"
+}
diff --git a/dbus_bindings/org.chromium.LibCrosService.dbus-xml b/dbus_bindings/org.chromium.LibCrosService.dbus-xml
new file mode 100644
index 0000000..2ea8313
--- /dev/null
+++ b/dbus_bindings/org.chromium.LibCrosService.dbus-xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/org/chromium/LibCrosService"
+      xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+  <interface name="org.chromium.LibCrosServiceInterface">
+    <method name="ResolveNetworkProxy">
+      <arg name="source_url" type="s" direction="in" />
+      <arg name="signal_interface" type="s" direction="in" />
+      <arg name="signal_name" type="s" direction="in" />
+      <annotation name="org.chromium.DBus.Method.Kind" value="simple" />
+    </method>
+  </interface>
+  <interface name="org.chromium.UpdateEngineLibcrosProxyResolvedInterface">
+    <signal name="ProxyResolved">
+      <arg name="source_url" type="s" direction="out" />
+      <arg name="proxy_info" type="s" direction="out" />
+      <arg name="error_message" type="s" direction="out" />
+    </signal>
+  </interface>
+</node>
diff --git a/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml b/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
new file mode 100644
index 0000000..bc4ec36
--- /dev/null
+++ b/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<node name="/org/chromium/UpdateEngine">
+  <interface name="org.chromium.UpdateEngineInterface">
+    <annotation name="org.freedesktop.DBus.GLib.CSymbol"
+                value="update_engine_service" />
+    <annotation name="org.freedesktop.DBus.GLib.ClientCSymbol"
+                value="update_engine_client" />
+    <method name="AttemptUpdate">
+      <arg type="s" name="app_version" direction="in" />
+      <arg type="s" name="omaha_url" direction="in" />
+    </method>
+    <!-- TODO(zeuthen,chromium:286399): Rename to AttemptUpdate and
+         update Chrome and other users of the AttemptUpdate() method
+         in lockstep.
+    -->
+    <method name="AttemptUpdateWithFlags">
+      <arg type="s" name="app_version" direction="in" />
+      <arg type="s" name="omaha_url" direction="in" />
+      <!-- See AttemptUpdateFlags enum in update_engine/dbus-constants.h. -->
+      <arg type="i" name="flags" direction="in" />
+    </method>
+    <method name="AttemptRollback">
+      <arg type="b" name="powerwash" direction="in" />
+    </method>
+    <method name="CanRollback">
+      <arg type="b" name="can_rollback" direction="out" />
+    </method>
+    <method name="ResetStatus">
+    </method>
+    <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>
+    <method name="SetChannel">
+      <arg type="s" name="target_channel" direction="in" />
+      <arg type="b" name="is_powerwash_allowed" direction="in" />
+    </method>
+    <method name="GetChannel">
+      <arg type="b" name="get_current_channel" direction="in" />
+      <arg type="s" name="channel" direction="out" />
+    </method>
+    <method name="SetP2PUpdatePermission">
+      <annotation name="org.freedesktop.DBus.GLib.CSymbol"
+        value="update_engine_service_set_p2p_update_permission" />
+      <annotation name="org.freedesktop.DBus.GLib.ClientCSymbol"
+        value="update_engine_client_set_p2p_update_permission" />
+      <arg type="b" name="enabled" direction="in" />
+    </method>
+    <method name="GetP2PUpdatePermission">
+      <annotation name="org.freedesktop.DBus.GLib.CSymbol"
+        value="update_engine_service_get_p2p_update_permission" />
+      <annotation name="org.freedesktop.DBus.GLib.ClientCSymbol"
+        value="update_engine_client_get_p2p_update_permission" />
+      <arg type="b" name="enabled" direction="out" />
+    </method>
+    <method name="SetUpdateOverCellularPermission">
+      <arg type="b" name="allowed" direction="in" />
+    </method>
+    <method name="GetUpdateOverCellularPermission">
+      <arg type="b" name="allowed" direction="out" />
+    </method>
+    <method name="GetDurationSinceUpdate">
+      <arg type="x" name="usec_wallclock" direction="out" />
+    </method>
+    <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" />
+    </method>
+    <method name="GetRollbackPartition">
+      <arg type="s" name="rollback_partition_name" direction="out" />
+    </method>
+  </interface>
+</node>
diff --git a/dbus_service.cc b/dbus_service.cc
new file mode 100644
index 0000000..6ed31d4
--- /dev/null
+++ b/dbus_service.cc
@@ -0,0 +1,342 @@
+//
+// 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/dbus_service.h"
+
+#include <set>
+#include <string>
+
+#include <base/location.h>
+#include <base/logging.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/bind_lambda.h>
+#include <brillo/message_loops/message_loop.h>
+#include <brillo/strings/string_utils.h>
+#include <policy/device_policy.h>
+#include <update_engine/dbus-constants.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/utils.h"
+#include "update_engine/connection_manager_interface.h"
+#include "update_engine/omaha_request_params.h"
+#include "update_engine/p2p_manager.h"
+#include "update_engine/update_attempter.h"
+
+using base::StringPrintf;
+using brillo::ErrorPtr;
+using brillo::string_utils::ToString;
+using std::set;
+using std::string;
+using update_engine::AttemptUpdateFlags;
+using update_engine::kAttemptUpdateFlagNonInteractive;
+
+namespace {
+// Log and set the error on the passed ErrorPtr.
+void LogAndSetError(ErrorPtr *error,
+                    const tracked_objects::Location& location,
+                    const string& reason) {
+  brillo::Error::AddTo(
+      error, location,
+      brillo::errors::dbus::kDomain,
+      update_engine::kUpdateEngineServiceErrorFailed, reason);
+  LOG(ERROR) << "Sending DBus Failure: " << location.ToString() << ": "
+             << reason;
+}
+}  // namespace
+
+namespace chromeos_update_engine {
+
+UpdateEngineService::UpdateEngineService(SystemState* system_state)
+    : system_state_(system_state) {}
+
+// org::chromium::UpdateEngineInterfaceInterface methods implementation.
+
+bool UpdateEngineService::AttemptUpdate(ErrorPtr* error,
+                                        const string& in_app_version,
+                                        const string& in_omaha_url) {
+  return AttemptUpdateWithFlags(
+      error, in_app_version, in_omaha_url, 0 /* no flags */);
+}
+
+bool UpdateEngineService::AttemptUpdateWithFlags(ErrorPtr* /* error */,
+                                                 const string& in_app_version,
+                                                 const string& in_omaha_url,
+                                                 int32_t in_flags_as_int) {
+  AttemptUpdateFlags flags = static_cast<AttemptUpdateFlags>(in_flags_as_int);
+  bool interactive = !(flags & kAttemptUpdateFlagNonInteractive);
+
+  LOG(INFO) << "Attempt update: app_version=\"" << in_app_version << "\" "
+            << "omaha_url=\"" << in_omaha_url << "\" "
+            << "flags=0x" << std::hex << flags << " "
+            << "interactive=" << (interactive? "yes" : "no");
+  system_state_->update_attempter()->CheckForUpdate(
+      in_app_version, in_omaha_url, interactive);
+  return true;
+}
+
+bool UpdateEngineService::AttemptRollback(ErrorPtr* error,
+                                          bool in_powerwash) {
+  LOG(INFO) << "Attempting rollback to non-active partitions.";
+
+  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;
+  }
+  return true;
+}
+
+bool UpdateEngineService::CanRollback(ErrorPtr* /* error */,
+                                      bool* out_can_rollback) {
+  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 (!system_state_->update_attempter()->ResetStatus()) {
+    // TODO(dgarrett): Give a more specific error code/reason.
+    LogAndSetError(error, FROM_HERE, "ResetStatus failed.");
+    return false;
+  }
+  return true;
+}
+
+bool UpdateEngineService::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) {
+  if (!system_state_->update_attempter()->GetStatus(out_last_checked_time,
+                                                    out_progress,
+                                                    out_current_operation,
+                                                    out_new_version,
+                                                    out_new_size)) {
+    LogAndSetError(error, FROM_HERE, "GetStatus failed.");
+    return false;
+  }
+  return true;
+}
+
+bool UpdateEngineService::RebootIfNeeded(ErrorPtr* error) {
+  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;
+  }
+  return true;
+}
+
+bool UpdateEngineService::SetChannel(ErrorPtr* error,
+                                     const string& in_target_channel,
+                                     bool in_is_powerwash_allowed) {
+  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();
+    }
+  }
+
+  bool delegated = false;
+  if (device_policy &&
+      device_policy->GetReleaseChannelDelegated(&delegated) && !delegated) {
+    LogAndSetError(
+        error, FROM_HERE,
+        "Cannot set target channel explicitly when channel "
+        "policy/settings is not delegated");
+    return false;
+  }
+
+  LOG(INFO) << "Setting destination channel to: " << in_target_channel;
+  string error_message;
+  if (!system_state_->request_params()->SetTargetChannel(
+          in_target_channel, in_is_powerwash_allowed, &error_message)) {
+    LogAndSetError(error, FROM_HERE, error_message);
+    return false;
+  }
+  return true;
+}
+
+bool UpdateEngineService::GetChannel(ErrorPtr* /* error */,
+                                     bool in_get_current_channel,
+                                     string* out_channel) {
+  OmahaRequestParams* rp = system_state_->request_params();
+  *out_channel = (in_get_current_channel ?
+                  rp->current_channel() : rp->target_channel());
+  return true;
+}
+
+bool UpdateEngineService::SetP2PUpdatePermission(ErrorPtr* error,
+                                                 bool in_enabled) {
+  PrefsInterface* prefs = system_state_->prefs();
+
+  if (!prefs->SetBoolean(kPrefsP2PEnabled, in_enabled)) {
+    LogAndSetError(
+        error, FROM_HERE,
+        StringPrintf("Error setting the update via p2p permission to %s.",
+                     ToString(in_enabled).c_str()));
+    return false;
+  }
+  return true;
+}
+
+bool UpdateEngineService::GetP2PUpdatePermission(ErrorPtr* error,
+                                                 bool* out_enabled) {
+  PrefsInterface* prefs = system_state_->prefs();
+
+  bool p2p_pref = false;  // Default if no setting is present.
+  if (prefs->Exists(kPrefsP2PEnabled) &&
+      !prefs->GetBoolean(kPrefsP2PEnabled, &p2p_pref)) {
+    LogAndSetError(error, FROM_HERE, "Error getting the P2PEnabled setting.");
+    return false;
+  }
+
+  *out_enabled = p2p_pref;
+  return true;
+}
+
+bool UpdateEngineService::SetUpdateOverCellularPermission(ErrorPtr* error,
+                                                          bool in_allowed) {
+  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();
+    }
+  }
+
+  // Check if this setting is allowed by the device policy.
+  if (device_policy &&
+      device_policy->GetAllowedConnectionTypesForUpdate(&allowed_types)) {
+    LogAndSetError(error, FROM_HERE,
+                   "Ignoring the update over cellular setting since there's "
+                   "a device policy enforcing this setting.");
+    return false;
+  }
+
+  // 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.
+
+  PrefsInterface* prefs = system_state_->prefs();
+
+  if (!prefs->SetBoolean(kPrefsUpdateOverCellularPermission, in_allowed)) {
+    LogAndSetError(error, FROM_HERE,
+                   string("Error setting the update over cellular to ") +
+                   (in_allowed ? "true" : "false"));
+    return false;
+  }
+  return true;
+}
+
+bool UpdateEngineService::GetUpdateOverCellularPermission(ErrorPtr* /* error */,
+                                                          bool* out_allowed) {
+  ConnectionManagerInterface* cm = system_state_->connection_manager();
+
+  // The device_policy is loaded in a lazy way before an update check and is
+  // used to determine if an update is allowed over cellular. Load the device
+  // policy now from the libbrillo cache if it wasn't already loaded.
+  if (!system_state_->device_policy()) {
+    UpdateAttempter* update_attempter = system_state_->update_attempter();
+    if (update_attempter)
+      update_attempter->RefreshDevicePolicy();
+  }
+
+  // Return the current setting based on the same logic used while checking for
+  // updates. A log message could be printed as the result of this test.
+  LOG(INFO) << "Checking if updates over cellular networks are allowed:";
+  *out_allowed = cm->IsUpdateAllowedOver(
+      chromeos_update_engine::NetworkConnectionType::kCellular,
+      chromeos_update_engine::NetworkTethering::kUnknown);
+  return true;
+}
+
+bool UpdateEngineService::GetDurationSinceUpdate(ErrorPtr* error,
+                                                 int64_t* out_usec_wallclock) {
+  base::Time time;
+  if (!system_state_->update_attempter()->GetBootTimeAtUpdate(&time)) {
+    LogAndSetError(error, FROM_HERE, "No pending update.");
+    return false;
+  }
+
+  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 = system_state_->update_attempter()->GetPrevVersion();
+  return true;
+}
+
+bool UpdateEngineService::GetRollbackPartition(
+    ErrorPtr* /* error */,
+    string* out_rollback_partition_name) {
+  BootControlInterface::Slot rollback_slot =
+      system_state_->update_attempter()->GetRollbackSlot();
+
+  if (rollback_slot == BootControlInterface::kInvalidSlot) {
+    out_rollback_partition_name->clear();
+    return true;
+  }
+
+  string name;
+  if (!system_state_->boot_control()->GetPartitionDevice(
+          "KERNEL", rollback_slot, &name)) {
+    LOG(ERROR) << "Invalid rollback device";
+    return false;
+  }
+
+  LOG(INFO) << "Getting rollback partition name. Result: " << name;
+  *out_rollback_partition_name = name;
+  return true;
+}
+
+UpdateEngineAdaptor::UpdateEngineAdaptor(SystemState* system_state,
+                                         const scoped_refptr<dbus::Bus>& bus)
+    : org::chromium::UpdateEngineInterfaceAdaptor(&dbus_service_),
+    bus_(bus),
+    dbus_service_(system_state),
+    dbus_object_(nullptr,
+                 bus,
+                 dbus::ObjectPath(update_engine::kUpdateEngineServicePath)) {}
+
+void UpdateEngineAdaptor::RegisterAsync(
+    const base::Callback<void(bool)>& completion_callback) {
+  RegisterWithDBusObject(&dbus_object_);
+  dbus_object_.RegisterAsync(completion_callback);
+}
+
+bool UpdateEngineAdaptor::RequestOwnership() {
+  return bus_->RequestOwnershipAndBlock(update_engine::kUpdateEngineServiceName,
+                                        dbus::Bus::REQUIRE_PRIMARY);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/dbus_service.h b/dbus_service.h
new file mode 100644
index 0000000..88bd90a
--- /dev/null
+++ b/dbus_service.h
@@ -0,0 +1,160 @@
+//
+// 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_DBUS_SERVICE_H_
+#define UPDATE_ENGINE_DBUS_SERVICE_H_
+
+#include <inttypes.h>
+
+#include <string>
+
+#include <base/memory/ref_counted.h>
+#include <brillo/errors/error.h>
+
+#include "update_engine/update_attempter.h"
+
+#include "dbus_bindings/org.chromium.UpdateEngineInterface.h"
+
+namespace chromeos_update_engine {
+
+class UpdateEngineService
+    : public org::chromium::UpdateEngineInterfaceInterface {
+ public:
+  explicit UpdateEngineService(SystemState* system_state);
+  virtual ~UpdateEngineService() = default;
+
+  // Implementation of org::chromium::UpdateEngineInterfaceInterface.
+  bool AttemptUpdate(brillo::ErrorPtr* error,
+                     const std::string& in_app_version,
+                     const std::string& in_omaha_url) override;
+
+  bool AttemptUpdateWithFlags(brillo::ErrorPtr* error,
+                              const std::string& in_app_version,
+                              const std::string& in_omaha_url,
+                              int32_t in_flags_as_int) override;
+
+  bool AttemptRollback(brillo::ErrorPtr* error, bool in_powerwash) override;
+
+  // Checks if the system rollback is available by verifying if the secondary
+  // system partition is valid and bootable.
+  bool CanRollback(brillo::ErrorPtr* error, bool* out_can_rollback) override;
+
+  // Resets the status of the update_engine to idle, ignoring any applied
+  // update. This is used for development only.
+  bool ResetStatus(brillo::ErrorPtr* error) 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;
+
+  // Changes the current channel of the device to the target channel. If the
+  // target channel is a less stable channel than the current channel, then the
+  // channel change happens immediately (at the next update check).  If the
+  // target channel is a more stable channel, then if is_powerwash_allowed is
+  // set to true, then also the change happens immediately but with a powerwash
+  // if required. Otherwise, the change takes effect eventually (when the
+  // version on the target channel goes above the version number of what the
+  // device currently has).
+  bool SetChannel(brillo::ErrorPtr* error,
+                  const std::string& in_target_channel,
+                  bool in_is_powerwash_allowed) override;
+
+  // If get_current_channel is set to true, populates |channel| with the name of
+  // the channel that the device is currently on. Otherwise, it populates it
+  // with the name of the channel the device is supposed to be (in case of a
+  // pending channel change).
+  bool GetChannel(brillo::ErrorPtr* error,
+                  bool in_get_current_channel,
+                  std::string* out_channel) override;
+
+  // Enables or disables the sharing and consuming updates over P2P feature
+  // according to the |enabled| argument passed.
+  bool SetP2PUpdatePermission(brillo::ErrorPtr* error,
+                              bool in_enabled) override;
+
+  // Returns the current value for the P2P enabled setting. This involves both
+  // sharing and consuming updates over P2P.
+  bool GetP2PUpdatePermission(brillo::ErrorPtr* error,
+                              bool* out_enabled) override;
+
+  // If there's no device policy installed, sets the update over cellular
+  // networks permission to the |allowed| value. Otherwise, this method returns
+  // with an error since this setting is overridden by the applied policy.
+  bool SetUpdateOverCellularPermission(brillo::ErrorPtr* error,
+                                       bool in_allowed) override;
+
+  // Returns the current value of the update over cellular network setting,
+  // either forced by the device policy if the device is enrolled or the current
+  // user preference otherwise.
+  bool GetUpdateOverCellularPermission(brillo::ErrorPtr* error,
+                                       bool* out_allowed) override;
+
+  // Returns the duration since the last successful update, as the
+  // duration on the wallclock. Returns an error if the device has not
+  // updated.
+  bool GetDurationSinceUpdate(brillo::ErrorPtr* error,
+                              int64_t* out_usec_wallclock) override;
+
+  // Returns the version string of OS that was used before the last reboot
+  // into an updated version. This is available only when rebooting into an
+  // update from previous version, otherwise an empty string is returned.
+  bool GetPrevVersion(brillo::ErrorPtr* error,
+                      std::string* out_prev_version) override;
+
+  // Returns the name of kernel partition that can be rolled back into.
+  bool GetRollbackPartition(brillo::ErrorPtr* error,
+                            std::string* out_rollback_partition_name) override;
+
+ private:
+  SystemState* system_state_;
+};
+
+// The UpdateEngineAdaptor class runs the UpdateEngineInterface in the fixed
+// object path, without an ObjectManager notifying the interfaces, since it is
+// all static and clients don't expect it to be implemented.
+class UpdateEngineAdaptor : public org::chromium::UpdateEngineInterfaceAdaptor {
+ public:
+  UpdateEngineAdaptor(SystemState* system_state,
+                      const scoped_refptr<dbus::Bus>& bus);
+  ~UpdateEngineAdaptor() = default;
+
+  // Register the DBus object with the update engine service asynchronously.
+  // Calls |copmletion_callback| when done passing a boolean indicating if the
+  // registration succeeded.
+  void RegisterAsync(const base::Callback<void(bool)>& completion_callback);
+
+  // Takes ownership of the well-known DBus name and returns whether it
+  // succeeded.
+  bool RequestOwnership();
+
+ private:
+  scoped_refptr<dbus::Bus> bus_;
+  UpdateEngineService dbus_service_;
+  brillo::dbus_utils::DBusObject dbus_object_;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_DBUS_SERVICE_H_
diff --git a/dbus_service_unittest.cc b/dbus_service_unittest.cc
new file mode 100644
index 0000000..db63b2d
--- /dev/null
+++ b/dbus_service_unittest.cc
@@ -0,0 +1,143 @@
+//
+// 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/dbus_service.h"
+
+#include <gtest/gtest.h>
+#include <string>
+
+#include <brillo/errors/error.h>
+#include <policy/libpolicy.h>
+#include <policy/mock_device_policy.h>
+#include <update_engine/dbus-constants.h>
+
+#include "update_engine/fake_system_state.h"
+
+using brillo::errors::dbus::kDomain;
+using std::string;
+using testing::Return;
+using testing::SetArgumentPointee;
+using testing::_;
+using update_engine::kUpdateEngineServiceErrorFailed;
+
+namespace chromeos_update_engine {
+
+class UpdateEngineServiceTest : public ::testing::Test {
+ protected:
+  UpdateEngineServiceTest()
+      : mock_update_attempter_(fake_system_state_.mock_update_attempter()),
+        dbus_service_(&fake_system_state_) {}
+
+  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_;
+  UpdateEngineService dbus_service_;
+};
+
+TEST_F(UpdateEngineServiceTest, AttemptUpdate) {
+  // Simple test to ensure that the default is an interactive check.
+  EXPECT_CALL(*mock_update_attempter_,
+              CheckForUpdate("app_ver", "url", true /* interactive */));
+  EXPECT_TRUE(dbus_service_.AttemptUpdate(&error_, "app_ver", "url"));
+  EXPECT_EQ(nullptr, error_);
+}
+
+TEST_F(UpdateEngineServiceTest, AttemptUpdateWithFlags) {
+  EXPECT_CALL(*mock_update_attempter_, CheckForUpdate(
+      "app_ver", "url", false /* interactive */));
+  // The update is non-interactive when we pass the non-interactive flag.
+  EXPECT_TRUE(dbus_service_.AttemptUpdateWithFlags(
+      &error_, "app_ver", "url",
+      update_engine::kAttemptUpdateFlagNonInteractive));
+  EXPECT_EQ(nullptr, error_);
+}
+
+// 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(*fake_system_state_.mock_request_params(),
+              SetTargetChannel("stable-channel", true, _))
+      .WillOnce(Return(true));
+  EXPECT_TRUE(dbus_service_.SetChannel(&error_, "stable-channel", true));
+  ASSERT_EQ(nullptr, error_);
+}
+
+// When the policy is present, the delegated value should be checked.
+TEST_F(UpdateEngineServiceTest, SetChannelWithDelegatedPolicy) {
+  policy::MockDevicePolicy mock_device_policy;
+  fake_system_state_.set_device_policy(&mock_device_policy);
+  EXPECT_CALL(mock_device_policy, GetReleaseChannelDelegated(_))
+      .WillOnce(DoAll(SetArgumentPointee<0>(true), Return(true)));
+  EXPECT_CALL(*fake_system_state_.mock_request_params(),
+              SetTargetChannel("beta-channel", true, _))
+      .WillOnce(Return(true));
+
+  EXPECT_TRUE(dbus_service_.SetChannel(&error_, "beta-channel", true));
+  ASSERT_EQ(nullptr, error_);
+}
+
+// When passing an invalid value (SetTargetChannel fails) an error should be
+// raised.
+TEST_F(UpdateEngineServiceTest, SetChannelWithInvalidChannel) {
+  EXPECT_CALL(*mock_update_attempter_, RefreshDevicePolicy());
+  EXPECT_CALL(*fake_system_state_.mock_request_params(),
+              SetTargetChannel("foo-channel", true, _)).WillOnce(Return(false));
+
+  EXPECT_FALSE(dbus_service_.SetChannel(&error_, "foo-channel", true));
+  ASSERT_NE(nullptr, error_);
+  EXPECT_TRUE(error_->HasError(kDomain, kUpdateEngineServiceErrorFailed));
+}
+
+TEST_F(UpdateEngineServiceTest, GetChannel) {
+  fake_system_state_.mock_request_params()->set_current_channel("current");
+  fake_system_state_.mock_request_params()->set_target_channel("target");
+  string channel;
+  EXPECT_TRUE(dbus_service_.GetChannel(
+      &error_, true /* get_current_channel */, &channel));
+  EXPECT_EQ(nullptr, error_);
+  EXPECT_EQ("current", channel);
+
+  EXPECT_TRUE(dbus_service_.GetChannel(
+      &error_, false /* get_current_channel */, &channel));
+  EXPECT_EQ(nullptr, error_);
+  EXPECT_EQ("target", channel);
+}
+
+TEST_F(UpdateEngineServiceTest, ResetStatusSucceeds) {
+  EXPECT_CALL(*mock_update_attempter_, ResetStatus()).WillOnce(Return(true));
+  EXPECT_TRUE(dbus_service_.ResetStatus(&error_));
+  EXPECT_EQ(nullptr, error_);
+}
+
+TEST_F(UpdateEngineServiceTest, ResetStatusFails) {
+  EXPECT_CALL(*mock_update_attempter_, ResetStatus()).WillOnce(Return(false));
+  EXPECT_FALSE(dbus_service_.ResetStatus(&error_));
+  ASSERT_NE(nullptr, error_);
+  EXPECT_TRUE(error_->HasError(kDomain, kUpdateEngineServiceErrorFailed));
+}
+
+}  // namespace chromeos_update_engine
diff --git a/dbus_test_utils.h b/dbus_test_utils.h
new file mode 100644
index 0000000..b3748ce
--- /dev/null
+++ b/dbus_test_utils.h
@@ -0,0 +1,89 @@
+//
+// 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_DBUS_TEST_UTILS_H_
+#define UPDATE_ENGINE_DBUS_TEST_UTILS_H_
+
+#include <set>
+#include <string>
+
+#include <base/bind.h>
+#include <brillo/message_loops/message_loop.h>
+#include <gmock/gmock.h>
+
+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),                                                \
+                Register##signal##SignalHandler(::testing::_, ::testing::_)) \
+        .WillOnce(::chromeos_update_engine::dbus_test_utils::GrabCallbacks(  \
+            &(mock_signal_handler)));                                        \
+  } while (false)
+
+template <typename T>
+class MockSignalHandler {
+ public:
+  MockSignalHandler() = default;
+  ~MockSignalHandler() {
+    if (callback_connected_task_ != brillo::MessageLoop::kTaskIdNull)
+      brillo::MessageLoop::current()->CancelTask(callback_connected_task_);
+  }
+
+  // Returns whether the signal handler is registered.
+  bool IsHandlerRegistered() const { return signal_callback_ != nullptr; }
+
+  const base::Callback<T>& signal_callback() { return *signal_callback_.get(); }
+
+  void GrabCallbacks(
+      const base::Callback<T>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) {
+    signal_callback_.reset(new base::Callback<T>(signal_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,
+        base::Bind(&MockSignalHandler<T>::OnCallbackConnected,
+                   base::Unretained(this)));
+  }
+
+ private:
+  void OnCallbackConnected() {
+    callback_connected_task_ = brillo::MessageLoop::kTaskIdNull;
+    on_connected_callback_->Run("", "", true);
+  }
+
+  brillo::MessageLoop::TaskId callback_connected_task_{
+      brillo::MessageLoop::kTaskIdNull};
+
+  std::unique_ptr<base::Callback<T>> signal_callback_;
+  std::unique_ptr<dbus::ObjectProxy::OnConnectedCallback>
+      on_connected_callback_;
+};
+
+// Defines the action that will call MockSignalHandler<T>::GrabCallbacks for the
+// right type.
+ACTION_P(GrabCallbacks, mock_signal_handler) {
+  mock_signal_handler->GrabCallbacks(arg0, arg1);
+}
+
+}  // namespace dbus_test_utils
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_DBUS_TEST_UTILS_H_
diff --git a/fake_file_writer.h b/fake_file_writer.h
new file mode 100644
index 0000000..43b71c7
--- /dev/null
+++ b/fake_file_writer.h
@@ -0,0 +1,76 @@
+//
+// 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_FAKE_FILE_WRITER_H_
+#define UPDATE_ENGINE_FAKE_FILE_WRITER_H_
+
+#include <vector>
+
+#include <base/macros.h>
+#include <brillo/secure_blob.h>
+
+#include "update_engine/payload_consumer/file_writer.h"
+
+// FakeFileWriter is an implementation of FileWriter. It will succeed
+// calls to Open(), Close(), but not do any work. All calls to Write()
+// will append the passed data to an internal vector.
+
+namespace chromeos_update_engine {
+
+class FakeFileWriter : public FileWriter {
+ public:
+  FakeFileWriter() : was_opened_(false), was_closed_(false) {}
+
+  virtual int Open(const char* path, int flags, mode_t mode) {
+    CHECK(!was_opened_);
+    CHECK(!was_closed_);
+    was_opened_ = true;
+    return 0;
+  }
+
+  virtual ssize_t Write(const void* bytes, size_t count) {
+    CHECK(was_opened_);
+    CHECK(!was_closed_);
+    const char* char_bytes = reinterpret_cast<const char*>(bytes);
+    bytes_.insert(bytes_.end(), char_bytes, char_bytes + count);
+    return count;
+  }
+
+  virtual int Close() {
+    CHECK(was_opened_);
+    CHECK(!was_closed_);
+    was_closed_ = true;
+    return 0;
+  }
+
+  const brillo::Blob& bytes() {
+    return bytes_;
+  }
+
+ private:
+  // The internal store of all bytes that have been written
+  brillo::Blob bytes_;
+
+  // These are just to ensure FileWriter methods are called properly.
+  bool was_opened_;
+  bool was_closed_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeFileWriter);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_FAKE_FILE_WRITER_H_
diff --git a/fake_p2p_manager.h b/fake_p2p_manager.h
new file mode 100644
index 0000000..a8cf4ea
--- /dev/null
+++ b/fake_p2p_manager.h
@@ -0,0 +1,130 @@
+//
+// 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_FAKE_P2P_MANAGER_H_
+#define UPDATE_ENGINE_FAKE_P2P_MANAGER_H_
+
+#include <string>
+
+#include "update_engine/p2p_manager.h"
+
+namespace chromeos_update_engine {
+
+// A fake implementation of P2PManager.
+class FakeP2PManager : public P2PManager {
+ public:
+  FakeP2PManager() :
+    is_p2p_enabled_(false),
+    ensure_p2p_running_result_(false),
+    ensure_p2p_not_running_result_(false),
+    perform_housekeeping_result_(false),
+    count_shared_files_result_(0) {}
+
+  // P2PManager overrides.
+  void SetDevicePolicy(const policy::DevicePolicy* device_policy) override {}
+
+  bool IsP2PEnabled() override {
+    return is_p2p_enabled_;
+  }
+
+  bool EnsureP2PRunning() override {
+    return ensure_p2p_running_result_;
+  }
+
+  bool EnsureP2PNotRunning() override {
+    return ensure_p2p_not_running_result_;
+  }
+
+  bool PerformHousekeeping() override {
+    return perform_housekeeping_result_;
+  }
+
+  void LookupUrlForFile(const std::string& file_id,
+                        size_t minimum_size,
+                        base::TimeDelta max_time_to_wait,
+                        LookupCallback callback) override {
+    callback.Run(lookup_url_for_file_result_);
+  }
+
+  bool FileShare(const std::string& file_id,
+                 size_t expected_size) override {
+    return false;
+  }
+
+  base::FilePath FileGetPath(const std::string& file_id) override {
+    return base::FilePath();
+  }
+
+  ssize_t FileGetSize(const std::string& file_id) override {
+    return -1;
+  }
+
+  ssize_t FileGetExpectedSize(const std::string& file_id) override {
+    return -1;
+  }
+
+  bool FileGetVisible(const std::string& file_id,
+                      bool *out_result) override {
+    return false;
+  }
+
+  bool FileMakeVisible(const std::string& file_id) override {
+    return false;
+  }
+
+  int CountSharedFiles() override {
+    return count_shared_files_result_;
+  }
+
+  // Methods for controlling what the fake returns and how it acts.
+  void SetP2PEnabled(bool is_p2p_enabled) {
+    is_p2p_enabled_ = is_p2p_enabled;
+  }
+
+  void SetEnsureP2PRunningResult(bool ensure_p2p_running_result) {
+    ensure_p2p_running_result_ = ensure_p2p_running_result;
+  }
+
+  void SetEnsureP2PNotRunningResult(bool ensure_p2p_not_running_result) {
+    ensure_p2p_not_running_result_ = ensure_p2p_not_running_result;
+  }
+
+  void SetPerformHousekeepingResult(bool perform_housekeeping_result) {
+    perform_housekeeping_result_ = perform_housekeeping_result;
+  }
+
+  void SetCountSharedFilesResult(int count_shared_files_result) {
+    count_shared_files_result_ = count_shared_files_result;
+  }
+
+  void SetLookupUrlForFileResult(const std::string& url) {
+    lookup_url_for_file_result_ = url;
+  }
+
+ private:
+  bool is_p2p_enabled_;
+  bool ensure_p2p_running_result_;
+  bool ensure_p2p_not_running_result_;
+  bool perform_housekeeping_result_;
+  int count_shared_files_result_;
+  std::string lookup_url_for_file_result_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeP2PManager);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_FAKE_P2P_MANAGER_H_
diff --git a/fake_p2p_manager_configuration.h b/fake_p2p_manager_configuration.h
new file mode 100644
index 0000000..3d3afe4
--- /dev/null
+++ b/fake_p2p_manager_configuration.h
@@ -0,0 +1,115 @@
+//
+// 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_FAKE_P2P_MANAGER_CONFIGURATION_H_
+#define UPDATE_ENGINE_FAKE_P2P_MANAGER_CONFIGURATION_H_
+
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/p2p_manager.h"
+
+#include <string>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
+
+namespace chromeos_update_engine {
+
+// Configuration for P2PManager for use in unit tests. Instead of
+// /var/cache/p2p, a temporary directory is used.
+class FakeP2PManagerConfiguration : public P2PManager::Configuration {
+ public:
+  FakeP2PManagerConfiguration() {
+    EXPECT_TRUE(utils::MakeTempDirectory("p2p-tc.XXXXXX", &p2p_dir_));
+  }
+
+  ~FakeP2PManagerConfiguration() {
+    if (p2p_dir_.size() > 0 &&
+        !base::DeleteFile(base::FilePath(p2p_dir_), true)) {
+      PLOG(ERROR) << "Unable to unlink files and directory in " << p2p_dir_;
+    }
+  }
+
+  // P2PManager::Configuration override
+  base::FilePath GetP2PDir() override {
+    return base::FilePath(p2p_dir_);
+  }
+
+  // P2PManager::Configuration override
+  std::vector<std::string> GetInitctlArgs(bool is_start) override {
+    return is_start ? initctl_start_args_ : initctl_stop_args_;
+  }
+
+  // P2PManager::Configuration override
+  std::vector<std::string> GetP2PClientArgs(const std::string &file_id,
+                                            size_t minimum_size) override {
+    std::vector<std::string> formatted_command = p2p_client_cmd_format_;
+    // Replace {variable} on the passed string.
+    std::string str_minimum_size = std::to_string(minimum_size);
+    for (std::string& arg : formatted_command) {
+      ReplaceSubstringsAfterOffset(&arg, 0, "{file_id}", file_id);
+      ReplaceSubstringsAfterOffset(&arg, 0, "{minsize}", str_minimum_size);
+    }
+    return formatted_command;
+  }
+
+  // Use |command_line| instead of "initctl start p2p" when attempting
+  // to start the p2p service.
+  void SetInitctlStartCommand(const std::vector<std::string>& command) {
+    initctl_start_args_ = command;
+  }
+
+  // Use |command_line| instead of "initctl stop p2p" when attempting
+  // to stop the p2p service.
+  void SetInitctlStopCommand(const std::vector<std::string>& command) {
+    initctl_stop_args_ = command;
+  }
+
+  // Use |command_format| instead of "p2p-client --get-url={file_id}
+  // --minimum-size={minsize}" when attempting to look up a file using
+  // p2p-client(1).
+  //
+  // The passed |command_format| argument can have "{file_id}" and "{minsize}"
+  // as substrings of any of its elements, that will be replaced by the
+  // corresponding values passed to GetP2PClientArgs().
+  void SetP2PClientCommand(const std::vector<std::string>& command_format) {
+    p2p_client_cmd_format_ = command_format;
+  }
+
+ private:
+  // The temporary directory used for p2p.
+  std::string p2p_dir_;
+
+  // Argument vector for starting p2p.
+  std::vector<std::string> initctl_start_args_{"initctl", "start", "p2p"};
+
+  // Argument vector for stopping p2p.
+  std::vector<std::string> initctl_stop_args_{"initctl", "stop", "p2p"};
+
+  // A string for generating the p2p-client command. See the
+  // SetP2PClientCommandLine() for details.
+  std::vector<std::string> p2p_client_cmd_format_{
+      "p2p-client", "--get-url={file_id}", "--minimum-size={minsize}"};
+
+  DISALLOW_COPY_AND_ASSIGN(FakeP2PManagerConfiguration);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_FAKE_P2P_MANAGER_CONFIGURATION_H_
diff --git a/fake_shill_proxy.cc b/fake_shill_proxy.cc
new file mode 100644
index 0000000..17698cd
--- /dev/null
+++ b/fake_shill_proxy.cc
@@ -0,0 +1,47 @@
+//
+// 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/fake_shill_proxy.h"
+
+using org::chromium::flimflam::ManagerProxyMock;
+using org::chromium::flimflam::ServiceProxyInterface;
+
+namespace chromeos_update_engine {
+
+FakeShillProxy::FakeShillProxy()
+    : manager_proxy_mock_(new ManagerProxyMock()) {}
+
+ManagerProxyMock* FakeShillProxy::GetManagerProxy() {
+  return manager_proxy_mock_.get();
+}
+
+std::unique_ptr<ServiceProxyInterface> FakeShillProxy::GetServiceForPath(
+    const dbus::ObjectPath& path) {
+  auto it = service_proxy_mocks_.find(path.value());
+  CHECK(it != service_proxy_mocks_.end()) << "No ServiceProxyMock set for "
+                                          << path.value();
+  std::unique_ptr<ServiceProxyInterface> result = std::move(it->second);
+  service_proxy_mocks_.erase(it);
+  return result;
+}
+
+void FakeShillProxy::SetServiceForPath(
+    const dbus::ObjectPath& path,
+    std::unique_ptr<ServiceProxyInterface> service_proxy) {
+  service_proxy_mocks_[path.value()] = std::move(service_proxy);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/fake_shill_proxy.h b/fake_shill_proxy.h
new file mode 100644
index 0000000..ae17eaa
--- /dev/null
+++ b/fake_shill_proxy.h
@@ -0,0 +1,66 @@
+//
+// 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_FAKE_SHILL_PROXY_H_
+#define UPDATE_ENGINE_FAKE_SHILL_PROXY_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include <base/macros.h>
+#include <shill/dbus-proxies.h>
+#include <shill/dbus-proxy-mocks.h>
+
+#include "update_engine/shill_proxy_interface.h"
+
+namespace chromeos_update_engine {
+
+// This class implements the connection to shill using real DBus calls.
+class FakeShillProxy : public ShillProxyInterface {
+ public:
+  FakeShillProxy();
+  ~FakeShillProxy() override = default;
+
+  // ShillProxyInterface overrides.
+
+  // GetManagerProxy returns the subclass ManagerProxyMock so tests can easily
+  // use it. Mocks for the return value of GetServiceForPath() can be provided
+  // with SetServiceForPath().
+  org::chromium::flimflam::ManagerProxyMock* GetManagerProxy() override;
+  std::unique_ptr<org::chromium::flimflam::ServiceProxyInterface>
+  GetServiceForPath(const dbus::ObjectPath& path) override;
+
+  // Sets the service_proxy that will be returned by GetServiceForPath().
+  void SetServiceForPath(
+      const dbus::ObjectPath& path,
+      std::unique_ptr<org::chromium::flimflam::ServiceProxyInterface>
+          service_proxy);
+
+ private:
+  std::unique_ptr<org::chromium::flimflam::ManagerProxyMock>
+      manager_proxy_mock_;
+
+  std::map<std::string,
+           std::unique_ptr<org::chromium::flimflam::ServiceProxyInterface>>
+      service_proxy_mocks_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeShillProxy);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_FAKE_SHILL_PROXY_H_
diff --git a/fake_system_state.cc b/fake_system_state.cc
new file mode 100644
index 0000000..49ba058
--- /dev/null
+++ b/fake_system_state.cc
@@ -0,0 +1,43 @@
+//
+// 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/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_(this, nullptr, nullptr, nullptr),
+      mock_request_params_(this),
+      fake_update_manager_(&fake_clock_),
+      clock_(&fake_clock_),
+      connection_manager_(&mock_connection_manager_),
+      hardware_(&fake_hardware_),
+      metrics_lib_(&mock_metrics_lib_),
+      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_),
+      p2p_manager_(&mock_p2p_manager_),
+      update_manager_(&fake_update_manager_),
+      device_policy_(nullptr),
+      fake_system_rebooted_(false) {
+  mock_payload_state_.Initialize(this);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/fake_system_state.h b/fake_system_state.h
new file mode 100644
index 0000000..75ef315
--- /dev/null
+++ b/fake_system_state.h
@@ -0,0 +1,274 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_FAKE_SYSTEM_STATE_H_
+#define UPDATE_ENGINE_FAKE_SYSTEM_STATE_H_
+
+#include <base/logging.h>
+#include <gmock/gmock.h>
+#include <policy/mock_device_policy.h>
+#include <power_manager/dbus-proxies.h>
+#include <power_manager/dbus-proxy-mocks.h>
+
+#include "metrics/metrics_library_mock.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/mock_prefs.h"
+#include "update_engine/mock_connection_manager.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_update_attempter.h"
+#include "update_engine/system_state.h"
+#include "update_engine/update_manager/fake_update_manager.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.
+class FakeSystemState : public SystemState {
+ public:
+  FakeSystemState();
+
+  // Base class overrides. All getters return the current implementation of
+  // various members, either the default (fake/mock) or the one set to override
+  // it by client code.
+
+  BootControlInterface* boot_control() override { return boot_control_; }
+
+  inline ClockInterface* clock() override { return clock_; }
+
+  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 ConnectionManagerInterface* connection_manager() override {
+    return connection_manager_;
+  }
+
+  inline HardwareInterface* hardware() override { return hardware_; }
+
+  inline MetricsLibraryInterface* metrics_lib() override {
+    return metrics_lib_;
+  }
+
+  inline PrefsInterface* prefs() override { return prefs_; }
+
+  inline PrefsInterface* powerwash_safe_prefs() override {
+    return powerwash_safe_prefs_;
+  }
+
+  inline PayloadStateInterface* payload_state() override {
+    return payload_state_;
+  }
+
+  inline UpdateAttempter* update_attempter() override {
+    return update_attempter_;
+  }
+
+  inline OmahaRequestParams* request_params() override {
+    return request_params_;
+  }
+
+  inline P2PManager* p2p_manager() override { return p2p_manager_; }
+
+  inline chromeos_update_manager::UpdateManager* update_manager() override {
+    return update_manager_;
+  }
+
+  inline org::chromium::PowerManagerProxyInterface* power_manager_proxy()
+      override {
+    return power_manager_proxy_;
+  }
+
+  inline bool system_rebooted() override { return fake_system_rebooted_; }
+
+  // Setters for the various members, can be used for overriding the default
+  // implementations. For convenience, setting to a null pointer will restore
+  // the default implementation.
+
+  void set_boot_control(BootControlInterface* boot_control) {
+    boot_control_ = boot_control ? boot_control : &fake_boot_control_;
+  }
+
+  inline void set_clock(ClockInterface* clock) {
+    clock_ = clock ? clock : &fake_clock_;
+  }
+
+  inline void set_connection_manager(
+      ConnectionManagerInterface* connection_manager) {
+    connection_manager_ = (connection_manager ? connection_manager :
+                           &mock_connection_manager_);
+  }
+
+  inline void set_hardware(HardwareInterface* hardware) {
+    hardware_ = hardware ? hardware : &fake_hardware_;
+  }
+
+  inline void set_metrics_lib(MetricsLibraryInterface* metrics_lib) {
+    metrics_lib_ = metrics_lib ? metrics_lib : &mock_metrics_lib_;
+  }
+
+  inline void set_prefs(PrefsInterface* prefs) {
+    prefs_ = prefs ? prefs : &mock_prefs_;
+  }
+
+  inline void set_powerwash_safe_prefs(PrefsInterface* powerwash_safe_prefs) {
+    powerwash_safe_prefs_ = (powerwash_safe_prefs ? powerwash_safe_prefs :
+                             &mock_powerwash_safe_prefs_);
+  }
+
+  inline void set_payload_state(PayloadStateInterface *payload_state) {
+    payload_state_ = payload_state ? payload_state : &mock_payload_state_;
+  }
+
+  inline void set_update_attempter(UpdateAttempter* update_attempter) {
+    update_attempter_ = (update_attempter ? update_attempter :
+                         &mock_update_attempter_);
+  }
+
+  inline void set_request_params(OmahaRequestParams* request_params) {
+    request_params_ = (request_params ? request_params :
+                       &mock_request_params_);
+  }
+
+  inline void set_p2p_manager(P2PManager *p2p_manager) {
+    p2p_manager_ = p2p_manager ? p2p_manager : &mock_p2p_manager_;
+  }
+
+  inline void set_update_manager(
+      chromeos_update_manager::UpdateManager *update_manager) {
+    update_manager_ = update_manager ? update_manager : &fake_update_manager_;
+  }
+
+  inline void set_system_rebooted(bool system_rebooted) {
+    fake_system_rebooted_ = system_rebooted;
+  }
+
+  // Getters for the built-in default implementations. These return the actual
+  // concrete type of each implementation. For additional safety, they will fail
+  // whenever the requested default was overridden by a different
+  // implementation.
+
+  inline FakeBootControl* fake_boot_control() {
+    CHECK(boot_control_ == &fake_boot_control_);
+    return &fake_boot_control_;
+  }
+
+  inline FakeClock* fake_clock() {
+    CHECK(clock_ == &fake_clock_);
+    return &fake_clock_;
+  }
+
+  inline testing::NiceMock<MockConnectionManager>* mock_connection_manager() {
+    CHECK(connection_manager_ == &mock_connection_manager_);
+    return &mock_connection_manager_;
+  }
+
+  inline FakeHardware* fake_hardware() {
+    CHECK(hardware_ == &fake_hardware_);
+    return &fake_hardware_;
+  }
+
+  inline testing::NiceMock<MetricsLibraryMock>* mock_metrics_lib() {
+    CHECK(metrics_lib_ == &mock_metrics_lib_);
+    return &mock_metrics_lib_;
+  }
+
+  inline testing::NiceMock<MockPrefs> *mock_prefs() {
+    CHECK(prefs_ == &mock_prefs_);
+    return &mock_prefs_;
+  }
+
+  inline testing::NiceMock<MockPrefs> *mock_powerwash_safe_prefs() {
+    CHECK(powerwash_safe_prefs_ == &mock_powerwash_safe_prefs_);
+    return &mock_powerwash_safe_prefs_;
+  }
+
+  inline testing::NiceMock<MockPayloadState>* mock_payload_state() {
+    CHECK(payload_state_ == &mock_payload_state_);
+    return &mock_payload_state_;
+  }
+
+  inline testing::NiceMock<MockUpdateAttempter>* mock_update_attempter() {
+    CHECK(update_attempter_ == &mock_update_attempter_);
+    return &mock_update_attempter_;
+  }
+
+  inline testing::NiceMock<MockOmahaRequestParams>* mock_request_params() {
+    CHECK(request_params_ == &mock_request_params_);
+    return &mock_request_params_;
+  }
+
+  inline testing::NiceMock<MockP2PManager>* mock_p2p_manager() {
+    CHECK(p2p_manager_ == &mock_p2p_manager_);
+    return &mock_p2p_manager_;
+  }
+
+  inline chromeos_update_manager::FakeUpdateManager* fake_update_manager() {
+    CHECK(update_manager_ == &fake_update_manager_);
+    return &fake_update_manager_;
+  }
+
+ private:
+  // Default mock/fake implementations (owned).
+  FakeBootControl fake_boot_control_;
+  FakeClock fake_clock_;
+  testing::NiceMock<MockConnectionManager> mock_connection_manager_;
+  FakeHardware fake_hardware_;
+  testing::NiceMock<MetricsLibraryMock> mock_metrics_lib_;
+  testing::NiceMock<MockPrefs> mock_prefs_;
+  testing::NiceMock<MockPrefs> mock_powerwash_safe_prefs_;
+  testing::NiceMock<MockPayloadState> mock_payload_state_;
+  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_;
+  org::chromium::PowerManagerProxyMock mock_power_manager_;
+
+  // Pointers to objects that client code can override. They are initialized to
+  // the default implementations above.
+  BootControlInterface* boot_control_{&fake_boot_control_};
+  ClockInterface* clock_;
+  ConnectionManagerInterface* connection_manager_;
+  HardwareInterface* hardware_;
+  MetricsLibraryInterface* metrics_lib_;
+  PrefsInterface* prefs_;
+  PrefsInterface* powerwash_safe_prefs_;
+  PayloadStateInterface* payload_state_;
+  UpdateAttempter* update_attempter_;
+  OmahaRequestParams* request_params_;
+  P2PManager* p2p_manager_;
+  chromeos_update_manager::UpdateManager* update_manager_;
+  org::chromium::PowerManagerProxyInterface* power_manager_proxy_{
+      &mock_power_manager_};
+
+  // Other object pointers (not preinitialized).
+  const policy::DevicePolicy* device_policy_;
+
+  // Other data members.
+  bool fake_system_rebooted_;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_FAKE_SYSTEM_STATE_H_
diff --git a/generate_pc_file.sh b/generate_pc_file.sh
new file mode 100755
index 0000000..ab101f4
--- /dev/null
+++ b/generate_pc_file.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+#
+# 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.
+#
+
+set -e
+
+OUT=$1
+shift
+PC_IN=$1
+shift
+INCLUDE_DIR=$1
+shift
+
+sed \
+  -e "s|@INCLUDE_DIR@|${INCLUDE_DIR}|g" \
+  "${PC_IN}.pc.in" > "${OUT}/${PC_IN}.pc"
diff --git a/hardware_android.cc b/hardware_android.cc
new file mode 100644
index 0000000..a20fe6f
--- /dev/null
+++ b/hardware_android.cc
@@ -0,0 +1,113 @@
+//
+// 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 <base/files/file_util.h>
+#include <brillo/make_unique_ptr.h>
+#include <cutils/properties.h>
+
+#include "update_engine/common/hardware.h"
+#include "update_engine/common/platform_constants.h"
+
+using std::string;
+
+namespace chromeos_update_engine {
+
+namespace hardware {
+
+// Factory defined in hardware.h.
+std::unique_ptr<HardwareInterface> CreateHardware() {
+  return brillo::make_unique_ptr(new 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 property_get_bool("ro.secure", 1) != 0;
+}
+
+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 property_get_bool("ro.debuggable", 0) != 1;
+}
+
+bool HardwareAndroid::IsOOBEComplete(base::Time* out_time_of_oobe) const {
+  LOG(WARNING) << "STUB: Assuming OOBE is complete.";
+  if (out_time_of_oobe)
+    *out_time_of_oobe = base::Time();
+  return true;
+}
+
+string HardwareAndroid::GetHardwareClass() const {
+  LOG(WARNING) << "STUB: GetHardwareClass().";
+  return "ANDROID";
+}
+
+string HardwareAndroid::GetFirmwareVersion() const {
+  LOG(WARNING) << "STUB: GetFirmwareVersion().";
+  return "0";
+}
+
+string HardwareAndroid::GetECVersion() const {
+  LOG(WARNING) << "STUB: GetECVersion().";
+  return "0";
+}
+
+int HardwareAndroid::GetPowerwashCount() const {
+  LOG(WARNING) << "STUB: Assuming no factory reset was performed.";
+  return 0;
+}
+
+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;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/hardware_android.h b/hardware_android.h
new file mode 100644
index 0000000..1b03661
--- /dev/null
+++ b/hardware_android.h
@@ -0,0 +1,53 @@
+//
+// 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_HARDWARE_ANDROID_H_
+#define UPDATE_ENGINE_HARDWARE_ANDROID_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <base/time/time.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 final : public HardwareInterface {
+ public:
+  HardwareAndroid() = default;
+  ~HardwareAndroid() override = default;
+
+  // HardwareInterface methods.
+  bool IsOfficialBuild() const override;
+  bool IsNormalBootMode() const override;
+  bool IsOOBEComplete(base::Time* out_time_of_oobe) const override;
+  std::string GetHardwareClass() const override;
+  std::string GetFirmwareVersion() const override;
+  std::string GetECVersion() const override;
+  int GetPowerwashCount() const override;
+  bool GetNonVolatileDirectory(base::FilePath* path) const override;
+  bool GetPowerwashSafeDirectory(base::FilePath* path) const override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HardwareAndroid);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_HARDWARE_ANDROID_H_
diff --git a/hardware_chromeos.cc b/hardware_chromeos.cc
new file mode 100644
index 0000000..ccb3978
--- /dev/null
+++ b/hardware_chromeos.cc
@@ -0,0 +1,153 @@
+//
+// 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.
+//
+
+#include "update_engine/hardware_chromeos.h"
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
+#include <brillo/make_unique_ptr.h>
+#include <vboot/crossystem.h>
+
+extern "C" {
+#include "vboot/vboot_host.h"
+}
+
+#include "update_engine/common/hardware.h"
+#include "update_engine/common/hwid_override.h"
+#include "update_engine/common/platform_constants.h"
+#include "update_engine/common/subprocess.h"
+#include "update_engine/common/utils.h"
+
+using std::string;
+using std::vector;
+
+namespace {
+
+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 whitelisted in the powerwash scripts.
+const char kPowerwashSafeDirectory[] =
+    "/mnt/stateful_partition/unencrypted/preserve";
+
+// The powerwash_count marker file contains the number of times the device was
+// powerwashed. This value is incremented by the clobber-state script when
+// a powerwash is performed.
+const char kPowerwashCountMarker[] = "powerwash_count";
+
+}  // namespace
+
+namespace chromeos_update_engine {
+
+namespace hardware {
+
+// Factory defined in hardware.h.
+std::unique_ptr<HardwareInterface> CreateHardware() {
+  return brillo::make_unique_ptr(new HardwareChromeOS());
+}
+
+}  // namespace hardware
+
+bool HardwareChromeOS::IsOfficialBuild() const {
+  return VbGetSystemPropertyInt("debug_build") == 0;
+}
+
+bool HardwareChromeOS::IsNormalBootMode() const {
+  bool dev_mode = VbGetSystemPropertyInt("devsw_boot") != 0;
+  return !dev_mode;
+}
+
+bool HardwareChromeOS::IsOOBEComplete(base::Time* out_time_of_oobe) const {
+  struct stat statbuf;
+  if (stat(kOOBECompletedMarker, &statbuf) != 0) {
+    if (errno != ENOENT) {
+      PLOG(ERROR) << "Error getting information about "
+                  << kOOBECompletedMarker;
+    }
+    return false;
+  }
+
+  if (out_time_of_oobe != nullptr)
+    *out_time_of_oobe = base::Time::FromTimeT(statbuf.st_mtime);
+  return true;
+}
+
+static string ReadValueFromCrosSystem(const string& key) {
+  char value_buffer[VB_MAX_STRING_PROPERTY];
+
+  const char* rv = VbGetSystemPropertyString(key.c_str(), value_buffer,
+                                             sizeof(value_buffer));
+  if (rv != nullptr) {
+    string return_value(value_buffer);
+    base::TrimWhitespaceASCII(return_value, base::TRIM_ALL, &return_value);
+    return return_value;
+  }
+
+  LOG(ERROR) << "Unable to read crossystem key " << key;
+  return "";
+}
+
+string HardwareChromeOS::GetHardwareClass() const {
+  if (USE_HWID_OVERRIDE) {
+    return HwidOverride::Read(base::FilePath("/"));
+  }
+  return ReadValueFromCrosSystem("hwid");
+}
+
+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::GetPowerwashCount() const {
+  int powerwash_count;
+  base::FilePath marker_path = base::FilePath(kPowerwashSafeDirectory).Append(
+      kPowerwashCountMarker);
+  string contents;
+  if (!utils::ReadFile(marker_path.value(), &contents))
+    return -1;
+  base::TrimWhitespaceASCII(contents, base::TRIM_TRAILING, &contents);
+  if (!base::StringToInt(contents, &powerwash_count))
+    return -1;
+  return powerwash_count;
+}
+
+bool HardwareChromeOS::GetNonVolatileDirectory(base::FilePath* path) const {
+  *path = base::FilePath(constants::kNonVolatileDirectory);
+  return true;
+}
+
+bool HardwareChromeOS::GetPowerwashSafeDirectory(base::FilePath* path) const {
+  *path = base::FilePath(kPowerwashSafeDirectory);
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/hardware_chromeos.h b/hardware_chromeos.h
new file mode 100644
index 0000000..80888ab
--- /dev/null
+++ b/hardware_chromeos.h
@@ -0,0 +1,54 @@
+//
+// 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_HARDWARE_CHROMEOS_H_
+#define UPDATE_ENGINE_HARDWARE_CHROMEOS_H_
+
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <base/time/time.h>
+
+#include "update_engine/common/hardware_interface.h"
+
+namespace chromeos_update_engine {
+
+// Implements the real interface with Chrome OS verified boot and recovery
+// process.
+class HardwareChromeOS final : public HardwareInterface {
+ public:
+  HardwareChromeOS() = default;
+  ~HardwareChromeOS() override = default;
+
+  // HardwareInterface methods.
+  bool IsOfficialBuild() const override;
+  bool IsNormalBootMode() const override;
+  bool IsOOBEComplete(base::Time* out_time_of_oobe) const override;
+  std::string GetHardwareClass() const override;
+  std::string GetFirmwareVersion() const override;
+  std::string GetECVersion() const override;
+  int GetPowerwashCount() const override;
+  bool GetNonVolatileDirectory(base::FilePath* path) const override;
+  bool GetPowerwashSafeDirectory(base::FilePath* path) const override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HardwareChromeOS);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_HARDWARE_CHROMEOS_H_
diff --git a/image_properties.h b/image_properties.h
new file mode 100644
index 0000000..6026c2e
--- /dev/null
+++ b/image_properties.h
@@ -0,0 +1,84 @@
+//
+// 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.
+//
+
+// This module abstracts the properties tied to the current running image. These
+// properties are meant to be constant during the life of this daemon, but can
+// be modified in dev-move or non-official builds.
+
+#ifndef UPDATE_ENGINE_IMAGE_PROPERTIES_H_
+#define UPDATE_ENGINE_IMAGE_PROPERTIES_H_
+
+#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 product version of this image.
+  std::string version;
+
+  // The board name this image was built for.
+  std::string board;
+
+  // The release channel this image was obtained from.
+  std::string current_channel;
+
+  // The Omaha URL this image should get updates from.
+  std::string omaha_url;
+};
+
+// The mutable image properties are read-write image properties, initialized
+// with values from the image but can be modified by storing them in the
+// stateful partition.
+struct MutableImageProperties {
+  // The release channel we are tracking.
+  std::string target_channel;
+
+  // Whether powerwash is allowed when downloading an update for the selected
+  // target_channel.
+  bool is_powerwash_allowed{false};
+};
+
+// 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(SystemState* system_state);
+
+// Loads the mutable image properties from the stateful partition if found or the
+// system image otherwise.
+MutableImageProperties LoadMutableImageProperties(SystemState* system_state);
+
+// Stores the mutable image properties in the stateful partition. Returns
+// whether the operation succeeded.
+bool StoreMutableImageProperties(SystemState* system_state,
+                                 const MutableImageProperties& properties);
+
+// Sets the root_prefix used to load files from during unittests to
+// |test_root_prefix|. Passing a nullptr value resets it to the default.
+namespace test {
+void SetImagePropertiesRootPrefix(const char* test_root_prefix);
+}  // namespace test
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_IMAGE_PROPERTIES_H_
diff --git a/image_properties_android.cc b/image_properties_android.cc
new file mode 100644
index 0000000..00822da
--- /dev/null
+++ b/image_properties_android.cc
@@ -0,0 +1,111 @@
+//
+// 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 <string>
+
+#include <base/logging.h>
+#include <brillo/osrelease_reader.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/system_state.h"
+
+namespace chromeos_update_engine {
+
+namespace {
+
+// Build time properties name used in Brillo.
+const char kProductId[] = "product_id";
+const char kProductVersion[] = "product_version";
+
+// Prefs used to store the target channel and powerwash settings.
+const char kPrefsImgPropChannelName[] = "img-prop-channel-name";
+const char kPrefsImgPropPowerwashAllowed[] = "img-prop-powerwash-allowed";
+
+std::string GetStringWithDefault(const brillo::OsReleaseReader& osrelease,
+                                 const std::string& key,
+                                 const std::string& default_value) {
+  std::string result;
+  if (osrelease.GetString(key, &result))
+    return result;
+  LOG(INFO) << "Cannot load ImageProperty " << key << ", using default value "
+            << default_value;
+  return default_value;
+}
+
+}  // namespace
+
+namespace test {
+void SetImagePropertiesRootPrefix(const char* /* test_root_prefix */) {}
+}  // namespace test
+
+ImageProperties LoadImageProperties(SystemState* system_state) {
+  ImageProperties result;
+
+  brillo::OsReleaseReader osrelease;
+  osrelease.Load();
+  result.product_id = GetStringWithDefault(
+      osrelease, kProductId, "developer-boards:brillo-starter-board");
+  result.canary_product_id = result.product_id;
+  result.version = GetStringWithDefault(osrelease, kProductVersion, "0.0.0.0");
+
+  result.board = "brillo";
+
+  // Brillo images don't have a channel assigned. We stored the name of the
+  // channel where we got the image from in prefs at the time of the update, so
+  // we use that as the current channel if available. During provisioning, there
+  // is no value assigned, so we default to the "stable-channel".
+  std::string current_channel_key =
+      kPrefsChannelOnSlotPrefix +
+      std::to_string(system_state->boot_control()->GetCurrentSlot());
+  std::string current_channel;
+  if (!system_state->prefs()->Exists(current_channel_key) ||
+      !system_state->prefs()->GetString(current_channel_key, &current_channel))
+    current_channel = "stable-channel";
+  result.current_channel = current_channel;
+
+  // Brillo only supports the official omaha URL.
+  result.omaha_url = constants::kOmahaDefaultProductionURL;
+
+  return result;
+}
+
+MutableImageProperties LoadMutableImageProperties(SystemState* system_state) {
+  MutableImageProperties result;
+  PrefsInterface* const prefs = system_state->prefs();
+  if (!prefs->GetString(kPrefsImgPropChannelName, &result.target_channel))
+    result.target_channel.clear();
+  if (!prefs->GetBoolean(kPrefsImgPropPowerwashAllowed,
+                         &result.is_powerwash_allowed)) {
+    result.is_powerwash_allowed = false;
+  }
+  return result;
+}
+
+bool StoreMutableImageProperties(SystemState* system_state,
+                                 const MutableImageProperties& properties) {
+  PrefsInterface* const prefs = system_state->prefs();
+  return (
+      prefs->SetString(kPrefsImgPropChannelName, properties.target_channel) &&
+      prefs->SetBoolean(kPrefsImgPropPowerwashAllowed,
+                        properties.is_powerwash_allowed));
+}
+
+}  // namespace chromeos_update_engine
diff --git a/image_properties_chromeos.cc b/image_properties_chromeos.cc
new file mode 100644
index 0000000..501e662
--- /dev/null
+++ b/image_properties_chromeos.cc
@@ -0,0 +1,150 @@
+//
+// 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 <string>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <brillo/key_value_store.h>
+
+#include "update_engine/common/constants.h"
+#include "update_engine/common/hardware_interface.h"
+#include "update_engine/common/platform_constants.h"
+#include "update_engine/system_state.h"
+
+namespace {
+
+const char kLsbRelease[] = "/etc/lsb-release";
+
+const char kLsbReleaseAppIdKey[] = "CHROMEOS_RELEASE_APPID";
+const char kLsbReleaseAutoUpdateServerKey[] = "CHROMEOS_AUSERVER";
+const char kLsbReleaseBoardAppIdKey[] = "CHROMEOS_BOARD_APPID";
+const char kLsbReleaseBoardKey[] = "CHROMEOS_RELEASE_BOARD";
+const char kLsbReleaseCanaryAppIdKey[] = "CHROMEOS_CANARY_APPID";
+const char kLsbReleaseIsPowerwashAllowedKey[] = "CHROMEOS_IS_POWERWASH_ALLOWED";
+const char kLsbReleaseUpdateChannelKey[] = "CHROMEOS_RELEASE_TRACK";
+const char kLsbReleaseVersionKey[] = "CHROMEOS_RELEASE_VERSION";
+
+const char kDefaultAppId[] = "{87efface-864d-49a5-9bb3-4b050a7c227a}";
+
+// A prefix added to the path, used for testing.
+const char* root_prefix = nullptr;
+
+std::string GetStringWithDefault(const brillo::KeyValueStore& store,
+                                 const std::string& key,
+                                 const std::string& default_value) {
+  std::string result;
+  if (store.GetString(key, &result))
+    return result;
+  LOG(INFO) << "Cannot load ImageProperty " << key << ", using default value "
+            << default_value;
+  return default_value;
+}
+
+enum class LsbReleaseSource {
+  kSystem,
+  kStateful,
+};
+
+// Loads the lsb-release properties into the key-value |store| reading the file
+// from either the system image or the stateful partition as specified by
+// |source|. The loaded values are added to the store, possibly overriding
+// existing values.
+void LoadLsbRelease(LsbReleaseSource source, brillo::KeyValueStore* store) {
+  std::string path;
+  if (root_prefix)
+    path = root_prefix;
+  if (source == LsbReleaseSource::kStateful)
+    path += chromeos_update_engine::kStatefulPartition;
+  store->Load(base::FilePath(path + kLsbRelease));
+}
+
+}  // namespace
+
+namespace chromeos_update_engine {
+
+namespace test {
+void SetImagePropertiesRootPrefix(const char* test_root_prefix) {
+  root_prefix = test_root_prefix;
+}
+}  // namespace test
+
+ImageProperties LoadImageProperties(SystemState* system_state) {
+  ImageProperties result;
+
+  brillo::KeyValueStore lsb_release;
+  LoadLsbRelease(LsbReleaseSource::kSystem, &lsb_release);
+  result.current_channel = GetStringWithDefault(
+      lsb_release, kLsbReleaseUpdateChannelKey, "stable-channel");
+
+  // 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 = system_state->hardware();
+  if (!hardware->IsOfficialBuild() || !hardware->IsNormalBootMode())
+    LoadLsbRelease(LsbReleaseSource::kStateful, &lsb_release);
+
+  // The release_app_id is used as the default appid, but can be override by
+  // the board appid in the general case or the canary appid for the canary
+  // channel only.
+  std::string release_app_id =
+      GetStringWithDefault(lsb_release, kLsbReleaseAppIdKey, kDefaultAppId);
+
+  result.product_id = GetStringWithDefault(
+      lsb_release, kLsbReleaseBoardAppIdKey, release_app_id);
+  result.canary_product_id = GetStringWithDefault(
+      lsb_release, kLsbReleaseCanaryAppIdKey, release_app_id);
+  result.board = GetStringWithDefault(lsb_release, kLsbReleaseBoardKey, "");
+  result.version = GetStringWithDefault(lsb_release, kLsbReleaseVersionKey, "");
+  result.omaha_url =
+      GetStringWithDefault(lsb_release, kLsbReleaseAutoUpdateServerKey,
+                           constants::kOmahaDefaultProductionURL);
+
+  return result;
+}
+
+MutableImageProperties LoadMutableImageProperties(SystemState* system_state) {
+  MutableImageProperties result;
+  brillo::KeyValueStore lsb_release;
+  LoadLsbRelease(LsbReleaseSource::kSystem, &lsb_release);
+  LoadLsbRelease(LsbReleaseSource::kStateful, &lsb_release);
+  result.target_channel = GetStringWithDefault(
+      lsb_release, kLsbReleaseUpdateChannelKey, "stable-channel");
+  if (!lsb_release.GetBoolean(kLsbReleaseIsPowerwashAllowedKey,
+                              &result.is_powerwash_allowed))
+    result.is_powerwash_allowed = false;
+  return result;
+}
+
+bool StoreMutableImageProperties(SystemState* system_state,
+                                 const MutableImageProperties& properties) {
+  brillo::KeyValueStore lsb_release;
+  LoadLsbRelease(LsbReleaseSource::kStateful, &lsb_release);
+  lsb_release.SetString(kLsbReleaseUpdateChannelKey, properties.target_channel);
+  lsb_release.SetBoolean(kLsbReleaseIsPowerwashAllowedKey,
+                         properties.is_powerwash_allowed);
+
+  std::string root_prefix_str = root_prefix ? root_prefix : "";
+  base::FilePath path(root_prefix_str + kStatefulPartition + kLsbRelease);
+  if (!base::DirectoryExists(path.DirName()))
+    base::CreateDirectory(path.DirName());
+  return lsb_release.Save(path);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/include/debugd/dbus-constants.h b/include/debugd/dbus-constants.h
new file mode 100644
index 0000000..3427a99
--- /dev/null
+++ b/include/debugd/dbus-constants.h
@@ -0,0 +1,56 @@
+// Copyright 2015 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.
+
+#ifndef SYSTEM_API_DBUS_DEBUGD_DBUS_CONSTANTS_H_
+#define SYSTEM_API_DBUS_DEBUGD_DBUS_CONSTANTS_H_
+
+namespace debugd {
+const char kDebugdInterface[] = "org.chromium.debugd";
+const char kDebugdServicePath[] = "/org/chromium/debugd";
+const char kDebugdServiceName[] = "org.chromium.debugd";
+
+// Methods.
+const char kDumpDebugLogs[] = "DumpDebugLogs";
+const char kGetDebugLogs[] = "GetDebugLogs";
+const char kGetInterfaces[] = "GetInterfaces";
+const char kGetModemStatus[] = "GetModemStatus";
+const char kGetNetworkStatus[] = "GetNetworkStatus";
+const char kGetPerfOutput[] = "GetPerfOutput";
+const char kGetRandomPerfOutput[] = "GetRandomPerfOutput";
+const char kGetRichPerfData[] = "GetRichPerfData";
+const char kGetRoutes[] = "GetRoutes";
+const char kGetWiMaxStatus[] = "GetWiMaxStatus";
+const char kSetDebugMode[] = "SetDebugMode";
+const char kSystraceStart[] = "SystraceStart";
+const char kSystraceStop[] = "SystraceStop";
+const char kSystraceStatus[] = "SystraceStatus";
+const char kGetLog[] = "GetLog";
+const char kGetAllLogs[] = "GetAllLogs";
+const char kGetUserLogFiles[] = "GetUserLogFiles";
+const char kGetFeedbackLogs[] = "GetFeedbackLogs";
+const char kTestICMP[] = "TestICMP";
+const char kTestICMPWithOptions[] = "TestICMPWithOptions";
+const char kLogKernelTaskStates[] = "LogKernelTaskStates";
+const char kUploadCrashes[] = "UploadCrashes";
+const char kRemoveRootfsVerification[] = "RemoveRootfsVerification";
+const char kEnableChromeRemoteDebugging[] = "EnableChromeRemoteDebugging";
+const char kEnableBootFromUsb[] = "EnableBootFromUsb";
+const char kConfigureSshServer[] = "ConfigureSshServer";
+const char kSetUserPassword[] = "SetUserPassword";
+const char kEnableChromeDevFeatures[] = "EnableChromeDevFeatures";
+const char kQueryDevFeatures[] = "QueryDevFeatures";
+
+// Values.
+enum DevFeatureFlag {
+  DEV_FEATURES_DISABLED = 1 << 0,
+  DEV_FEATURE_ROOTFS_VERIFICATION_REMOVED = 1 << 1,
+  DEV_FEATURE_BOOT_FROM_USB_ENABLED = 1 << 2,
+  DEV_FEATURE_SSH_SERVER_CONFIGURED = 1 << 3,
+  DEV_FEATURE_DEV_MODE_ROOT_PASSWORD_SET = 1 << 4,
+  DEV_FEATURE_SYSTEM_ROOT_PASSWORD_SET = 1 << 5,
+  DEV_FEATURE_CHROME_REMOTE_DEBUGGING_ENABLED = 1 << 6,
+};
+}  // namespace debugd
+
+#endif  // SYSTEM_API_DBUS_DEBUGD_DBUS_CONSTANTS_H_
diff --git a/include/debugd/dbus-proxies.h b/include/debugd/dbus-proxies.h
new file mode 100644
index 0000000..a528480
--- /dev/null
+++ b/include/debugd/dbus-proxies.h
@@ -0,0 +1,2334 @@
+// Automatic generation of D-Bus interfaces:
+//  - org.chromium.debugd
+#ifndef ____CHROMEOS_DBUS_BINDING____________________BUILD_LINK_VAR_CACHE_PORTAGE_CHROMEOS_BASE_DEBUGD_CLIENT_OUT_DEFAULT_GEN_INCLUDE_DEBUGD_DBUS_PROXIES_H
+#define ____CHROMEOS_DBUS_BINDING____________________BUILD_LINK_VAR_CACHE_PORTAGE_CHROMEOS_BASE_DEBUGD_CLIENT_OUT_DEFAULT_GEN_INCLUDE_DEBUGD_DBUS_PROXIES_H
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/logging.h>
+#include <base/macros.h>
+#include <base/memory/ref_counted.h>
+#include <brillo/any.h>
+#include <brillo/dbus/dbus_method_invoker.h>
+#include <brillo/dbus/dbus_property.h>
+#include <brillo/dbus/dbus_signal_handler.h>
+#include <brillo/errors/error.h>
+#include <brillo/variant_dictionary.h>
+#include <dbus/bus.h>
+#include <dbus/message.h>
+#include <dbus/object_manager.h>
+#include <dbus/object_path.h>
+#include <dbus/object_proxy.h>
+
+namespace org {
+namespace chromium {
+
+// Abstract interface proxy for org::chromium::debugd.
+class debugdProxyInterface {
+ public:
+  virtual ~debugdProxyInterface() = default;
+
+  // Starts pinging the specified hostname with the specified options, with
+  // output directed to the given output file descriptor. The returned opaque
+  // string functions as a handle for this particular ping. Multiple pings
+  // can be running at once.
+  virtual bool PingStart(
+      const dbus::FileDescriptor& in_outfd,
+      const std::string& in_destination,
+      const brillo::VariantDictionary& in_options,
+      std::string* out_handle,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Starts pinging the specified hostname with the specified options, with
+  // output directed to the given output file descriptor. The returned opaque
+  // string functions as a handle for this particular ping. Multiple pings
+  // can be running at once.
+  virtual void PingStartAsync(
+      const dbus::FileDescriptor& in_outfd,
+      const std::string& in_destination,
+      const brillo::VariantDictionary& in_options,
+      const base::Callback<void(const std::string& /*handle*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Stops a running ping.
+  virtual bool PingStop(
+      const std::string& in_handle,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Stops a running ping.
+  virtual void PingStopAsync(
+      const std::string& in_handle,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Start system/kernel tracing.  If tracing is already enabled it is
+  // stopped first and any collected events are discarded.  The kernel
+  // must have been configured to support tracing.
+  virtual bool SystraceStart(
+      const std::string& in_categories,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Start system/kernel tracing.  If tracing is already enabled it is
+  // stopped first and any collected events are discarded.  The kernel
+  // must have been configured to support tracing.
+  virtual void SystraceStartAsync(
+      const std::string& in_categories,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Stop system/kernel tracing and write the collected event data.
+  virtual bool SystraceStop(
+      const dbus::FileDescriptor& in_outfd,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Stop system/kernel tracing and write the collected event data.
+  virtual void SystraceStopAsync(
+      const dbus::FileDescriptor& in_outfd,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Return current status for system/kernel tracing including whether it
+  // is enabled, the tracing clock, and the set of events enabled.
+  virtual bool SystraceStatus(
+      std::string* out_status,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Return current status for system/kernel tracing including whether it
+  // is enabled, the tracing clock, and the set of events enabled.
+  virtual void SystraceStatusAsync(
+      const base::Callback<void(const std::string& /*status*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool TracePathStart(
+      const dbus::FileDescriptor& in_outfd,
+      const std::string& in_destination,
+      const brillo::VariantDictionary& in_options,
+      std::string* out_handle,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void TracePathStartAsync(
+      const dbus::FileDescriptor& in_outfd,
+      const std::string& in_destination,
+      const brillo::VariantDictionary& in_options,
+      const base::Callback<void(const std::string& /*handle*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Stops a running tracepath.
+  virtual bool TracePathStop(
+      const std::string& in_handle,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Stops a running tracepath.
+  virtual void TracePathStopAsync(
+      const std::string& in_handle,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Returns the routing table.
+  virtual bool GetRoutes(
+      const brillo::VariantDictionary& in_options,
+      std::vector<std::string>* out_result,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Returns the routing table.
+  virtual void GetRoutesAsync(
+      const brillo::VariantDictionary& in_options,
+      const base::Callback<void(const std::vector<std::string>& /*result*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Returns modem information as a JSON string. See the design document for
+  // a rationale.
+  virtual bool GetModemStatus(
+      std::string* out_status,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Returns modem information as a JSON string. See the design document for
+  // a rationale.
+  virtual void GetModemStatusAsync(
+      const base::Callback<void(const std::string& /*status*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Runs the specified command through the modem serial interface and
+  // returns the output.
+  virtual bool RunModemCommand(
+      const std::string& in_command,
+      std::string* out_status,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Runs the specified command through the modem serial interface and
+  // returns the output.
+  virtual void RunModemCommandAsync(
+      const std::string& in_command,
+      const base::Callback<void(const std::string& /*status*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Returns network information as a JSON string. See the design document
+  // for a rationale.
+  virtual bool GetNetworkStatus(
+      std::string* out_status,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Returns network information as a JSON string. See the design document
+  // for a rationale.
+  virtual void GetNetworkStatusAsync(
+      const base::Callback<void(const std::string& /*status*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Returns WiMAX information as a JSON string. See the design document for
+  // a rationale.
+  virtual bool GetWiMaxStatus(
+      std::string* out_status,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Returns WiMAX information as a JSON string. See the design document for
+  // a rationale.
+  virtual void GetWiMaxStatusAsync(
+      const base::Callback<void(const std::string& /*status*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Runs system-wide perf profiling. The profile parameters are selected by
+  // perf_args.
+  virtual bool GetPerfOutput(
+      uint32_t in_duration_sec,
+      const std::vector<std::string>& in_perf_args,
+      int32_t* out_status,
+      std::vector<uint8_t>* out_perf_data,
+      std::vector<uint8_t>* out_perf_stat,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Runs system-wide perf profiling. The profile parameters are selected by
+  // perf_args.
+  virtual void GetPerfOutputAsync(
+      uint32_t in_duration_sec,
+      const std::vector<std::string>& in_perf_args,
+      const base::Callback<void(int32_t /*status*/, const std::vector<uint8_t>& /*perf_data*/, const std::vector<uint8_t>& /*perf_stat*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Runs system-wide perf profiling. It can can profile events other than
+  // cycles (example: iTLB-misses), and can collect branch profiles. It can
+  // also return raw counter values. The exact profile or counters to be
+  // collected is chosen at random and depends on what CPU is used by the
+  // system (certain CPUs do not support certain profiling modes).
+  virtual bool GetRandomPerfOutput(
+      uint32_t in_duration_sec,
+      int32_t* out_status,
+      std::vector<uint8_t>* out_perf_data,
+      std::vector<uint8_t>* out_perf_stat,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Runs system-wide perf profiling. It can can profile events other than
+  // cycles (example: iTLB-misses), and can collect branch profiles. It can
+  // also return raw counter values. The exact profile or counters to be
+  // collected is chosen at random and depends on what CPU is used by the
+  // system (certain CPUs do not support certain profiling modes).
+  virtual void GetRandomPerfOutputAsync(
+      uint32_t in_duration_sec,
+      const base::Callback<void(int32_t /*status*/, const std::vector<uint8_t>& /*perf_data*/, const std::vector<uint8_t>& /*perf_stat*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Returns perf event data. Does systemwide profiling. It can profile
+  // events other than cycles (example: iTLB-misses), and can collect branch
+  // profiles. The exact profile to be collected is chosen at random
+  // and depends on what CPU is used by the system (certain CPUs do not
+  // support certain profiling modes).
+  virtual bool GetRichPerfData(
+      uint32_t in_duration_sec,
+      std::vector<uint8_t>* out_status,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Returns perf event data. Does systemwide profiling. It can profile
+  // events other than cycles (example: iTLB-misses), and can collect branch
+  // profiles. The exact profile to be collected is chosen at random
+  // and depends on what CPU is used by the system (certain CPUs do not
+  // support certain profiling modes).
+  virtual void GetRichPerfDataAsync(
+      uint32_t in_duration_sec,
+      const base::Callback<void(const std::vector<uint8_t>& /*status*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // DEPRECATED: Use DumpDebugLogs instead.
+  // Packages up system logs into a .tar.gz and returns it over the supplied
+  // file descriptor.
+  virtual bool GetDebugLogs(
+      const dbus::FileDescriptor& in_outfd,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // DEPRECATED: Use DumpDebugLogs instead.
+  // Packages up system logs into a .tar.gz and returns it over the supplied
+  // file descriptor.
+  virtual void GetDebugLogsAsync(
+      const dbus::FileDescriptor& in_outfd,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Packages up system logs into a .tar(.gz) and returns it over the
+  // supplied file descriptor.
+  virtual bool DumpDebugLogs(
+      bool in_is_compressed,
+      const dbus::FileDescriptor& in_outfd,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Packages up system logs into a .tar(.gz) and returns it over the
+  // supplied file descriptor.
+  virtual void DumpDebugLogsAsync(
+      bool in_is_compressed,
+      const dbus::FileDescriptor& in_outfd,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Enables or disables debug mode for a specified subsystem.
+  virtual bool SetDebugMode(
+      const std::string& in_subsystem,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Enables or disables debug mode for a specified subsystem.
+  virtual void SetDebugModeAsync(
+      const std::string& in_subsystem,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Fetches the contents of a single system log, identified by name. See
+  // /src/log_tool.cc for a list of valid names.
+  virtual bool GetLog(
+      const std::string& in_log,
+      std::string* out_contents,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Fetches the contents of a single system log, identified by name. See
+  // /src/log_tool.cc for a list of valid names.
+  virtual void GetLogAsync(
+      const std::string& in_log,
+      const base::Callback<void(const std::string& /*contents*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Returns all the system logs.
+  virtual bool GetAllLogs(
+      std::map<std::string, std::string>* out_logs,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Returns all the system logs.
+  virtual void GetAllLogsAsync(
+      const base::Callback<void(const std::map<std::string, std::string>& /*logs*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Returns system logs for feedback reports.
+  virtual bool GetFeedbackLogs(
+      std::map<std::string, std::string>* out_logs,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Returns system logs for feedback reports.
+  virtual void GetFeedbackLogsAsync(
+      const base::Callback<void(const std::map<std::string, std::string>& /*logs*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Returns list of User log file names that Chrome itself must collect.
+  // These logfiles are relative to the user's profile path and must be
+  // collected separately for each user.
+  virtual bool GetUserLogFiles(
+      std::map<std::string, std::string>* out_user_log_files,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Returns list of User log file names that Chrome itself must collect.
+  // These logfiles are relative to the user's profile path and must be
+  // collected separately for each user.
+  virtual void GetUserLogFilesAsync(
+      const base::Callback<void(const std::map<std::string, std::string>& /*user_log_files*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Example method. See /doc/hacking.md.
+  virtual bool GetExample(
+      std::string* out_result,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Example method. See /doc/hacking.md.
+  virtual void GetExampleAsync(
+      const base::Callback<void(const std::string& /*result*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Returns information about network interfaces as a JSON string.
+  virtual bool GetInterfaces(
+      std::string* out_result,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Returns information about network interfaces as a JSON string.
+  virtual void GetInterfacesAsync(
+      const base::Callback<void(const std::string& /*result*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Tests ICMP connectivity to a specified host.
+  virtual bool TestICMP(
+      const std::string& in_host,
+      std::string* out_result,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Tests ICMP connectivity to a specified host.
+  virtual void TestICMPAsync(
+      const std::string& in_host,
+      const base::Callback<void(const std::string& /*result*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Tests ICMP connectivity to a specified host (with options).
+  virtual bool TestICMPWithOptions(
+      const std::string& in_host,
+      const std::map<std::string, std::string>& in_options,
+      std::string* out_result,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Tests ICMP connectivity to a specified host (with options).
+  virtual void TestICMPWithOptionsAsync(
+      const std::string& in_host,
+      const std::map<std::string, std::string>& in_options,
+      const base::Callback<void(const std::string& /*result*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Runs BatteryFirmware utility.
+  virtual bool BatteryFirmware(
+      const std::string& in_option,
+      std::string* out_result,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Runs BatteryFirmware utility.
+  virtual void BatteryFirmwareAsync(
+      const std::string& in_option,
+      const base::Callback<void(const std::string& /*result*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Runs Smartctl utility.
+  virtual bool Smartctl(
+      const std::string& in_option,
+      std::string* out_result,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Runs Smartctl utility.
+  virtual void SmartctlAsync(
+      const std::string& in_option,
+      const base::Callback<void(const std::string& /*result*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Starts running memtester.
+  virtual bool MemtesterStart(
+      const dbus::FileDescriptor& in_outfd,
+      uint32_t in_memory,
+      std::string* out_status,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Starts running memtester.
+  virtual void MemtesterStartAsync(
+      const dbus::FileDescriptor& in_outfd,
+      uint32_t in_memory,
+      const base::Callback<void(const std::string& /*status*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Stops running memtester.
+  virtual bool MemtesterStop(
+      const std::string& in_handle,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Stops running memtester.
+  virtual void MemtesterStopAsync(
+      const std::string& in_handle,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Starts running badblocks test.
+  virtual bool BadblocksStart(
+      const dbus::FileDescriptor& in_outfd,
+      std::string* out_status,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Starts running badblocks test.
+  virtual void BadblocksStartAsync(
+      const dbus::FileDescriptor& in_outfd,
+      const base::Callback<void(const std::string& /*status*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Stops running badblocks.
+  virtual bool BadblocksStop(
+      const std::string& in_handle,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Stops running badblocks.
+  virtual void BadblocksStopAsync(
+      const std::string& in_handle,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Starts a packet capture with the specified options, with diagnostic
+  // status directed to the "statfd" file descriptor and packet capture
+  // data sent to the "outfd" file descriptor.  The returned opaque string
+  // functions as a handle for this particular packet capture.  Multiple
+  // captures can be running at once.  Captures can be initiated on
+  // Ethernet-like devices or WiFi devices in "client mode" (showing only
+  // Ethernet frames) by specifying the "device" parameter (see below).
+  // By specifying a channel, the script will find or create a "monitor
+  // mode" interface if one is available and produce an "over the air"
+  // packet capture.  The name of the output packet capture file is sent
+  // to the output file descriptor.
+  virtual bool PacketCaptureStart(
+      const dbus::FileDescriptor& in_statfd,
+      const dbus::FileDescriptor& in_outfd,
+      const brillo::VariantDictionary& in_options,
+      std::string* out_handle,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Starts a packet capture with the specified options, with diagnostic
+  // status directed to the "statfd" file descriptor and packet capture
+  // data sent to the "outfd" file descriptor.  The returned opaque string
+  // functions as a handle for this particular packet capture.  Multiple
+  // captures can be running at once.  Captures can be initiated on
+  // Ethernet-like devices or WiFi devices in "client mode" (showing only
+  // Ethernet frames) by specifying the "device" parameter (see below).
+  // By specifying a channel, the script will find or create a "monitor
+  // mode" interface if one is available and produce an "over the air"
+  // packet capture.  The name of the output packet capture file is sent
+  // to the output file descriptor.
+  virtual void PacketCaptureStartAsync(
+      const dbus::FileDescriptor& in_statfd,
+      const dbus::FileDescriptor& in_outfd,
+      const brillo::VariantDictionary& in_options,
+      const base::Callback<void(const std::string& /*handle*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Stops a running packet capture.
+  virtual bool PacketCaptureStop(
+      const std::string& in_handle,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Stops a running packet capture.
+  virtual void PacketCaptureStopAsync(
+      const std::string& in_handle,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Triggers show-task-states(T) SysRq.
+  // See https://www.kernel.org/doc/Documentation/sysrq.txt.
+  virtual bool LogKernelTaskStates(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Triggers show-task-states(T) SysRq.
+  // See https://www.kernel.org/doc/Documentation/sysrq.txt.
+  virtual void LogKernelTaskStatesAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Triggers uploading of system crashes (the crash_sender program).
+  virtual bool UploadCrashes(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Triggers uploading of system crashes (the crash_sender program).
+  virtual void UploadCrashesAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Removes rootfs verification. Requires a system reboot before it will
+  // take effect. Restricted to pre-owner dev mode.
+  virtual bool RemoveRootfsVerification(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Removes rootfs verification. Requires a system reboot before it will
+  // take effect. Restricted to pre-owner dev mode.
+  virtual void RemoveRootfsVerificationAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Enables OS booting from a USB image. Restricted to pre-owner dev mode.
+  virtual bool EnableBootFromUsb(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Enables OS booting from a USB image. Restricted to pre-owner dev mode.
+  virtual void EnableBootFromUsbAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Sets up sshd to provide an SSH server immediately and on future reboots.
+  // Also installs the test SSH keys to allow access by cros tools. Requires
+  // that rootfs verification has been removed. Restricted to pre-owner dev
+  // mode.
+  virtual bool ConfigureSshServer(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Sets up sshd to provide an SSH server immediately and on future reboots.
+  // Also installs the test SSH keys to allow access by cros tools. Requires
+  // that rootfs verification has been removed. Restricted to pre-owner dev
+  // mode.
+  virtual void ConfigureSshServerAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Sets both the system and dev mode password for the indicated account.
+  // Restricted to pre-owner dev mode.
+  virtual bool SetUserPassword(
+      const std::string& in_username,
+      const std::string& in_password,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Sets both the system and dev mode password for the indicated account.
+  // Restricted to pre-owner dev mode.
+  virtual void SetUserPasswordAsync(
+      const std::string& in_username,
+      const std::string& in_password,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Sets up Chrome for remote debugging. It will take effect after a reboot
+  // and using port 9222.
+  // Requires that rootfs verification has been removed. Restricted to
+  // pre-owner dev mode.
+  virtual bool EnableChromeRemoteDebugging(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Sets up Chrome for remote debugging. It will take effect after a reboot
+  // and using port 9222.
+  // Requires that rootfs verification has been removed. Restricted to
+  // pre-owner dev mode.
+  virtual void EnableChromeRemoteDebuggingAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Convenience function to enable a predefined set of tools from the Chrome
+  // UI. Equivalent to calling these functions in order:
+  //   1. EnableBootFromUsb()
+  //   2. ConfigureSshServer()
+  //   3. SetUserPassword("root", root_password)
+  // Requires that rootfs verification has been removed. If any sub-function
+  // fails, this function will exit with an error without attempting any
+  // further configuration or rollback. Restricted to pre-owner dev mode.
+  virtual bool EnableChromeDevFeatures(
+      const std::string& in_root_password,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Convenience function to enable a predefined set of tools from the Chrome
+  // UI. Equivalent to calling these functions in order:
+  //   1. EnableBootFromUsb()
+  //   2. ConfigureSshServer()
+  //   3. SetUserPassword("root", root_password)
+  // Requires that rootfs verification has been removed. If any sub-function
+  // fails, this function will exit with an error without attempting any
+  // further configuration or rollback. Restricted to pre-owner dev mode.
+  virtual void EnableChromeDevFeaturesAsync(
+      const std::string& in_root_password,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Queries which dev features have been enabled. Each dev feature will be
+  // indicated by a bit flag in the return value. Flags are defined in the
+  // DevFeatureFlag enumeration. If the dev tools are unavailable (system is
+  // not in dev mode/pre-login state), the DEV_FEATURES_DISABLED flag will be
+  // set and the rest of the bits will always be set to 0.
+  virtual bool QueryDevFeatures(
+      int32_t* out_features,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Queries which dev features have been enabled. Each dev feature will be
+  // indicated by a bit flag in the return value. Flags are defined in the
+  // DevFeatureFlag enumeration. If the dev tools are unavailable (system is
+  // not in dev mode/pre-login state), the DEV_FEATURES_DISABLED flag will be
+  // set and the rest of the bits will always be set to 0.
+  virtual void QueryDevFeaturesAsync(
+      const base::Callback<void(int32_t /*features*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Allow uploading of device coredump files.
+  virtual bool EnableDevCoredumpUpload(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Allow uploading of device coredump files.
+  virtual void EnableDevCoredumpUploadAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Disallow uploading of device coredump files.
+  virtual bool DisableDevCoredumpUpload(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // Disallow uploading of device coredump files.
+  virtual void DisableDevCoredumpUploadAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+};
+
+}  // namespace chromium
+}  // namespace org
+
+namespace org {
+namespace chromium {
+
+// Interface proxy for org::chromium::debugd.
+class debugdProxy final : public debugdProxyInterface {
+ public:
+  debugdProxy(const scoped_refptr<dbus::Bus>& bus) :
+      bus_{bus},
+      dbus_object_proxy_{
+          bus_->GetObjectProxy(service_name_, object_path_)} {
+  }
+
+  ~debugdProxy() override {
+    bus_->RemoveObjectProxy(
+        service_name_, object_path_, base::Bind(&base::DoNothing));
+  }
+
+  void ReleaseObjectProxy(const base::Closure& callback) {
+    bus_->RemoveObjectProxy(service_name_, object_path_, callback);
+  }
+
+  const dbus::ObjectPath& GetObjectPath() const {
+    return object_path_;
+  }
+
+  dbus::ObjectProxy* GetObjectProxy() const { return dbus_object_proxy_; }
+
+  // Starts pinging the specified hostname with the specified options, with
+  // output directed to the given output file descriptor. The returned opaque
+  // string functions as a handle for this particular ping. Multiple pings
+  // can be running at once.
+  bool PingStart(
+      const dbus::FileDescriptor& in_outfd,
+      const std::string& in_destination,
+      const brillo::VariantDictionary& in_options,
+      std::string* out_handle,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "PingStart",
+        error,
+        in_outfd,
+        in_destination,
+        in_options);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_handle);
+  }
+
+  // Starts pinging the specified hostname with the specified options, with
+  // output directed to the given output file descriptor. The returned opaque
+  // string functions as a handle for this particular ping. Multiple pings
+  // can be running at once.
+  void PingStartAsync(
+      const dbus::FileDescriptor& in_outfd,
+      const std::string& in_destination,
+      const brillo::VariantDictionary& in_options,
+      const base::Callback<void(const std::string& /*handle*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "PingStart",
+        success_callback,
+        error_callback,
+        in_outfd,
+        in_destination,
+        in_options);
+  }
+
+  // Stops a running ping.
+  bool PingStop(
+      const std::string& in_handle,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "PingStop",
+        error,
+        in_handle);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // Stops a running ping.
+  void PingStopAsync(
+      const std::string& in_handle,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "PingStop",
+        success_callback,
+        error_callback,
+        in_handle);
+  }
+
+  // Start system/kernel tracing.  If tracing is already enabled it is
+  // stopped first and any collected events are discarded.  The kernel
+  // must have been configured to support tracing.
+  bool SystraceStart(
+      const std::string& in_categories,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "SystraceStart",
+        error,
+        in_categories);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // Start system/kernel tracing.  If tracing is already enabled it is
+  // stopped first and any collected events are discarded.  The kernel
+  // must have been configured to support tracing.
+  void SystraceStartAsync(
+      const std::string& in_categories,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "SystraceStart",
+        success_callback,
+        error_callback,
+        in_categories);
+  }
+
+  // Stop system/kernel tracing and write the collected event data.
+  bool SystraceStop(
+      const dbus::FileDescriptor& in_outfd,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "SystraceStop",
+        error,
+        in_outfd);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // Stop system/kernel tracing and write the collected event data.
+  void SystraceStopAsync(
+      const dbus::FileDescriptor& in_outfd,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "SystraceStop",
+        success_callback,
+        error_callback,
+        in_outfd);
+  }
+
+  // Return current status for system/kernel tracing including whether it
+  // is enabled, the tracing clock, and the set of events enabled.
+  bool SystraceStatus(
+      std::string* out_status,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "SystraceStatus",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_status);
+  }
+
+  // Return current status for system/kernel tracing including whether it
+  // is enabled, the tracing clock, and the set of events enabled.
+  void SystraceStatusAsync(
+      const base::Callback<void(const std::string& /*status*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "SystraceStatus",
+        success_callback,
+        error_callback);
+  }
+
+  bool TracePathStart(
+      const dbus::FileDescriptor& in_outfd,
+      const std::string& in_destination,
+      const brillo::VariantDictionary& in_options,
+      std::string* out_handle,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "TracePathStart",
+        error,
+        in_outfd,
+        in_destination,
+        in_options);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_handle);
+  }
+
+  void TracePathStartAsync(
+      const dbus::FileDescriptor& in_outfd,
+      const std::string& in_destination,
+      const brillo::VariantDictionary& in_options,
+      const base::Callback<void(const std::string& /*handle*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "TracePathStart",
+        success_callback,
+        error_callback,
+        in_outfd,
+        in_destination,
+        in_options);
+  }
+
+  // Stops a running tracepath.
+  bool TracePathStop(
+      const std::string& in_handle,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "TracePathStop",
+        error,
+        in_handle);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // Stops a running tracepath.
+  void TracePathStopAsync(
+      const std::string& in_handle,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "TracePathStop",
+        success_callback,
+        error_callback,
+        in_handle);
+  }
+
+  // Returns the routing table.
+  bool GetRoutes(
+      const brillo::VariantDictionary& in_options,
+      std::vector<std::string>* out_result,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetRoutes",
+        error,
+        in_options);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_result);
+  }
+
+  // Returns the routing table.
+  void GetRoutesAsync(
+      const brillo::VariantDictionary& in_options,
+      const base::Callback<void(const std::vector<std::string>& /*result*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetRoutes",
+        success_callback,
+        error_callback,
+        in_options);
+  }
+
+  // Returns modem information as a JSON string. See the design document for
+  // a rationale.
+  bool GetModemStatus(
+      std::string* out_status,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetModemStatus",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_status);
+  }
+
+  // Returns modem information as a JSON string. See the design document for
+  // a rationale.
+  void GetModemStatusAsync(
+      const base::Callback<void(const std::string& /*status*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetModemStatus",
+        success_callback,
+        error_callback);
+  }
+
+  // Runs the specified command through the modem serial interface and
+  // returns the output.
+  bool RunModemCommand(
+      const std::string& in_command,
+      std::string* out_status,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "RunModemCommand",
+        error,
+        in_command);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_status);
+  }
+
+  // Runs the specified command through the modem serial interface and
+  // returns the output.
+  void RunModemCommandAsync(
+      const std::string& in_command,
+      const base::Callback<void(const std::string& /*status*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "RunModemCommand",
+        success_callback,
+        error_callback,
+        in_command);
+  }
+
+  // Returns network information as a JSON string. See the design document
+  // for a rationale.
+  bool GetNetworkStatus(
+      std::string* out_status,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetNetworkStatus",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_status);
+  }
+
+  // Returns network information as a JSON string. See the design document
+  // for a rationale.
+  void GetNetworkStatusAsync(
+      const base::Callback<void(const std::string& /*status*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetNetworkStatus",
+        success_callback,
+        error_callback);
+  }
+
+  // Returns WiMAX information as a JSON string. See the design document for
+  // a rationale.
+  bool GetWiMaxStatus(
+      std::string* out_status,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetWiMaxStatus",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_status);
+  }
+
+  // Returns WiMAX information as a JSON string. See the design document for
+  // a rationale.
+  void GetWiMaxStatusAsync(
+      const base::Callback<void(const std::string& /*status*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetWiMaxStatus",
+        success_callback,
+        error_callback);
+  }
+
+  // Runs system-wide perf profiling. The profile parameters are selected by
+  // perf_args.
+  bool GetPerfOutput(
+      uint32_t in_duration_sec,
+      const std::vector<std::string>& in_perf_args,
+      int32_t* out_status,
+      std::vector<uint8_t>* out_perf_data,
+      std::vector<uint8_t>* out_perf_stat,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetPerfOutput",
+        error,
+        in_duration_sec,
+        in_perf_args);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_status, out_perf_data, out_perf_stat);
+  }
+
+  // Runs system-wide perf profiling. The profile parameters are selected by
+  // perf_args.
+  void GetPerfOutputAsync(
+      uint32_t in_duration_sec,
+      const std::vector<std::string>& in_perf_args,
+      const base::Callback<void(int32_t /*status*/, const std::vector<uint8_t>& /*perf_data*/, const std::vector<uint8_t>& /*perf_stat*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetPerfOutput",
+        success_callback,
+        error_callback,
+        in_duration_sec,
+        in_perf_args);
+  }
+
+  // Runs system-wide perf profiling. It can can profile events other than
+  // cycles (example: iTLB-misses), and can collect branch profiles. It can
+  // also return raw counter values. The exact profile or counters to be
+  // collected is chosen at random and depends on what CPU is used by the
+  // system (certain CPUs do not support certain profiling modes).
+  bool GetRandomPerfOutput(
+      uint32_t in_duration_sec,
+      int32_t* out_status,
+      std::vector<uint8_t>* out_perf_data,
+      std::vector<uint8_t>* out_perf_stat,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetRandomPerfOutput",
+        error,
+        in_duration_sec);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_status, out_perf_data, out_perf_stat);
+  }
+
+  // Runs system-wide perf profiling. It can can profile events other than
+  // cycles (example: iTLB-misses), and can collect branch profiles. It can
+  // also return raw counter values. The exact profile or counters to be
+  // collected is chosen at random and depends on what CPU is used by the
+  // system (certain CPUs do not support certain profiling modes).
+  void GetRandomPerfOutputAsync(
+      uint32_t in_duration_sec,
+      const base::Callback<void(int32_t /*status*/, const std::vector<uint8_t>& /*perf_data*/, const std::vector<uint8_t>& /*perf_stat*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetRandomPerfOutput",
+        success_callback,
+        error_callback,
+        in_duration_sec);
+  }
+
+  // Returns perf event data. Does systemwide profiling. It can profile
+  // events other than cycles (example: iTLB-misses), and can collect branch
+  // profiles. The exact profile to be collected is chosen at random
+  // and depends on what CPU is used by the system (certain CPUs do not
+  // support certain profiling modes).
+  bool GetRichPerfData(
+      uint32_t in_duration_sec,
+      std::vector<uint8_t>* out_status,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetRichPerfData",
+        error,
+        in_duration_sec);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_status);
+  }
+
+  // Returns perf event data. Does systemwide profiling. It can profile
+  // events other than cycles (example: iTLB-misses), and can collect branch
+  // profiles. The exact profile to be collected is chosen at random
+  // and depends on what CPU is used by the system (certain CPUs do not
+  // support certain profiling modes).
+  void GetRichPerfDataAsync(
+      uint32_t in_duration_sec,
+      const base::Callback<void(const std::vector<uint8_t>& /*status*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetRichPerfData",
+        success_callback,
+        error_callback,
+        in_duration_sec);
+  }
+
+  // DEPRECATED: Use DumpDebugLogs instead.
+  // Packages up system logs into a .tar.gz and returns it over the supplied
+  // file descriptor.
+  bool GetDebugLogs(
+      const dbus::FileDescriptor& in_outfd,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetDebugLogs",
+        error,
+        in_outfd);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // DEPRECATED: Use DumpDebugLogs instead.
+  // Packages up system logs into a .tar.gz and returns it over the supplied
+  // file descriptor.
+  void GetDebugLogsAsync(
+      const dbus::FileDescriptor& in_outfd,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetDebugLogs",
+        success_callback,
+        error_callback,
+        in_outfd);
+  }
+
+  // Packages up system logs into a .tar(.gz) and returns it over the
+  // supplied file descriptor.
+  bool DumpDebugLogs(
+      bool in_is_compressed,
+      const dbus::FileDescriptor& in_outfd,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "DumpDebugLogs",
+        error,
+        in_is_compressed,
+        in_outfd);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // Packages up system logs into a .tar(.gz) and returns it over the
+  // supplied file descriptor.
+  void DumpDebugLogsAsync(
+      bool in_is_compressed,
+      const dbus::FileDescriptor& in_outfd,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "DumpDebugLogs",
+        success_callback,
+        error_callback,
+        in_is_compressed,
+        in_outfd);
+  }
+
+  // Enables or disables debug mode for a specified subsystem.
+  bool SetDebugMode(
+      const std::string& in_subsystem,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "SetDebugMode",
+        error,
+        in_subsystem);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // Enables or disables debug mode for a specified subsystem.
+  void SetDebugModeAsync(
+      const std::string& in_subsystem,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "SetDebugMode",
+        success_callback,
+        error_callback,
+        in_subsystem);
+  }
+
+  // Fetches the contents of a single system log, identified by name. See
+  // /src/log_tool.cc for a list of valid names.
+  bool GetLog(
+      const std::string& in_log,
+      std::string* out_contents,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetLog",
+        error,
+        in_log);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_contents);
+  }
+
+  // Fetches the contents of a single system log, identified by name. See
+  // /src/log_tool.cc for a list of valid names.
+  void GetLogAsync(
+      const std::string& in_log,
+      const base::Callback<void(const std::string& /*contents*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetLog",
+        success_callback,
+        error_callback,
+        in_log);
+  }
+
+  // Returns all the system logs.
+  bool GetAllLogs(
+      std::map<std::string, std::string>* out_logs,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetAllLogs",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_logs);
+  }
+
+  // Returns all the system logs.
+  void GetAllLogsAsync(
+      const base::Callback<void(const std::map<std::string, std::string>& /*logs*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetAllLogs",
+        success_callback,
+        error_callback);
+  }
+
+  // Returns system logs for feedback reports.
+  bool GetFeedbackLogs(
+      std::map<std::string, std::string>* out_logs,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetFeedbackLogs",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_logs);
+  }
+
+  // Returns system logs for feedback reports.
+  void GetFeedbackLogsAsync(
+      const base::Callback<void(const std::map<std::string, std::string>& /*logs*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetFeedbackLogs",
+        success_callback,
+        error_callback);
+  }
+
+  // Returns list of User log file names that Chrome itself must collect.
+  // These logfiles are relative to the user's profile path and must be
+  // collected separately for each user.
+  bool GetUserLogFiles(
+      std::map<std::string, std::string>* out_user_log_files,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetUserLogFiles",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_user_log_files);
+  }
+
+  // Returns list of User log file names that Chrome itself must collect.
+  // These logfiles are relative to the user's profile path and must be
+  // collected separately for each user.
+  void GetUserLogFilesAsync(
+      const base::Callback<void(const std::map<std::string, std::string>& /*user_log_files*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetUserLogFiles",
+        success_callback,
+        error_callback);
+  }
+
+  // Example method. See /doc/hacking.md.
+  bool GetExample(
+      std::string* out_result,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetExample",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_result);
+  }
+
+  // Example method. See /doc/hacking.md.
+  void GetExampleAsync(
+      const base::Callback<void(const std::string& /*result*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetExample",
+        success_callback,
+        error_callback);
+  }
+
+  // Returns information about network interfaces as a JSON string.
+  bool GetInterfaces(
+      std::string* out_result,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetInterfaces",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_result);
+  }
+
+  // Returns information about network interfaces as a JSON string.
+  void GetInterfacesAsync(
+      const base::Callback<void(const std::string& /*result*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "GetInterfaces",
+        success_callback,
+        error_callback);
+  }
+
+  // Tests ICMP connectivity to a specified host.
+  bool TestICMP(
+      const std::string& in_host,
+      std::string* out_result,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "TestICMP",
+        error,
+        in_host);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_result);
+  }
+
+  // Tests ICMP connectivity to a specified host.
+  void TestICMPAsync(
+      const std::string& in_host,
+      const base::Callback<void(const std::string& /*result*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "TestICMP",
+        success_callback,
+        error_callback,
+        in_host);
+  }
+
+  // Tests ICMP connectivity to a specified host (with options).
+  bool TestICMPWithOptions(
+      const std::string& in_host,
+      const std::map<std::string, std::string>& in_options,
+      std::string* out_result,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "TestICMPWithOptions",
+        error,
+        in_host,
+        in_options);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_result);
+  }
+
+  // Tests ICMP connectivity to a specified host (with options).
+  void TestICMPWithOptionsAsync(
+      const std::string& in_host,
+      const std::map<std::string, std::string>& in_options,
+      const base::Callback<void(const std::string& /*result*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "TestICMPWithOptions",
+        success_callback,
+        error_callback,
+        in_host,
+        in_options);
+  }
+
+  // Runs BatteryFirmware utility.
+  bool BatteryFirmware(
+      const std::string& in_option,
+      std::string* out_result,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "BatteryFirmware",
+        error,
+        in_option);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_result);
+  }
+
+  // Runs BatteryFirmware utility.
+  void BatteryFirmwareAsync(
+      const std::string& in_option,
+      const base::Callback<void(const std::string& /*result*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "BatteryFirmware",
+        success_callback,
+        error_callback,
+        in_option);
+  }
+
+  // Runs Smartctl utility.
+  bool Smartctl(
+      const std::string& in_option,
+      std::string* out_result,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "Smartctl",
+        error,
+        in_option);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_result);
+  }
+
+  // Runs Smartctl utility.
+  void SmartctlAsync(
+      const std::string& in_option,
+      const base::Callback<void(const std::string& /*result*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "Smartctl",
+        success_callback,
+        error_callback,
+        in_option);
+  }
+
+  // Starts running memtester.
+  bool MemtesterStart(
+      const dbus::FileDescriptor& in_outfd,
+      uint32_t in_memory,
+      std::string* out_status,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "MemtesterStart",
+        error,
+        in_outfd,
+        in_memory);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_status);
+  }
+
+  // Starts running memtester.
+  void MemtesterStartAsync(
+      const dbus::FileDescriptor& in_outfd,
+      uint32_t in_memory,
+      const base::Callback<void(const std::string& /*status*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "MemtesterStart",
+        success_callback,
+        error_callback,
+        in_outfd,
+        in_memory);
+  }
+
+  // Stops running memtester.
+  bool MemtesterStop(
+      const std::string& in_handle,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "MemtesterStop",
+        error,
+        in_handle);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // Stops running memtester.
+  void MemtesterStopAsync(
+      const std::string& in_handle,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "MemtesterStop",
+        success_callback,
+        error_callback,
+        in_handle);
+  }
+
+  // Starts running badblocks test.
+  bool BadblocksStart(
+      const dbus::FileDescriptor& in_outfd,
+      std::string* out_status,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "BadblocksStart",
+        error,
+        in_outfd);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_status);
+  }
+
+  // Starts running badblocks test.
+  void BadblocksStartAsync(
+      const dbus::FileDescriptor& in_outfd,
+      const base::Callback<void(const std::string& /*status*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "BadblocksStart",
+        success_callback,
+        error_callback,
+        in_outfd);
+  }
+
+  // Stops running badblocks.
+  bool BadblocksStop(
+      const std::string& in_handle,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "BadblocksStop",
+        error,
+        in_handle);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // Stops running badblocks.
+  void BadblocksStopAsync(
+      const std::string& in_handle,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "BadblocksStop",
+        success_callback,
+        error_callback,
+        in_handle);
+  }
+
+  // Starts a packet capture with the specified options, with diagnostic
+  // status directed to the "statfd" file descriptor and packet capture
+  // data sent to the "outfd" file descriptor.  The returned opaque string
+  // functions as a handle for this particular packet capture.  Multiple
+  // captures can be running at once.  Captures can be initiated on
+  // Ethernet-like devices or WiFi devices in "client mode" (showing only
+  // Ethernet frames) by specifying the "device" parameter (see below).
+  // By specifying a channel, the script will find or create a "monitor
+  // mode" interface if one is available and produce an "over the air"
+  // packet capture.  The name of the output packet capture file is sent
+  // to the output file descriptor.
+  bool PacketCaptureStart(
+      const dbus::FileDescriptor& in_statfd,
+      const dbus::FileDescriptor& in_outfd,
+      const brillo::VariantDictionary& in_options,
+      std::string* out_handle,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "PacketCaptureStart",
+        error,
+        in_statfd,
+        in_outfd,
+        in_options);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_handle);
+  }
+
+  // Starts a packet capture with the specified options, with diagnostic
+  // status directed to the "statfd" file descriptor and packet capture
+  // data sent to the "outfd" file descriptor.  The returned opaque string
+  // functions as a handle for this particular packet capture.  Multiple
+  // captures can be running at once.  Captures can be initiated on
+  // Ethernet-like devices or WiFi devices in "client mode" (showing only
+  // Ethernet frames) by specifying the "device" parameter (see below).
+  // By specifying a channel, the script will find or create a "monitor
+  // mode" interface if one is available and produce an "over the air"
+  // packet capture.  The name of the output packet capture file is sent
+  // to the output file descriptor.
+  void PacketCaptureStartAsync(
+      const dbus::FileDescriptor& in_statfd,
+      const dbus::FileDescriptor& in_outfd,
+      const brillo::VariantDictionary& in_options,
+      const base::Callback<void(const std::string& /*handle*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "PacketCaptureStart",
+        success_callback,
+        error_callback,
+        in_statfd,
+        in_outfd,
+        in_options);
+  }
+
+  // Stops a running packet capture.
+  bool PacketCaptureStop(
+      const std::string& in_handle,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "PacketCaptureStop",
+        error,
+        in_handle);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // Stops a running packet capture.
+  void PacketCaptureStopAsync(
+      const std::string& in_handle,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "PacketCaptureStop",
+        success_callback,
+        error_callback,
+        in_handle);
+  }
+
+  // Triggers show-task-states(T) SysRq.
+  // See https://www.kernel.org/doc/Documentation/sysrq.txt.
+  bool LogKernelTaskStates(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "LogKernelTaskStates",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // Triggers show-task-states(T) SysRq.
+  // See https://www.kernel.org/doc/Documentation/sysrq.txt.
+  void LogKernelTaskStatesAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "LogKernelTaskStates",
+        success_callback,
+        error_callback);
+  }
+
+  // Triggers uploading of system crashes (the crash_sender program).
+  bool UploadCrashes(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "UploadCrashes",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // Triggers uploading of system crashes (the crash_sender program).
+  void UploadCrashesAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "UploadCrashes",
+        success_callback,
+        error_callback);
+  }
+
+  // Removes rootfs verification. Requires a system reboot before it will
+  // take effect. Restricted to pre-owner dev mode.
+  bool RemoveRootfsVerification(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "RemoveRootfsVerification",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // Removes rootfs verification. Requires a system reboot before it will
+  // take effect. Restricted to pre-owner dev mode.
+  void RemoveRootfsVerificationAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "RemoveRootfsVerification",
+        success_callback,
+        error_callback);
+  }
+
+  // Enables OS booting from a USB image. Restricted to pre-owner dev mode.
+  bool EnableBootFromUsb(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "EnableBootFromUsb",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // Enables OS booting from a USB image. Restricted to pre-owner dev mode.
+  void EnableBootFromUsbAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "EnableBootFromUsb",
+        success_callback,
+        error_callback);
+  }
+
+  // Sets up sshd to provide an SSH server immediately and on future reboots.
+  // Also installs the test SSH keys to allow access by cros tools. Requires
+  // that rootfs verification has been removed. Restricted to pre-owner dev
+  // mode.
+  bool ConfigureSshServer(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "ConfigureSshServer",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // Sets up sshd to provide an SSH server immediately and on future reboots.
+  // Also installs the test SSH keys to allow access by cros tools. Requires
+  // that rootfs verification has been removed. Restricted to pre-owner dev
+  // mode.
+  void ConfigureSshServerAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "ConfigureSshServer",
+        success_callback,
+        error_callback);
+  }
+
+  // Sets both the system and dev mode password for the indicated account.
+  // Restricted to pre-owner dev mode.
+  bool SetUserPassword(
+      const std::string& in_username,
+      const std::string& in_password,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "SetUserPassword",
+        error,
+        in_username,
+        in_password);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // Sets both the system and dev mode password for the indicated account.
+  // Restricted to pre-owner dev mode.
+  void SetUserPasswordAsync(
+      const std::string& in_username,
+      const std::string& in_password,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "SetUserPassword",
+        success_callback,
+        error_callback,
+        in_username,
+        in_password);
+  }
+
+  // Sets up Chrome for remote debugging. It will take effect after a reboot
+  // and using port 9222.
+  // Requires that rootfs verification has been removed. Restricted to
+  // pre-owner dev mode.
+  bool EnableChromeRemoteDebugging(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "EnableChromeRemoteDebugging",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // Sets up Chrome for remote debugging. It will take effect after a reboot
+  // and using port 9222.
+  // Requires that rootfs verification has been removed. Restricted to
+  // pre-owner dev mode.
+  void EnableChromeRemoteDebuggingAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "EnableChromeRemoteDebugging",
+        success_callback,
+        error_callback);
+  }
+
+  // Convenience function to enable a predefined set of tools from the Chrome
+  // UI. Equivalent to calling these functions in order:
+  //   1. EnableBootFromUsb()
+  //   2. ConfigureSshServer()
+  //   3. SetUserPassword("root", root_password)
+  // Requires that rootfs verification has been removed. If any sub-function
+  // fails, this function will exit with an error without attempting any
+  // further configuration or rollback. Restricted to pre-owner dev mode.
+  bool EnableChromeDevFeatures(
+      const std::string& in_root_password,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "EnableChromeDevFeatures",
+        error,
+        in_root_password);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // Convenience function to enable a predefined set of tools from the Chrome
+  // UI. Equivalent to calling these functions in order:
+  //   1. EnableBootFromUsb()
+  //   2. ConfigureSshServer()
+  //   3. SetUserPassword("root", root_password)
+  // Requires that rootfs verification has been removed. If any sub-function
+  // fails, this function will exit with an error without attempting any
+  // further configuration or rollback. Restricted to pre-owner dev mode.
+  void EnableChromeDevFeaturesAsync(
+      const std::string& in_root_password,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "EnableChromeDevFeatures",
+        success_callback,
+        error_callback,
+        in_root_password);
+  }
+
+  // Queries which dev features have been enabled. Each dev feature will be
+  // indicated by a bit flag in the return value. Flags are defined in the
+  // DevFeatureFlag enumeration. If the dev tools are unavailable (system is
+  // not in dev mode/pre-login state), the DEV_FEATURES_DISABLED flag will be
+  // set and the rest of the bits will always be set to 0.
+  bool QueryDevFeatures(
+      int32_t* out_features,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "QueryDevFeatures",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_features);
+  }
+
+  // Queries which dev features have been enabled. Each dev feature will be
+  // indicated by a bit flag in the return value. Flags are defined in the
+  // DevFeatureFlag enumeration. If the dev tools are unavailable (system is
+  // not in dev mode/pre-login state), the DEV_FEATURES_DISABLED flag will be
+  // set and the rest of the bits will always be set to 0.
+  void QueryDevFeaturesAsync(
+      const base::Callback<void(int32_t /*features*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "QueryDevFeatures",
+        success_callback,
+        error_callback);
+  }
+
+  // Allow uploading of device coredump files.
+  bool EnableDevCoredumpUpload(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "EnableDevCoredumpUpload",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // Allow uploading of device coredump files.
+  void EnableDevCoredumpUploadAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "EnableDevCoredumpUpload",
+        success_callback,
+        error_callback);
+  }
+
+  // Disallow uploading of device coredump files.
+  bool DisableDevCoredumpUpload(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "DisableDevCoredumpUpload",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // Disallow uploading of device coredump files.
+  void DisableDevCoredumpUploadAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.debugd",
+        "DisableDevCoredumpUpload",
+        success_callback,
+        error_callback);
+  }
+
+ private:
+  scoped_refptr<dbus::Bus> bus_;
+  const std::string service_name_{"org.chromium.debugd"};
+  const dbus::ObjectPath object_path_{"/org/chromium/debugd"};
+  dbus::ObjectProxy* dbus_object_proxy_;
+
+  DISALLOW_COPY_AND_ASSIGN(debugdProxy);
+};
+
+}  // namespace chromium
+}  // namespace org
+
+#endif  // ____CHROMEOS_DBUS_BINDING____________________BUILD_LINK_VAR_CACHE_PORTAGE_CHROMEOS_BASE_DEBUGD_CLIENT_OUT_DEFAULT_GEN_INCLUDE_DEBUGD_DBUS_PROXIES_H
diff --git a/include/debugd/dbus-proxy-mocks.h b/include/debugd/dbus-proxy-mocks.h
new file mode 100644
index 0000000..042a9fd
--- /dev/null
+++ b/include/debugd/dbus-proxy-mocks.h
@@ -0,0 +1,453 @@
+// Automatic generation of D-Bus interface mock proxies for:
+//  - org.chromium.debugd
+#ifndef ____CHROMEOS_DBUS_BINDING____________________BUILD_LINK_VAR_CACHE_PORTAGE_CHROMEOS_BASE_DEBUGD_CLIENT_OUT_DEFAULT_GEN_INCLUDE_DEBUGD_DBUS_PROXY_MOCKS_H
+#define ____CHROMEOS_DBUS_BINDING____________________BUILD_LINK_VAR_CACHE_PORTAGE_CHROMEOS_BASE_DEBUGD_CLIENT_OUT_DEFAULT_GEN_INCLUDE_DEBUGD_DBUS_PROXY_MOCKS_H
+#include <string>
+#include <vector>
+
+#include <base/callback_forward.h>
+#include <base/logging.h>
+#include <base/macros.h>
+#include <brillo/any.h>
+#include <brillo/errors/error.h>
+#include <brillo/variant_dictionary.h>
+#include <gmock/gmock.h>
+
+#include "debugd/dbus-proxies.h"
+
+namespace org {
+namespace chromium {
+
+// Mock object for debugdProxyInterface.
+class debugdProxyMock : public debugdProxyInterface {
+ public:
+  debugdProxyMock() = default;
+
+  MOCK_METHOD6(PingStart,
+               bool(const dbus::FileDescriptor& /*in_outfd*/,
+                    const std::string& /*in_destination*/,
+                    const brillo::VariantDictionary& /*in_options*/,
+                    std::string* /*out_handle*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD6(PingStartAsync,
+               void(const dbus::FileDescriptor& /*in_outfd*/,
+                    const std::string& /*in_destination*/,
+                    const brillo::VariantDictionary& /*in_options*/,
+                    const base::Callback<void(const std::string& /*handle*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(PingStop,
+               bool(const std::string& /*in_handle*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(PingStopAsync,
+               void(const std::string& /*in_handle*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(SystraceStart,
+               bool(const std::string& /*in_categories*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(SystraceStartAsync,
+               void(const std::string& /*in_categories*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(SystraceStop,
+               bool(const dbus::FileDescriptor& /*in_outfd*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(SystraceStopAsync,
+               void(const dbus::FileDescriptor& /*in_outfd*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(SystraceStatus,
+               bool(std::string* /*out_status*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(SystraceStatusAsync,
+               void(const base::Callback<void(const std::string& /*status*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD6(TracePathStart,
+               bool(const dbus::FileDescriptor& /*in_outfd*/,
+                    const std::string& /*in_destination*/,
+                    const brillo::VariantDictionary& /*in_options*/,
+                    std::string* /*out_handle*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD6(TracePathStartAsync,
+               void(const dbus::FileDescriptor& /*in_outfd*/,
+                    const std::string& /*in_destination*/,
+                    const brillo::VariantDictionary& /*in_options*/,
+                    const base::Callback<void(const std::string& /*handle*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(TracePathStop,
+               bool(const std::string& /*in_handle*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(TracePathStopAsync,
+               void(const std::string& /*in_handle*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(GetRoutes,
+               bool(const brillo::VariantDictionary& /*in_options*/,
+                    std::vector<std::string>* /*out_result*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(GetRoutesAsync,
+               void(const brillo::VariantDictionary& /*in_options*/,
+                    const base::Callback<void(const std::vector<std::string>& /*result*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetModemStatus,
+               bool(std::string* /*out_status*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetModemStatusAsync,
+               void(const base::Callback<void(const std::string& /*status*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(RunModemCommand,
+               bool(const std::string& /*in_command*/,
+                    std::string* /*out_status*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(RunModemCommandAsync,
+               void(const std::string& /*in_command*/,
+                    const base::Callback<void(const std::string& /*status*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetNetworkStatus,
+               bool(std::string* /*out_status*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetNetworkStatusAsync,
+               void(const base::Callback<void(const std::string& /*status*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetWiMaxStatus,
+               bool(std::string* /*out_status*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetWiMaxStatusAsync,
+               void(const base::Callback<void(const std::string& /*status*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD7(GetPerfOutput,
+               bool(uint32_t /*in_duration_sec*/,
+                    const std::vector<std::string>& /*in_perf_args*/,
+                    int32_t* /*out_status*/,
+                    std::vector<uint8_t>* /*out_perf_data*/,
+                    std::vector<uint8_t>* /*out_perf_stat*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD5(GetPerfOutputAsync,
+               void(uint32_t /*in_duration_sec*/,
+                    const std::vector<std::string>& /*in_perf_args*/,
+                    const base::Callback<void(int32_t /*status*/, const std::vector<uint8_t>& /*perf_data*/, const std::vector<uint8_t>& /*perf_stat*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD6(GetRandomPerfOutput,
+               bool(uint32_t /*in_duration_sec*/,
+                    int32_t* /*out_status*/,
+                    std::vector<uint8_t>* /*out_perf_data*/,
+                    std::vector<uint8_t>* /*out_perf_stat*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(GetRandomPerfOutputAsync,
+               void(uint32_t /*in_duration_sec*/,
+                    const base::Callback<void(int32_t /*status*/, const std::vector<uint8_t>& /*perf_data*/, const std::vector<uint8_t>& /*perf_stat*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(GetRichPerfData,
+               bool(uint32_t /*in_duration_sec*/,
+                    std::vector<uint8_t>* /*out_status*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(GetRichPerfDataAsync,
+               void(uint32_t /*in_duration_sec*/,
+                    const base::Callback<void(const std::vector<uint8_t>& /*status*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetDebugLogs,
+               bool(const dbus::FileDescriptor& /*in_outfd*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(GetDebugLogsAsync,
+               void(const dbus::FileDescriptor& /*in_outfd*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(DumpDebugLogs,
+               bool(bool /*in_is_compressed*/,
+                    const dbus::FileDescriptor& /*in_outfd*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD5(DumpDebugLogsAsync,
+               void(bool /*in_is_compressed*/,
+                    const dbus::FileDescriptor& /*in_outfd*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(SetDebugMode,
+               bool(const std::string& /*in_subsystem*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(SetDebugModeAsync,
+               void(const std::string& /*in_subsystem*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(GetLog,
+               bool(const std::string& /*in_log*/,
+                    std::string* /*out_contents*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(GetLogAsync,
+               void(const std::string& /*in_log*/,
+                    const base::Callback<void(const std::string& /*contents*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetAllLogs,
+               bool(std::map<std::string, std::string>* /*out_logs*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetAllLogsAsync,
+               void(const base::Callback<void(const std::map<std::string, std::string>& /*logs*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetFeedbackLogs,
+               bool(std::map<std::string, std::string>* /*out_logs*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetFeedbackLogsAsync,
+               void(const base::Callback<void(const std::map<std::string, std::string>& /*logs*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetUserLogFiles,
+               bool(std::map<std::string, std::string>* /*out_user_log_files*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetUserLogFilesAsync,
+               void(const base::Callback<void(const std::map<std::string, std::string>& /*user_log_files*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetExample,
+               bool(std::string* /*out_result*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetExampleAsync,
+               void(const base::Callback<void(const std::string& /*result*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetInterfaces,
+               bool(std::string* /*out_result*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetInterfacesAsync,
+               void(const base::Callback<void(const std::string& /*result*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(TestICMP,
+               bool(const std::string& /*in_host*/,
+                    std::string* /*out_result*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(TestICMPAsync,
+               void(const std::string& /*in_host*/,
+                    const base::Callback<void(const std::string& /*result*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD5(TestICMPWithOptions,
+               bool(const std::string& /*in_host*/,
+                    const std::map<std::string, std::string>& /*in_options*/,
+                    std::string* /*out_result*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD5(TestICMPWithOptionsAsync,
+               void(const std::string& /*in_host*/,
+                    const std::map<std::string, std::string>& /*in_options*/,
+                    const base::Callback<void(const std::string& /*result*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(BatteryFirmware,
+               bool(const std::string& /*in_option*/,
+                    std::string* /*out_result*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(BatteryFirmwareAsync,
+               void(const std::string& /*in_option*/,
+                    const base::Callback<void(const std::string& /*result*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(Smartctl,
+               bool(const std::string& /*in_option*/,
+                    std::string* /*out_result*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(SmartctlAsync,
+               void(const std::string& /*in_option*/,
+                    const base::Callback<void(const std::string& /*result*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD5(MemtesterStart,
+               bool(const dbus::FileDescriptor& /*in_outfd*/,
+                    uint32_t /*in_memory*/,
+                    std::string* /*out_status*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD5(MemtesterStartAsync,
+               void(const dbus::FileDescriptor& /*in_outfd*/,
+                    uint32_t /*in_memory*/,
+                    const base::Callback<void(const std::string& /*status*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(MemtesterStop,
+               bool(const std::string& /*in_handle*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(MemtesterStopAsync,
+               void(const std::string& /*in_handle*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(BadblocksStart,
+               bool(const dbus::FileDescriptor& /*in_outfd*/,
+                    std::string* /*out_status*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(BadblocksStartAsync,
+               void(const dbus::FileDescriptor& /*in_outfd*/,
+                    const base::Callback<void(const std::string& /*status*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(BadblocksStop,
+               bool(const std::string& /*in_handle*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(BadblocksStopAsync,
+               void(const std::string& /*in_handle*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD6(PacketCaptureStart,
+               bool(const dbus::FileDescriptor& /*in_statfd*/,
+                    const dbus::FileDescriptor& /*in_outfd*/,
+                    const brillo::VariantDictionary& /*in_options*/,
+                    std::string* /*out_handle*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD6(PacketCaptureStartAsync,
+               void(const dbus::FileDescriptor& /*in_statfd*/,
+                    const dbus::FileDescriptor& /*in_outfd*/,
+                    const brillo::VariantDictionary& /*in_options*/,
+                    const base::Callback<void(const std::string& /*handle*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(PacketCaptureStop,
+               bool(const std::string& /*in_handle*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(PacketCaptureStopAsync,
+               void(const std::string& /*in_handle*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD2(LogKernelTaskStates,
+               bool(brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(LogKernelTaskStatesAsync,
+               void(const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD2(UploadCrashes,
+               bool(brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(UploadCrashesAsync,
+               void(const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD2(RemoveRootfsVerification,
+               bool(brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(RemoveRootfsVerificationAsync,
+               void(const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD2(EnableBootFromUsb,
+               bool(brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(EnableBootFromUsbAsync,
+               void(const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD2(ConfigureSshServer,
+               bool(brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(ConfigureSshServerAsync,
+               void(const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(SetUserPassword,
+               bool(const std::string& /*in_username*/,
+                    const std::string& /*in_password*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD5(SetUserPasswordAsync,
+               void(const std::string& /*in_username*/,
+                    const std::string& /*in_password*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD2(EnableChromeRemoteDebugging,
+               bool(brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(EnableChromeRemoteDebuggingAsync,
+               void(const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(EnableChromeDevFeatures,
+               bool(const std::string& /*in_root_password*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(EnableChromeDevFeaturesAsync,
+               void(const std::string& /*in_root_password*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(QueryDevFeatures,
+               bool(int32_t* /*out_features*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(QueryDevFeaturesAsync,
+               void(const base::Callback<void(int32_t /*features*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD2(EnableDevCoredumpUpload,
+               bool(brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(EnableDevCoredumpUploadAsync,
+               void(const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD2(DisableDevCoredumpUpload,
+               bool(brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(DisableDevCoredumpUploadAsync,
+               void(const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(debugdProxyMock);
+};
+}  // namespace chromium
+}  // namespace org
+
+#endif  // ____CHROMEOS_DBUS_BINDING____________________BUILD_LINK_VAR_CACHE_PORTAGE_CHROMEOS_BASE_DEBUGD_CLIENT_OUT_DEFAULT_GEN_INCLUDE_DEBUGD_DBUS_PROXY_MOCKS_H
diff --git a/include/power_manager/dbus-constants.h b/include/power_manager/dbus-constants.h
new file mode 100644
index 0000000..fa42cb1
--- /dev/null
+++ b/include/power_manager/dbus-constants.h
@@ -0,0 +1,66 @@
+// Copyright 2015 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.
+
+#ifndef SYSTEM_API_DBUS_POWER_MANAGER_DBUS_CONSTANTS_H_
+#define SYSTEM_API_DBUS_POWER_MANAGER_DBUS_CONSTANTS_H_
+
+namespace power_manager {
+// powerd
+const char kPowerManagerInterface[] = "org.chromium.PowerManager";
+const char kPowerManagerServicePath[] = "/org/chromium/PowerManager";
+const char kPowerManagerServiceName[] = "org.chromium.PowerManager";
+// Methods exposed by powerd.
+const char kDecreaseScreenBrightnessMethod[] = "DecreaseScreenBrightness";
+const char kIncreaseScreenBrightnessMethod[] = "IncreaseScreenBrightness";
+const char kGetScreenBrightnessPercentMethod[] = "GetScreenBrightnessPercent";
+const char kSetScreenBrightnessPercentMethod[] = "SetScreenBrightnessPercent";
+const char kDecreaseKeyboardBrightnessMethod[] = "DecreaseKeyboardBrightness";
+const char kIncreaseKeyboardBrightnessMethod[] = "IncreaseKeyboardBrightness";
+const char kRequestRestartMethod[] = "RequestRestart";
+const char kRequestShutdownMethod[] = "RequestShutdown";
+const char kRequestSuspendMethod[] = "RequestSuspend";
+const char kGetPowerSupplyPropertiesMethod[] = "GetPowerSupplyProperties";
+const char kHandleUserActivityMethod[] = "HandleUserActivity";
+const char kHandleVideoActivityMethod[] = "HandleVideoActivity";
+const char kSetIsProjectingMethod[] = "SetIsProjecting";
+const char kSetPolicyMethod[] = "SetPolicy";
+const char kSetPowerSourceMethod[] = "SetPowerSource";
+const char kRegisterSuspendDelayMethod[] = "RegisterSuspendDelay";
+const char kUnregisterSuspendDelayMethod[] = "UnregisterSuspendDelay";
+const char kHandleSuspendReadinessMethod[] = "HandleSuspendReadiness";
+const char kRegisterDarkSuspendDelayMethod[] = "RegisterDarkSuspendDelay";
+const char kUnregisterDarkSuspendDelayMethod[] = "UnregisterDarkSuspendDelay";
+const char kHandleDarkSuspendReadinessMethod[] = "HandleDarkSuspendReadiness";
+const char kHandlePowerButtonAcknowledgmentMethod[] =
+    "HandlePowerButtonAcknowledgment";
+const char kRecordDarkResumeWakeReasonMethod[] = "RecordDarkResumeWakeReason";
+// Signals emitted by powerd.
+const char kBrightnessChangedSignal[] = "BrightnessChanged";
+const char kKeyboardBrightnessChangedSignal[] = "KeyboardBrightnessChanged";
+const char kPeripheralBatteryStatusSignal[] = "PeripheralBatteryStatus";
+const char kPowerSupplyPollSignal[] = "PowerSupplyPoll";
+const char kSuspendImminentSignal[] = "SuspendImminent";
+const char kDarkSuspendImminentSignal[] = "DarkSuspendImminent";
+const char kSuspendDoneSignal[] = "SuspendDone";
+const char kInputEventSignal[] = "InputEvent";
+const char kIdleActionImminentSignal[] = "IdleActionImminent";
+const char kIdleActionDeferredSignal[] = "IdleActionDeferred";
+// Values
+const int kBrightnessTransitionGradual = 1;
+const int kBrightnessTransitionInstant = 2;
+enum UserActivityType {
+  USER_ACTIVITY_OTHER = 0,
+  USER_ACTIVITY_BRIGHTNESS_UP_KEY_PRESS = 1,
+  USER_ACTIVITY_BRIGHTNESS_DOWN_KEY_PRESS = 2,
+  USER_ACTIVITY_VOLUME_UP_KEY_PRESS = 3,
+  USER_ACTIVITY_VOLUME_DOWN_KEY_PRESS = 4,
+  USER_ACTIVITY_VOLUME_MUTE_KEY_PRESS = 5,
+};
+enum RequestRestartReason {
+  REQUEST_RESTART_FOR_USER = 0,
+  REQUEST_RESTART_FOR_UPDATE = 1,
+};
+}  // namespace power_manager
+
+#endif  // SYSTEM_API_DBUS_POWER_MANAGER_DBUS_CONSTANTS_H_
diff --git a/include/power_manager/dbus-proxies.h b/include/power_manager/dbus-proxies.h
new file mode 100644
index 0000000..e66848d
--- /dev/null
+++ b/include/power_manager/dbus-proxies.h
@@ -0,0 +1,1280 @@
+// Automatic generation of D-Bus interfaces:
+//  - org.chromium.PowerManager
+#ifndef ____CHROMEOS_DBUS_BINDING____________________BUILD_LINK_TMP_PORTAGE_CHROMEOS_BASE_POWER_MANAGER_9999_WORK_BUILD_OUT_DEFAULT_GEN_INCLUDE_POWER_MANAGER_DBUS_PROXIES_H
+#define ____CHROMEOS_DBUS_BINDING____________________BUILD_LINK_TMP_PORTAGE_CHROMEOS_BASE_POWER_MANAGER_9999_WORK_BUILD_OUT_DEFAULT_GEN_INCLUDE_POWER_MANAGER_DBUS_PROXIES_H
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/logging.h>
+#include <base/macros.h>
+#include <base/memory/ref_counted.h>
+#include <brillo/any.h>
+#include <brillo/dbus/dbus_method_invoker.h>
+#include <brillo/dbus/dbus_property.h>
+#include <brillo/dbus/dbus_signal_handler.h>
+#include <brillo/errors/error.h>
+#include <brillo/variant_dictionary.h>
+#include <dbus/bus.h>
+#include <dbus/message.h>
+#include <dbus/object_manager.h>
+#include <dbus/object_path.h>
+#include <dbus/object_proxy.h>
+
+namespace org {
+namespace chromium {
+
+// Abstract interface proxy for org::chromium::PowerManager.
+class PowerManagerProxyInterface {
+ public:
+  virtual ~PowerManagerProxyInterface() = default;
+
+  virtual bool RequestShutdown(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void RequestShutdownAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |reason| arg is a power_manager::RequestRestartReason value.
+  virtual bool RequestRestart(
+      int32_t in_reason,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |reason| arg is a power_manager::RequestRestartReason value.
+  virtual void RequestRestartAsync(
+      int32_t in_reason,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |external_wakeup_count| arg is optional, and it will call two
+  // different methods in the backend. This can't be expressed in the DBus
+  // Introspection XML file.
+  virtual bool RequestSuspend(
+      uint64_t in_external_wakeup_count,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |external_wakeup_count| arg is optional, and it will call two
+  // different methods in the backend. This can't be expressed in the DBus
+  // Introspection XML file.
+  virtual void RequestSuspendAsync(
+      uint64_t in_external_wakeup_count,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool DecreaseScreenBrightness(
+      bool in_allow_off,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void DecreaseScreenBrightnessAsync(
+      bool in_allow_off,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool IncreaseScreenBrightness(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void IncreaseScreenBrightnessAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool GetScreenBrightnessPercent(
+      double* out_percent,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void GetScreenBrightnessPercentAsync(
+      const base::Callback<void(double /*percent*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |style| arg must be one of the values:
+  //   power_manager::kBrightnessTransitionGradual or
+  //   power_manager::kBrightnessTransitionInstant.
+  virtual bool SetScreenBrightnessPercent(
+      double in_percent,
+      int32_t in_style,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |style| arg must be one of the values:
+  //   power_manager::kBrightnessTransitionGradual or
+  //   power_manager::kBrightnessTransitionInstant.
+  virtual void SetScreenBrightnessPercentAsync(
+      double in_percent,
+      int32_t in_style,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool DecreaseKeyboardBrightness(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void DecreaseKeyboardBrightnessAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool IncreaseKeyboardBrightness(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void IncreaseKeyboardBrightnessAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::PowerSupplyProperties protobuf.
+  virtual bool GetPowerSupplyProperties(
+      std::vector<uint8_t>* out_serialized_proto,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::PowerSupplyProperties protobuf.
+  virtual void GetPowerSupplyPropertiesAsync(
+      const base::Callback<void(const std::vector<uint8_t>& /*serialized_proto*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool HandleVideoActivity(
+      bool in_fullscreen,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void HandleVideoActivityAsync(
+      bool in_fullscreen,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |type| arg is a power_manager::UserActivityType.
+  virtual bool HandleUserActivity(
+      int32_t in_type,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |type| arg is a power_manager::UserActivityType.
+  virtual void HandleUserActivityAsync(
+      int32_t in_type,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool SetIsProjecting(
+      bool in_is_projecting,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void SetIsProjectingAsync(
+      bool in_is_projecting,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::PowerManagementPolicy protobuf.
+  virtual bool SetPolicy(
+      const std::vector<uint8_t>& in_serialized_proto,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::PowerManagementPolicy protobuf.
+  virtual void SetPolicyAsync(
+      const std::vector<uint8_t>& in_serialized_proto,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool SetPowerSource(
+      const std::string& in_id,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void SetPowerSourceAsync(
+      const std::string& in_id,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |timestamp_internal| arg is represented as the return value of
+  // base::TimeTicks::ToInternalValue().
+  virtual bool HandlePowerButtonAcknowledgment(
+      int64_t in_timestamp_internal,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |timestamp_internal| arg is represented as the return value of
+  // base::TimeTicks::ToInternalValue().
+  virtual void HandlePowerButtonAcknowledgmentAsync(
+      int64_t in_timestamp_internal,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |serialized_request_proto| arg is a serialized
+  // power_manager::RegisterSuspendDelayRequest protobuf.
+  // The |serialized_reply_proto| arg is a serialized
+  // RegisterSuspendDelayReply protobuf.
+  virtual bool RegisterSuspendDelay(
+      const std::vector<uint8_t>& in_serialized_request_proto,
+      std::vector<uint8_t>* out_serialized_reply_proto,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |serialized_request_proto| arg is a serialized
+  // power_manager::RegisterSuspendDelayRequest protobuf.
+  // The |serialized_reply_proto| arg is a serialized
+  // RegisterSuspendDelayReply protobuf.
+  virtual void RegisterSuspendDelayAsync(
+      const std::vector<uint8_t>& in_serialized_request_proto,
+      const base::Callback<void(const std::vector<uint8_t>& /*serialized_reply_proto*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::UnregisterSuspendDelayRequest protobuf.
+  virtual bool UnregisterSuspendDelay(
+      const std::vector<uint8_t>& in_serialized_proto,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::UnregisterSuspendDelayRequest protobuf.
+  virtual void UnregisterSuspendDelayAsync(
+      const std::vector<uint8_t>& in_serialized_proto,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::SuspendReadinessInfo protobuf.
+  virtual bool HandleSuspendReadiness(
+      const std::vector<uint8_t>& in_serialized_proto,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::SuspendReadinessInfo protobuf.
+  virtual void HandleSuspendReadinessAsync(
+      const std::vector<uint8_t>& in_serialized_proto,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |serialized_request_proto| arg is a serialized
+  // power_manager::RegisterSuspendDelayRequest protobuf.
+  // The |serialized_reply_proto| arg is a serialized
+  // RegisterSuspendDelayReply protobuf.
+  virtual bool RegisterDarkSuspendDelay(
+      const std::vector<uint8_t>& in_serialized_request_proto,
+      std::vector<uint8_t>* out_serialized_reply_proto,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |serialized_request_proto| arg is a serialized
+  // power_manager::RegisterSuspendDelayRequest protobuf.
+  // The |serialized_reply_proto| arg is a serialized
+  // RegisterSuspendDelayReply protobuf.
+  virtual void RegisterDarkSuspendDelayAsync(
+      const std::vector<uint8_t>& in_serialized_request_proto,
+      const base::Callback<void(const std::vector<uint8_t>& /*serialized_reply_proto*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::UnregisterSuspendDelayRequest protobuf.
+  virtual bool UnregisterDarkSuspendDelay(
+      const std::vector<uint8_t>& in_serialized_proto,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::UnregisterSuspendDelayRequest protobuf.
+  virtual void UnregisterDarkSuspendDelayAsync(
+      const std::vector<uint8_t>& in_serialized_proto,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::SuspendReadinessInfo protobuf.
+  virtual bool HandleDarkSuspendReadiness(
+      const std::vector<uint8_t>& in_serialized_proto,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::SuspendReadinessInfo protobuf.
+  virtual void HandleDarkSuspendReadinessAsync(
+      const std::vector<uint8_t>& in_serialized_proto,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::DarkResumeWakeReason protobuf.
+  virtual bool RecordDarkResumeWakeReason(
+      const std::vector<uint8_t>& in_serialized_proto,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::DarkResumeWakeReason protobuf.
+  virtual void RecordDarkResumeWakeReasonAsync(
+      const std::vector<uint8_t>& in_serialized_proto,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void RegisterBrightnessChangedSignalHandler(
+      const base::Callback<void(int32_t,
+                                bool)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) = 0;
+
+  virtual void RegisterKeyboardBrightnessChangedSignalHandler(
+      const base::Callback<void(int32_t,
+                                bool)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) = 0;
+
+  virtual void RegisterPeripheralBatteryStatusSignalHandler(
+      const base::Callback<void(const std::vector<uint8_t>&)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) = 0;
+
+  virtual void RegisterPowerSupplyPollSignalHandler(
+      const base::Callback<void(const std::vector<uint8_t>&)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) = 0;
+
+  virtual void RegisterSuspendImminentSignalHandler(
+      const base::Callback<void(const std::vector<uint8_t>&)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) = 0;
+
+  virtual void RegisterSuspendDoneSignalHandler(
+      const base::Callback<void(const std::vector<uint8_t>&)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) = 0;
+
+  virtual void RegisterDarkSuspendImminentSignalHandler(
+      const base::Callback<void(const std::vector<uint8_t>&)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) = 0;
+
+  virtual void RegisterInputEventSignalHandler(
+      const base::Callback<void(const std::vector<uint8_t>&)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) = 0;
+
+  virtual void RegisterIdleActionImminentSignalHandler(
+      const base::Callback<void(const std::vector<uint8_t>&)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) = 0;
+
+  virtual void RegisterIdleActionDeferredSignalHandler(
+      const base::Closure& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) = 0;
+};
+
+}  // namespace chromium
+}  // namespace org
+
+namespace org {
+namespace chromium {
+
+// Interface proxy for org::chromium::PowerManager.
+class PowerManagerProxy final : public PowerManagerProxyInterface {
+ public:
+  PowerManagerProxy(const scoped_refptr<dbus::Bus>& bus) :
+      bus_{bus},
+      dbus_object_proxy_{
+          bus_->GetObjectProxy(service_name_, object_path_)} {
+  }
+
+  ~PowerManagerProxy() override {
+    bus_->RemoveObjectProxy(
+        service_name_, object_path_, base::Bind(&base::DoNothing));
+  }
+
+  void RegisterBrightnessChangedSignalHandler(
+      const base::Callback<void(int32_t,
+                                bool)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) override {
+    brillo::dbus_utils::ConnectToSignal(
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "BrightnessChanged",
+        signal_callback,
+        on_connected_callback);
+  }
+
+  void RegisterKeyboardBrightnessChangedSignalHandler(
+      const base::Callback<void(int32_t,
+                                bool)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) override {
+    brillo::dbus_utils::ConnectToSignal(
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "KeyboardBrightnessChanged",
+        signal_callback,
+        on_connected_callback);
+  }
+
+  void RegisterPeripheralBatteryStatusSignalHandler(
+      const base::Callback<void(const std::vector<uint8_t>&)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) override {
+    brillo::dbus_utils::ConnectToSignal(
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "PeripheralBatteryStatus",
+        signal_callback,
+        on_connected_callback);
+  }
+
+  void RegisterPowerSupplyPollSignalHandler(
+      const base::Callback<void(const std::vector<uint8_t>&)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) override {
+    brillo::dbus_utils::ConnectToSignal(
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "PowerSupplyPoll",
+        signal_callback,
+        on_connected_callback);
+  }
+
+  void RegisterSuspendImminentSignalHandler(
+      const base::Callback<void(const std::vector<uint8_t>&)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) override {
+    brillo::dbus_utils::ConnectToSignal(
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "SuspendImminent",
+        signal_callback,
+        on_connected_callback);
+  }
+
+  void RegisterSuspendDoneSignalHandler(
+      const base::Callback<void(const std::vector<uint8_t>&)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) override {
+    brillo::dbus_utils::ConnectToSignal(
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "SuspendDone",
+        signal_callback,
+        on_connected_callback);
+  }
+
+  void RegisterDarkSuspendImminentSignalHandler(
+      const base::Callback<void(const std::vector<uint8_t>&)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) override {
+    brillo::dbus_utils::ConnectToSignal(
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "DarkSuspendImminent",
+        signal_callback,
+        on_connected_callback);
+  }
+
+  void RegisterInputEventSignalHandler(
+      const base::Callback<void(const std::vector<uint8_t>&)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) override {
+    brillo::dbus_utils::ConnectToSignal(
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "InputEvent",
+        signal_callback,
+        on_connected_callback);
+  }
+
+  void RegisterIdleActionImminentSignalHandler(
+      const base::Callback<void(const std::vector<uint8_t>&)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) override {
+    brillo::dbus_utils::ConnectToSignal(
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "IdleActionImminent",
+        signal_callback,
+        on_connected_callback);
+  }
+
+  void RegisterIdleActionDeferredSignalHandler(
+      const base::Closure& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) override {
+    brillo::dbus_utils::ConnectToSignal(
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "IdleActionDeferred",
+        signal_callback,
+        on_connected_callback);
+  }
+
+  void ReleaseObjectProxy(const base::Closure& callback) {
+    bus_->RemoveObjectProxy(service_name_, object_path_, callback);
+  }
+
+  const dbus::ObjectPath& GetObjectPath() const {
+    return object_path_;
+  }
+
+  dbus::ObjectProxy* GetObjectProxy() const { return dbus_object_proxy_; }
+
+  bool RequestShutdown(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "RequestShutdown",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  void RequestShutdownAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "RequestShutdown",
+        success_callback,
+        error_callback);
+  }
+
+  // The |reason| arg is a power_manager::RequestRestartReason value.
+  bool RequestRestart(
+      int32_t in_reason,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "RequestRestart",
+        error,
+        in_reason);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // The |reason| arg is a power_manager::RequestRestartReason value.
+  void RequestRestartAsync(
+      int32_t in_reason,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "RequestRestart",
+        success_callback,
+        error_callback,
+        in_reason);
+  }
+
+  // The |external_wakeup_count| arg is optional, and it will call two
+  // different methods in the backend. This can't be expressed in the DBus
+  // Introspection XML file.
+  bool RequestSuspend(
+      uint64_t in_external_wakeup_count,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "RequestSuspend",
+        error,
+        in_external_wakeup_count);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // The |external_wakeup_count| arg is optional, and it will call two
+  // different methods in the backend. This can't be expressed in the DBus
+  // Introspection XML file.
+  void RequestSuspendAsync(
+      uint64_t in_external_wakeup_count,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "RequestSuspend",
+        success_callback,
+        error_callback,
+        in_external_wakeup_count);
+  }
+
+  bool DecreaseScreenBrightness(
+      bool in_allow_off,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "DecreaseScreenBrightness",
+        error,
+        in_allow_off);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  void DecreaseScreenBrightnessAsync(
+      bool in_allow_off,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "DecreaseScreenBrightness",
+        success_callback,
+        error_callback,
+        in_allow_off);
+  }
+
+  bool IncreaseScreenBrightness(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "IncreaseScreenBrightness",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  void IncreaseScreenBrightnessAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "IncreaseScreenBrightness",
+        success_callback,
+        error_callback);
+  }
+
+  bool GetScreenBrightnessPercent(
+      double* out_percent,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "GetScreenBrightnessPercent",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_percent);
+  }
+
+  void GetScreenBrightnessPercentAsync(
+      const base::Callback<void(double /*percent*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "GetScreenBrightnessPercent",
+        success_callback,
+        error_callback);
+  }
+
+  // The |style| arg must be one of the values:
+  //   power_manager::kBrightnessTransitionGradual or
+  //   power_manager::kBrightnessTransitionInstant.
+  bool SetScreenBrightnessPercent(
+      double in_percent,
+      int32_t in_style,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "SetScreenBrightnessPercent",
+        error,
+        in_percent,
+        in_style);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // The |style| arg must be one of the values:
+  //   power_manager::kBrightnessTransitionGradual or
+  //   power_manager::kBrightnessTransitionInstant.
+  void SetScreenBrightnessPercentAsync(
+      double in_percent,
+      int32_t in_style,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "SetScreenBrightnessPercent",
+        success_callback,
+        error_callback,
+        in_percent,
+        in_style);
+  }
+
+  bool DecreaseKeyboardBrightness(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "DecreaseKeyboardBrightness",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  void DecreaseKeyboardBrightnessAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "DecreaseKeyboardBrightness",
+        success_callback,
+        error_callback);
+  }
+
+  bool IncreaseKeyboardBrightness(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "IncreaseKeyboardBrightness",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  void IncreaseKeyboardBrightnessAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "IncreaseKeyboardBrightness",
+        success_callback,
+        error_callback);
+  }
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::PowerSupplyProperties protobuf.
+  bool GetPowerSupplyProperties(
+      std::vector<uint8_t>* out_serialized_proto,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "GetPowerSupplyProperties",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_serialized_proto);
+  }
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::PowerSupplyProperties protobuf.
+  void GetPowerSupplyPropertiesAsync(
+      const base::Callback<void(const std::vector<uint8_t>& /*serialized_proto*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "GetPowerSupplyProperties",
+        success_callback,
+        error_callback);
+  }
+
+  bool HandleVideoActivity(
+      bool in_fullscreen,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "HandleVideoActivity",
+        error,
+        in_fullscreen);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  void HandleVideoActivityAsync(
+      bool in_fullscreen,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "HandleVideoActivity",
+        success_callback,
+        error_callback,
+        in_fullscreen);
+  }
+
+  // The |type| arg is a power_manager::UserActivityType.
+  bool HandleUserActivity(
+      int32_t in_type,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "HandleUserActivity",
+        error,
+        in_type);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // The |type| arg is a power_manager::UserActivityType.
+  void HandleUserActivityAsync(
+      int32_t in_type,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "HandleUserActivity",
+        success_callback,
+        error_callback,
+        in_type);
+  }
+
+  bool SetIsProjecting(
+      bool in_is_projecting,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "SetIsProjecting",
+        error,
+        in_is_projecting);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  void SetIsProjectingAsync(
+      bool in_is_projecting,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "SetIsProjecting",
+        success_callback,
+        error_callback,
+        in_is_projecting);
+  }
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::PowerManagementPolicy protobuf.
+  bool SetPolicy(
+      const std::vector<uint8_t>& in_serialized_proto,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "SetPolicy",
+        error,
+        in_serialized_proto);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::PowerManagementPolicy protobuf.
+  void SetPolicyAsync(
+      const std::vector<uint8_t>& in_serialized_proto,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "SetPolicy",
+        success_callback,
+        error_callback,
+        in_serialized_proto);
+  }
+
+  bool SetPowerSource(
+      const std::string& in_id,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "SetPowerSource",
+        error,
+        in_id);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  void SetPowerSourceAsync(
+      const std::string& in_id,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "SetPowerSource",
+        success_callback,
+        error_callback,
+        in_id);
+  }
+
+  // The |timestamp_internal| arg is represented as the return value of
+  // base::TimeTicks::ToInternalValue().
+  bool HandlePowerButtonAcknowledgment(
+      int64_t in_timestamp_internal,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "HandlePowerButtonAcknowledgment",
+        error,
+        in_timestamp_internal);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // The |timestamp_internal| arg is represented as the return value of
+  // base::TimeTicks::ToInternalValue().
+  void HandlePowerButtonAcknowledgmentAsync(
+      int64_t in_timestamp_internal,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "HandlePowerButtonAcknowledgment",
+        success_callback,
+        error_callback,
+        in_timestamp_internal);
+  }
+
+  // The |serialized_request_proto| arg is a serialized
+  // power_manager::RegisterSuspendDelayRequest protobuf.
+  // The |serialized_reply_proto| arg is a serialized
+  // RegisterSuspendDelayReply protobuf.
+  bool RegisterSuspendDelay(
+      const std::vector<uint8_t>& in_serialized_request_proto,
+      std::vector<uint8_t>* out_serialized_reply_proto,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "RegisterSuspendDelay",
+        error,
+        in_serialized_request_proto);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_serialized_reply_proto);
+  }
+
+  // The |serialized_request_proto| arg is a serialized
+  // power_manager::RegisterSuspendDelayRequest protobuf.
+  // The |serialized_reply_proto| arg is a serialized
+  // RegisterSuspendDelayReply protobuf.
+  void RegisterSuspendDelayAsync(
+      const std::vector<uint8_t>& in_serialized_request_proto,
+      const base::Callback<void(const std::vector<uint8_t>& /*serialized_reply_proto*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "RegisterSuspendDelay",
+        success_callback,
+        error_callback,
+        in_serialized_request_proto);
+  }
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::UnregisterSuspendDelayRequest protobuf.
+  bool UnregisterSuspendDelay(
+      const std::vector<uint8_t>& in_serialized_proto,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "UnregisterSuspendDelay",
+        error,
+        in_serialized_proto);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::UnregisterSuspendDelayRequest protobuf.
+  void UnregisterSuspendDelayAsync(
+      const std::vector<uint8_t>& in_serialized_proto,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "UnregisterSuspendDelay",
+        success_callback,
+        error_callback,
+        in_serialized_proto);
+  }
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::SuspendReadinessInfo protobuf.
+  bool HandleSuspendReadiness(
+      const std::vector<uint8_t>& in_serialized_proto,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "HandleSuspendReadiness",
+        error,
+        in_serialized_proto);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::SuspendReadinessInfo protobuf.
+  void HandleSuspendReadinessAsync(
+      const std::vector<uint8_t>& in_serialized_proto,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "HandleSuspendReadiness",
+        success_callback,
+        error_callback,
+        in_serialized_proto);
+  }
+
+  // The |serialized_request_proto| arg is a serialized
+  // power_manager::RegisterSuspendDelayRequest protobuf.
+  // The |serialized_reply_proto| arg is a serialized
+  // RegisterSuspendDelayReply protobuf.
+  bool RegisterDarkSuspendDelay(
+      const std::vector<uint8_t>& in_serialized_request_proto,
+      std::vector<uint8_t>* out_serialized_reply_proto,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "RegisterDarkSuspendDelay",
+        error,
+        in_serialized_request_proto);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_serialized_reply_proto);
+  }
+
+  // The |serialized_request_proto| arg is a serialized
+  // power_manager::RegisterSuspendDelayRequest protobuf.
+  // The |serialized_reply_proto| arg is a serialized
+  // RegisterSuspendDelayReply protobuf.
+  void RegisterDarkSuspendDelayAsync(
+      const std::vector<uint8_t>& in_serialized_request_proto,
+      const base::Callback<void(const std::vector<uint8_t>& /*serialized_reply_proto*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "RegisterDarkSuspendDelay",
+        success_callback,
+        error_callback,
+        in_serialized_request_proto);
+  }
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::UnregisterSuspendDelayRequest protobuf.
+  bool UnregisterDarkSuspendDelay(
+      const std::vector<uint8_t>& in_serialized_proto,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "UnregisterDarkSuspendDelay",
+        error,
+        in_serialized_proto);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::UnregisterSuspendDelayRequest protobuf.
+  void UnregisterDarkSuspendDelayAsync(
+      const std::vector<uint8_t>& in_serialized_proto,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "UnregisterDarkSuspendDelay",
+        success_callback,
+        error_callback,
+        in_serialized_proto);
+  }
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::SuspendReadinessInfo protobuf.
+  bool HandleDarkSuspendReadiness(
+      const std::vector<uint8_t>& in_serialized_proto,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "HandleDarkSuspendReadiness",
+        error,
+        in_serialized_proto);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::SuspendReadinessInfo protobuf.
+  void HandleDarkSuspendReadinessAsync(
+      const std::vector<uint8_t>& in_serialized_proto,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "HandleDarkSuspendReadiness",
+        success_callback,
+        error_callback,
+        in_serialized_proto);
+  }
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::DarkResumeWakeReason protobuf.
+  bool RecordDarkResumeWakeReason(
+      const std::vector<uint8_t>& in_serialized_proto,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "RecordDarkResumeWakeReason",
+        error,
+        in_serialized_proto);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  // The |serialized_proto| arg is a serialized
+  // power_manager::DarkResumeWakeReason protobuf.
+  void RecordDarkResumeWakeReasonAsync(
+      const std::vector<uint8_t>& in_serialized_proto,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.PowerManager",
+        "RecordDarkResumeWakeReason",
+        success_callback,
+        error_callback,
+        in_serialized_proto);
+  }
+
+ private:
+  scoped_refptr<dbus::Bus> bus_;
+  const std::string service_name_{"org.chromium.PowerManager"};
+  const dbus::ObjectPath object_path_{"/org/chromium/PowerManager"};
+  dbus::ObjectProxy* dbus_object_proxy_;
+
+  DISALLOW_COPY_AND_ASSIGN(PowerManagerProxy);
+};
+
+}  // namespace chromium
+}  // namespace org
+
+#endif  // ____CHROMEOS_DBUS_BINDING____________________BUILD_LINK_TMP_PORTAGE_CHROMEOS_BASE_POWER_MANAGER_9999_WORK_BUILD_OUT_DEFAULT_GEN_INCLUDE_POWER_MANAGER_DBUS_PROXIES_H
diff --git a/include/power_manager/dbus-proxy-mocks.h b/include/power_manager/dbus-proxy-mocks.h
new file mode 100644
index 0000000..d4e3dd0
--- /dev/null
+++ b/include/power_manager/dbus-proxy-mocks.h
@@ -0,0 +1,266 @@
+// Automatic generation of D-Bus interface mock proxies for:
+//  - org.chromium.PowerManager
+#ifndef ____CHROMEOS_DBUS_BINDING____________________BUILD_LINK_TMP_PORTAGE_CHROMEOS_BASE_POWER_MANAGER_9999_WORK_BUILD_OUT_DEFAULT_GEN_INCLUDE_POWER_MANAGER_DBUS_PROXY_MOCKS_H
+#define ____CHROMEOS_DBUS_BINDING____________________BUILD_LINK_TMP_PORTAGE_CHROMEOS_BASE_POWER_MANAGER_9999_WORK_BUILD_OUT_DEFAULT_GEN_INCLUDE_POWER_MANAGER_DBUS_PROXY_MOCKS_H
+#include <string>
+#include <vector>
+
+#include <base/callback_forward.h>
+#include <base/logging.h>
+#include <base/macros.h>
+#include <brillo/any.h>
+#include <brillo/errors/error.h>
+#include <brillo/variant_dictionary.h>
+#include <gmock/gmock.h>
+
+#include "power_manager/dbus-proxies.h"
+
+namespace org {
+namespace chromium {
+
+// Mock object for PowerManagerProxyInterface.
+class PowerManagerProxyMock : public PowerManagerProxyInterface {
+ public:
+  PowerManagerProxyMock() = default;
+
+  MOCK_METHOD2(RequestShutdown,
+               bool(brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(RequestShutdownAsync,
+               void(const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(RequestRestart,
+               bool(int32_t /*in_reason*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(RequestRestartAsync,
+               void(int32_t /*in_reason*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(RequestSuspend,
+               bool(uint64_t /*in_external_wakeup_count*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(RequestSuspendAsync,
+               void(uint64_t /*in_external_wakeup_count*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(DecreaseScreenBrightness,
+               bool(bool /*in_allow_off*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(DecreaseScreenBrightnessAsync,
+               void(bool /*in_allow_off*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD2(IncreaseScreenBrightness,
+               bool(brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(IncreaseScreenBrightnessAsync,
+               void(const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetScreenBrightnessPercent,
+               bool(double* /*out_percent*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetScreenBrightnessPercentAsync,
+               void(const base::Callback<void(double /*percent*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(SetScreenBrightnessPercent,
+               bool(double /*in_percent*/,
+                    int32_t /*in_style*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD5(SetScreenBrightnessPercentAsync,
+               void(double /*in_percent*/,
+                    int32_t /*in_style*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD2(DecreaseKeyboardBrightness,
+               bool(brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(DecreaseKeyboardBrightnessAsync,
+               void(const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD2(IncreaseKeyboardBrightness,
+               bool(brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(IncreaseKeyboardBrightnessAsync,
+               void(const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetPowerSupplyProperties,
+               bool(std::vector<uint8_t>* /*out_serialized_proto*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetPowerSupplyPropertiesAsync,
+               void(const base::Callback<void(const std::vector<uint8_t>& /*serialized_proto*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(HandleVideoActivity,
+               bool(bool /*in_fullscreen*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(HandleVideoActivityAsync,
+               void(bool /*in_fullscreen*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(HandleUserActivity,
+               bool(int32_t /*in_type*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(HandleUserActivityAsync,
+               void(int32_t /*in_type*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(SetIsProjecting,
+               bool(bool /*in_is_projecting*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(SetIsProjectingAsync,
+               void(bool /*in_is_projecting*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(SetPolicy,
+               bool(const std::vector<uint8_t>& /*in_serialized_proto*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(SetPolicyAsync,
+               void(const std::vector<uint8_t>& /*in_serialized_proto*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(SetPowerSource,
+               bool(const std::string& /*in_id*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(SetPowerSourceAsync,
+               void(const std::string& /*in_id*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(HandlePowerButtonAcknowledgment,
+               bool(int64_t /*in_timestamp_internal*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(HandlePowerButtonAcknowledgmentAsync,
+               void(int64_t /*in_timestamp_internal*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(RegisterSuspendDelay,
+               bool(const std::vector<uint8_t>& /*in_serialized_request_proto*/,
+                    std::vector<uint8_t>* /*out_serialized_reply_proto*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(RegisterSuspendDelayAsync,
+               void(const std::vector<uint8_t>& /*in_serialized_request_proto*/,
+                    const base::Callback<void(const std::vector<uint8_t>& /*serialized_reply_proto*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(UnregisterSuspendDelay,
+               bool(const std::vector<uint8_t>& /*in_serialized_proto*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(UnregisterSuspendDelayAsync,
+               void(const std::vector<uint8_t>& /*in_serialized_proto*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(HandleSuspendReadiness,
+               bool(const std::vector<uint8_t>& /*in_serialized_proto*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(HandleSuspendReadinessAsync,
+               void(const std::vector<uint8_t>& /*in_serialized_proto*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(RegisterDarkSuspendDelay,
+               bool(const std::vector<uint8_t>& /*in_serialized_request_proto*/,
+                    std::vector<uint8_t>* /*out_serialized_reply_proto*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(RegisterDarkSuspendDelayAsync,
+               void(const std::vector<uint8_t>& /*in_serialized_request_proto*/,
+                    const base::Callback<void(const std::vector<uint8_t>& /*serialized_reply_proto*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(UnregisterDarkSuspendDelay,
+               bool(const std::vector<uint8_t>& /*in_serialized_proto*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(UnregisterDarkSuspendDelayAsync,
+               void(const std::vector<uint8_t>& /*in_serialized_proto*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(HandleDarkSuspendReadiness,
+               bool(const std::vector<uint8_t>& /*in_serialized_proto*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(HandleDarkSuspendReadinessAsync,
+               void(const std::vector<uint8_t>& /*in_serialized_proto*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(RecordDarkResumeWakeReason,
+               bool(const std::vector<uint8_t>& /*in_serialized_proto*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(RecordDarkResumeWakeReasonAsync,
+               void(const std::vector<uint8_t>& /*in_serialized_proto*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD2(RegisterBrightnessChangedSignalHandler,
+               void(const base::Callback<void(int32_t,
+                                              bool)>& /*signal_callback*/,
+                    dbus::ObjectProxy::OnConnectedCallback /*on_connected_callback*/));
+  MOCK_METHOD2(RegisterKeyboardBrightnessChangedSignalHandler,
+               void(const base::Callback<void(int32_t,
+                                              bool)>& /*signal_callback*/,
+                    dbus::ObjectProxy::OnConnectedCallback /*on_connected_callback*/));
+  MOCK_METHOD2(RegisterPeripheralBatteryStatusSignalHandler,
+               void(const base::Callback<void(const std::vector<uint8_t>&)>& /*signal_callback*/,
+                    dbus::ObjectProxy::OnConnectedCallback /*on_connected_callback*/));
+  MOCK_METHOD2(RegisterPowerSupplyPollSignalHandler,
+               void(const base::Callback<void(const std::vector<uint8_t>&)>& /*signal_callback*/,
+                    dbus::ObjectProxy::OnConnectedCallback /*on_connected_callback*/));
+  MOCK_METHOD2(RegisterSuspendImminentSignalHandler,
+               void(const base::Callback<void(const std::vector<uint8_t>&)>& /*signal_callback*/,
+                    dbus::ObjectProxy::OnConnectedCallback /*on_connected_callback*/));
+  MOCK_METHOD2(RegisterSuspendDoneSignalHandler,
+               void(const base::Callback<void(const std::vector<uint8_t>&)>& /*signal_callback*/,
+                    dbus::ObjectProxy::OnConnectedCallback /*on_connected_callback*/));
+  MOCK_METHOD2(RegisterDarkSuspendImminentSignalHandler,
+               void(const base::Callback<void(const std::vector<uint8_t>&)>& /*signal_callback*/,
+                    dbus::ObjectProxy::OnConnectedCallback /*on_connected_callback*/));
+  MOCK_METHOD2(RegisterInputEventSignalHandler,
+               void(const base::Callback<void(const std::vector<uint8_t>&)>& /*signal_callback*/,
+                    dbus::ObjectProxy::OnConnectedCallback /*on_connected_callback*/));
+  MOCK_METHOD2(RegisterIdleActionImminentSignalHandler,
+               void(const base::Callback<void(const std::vector<uint8_t>&)>& /*signal_callback*/,
+                    dbus::ObjectProxy::OnConnectedCallback /*on_connected_callback*/));
+  MOCK_METHOD2(RegisterIdleActionDeferredSignalHandler,
+               void(const base::Closure& /*signal_callback*/,
+                    dbus::ObjectProxy::OnConnectedCallback /*on_connected_callback*/));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PowerManagerProxyMock);
+};
+}  // namespace chromium
+}  // namespace org
+
+#endif  // ____CHROMEOS_DBUS_BINDING____________________BUILD_LINK_TMP_PORTAGE_CHROMEOS_BASE_POWER_MANAGER_9999_WORK_BUILD_OUT_DEFAULT_GEN_INCLUDE_POWER_MANAGER_DBUS_PROXY_MOCKS_H
diff --git a/include/session_manager/dbus-proxies.h b/include/session_manager/dbus-proxies.h
new file mode 100644
index 0000000..2ca0128
--- /dev/null
+++ b/include/session_manager/dbus-proxies.h
@@ -0,0 +1,1065 @@
+// Automatic generation of D-Bus interfaces:
+//  - org.chromium.SessionManagerInterface
+#ifndef ____CHROMEOS_DBUS_BINDING____________________BUILD_LINK_VAR_CACHE_PORTAGE_CHROMEOS_BASE_CHROMEOS_LOGIN_OUT_DEFAULT_GEN_INCLUDE_SESSION_MANAGER_DBUS_PROXIES_H
+#define ____CHROMEOS_DBUS_BINDING____________________BUILD_LINK_VAR_CACHE_PORTAGE_CHROMEOS_BASE_CHROMEOS_LOGIN_OUT_DEFAULT_GEN_INCLUDE_SESSION_MANAGER_DBUS_PROXIES_H
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/logging.h>
+#include <base/macros.h>
+#include <base/memory/ref_counted.h>
+#include <brillo/any.h>
+#include <brillo/dbus/dbus_method_invoker.h>
+#include <brillo/dbus/dbus_property.h>
+#include <brillo/dbus/dbus_signal_handler.h>
+#include <brillo/errors/error.h>
+#include <brillo/variant_dictionary.h>
+#include <dbus/bus.h>
+#include <dbus/message.h>
+#include <dbus/object_manager.h>
+#include <dbus/object_path.h>
+#include <dbus/object_proxy.h>
+
+namespace org {
+namespace chromium {
+
+// Abstract interface proxy for org::chromium::SessionManagerInterface.
+class SessionManagerInterfaceProxyInterface {
+ public:
+  virtual ~SessionManagerInterfaceProxyInterface() = default;
+
+  virtual bool EmitLoginPromptVisible(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void EmitLoginPromptVisibleAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool EnableChromeTesting(
+      bool in_force_relaunch,
+      const std::vector<std::string>& in_extra_arguments,
+      std::string* out_filepath,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void EnableChromeTestingAsync(
+      bool in_force_relaunch,
+      const std::vector<std::string>& in_extra_arguments,
+      const base::Callback<void(const std::string& /*filepath*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool StartSession(
+      const std::string& in_email_address,
+      const std::string& in_unique_identifier,
+      bool* out_done,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void StartSessionAsync(
+      const std::string& in_email_address,
+      const std::string& in_unique_identifier,
+      const base::Callback<void(bool /*done*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool StopSession(
+      const std::string& in_unique_identifier,
+      bool* out_done,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void StopSessionAsync(
+      const std::string& in_unique_identifier,
+      const base::Callback<void(bool /*done*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool StorePolicy(
+      const std::vector<uint8_t>& in_policy_blob,
+      bool* out_done,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void StorePolicyAsync(
+      const std::vector<uint8_t>& in_policy_blob,
+      const base::Callback<void(bool /*done*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool RetrievePolicy(
+      std::vector<uint8_t>* out_policy_blob,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void RetrievePolicyAsync(
+      const base::Callback<void(const std::vector<uint8_t>& /*policy_blob*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool StorePolicyForUser(
+      const std::string& in_user_email,
+      const std::vector<uint8_t>& in_policy_blob,
+      bool* out_done,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void StorePolicyForUserAsync(
+      const std::string& in_user_email,
+      const std::vector<uint8_t>& in_policy_blob,
+      const base::Callback<void(bool /*done*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool RetrievePolicyForUser(
+      const std::string& in_user_email,
+      std::vector<uint8_t>* out_policy_blob,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void RetrievePolicyForUserAsync(
+      const std::string& in_user_email,
+      const base::Callback<void(const std::vector<uint8_t>& /*policy_blob*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool StoreDeviceLocalAccountPolicy(
+      const std::string& in_account_id,
+      const std::vector<uint8_t>& in_policy_blob,
+      bool* out_done,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void StoreDeviceLocalAccountPolicyAsync(
+      const std::string& in_account_id,
+      const std::vector<uint8_t>& in_policy_blob,
+      const base::Callback<void(bool /*done*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool RetrieveDeviceLocalAccountPolicy(
+      const std::string& in_account_id,
+      std::vector<uint8_t>* out_policy_blob,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void RetrieveDeviceLocalAccountPolicyAsync(
+      const std::string& in_account_id,
+      const base::Callback<void(const std::vector<uint8_t>& /*policy_blob*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool RetrieveSessionState(
+      std::string* out_state,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void RetrieveSessionStateAsync(
+      const base::Callback<void(const std::string& /*state*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool RetrieveActiveSessions(
+      std::map<std::string, std::string>* out_sessions,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void RetrieveActiveSessionsAsync(
+      const base::Callback<void(const std::map<std::string, std::string>& /*sessions*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool HandleSupervisedUserCreationStarting(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void HandleSupervisedUserCreationStartingAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool HandleSupervisedUserCreationFinished(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void HandleSupervisedUserCreationFinishedAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool LockScreen(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void LockScreenAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool HandleLockScreenShown(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void HandleLockScreenShownAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool HandleLockScreenDismissed(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void HandleLockScreenDismissedAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool RestartJob(
+      const dbus::FileDescriptor& in_cred_fd,
+      const std::vector<std::string>& in_argv,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void RestartJobAsync(
+      const dbus::FileDescriptor& in_cred_fd,
+      const std::vector<std::string>& in_argv,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool StartDeviceWipe(
+      bool* out_done,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void StartDeviceWipeAsync(
+      const base::Callback<void(bool /*done*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool SetFlagsForUser(
+      const std::string& in_user_email,
+      const std::vector<std::string>& in_flags,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void SetFlagsForUserAsync(
+      const std::string& in_user_email,
+      const std::vector<std::string>& in_flags,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool GetServerBackedStateKeys(
+      std::vector<std::vector<uint8_t>>* out_state_keys,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void GetServerBackedStateKeysAsync(
+      const base::Callback<void(const std::vector<std::vector<uint8_t>>& /*state_keys*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual bool InitMachineInfo(
+      const std::string& in_data,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void InitMachineInfoAsync(
+      const std::string& in_data,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+  virtual void RegisterLoginPromptVisibleSignalHandler(
+      const base::Closure& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) = 0;
+
+  virtual void RegisterSessionStateChangedSignalHandler(
+      const base::Callback<void(const std::string&)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) = 0;
+
+  virtual void RegisterSetOwnerKeyCompleteSignalHandler(
+      const base::Callback<void(const std::string&)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) = 0;
+
+  virtual void RegisterPropertyChangeCompleteSignalHandler(
+      const base::Callback<void(const std::string&)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) = 0;
+
+  virtual void RegisterScreenIsLockedSignalHandler(
+      const base::Closure& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) = 0;
+
+  virtual void RegisterScreenIsUnlockedSignalHandler(
+      const base::Closure& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) = 0;
+};
+
+}  // namespace chromium
+}  // namespace org
+
+namespace org {
+namespace chromium {
+
+// Interface proxy for org::chromium::SessionManagerInterface.
+class SessionManagerInterfaceProxy final : public SessionManagerInterfaceProxyInterface {
+ public:
+  SessionManagerInterfaceProxy(const scoped_refptr<dbus::Bus>& bus) :
+      bus_{bus},
+      dbus_object_proxy_{
+          bus_->GetObjectProxy(service_name_, object_path_)} {
+  }
+
+  ~SessionManagerInterfaceProxy() override {
+    bus_->RemoveObjectProxy(
+        service_name_, object_path_, base::Bind(&base::DoNothing));
+  }
+
+  void RegisterLoginPromptVisibleSignalHandler(
+      const base::Closure& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) override {
+    brillo::dbus_utils::ConnectToSignal(
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "LoginPromptVisible",
+        signal_callback,
+        on_connected_callback);
+  }
+
+  void RegisterSessionStateChangedSignalHandler(
+      const base::Callback<void(const std::string&)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) override {
+    brillo::dbus_utils::ConnectToSignal(
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "SessionStateChanged",
+        signal_callback,
+        on_connected_callback);
+  }
+
+  void RegisterSetOwnerKeyCompleteSignalHandler(
+      const base::Callback<void(const std::string&)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) override {
+    brillo::dbus_utils::ConnectToSignal(
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "SetOwnerKeyComplete",
+        signal_callback,
+        on_connected_callback);
+  }
+
+  void RegisterPropertyChangeCompleteSignalHandler(
+      const base::Callback<void(const std::string&)>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) override {
+    brillo::dbus_utils::ConnectToSignal(
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "PropertyChangeComplete",
+        signal_callback,
+        on_connected_callback);
+  }
+
+  void RegisterScreenIsLockedSignalHandler(
+      const base::Closure& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) override {
+    brillo::dbus_utils::ConnectToSignal(
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "ScreenIsLocked",
+        signal_callback,
+        on_connected_callback);
+  }
+
+  void RegisterScreenIsUnlockedSignalHandler(
+      const base::Closure& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) override {
+    brillo::dbus_utils::ConnectToSignal(
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "ScreenIsUnlocked",
+        signal_callback,
+        on_connected_callback);
+  }
+
+  void ReleaseObjectProxy(const base::Closure& callback) {
+    bus_->RemoveObjectProxy(service_name_, object_path_, callback);
+  }
+
+  const dbus::ObjectPath& GetObjectPath() const {
+    return object_path_;
+  }
+
+  dbus::ObjectProxy* GetObjectProxy() const { return dbus_object_proxy_; }
+
+  bool EmitLoginPromptVisible(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "EmitLoginPromptVisible",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  void EmitLoginPromptVisibleAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "EmitLoginPromptVisible",
+        success_callback,
+        error_callback);
+  }
+
+  bool EnableChromeTesting(
+      bool in_force_relaunch,
+      const std::vector<std::string>& in_extra_arguments,
+      std::string* out_filepath,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "EnableChromeTesting",
+        error,
+        in_force_relaunch,
+        in_extra_arguments);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_filepath);
+  }
+
+  void EnableChromeTestingAsync(
+      bool in_force_relaunch,
+      const std::vector<std::string>& in_extra_arguments,
+      const base::Callback<void(const std::string& /*filepath*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "EnableChromeTesting",
+        success_callback,
+        error_callback,
+        in_force_relaunch,
+        in_extra_arguments);
+  }
+
+  bool StartSession(
+      const std::string& in_email_address,
+      const std::string& in_unique_identifier,
+      bool* out_done,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "StartSession",
+        error,
+        in_email_address,
+        in_unique_identifier);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_done);
+  }
+
+  void StartSessionAsync(
+      const std::string& in_email_address,
+      const std::string& in_unique_identifier,
+      const base::Callback<void(bool /*done*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "StartSession",
+        success_callback,
+        error_callback,
+        in_email_address,
+        in_unique_identifier);
+  }
+
+  bool StopSession(
+      const std::string& in_unique_identifier,
+      bool* out_done,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "StopSession",
+        error,
+        in_unique_identifier);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_done);
+  }
+
+  void StopSessionAsync(
+      const std::string& in_unique_identifier,
+      const base::Callback<void(bool /*done*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "StopSession",
+        success_callback,
+        error_callback,
+        in_unique_identifier);
+  }
+
+  bool StorePolicy(
+      const std::vector<uint8_t>& in_policy_blob,
+      bool* out_done,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "StorePolicy",
+        error,
+        in_policy_blob);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_done);
+  }
+
+  void StorePolicyAsync(
+      const std::vector<uint8_t>& in_policy_blob,
+      const base::Callback<void(bool /*done*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "StorePolicy",
+        success_callback,
+        error_callback,
+        in_policy_blob);
+  }
+
+  bool RetrievePolicy(
+      std::vector<uint8_t>* out_policy_blob,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "RetrievePolicy",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_policy_blob);
+  }
+
+  void RetrievePolicyAsync(
+      const base::Callback<void(const std::vector<uint8_t>& /*policy_blob*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "RetrievePolicy",
+        success_callback,
+        error_callback);
+  }
+
+  bool StorePolicyForUser(
+      const std::string& in_user_email,
+      const std::vector<uint8_t>& in_policy_blob,
+      bool* out_done,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "StorePolicyForUser",
+        error,
+        in_user_email,
+        in_policy_blob);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_done);
+  }
+
+  void StorePolicyForUserAsync(
+      const std::string& in_user_email,
+      const std::vector<uint8_t>& in_policy_blob,
+      const base::Callback<void(bool /*done*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "StorePolicyForUser",
+        success_callback,
+        error_callback,
+        in_user_email,
+        in_policy_blob);
+  }
+
+  bool RetrievePolicyForUser(
+      const std::string& in_user_email,
+      std::vector<uint8_t>* out_policy_blob,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "RetrievePolicyForUser",
+        error,
+        in_user_email);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_policy_blob);
+  }
+
+  void RetrievePolicyForUserAsync(
+      const std::string& in_user_email,
+      const base::Callback<void(const std::vector<uint8_t>& /*policy_blob*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "RetrievePolicyForUser",
+        success_callback,
+        error_callback,
+        in_user_email);
+  }
+
+  bool StoreDeviceLocalAccountPolicy(
+      const std::string& in_account_id,
+      const std::vector<uint8_t>& in_policy_blob,
+      bool* out_done,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "StoreDeviceLocalAccountPolicy",
+        error,
+        in_account_id,
+        in_policy_blob);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_done);
+  }
+
+  void StoreDeviceLocalAccountPolicyAsync(
+      const std::string& in_account_id,
+      const std::vector<uint8_t>& in_policy_blob,
+      const base::Callback<void(bool /*done*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "StoreDeviceLocalAccountPolicy",
+        success_callback,
+        error_callback,
+        in_account_id,
+        in_policy_blob);
+  }
+
+  bool RetrieveDeviceLocalAccountPolicy(
+      const std::string& in_account_id,
+      std::vector<uint8_t>* out_policy_blob,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "RetrieveDeviceLocalAccountPolicy",
+        error,
+        in_account_id);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_policy_blob);
+  }
+
+  void RetrieveDeviceLocalAccountPolicyAsync(
+      const std::string& in_account_id,
+      const base::Callback<void(const std::vector<uint8_t>& /*policy_blob*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "RetrieveDeviceLocalAccountPolicy",
+        success_callback,
+        error_callback,
+        in_account_id);
+  }
+
+  bool RetrieveSessionState(
+      std::string* out_state,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "RetrieveSessionState",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_state);
+  }
+
+  void RetrieveSessionStateAsync(
+      const base::Callback<void(const std::string& /*state*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "RetrieveSessionState",
+        success_callback,
+        error_callback);
+  }
+
+  bool RetrieveActiveSessions(
+      std::map<std::string, std::string>* out_sessions,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "RetrieveActiveSessions",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_sessions);
+  }
+
+  void RetrieveActiveSessionsAsync(
+      const base::Callback<void(const std::map<std::string, std::string>& /*sessions*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "RetrieveActiveSessions",
+        success_callback,
+        error_callback);
+  }
+
+  bool HandleSupervisedUserCreationStarting(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "HandleSupervisedUserCreationStarting",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  void HandleSupervisedUserCreationStartingAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "HandleSupervisedUserCreationStarting",
+        success_callback,
+        error_callback);
+  }
+
+  bool HandleSupervisedUserCreationFinished(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "HandleSupervisedUserCreationFinished",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  void HandleSupervisedUserCreationFinishedAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "HandleSupervisedUserCreationFinished",
+        success_callback,
+        error_callback);
+  }
+
+  bool LockScreen(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "LockScreen",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  void LockScreenAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "LockScreen",
+        success_callback,
+        error_callback);
+  }
+
+  bool HandleLockScreenShown(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "HandleLockScreenShown",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  void HandleLockScreenShownAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "HandleLockScreenShown",
+        success_callback,
+        error_callback);
+  }
+
+  bool HandleLockScreenDismissed(
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "HandleLockScreenDismissed",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  void HandleLockScreenDismissedAsync(
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "HandleLockScreenDismissed",
+        success_callback,
+        error_callback);
+  }
+
+  bool RestartJob(
+      const dbus::FileDescriptor& in_cred_fd,
+      const std::vector<std::string>& in_argv,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "RestartJob",
+        error,
+        in_cred_fd,
+        in_argv);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  void RestartJobAsync(
+      const dbus::FileDescriptor& in_cred_fd,
+      const std::vector<std::string>& in_argv,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "RestartJob",
+        success_callback,
+        error_callback,
+        in_cred_fd,
+        in_argv);
+  }
+
+  bool StartDeviceWipe(
+      bool* out_done,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "StartDeviceWipe",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_done);
+  }
+
+  void StartDeviceWipeAsync(
+      const base::Callback<void(bool /*done*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "StartDeviceWipe",
+        success_callback,
+        error_callback);
+  }
+
+  bool SetFlagsForUser(
+      const std::string& in_user_email,
+      const std::vector<std::string>& in_flags,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "SetFlagsForUser",
+        error,
+        in_user_email,
+        in_flags);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  void SetFlagsForUserAsync(
+      const std::string& in_user_email,
+      const std::vector<std::string>& in_flags,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "SetFlagsForUser",
+        success_callback,
+        error_callback,
+        in_user_email,
+        in_flags);
+  }
+
+  bool GetServerBackedStateKeys(
+      std::vector<std::vector<uint8_t>>* out_state_keys,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "GetServerBackedStateKeys",
+        error);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_state_keys);
+  }
+
+  void GetServerBackedStateKeysAsync(
+      const base::Callback<void(const std::vector<std::vector<uint8_t>>& /*state_keys*/)>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "GetServerBackedStateKeys",
+        success_callback,
+        error_callback);
+  }
+
+  bool InitMachineInfo(
+      const std::string& in_data,
+      brillo::ErrorPtr* error,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "InitMachineInfo",
+        error,
+        in_data);
+    return response && brillo::dbus_utils::ExtractMethodCallResults(
+        response.get(), error);
+  }
+
+  void InitMachineInfoAsync(
+      const std::string& in_data,
+      const base::Callback<void()>& success_callback,
+      const base::Callback<void(brillo::Error*)>& error_callback,
+      int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+    brillo::dbus_utils::CallMethodWithTimeout(
+        timeout_ms,
+        dbus_object_proxy_,
+        "org.chromium.SessionManagerInterface",
+        "InitMachineInfo",
+        success_callback,
+        error_callback,
+        in_data);
+  }
+
+ private:
+  scoped_refptr<dbus::Bus> bus_;
+  const std::string service_name_{"org.chromium.SessionManager"};
+  const dbus::ObjectPath object_path_{"/org/chromium/SessionManager"};
+  dbus::ObjectProxy* dbus_object_proxy_;
+
+  DISALLOW_COPY_AND_ASSIGN(SessionManagerInterfaceProxy);
+};
+
+}  // namespace chromium
+}  // namespace org
+
+#endif  // ____CHROMEOS_DBUS_BINDING____________________BUILD_LINK_VAR_CACHE_PORTAGE_CHROMEOS_BASE_CHROMEOS_LOGIN_OUT_DEFAULT_GEN_INCLUDE_SESSION_MANAGER_DBUS_PROXIES_H
diff --git a/include/session_manager/dbus-proxy-mocks.h b/include/session_manager/dbus-proxy-mocks.h
new file mode 100644
index 0000000..2b6ce4d
--- /dev/null
+++ b/include/session_manager/dbus-proxy-mocks.h
@@ -0,0 +1,252 @@
+// Automatic generation of D-Bus interface mock proxies for:
+//  - org.chromium.SessionManagerInterface
+#ifndef ____CHROMEOS_DBUS_BINDING____________________BUILD_LINK_VAR_CACHE_PORTAGE_CHROMEOS_BASE_CHROMEOS_LOGIN_OUT_DEFAULT_GEN_INCLUDE_SESSION_MANAGER_DBUS_PROXY_MOCKS_H
+#define ____CHROMEOS_DBUS_BINDING____________________BUILD_LINK_VAR_CACHE_PORTAGE_CHROMEOS_BASE_CHROMEOS_LOGIN_OUT_DEFAULT_GEN_INCLUDE_SESSION_MANAGER_DBUS_PROXY_MOCKS_H
+#include <string>
+#include <vector>
+
+#include <base/callback_forward.h>
+#include <base/logging.h>
+#include <base/macros.h>
+#include <brillo/any.h>
+#include <brillo/errors/error.h>
+#include <brillo/variant_dictionary.h>
+#include <gmock/gmock.h>
+
+#include "session_manager/dbus-proxies.h"
+
+namespace org {
+namespace chromium {
+
+// Mock object for SessionManagerInterfaceProxyInterface.
+class SessionManagerInterfaceProxyMock : public SessionManagerInterfaceProxyInterface {
+ public:
+  SessionManagerInterfaceProxyMock() = default;
+
+  MOCK_METHOD2(EmitLoginPromptVisible,
+               bool(brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(EmitLoginPromptVisibleAsync,
+               void(const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD5(EnableChromeTesting,
+               bool(bool /*in_force_relaunch*/,
+                    const std::vector<std::string>& /*in_extra_arguments*/,
+                    std::string* /*out_filepath*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD5(EnableChromeTestingAsync,
+               void(bool /*in_force_relaunch*/,
+                    const std::vector<std::string>& /*in_extra_arguments*/,
+                    const base::Callback<void(const std::string& /*filepath*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD5(StartSession,
+               bool(const std::string& /*in_email_address*/,
+                    const std::string& /*in_unique_identifier*/,
+                    bool* /*out_done*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD5(StartSessionAsync,
+               void(const std::string& /*in_email_address*/,
+                    const std::string& /*in_unique_identifier*/,
+                    const base::Callback<void(bool /*done*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(StopSession,
+               bool(const std::string& /*in_unique_identifier*/,
+                    bool* /*out_done*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(StopSessionAsync,
+               void(const std::string& /*in_unique_identifier*/,
+                    const base::Callback<void(bool /*done*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(StorePolicy,
+               bool(const std::vector<uint8_t>& /*in_policy_blob*/,
+                    bool* /*out_done*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(StorePolicyAsync,
+               void(const std::vector<uint8_t>& /*in_policy_blob*/,
+                    const base::Callback<void(bool /*done*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(RetrievePolicy,
+               bool(std::vector<uint8_t>* /*out_policy_blob*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(RetrievePolicyAsync,
+               void(const base::Callback<void(const std::vector<uint8_t>& /*policy_blob*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD5(StorePolicyForUser,
+               bool(const std::string& /*in_user_email*/,
+                    const std::vector<uint8_t>& /*in_policy_blob*/,
+                    bool* /*out_done*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD5(StorePolicyForUserAsync,
+               void(const std::string& /*in_user_email*/,
+                    const std::vector<uint8_t>& /*in_policy_blob*/,
+                    const base::Callback<void(bool /*done*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(RetrievePolicyForUser,
+               bool(const std::string& /*in_user_email*/,
+                    std::vector<uint8_t>* /*out_policy_blob*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(RetrievePolicyForUserAsync,
+               void(const std::string& /*in_user_email*/,
+                    const base::Callback<void(const std::vector<uint8_t>& /*policy_blob*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD5(StoreDeviceLocalAccountPolicy,
+               bool(const std::string& /*in_account_id*/,
+                    const std::vector<uint8_t>& /*in_policy_blob*/,
+                    bool* /*out_done*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD5(StoreDeviceLocalAccountPolicyAsync,
+               void(const std::string& /*in_account_id*/,
+                    const std::vector<uint8_t>& /*in_policy_blob*/,
+                    const base::Callback<void(bool /*done*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(RetrieveDeviceLocalAccountPolicy,
+               bool(const std::string& /*in_account_id*/,
+                    std::vector<uint8_t>* /*out_policy_blob*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(RetrieveDeviceLocalAccountPolicyAsync,
+               void(const std::string& /*in_account_id*/,
+                    const base::Callback<void(const std::vector<uint8_t>& /*policy_blob*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(RetrieveSessionState,
+               bool(std::string* /*out_state*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(RetrieveSessionStateAsync,
+               void(const base::Callback<void(const std::string& /*state*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(RetrieveActiveSessions,
+               bool(std::map<std::string, std::string>* /*out_sessions*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(RetrieveActiveSessionsAsync,
+               void(const base::Callback<void(const std::map<std::string, std::string>& /*sessions*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD2(HandleSupervisedUserCreationStarting,
+               bool(brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(HandleSupervisedUserCreationStartingAsync,
+               void(const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD2(HandleSupervisedUserCreationFinished,
+               bool(brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(HandleSupervisedUserCreationFinishedAsync,
+               void(const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD2(LockScreen,
+               bool(brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(LockScreenAsync,
+               void(const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD2(HandleLockScreenShown,
+               bool(brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(HandleLockScreenShownAsync,
+               void(const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD2(HandleLockScreenDismissed,
+               bool(brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(HandleLockScreenDismissedAsync,
+               void(const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(RestartJob,
+               bool(const dbus::FileDescriptor& /*in_cred_fd*/,
+                    const std::vector<std::string>& /*in_argv*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD5(RestartJobAsync,
+               void(const dbus::FileDescriptor& /*in_cred_fd*/,
+                    const std::vector<std::string>& /*in_argv*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(StartDeviceWipe,
+               bool(bool* /*out_done*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(StartDeviceWipeAsync,
+               void(const base::Callback<void(bool /*done*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(SetFlagsForUser,
+               bool(const std::string& /*in_user_email*/,
+                    const std::vector<std::string>& /*in_flags*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD5(SetFlagsForUserAsync,
+               void(const std::string& /*in_user_email*/,
+                    const std::vector<std::string>& /*in_flags*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetServerBackedStateKeys,
+               bool(std::vector<std::vector<uint8_t>>* /*out_state_keys*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(GetServerBackedStateKeysAsync,
+               void(const base::Callback<void(const std::vector<std::vector<uint8_t>>& /*state_keys*/)>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD3(InitMachineInfo,
+               bool(const std::string& /*in_data*/,
+                    brillo::ErrorPtr* /*error*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD4(InitMachineInfoAsync,
+               void(const std::string& /*in_data*/,
+                    const base::Callback<void()>& /*success_callback*/,
+                    const base::Callback<void(brillo::Error*)>& /*error_callback*/,
+                    int /*timeout_ms*/));
+  MOCK_METHOD2(RegisterLoginPromptVisibleSignalHandler,
+               void(const base::Closure& /*signal_callback*/,
+                    dbus::ObjectProxy::OnConnectedCallback /*on_connected_callback*/));
+  MOCK_METHOD2(RegisterSessionStateChangedSignalHandler,
+               void(const base::Callback<void(const std::string&)>& /*signal_callback*/,
+                    dbus::ObjectProxy::OnConnectedCallback /*on_connected_callback*/));
+  MOCK_METHOD2(RegisterSetOwnerKeyCompleteSignalHandler,
+               void(const base::Callback<void(const std::string&)>& /*signal_callback*/,
+                    dbus::ObjectProxy::OnConnectedCallback /*on_connected_callback*/));
+  MOCK_METHOD2(RegisterPropertyChangeCompleteSignalHandler,
+               void(const base::Callback<void(const std::string&)>& /*signal_callback*/,
+                    dbus::ObjectProxy::OnConnectedCallback /*on_connected_callback*/));
+  MOCK_METHOD2(RegisterScreenIsLockedSignalHandler,
+               void(const base::Closure& /*signal_callback*/,
+                    dbus::ObjectProxy::OnConnectedCallback /*on_connected_callback*/));
+  MOCK_METHOD2(RegisterScreenIsUnlockedSignalHandler,
+               void(const base::Closure& /*signal_callback*/,
+                    dbus::ObjectProxy::OnConnectedCallback /*on_connected_callback*/));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SessionManagerInterfaceProxyMock);
+};
+}  // namespace chromium
+}  // namespace org
+
+#endif  // ____CHROMEOS_DBUS_BINDING____________________BUILD_LINK_VAR_CACHE_PORTAGE_CHROMEOS_BASE_CHROMEOS_LOGIN_OUT_DEFAULT_GEN_INCLUDE_SESSION_MANAGER_DBUS_PROXY_MOCKS_H
diff --git a/include/update_engine/dbus-constants.h b/include/update_engine/dbus-constants.h
new file mode 100644
index 0000000..94e35f1
--- /dev/null
+++ b/include/update_engine/dbus-constants.h
@@ -0,0 +1,51 @@
+// Copyright 2015 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.
+
+#ifndef SYSTEM_API_DBUS_UPDATE_ENGINE_DBUS_CONSTANTS_H_
+#define SYSTEM_API_DBUS_UPDATE_ENGINE_DBUS_CONSTANTS_H_
+
+namespace update_engine {
+const char kUpdateEngineInterface[] = "org.chromium.UpdateEngineInterface";
+const char kUpdateEngineServicePath[] = "/org/chromium/UpdateEngine";
+const char kUpdateEngineServiceName[] = "org.chromium.UpdateEngine";
+
+// Generic UpdateEngine D-Bus error.
+static const char* const kUpdateEngineServiceErrorFailed =
+    "org.chromium.UpdateEngine.Error.Failed";
+
+// Methods.
+const char kAttemptUpdate[] = "AttemptUpdate";
+const char kGetStatus[] = "GetStatus";
+const char kRebootIfNeeded[] = "RebootIfNeeded";
+const char kSetChannel[] = "SetChannel";
+const char kGetChannel[] = "GetChannel";
+const char kAttemptRollback[] = "AttemptRollback";
+const char kCanRollback[] = "CanRollback";
+
+// Signals.
+const char kStatusUpdate[] = "StatusUpdate";
+
+// Flags used in the AttemptUpdateWithFlags() D-Bus method.
+typedef enum {
+  kAttemptUpdateFlagNonInteractive = (1<<0)
+} AttemptUpdateFlags;
+
+// Operations contained in StatusUpdate signals.
+const char kUpdateStatusIdle[] = "UPDATE_STATUS_IDLE";
+const char kUpdateStatusCheckingForUpdate[] =
+    "UPDATE_STATUS_CHECKING_FOR_UPDATE";
+const char kUpdateStatusUpdateAvailable[] = "UPDATE_STATUS_UPDATE_AVAILABLE";
+const char kUpdateStatusDownloading[] = "UPDATE_STATUS_DOWNLOADING";
+const char kUpdateStatusVerifying[] = "UPDATE_STATUS_VERIFYING";
+const char kUpdateStatusFinalizing[] = "UPDATE_STATUS_FINALIZING";
+const char kUpdateStatusUpdatedNeedReboot[] =
+    "UPDATE_STATUS_UPDATED_NEED_REBOOT";
+const char kUpdateStatusReportingErrorEvent[] =
+    "UPDATE_STATUS_REPORTING_ERROR_EVENT";
+const char kUpdateStatusAttemptingRollback[] =
+    "UPDATE_STATUS_ATTEMPTING_ROLLBACK";
+const char kUpdateStatusDisabled[] = "UPDATE_STATUS_DISABLED";
+}  // namespace update_engine
+
+#endif  // SYSTEM_API_DBUS_UPDATE_ENGINE_DBUS_CONSTANTS_H_
diff --git a/init/update-engine.conf b/init/update-engine.conf
new file mode 100644
index 0000000..4c05cf4
--- /dev/null
+++ b/init/update-engine.conf
@@ -0,0 +1,39 @@
+#
+# 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.
+#
+
+description     "System software update service"
+author          "chromium-os-dev@chromium.org"
+
+# N.B. The chromeos-factoryinstall ebuild edits the 'start on' line so as
+# to disable update_engine in factory images.  Do not change this without
+# also updating that reference.
+start on starting system-services
+stop on stopping system-services
+respawn
+
+expect fork
+
+# Runs the daemon at low/idle IO priority so that updates don't
+# impact system responsiveness.
+exec ionice -c3 update_engine
+
+# Put update_engine process in its own cgroup.
+# Default cpu.shares is 1024.
+post-start script
+  cgroup_dir="/sys/fs/cgroup/cpu/${UPSTART_JOB}"
+  mkdir -p "${cgroup_dir}"
+  echo $(status | cut -f 4 -d ' ') > "${cgroup_dir}/tasks"
+end script
diff --git a/libcros_proxy.cc b/libcros_proxy.cc
new file mode 100644
index 0000000..689ed39
--- /dev/null
+++ b/libcros_proxy.cc
@@ -0,0 +1,57 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/libcros_proxy.h"
+
+using org::chromium::LibCrosServiceInterfaceProxy;
+using org::chromium::LibCrosServiceInterfaceProxyInterface;
+using org::chromium::UpdateEngineLibcrosProxyResolvedInterfaceProxy;
+using org::chromium::UpdateEngineLibcrosProxyResolvedInterfaceProxyInterface;
+
+namespace {
+const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
+}  // namespace
+
+namespace chromeos_update_engine {
+
+LibCrosProxy::LibCrosProxy(
+    std::unique_ptr<LibCrosServiceInterfaceProxyInterface>
+        service_interface_proxy,
+    std::unique_ptr<UpdateEngineLibcrosProxyResolvedInterfaceProxyInterface>
+        ue_proxy_resolved_interface)
+    : service_interface_proxy_(std::move(service_interface_proxy)),
+      ue_proxy_resolved_interface_(std::move(ue_proxy_resolved_interface)) {
+}
+
+LibCrosProxy::LibCrosProxy(const scoped_refptr<dbus::Bus>& bus)
+    : service_interface_proxy_(
+          new LibCrosServiceInterfaceProxy(bus, kLibCrosServiceName)),
+      ue_proxy_resolved_interface_(
+          new UpdateEngineLibcrosProxyResolvedInterfaceProxy(
+              bus,
+              kLibCrosServiceName)) {
+}
+
+LibCrosServiceInterfaceProxyInterface* LibCrosProxy::service_interface_proxy() {
+  return service_interface_proxy_.get();
+}
+
+UpdateEngineLibcrosProxyResolvedInterfaceProxyInterface*
+LibCrosProxy::ue_proxy_resolved_interface() {
+  return ue_proxy_resolved_interface_.get();
+}
+
+}  // namespace chromeos_update_engine
diff --git a/libcros_proxy.h b/libcros_proxy.h
new file mode 100644
index 0000000..afb5d54
--- /dev/null
+++ b/libcros_proxy.h
@@ -0,0 +1,62 @@
+//
+// 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_LIBCROS_PROXY_H_
+#define UPDATE_ENGINE_LIBCROS_PROXY_H_
+
+#include <memory>
+
+#include <base/macros.h>
+#include <dbus/bus.h>
+
+#include "libcros/dbus-proxies.h"
+
+namespace chromeos_update_engine {
+
+// This class handles the DBus connection with chrome to resolve proxies. This
+// is a thin class to just hold the generated proxies (real or mocked ones).
+class LibCrosProxy final {
+ public:
+  explicit LibCrosProxy(const scoped_refptr<dbus::Bus>& bus);
+  LibCrosProxy(
+      std::unique_ptr<org::chromium::LibCrosServiceInterfaceProxyInterface>
+          service_interface_proxy,
+      std::unique_ptr<
+          org::chromium::
+              UpdateEngineLibcrosProxyResolvedInterfaceProxyInterface>
+          ue_proxy_resolved_interface);
+
+  ~LibCrosProxy() = default;
+
+  // Getters for the two proxies.
+  org::chromium::LibCrosServiceInterfaceProxyInterface*
+  service_interface_proxy();
+  org::chromium::UpdateEngineLibcrosProxyResolvedInterfaceProxyInterface*
+  ue_proxy_resolved_interface();
+
+ private:
+  std::unique_ptr<org::chromium::LibCrosServiceInterfaceProxyInterface>
+      service_interface_proxy_;
+  std::unique_ptr<
+      org::chromium::UpdateEngineLibcrosProxyResolvedInterfaceProxyInterface>
+      ue_proxy_resolved_interface_;
+
+  DISALLOW_COPY_AND_ASSIGN(LibCrosProxy);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_LIBCROS_PROXY_H_
diff --git a/libupdate_engine-client-test.pc.in b/libupdate_engine-client-test.pc.in
new file mode 100644
index 0000000..92a4af3
--- /dev/null
+++ b/libupdate_engine-client-test.pc.in
@@ -0,0 +1,6 @@
+include_dir=@INCLUDE_DIR@
+
+Name: libupdate_engine-client-test
+Version: 1.0
+Description: update_engine client interface mock library
+Cflags: -I${include_dir}
diff --git a/libupdate_engine-client.pc.in b/libupdate_engine-client.pc.in
new file mode 100644
index 0000000..4c87e1d
--- /dev/null
+++ b/libupdate_engine-client.pc.in
@@ -0,0 +1,6 @@
+include_dir=@INCLUDE_DIR@
+
+Name: libupdate_engine-client
+Version: 1.0
+Description: update_engine client interface library
+Cflags: -I${include_dir}
diff --git a/local_coverage_rate b/local_coverage_rate
new file mode 100755
index 0000000..b189806
--- /dev/null
+++ b/local_coverage_rate
@@ -0,0 +1,101 @@
+#!/bin/bash
+
+#
+# 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.
+#
+
+# Calculates the test-coverage percentage for non-test files in the
+# update_engine directory. Requires a file 'app.info' to contain the
+# results of running the unittests while collecting coverage data.
+
+cat app.info | awk -F '[,:]' '
+
+BEGIN { OFS = ":"; }
+
+/^SF:/{ FILEN = $2; }
+
+/^end_of_record$/{ FILEN = ""; }
+
+/^DA:/{ print FILEN, $2, $3; }
+
+' | sort | awk -F : '
+BEGIN {
+  OFS = ":";
+  FILEN = "";
+  LINE = "";
+  HITS = 0;
+}
+{
+  NEWFILEN = $1;
+  NEWLINE = $2;
+  if ((NEWFILEN == FILEN) && (NEWLINE == LINE)) {
+    HITS += $3
+  } else {
+    if (FILEN != "") {
+      print FILEN, LINE, HITS;
+    }
+    FILEN = NEWFILEN;
+    LINE = NEWLINE;
+    HITS = $3;
+  }
+}
+' | grep '^.*\/trunk\/src\/platform\/update_engine\/' | \
+fgrep -v '_unittest.cc:' | \
+fgrep -v '/test_utils.' | \
+fgrep -v '/test_http_server.cc' | \
+fgrep -v '/testrunner.cc' | \
+fgrep -v '/mock' | \
+fgrep -v '.pb.cc' | \
+awk -F : '
+
+function printfile() {
+  if (FNAME != "")
+    printf "%-40s %4d / %4d: %5.1f%%\n", FNAME, FILE_GOOD_LINES,
+        (FILE_BAD_LINES + FILE_GOOD_LINES),
+        (FILE_GOOD_LINES * 100) / (FILE_BAD_LINES + FILE_GOOD_LINES);
+}
+
+BEGIN {
+  FNAME = "";
+  FILE_BAD_LINES = 0;
+  FILE_GOOD_LINES = 0;
+}
+{
+  // calc filename
+  ARR_SIZE = split($1, PARTS, "/");
+  NEWFNAME = PARTS[ARR_SIZE];
+  if (NEWFNAME != FNAME) {
+    printfile();
+    FILE_BAD_LINES = 0;
+    FILE_GOOD_LINES = 0;
+    FNAME = NEWFNAME;
+  }
+  if ($3 == "0") {
+    BAD_LINES += 1;
+    FILE_BAD_LINES += 1;
+  } else {
+    GOOD_LINES += 1;
+    FILE_GOOD_LINES += 1;
+  }
+}
+
+END {
+  printfile();
+  print "---\nSummary: tested " GOOD_LINES " / " (BAD_LINES + GOOD_LINES);
+  printf(
+    "Test coverage: %.1f%%\n",
+    ((GOOD_LINES * 100) / (BAD_LINES + GOOD_LINES)));
+}
+'
diff --git a/main.cc b/main.cc
new file mode 100644
index 0000000..8e88f23
--- /dev/null
+++ b/main.cc
@@ -0,0 +1,132 @@
+//
+// 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 <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <xz.h>
+
+#include <string>
+
+#include <base/at_exit.h>
+#include <base/command_line.h>
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/flag_helper.h>
+#include <brillo/message_loops/base_message_loop.h>
+
+#include "update_engine/common/terminator.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/daemon.h"
+
+using std::string;
+
+namespace chromeos_update_engine {
+namespace {
+
+void SetupLogSymlink(const string& symlink_path, const string& log_path) {
+  // TODO(petkov): To ensure a smooth transition between non-timestamped and
+  // timestamped logs, move an existing log to start the first timestamped
+  // one. This code can go away once all clients are switched to this version or
+  // we stop caring about the old-style logs.
+  if (utils::FileExists(symlink_path.c_str()) &&
+      !utils::IsSymlink(symlink_path.c_str())) {
+    base::ReplaceFile(base::FilePath(symlink_path),
+                      base::FilePath(log_path),
+                      nullptr);
+  }
+  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;
+  }
+}
+
+string GetTimeAsString(time_t utime) {
+  struct tm tm;
+  CHECK_EQ(localtime_r(&utime, &tm), &tm);
+  char str[16];
+  CHECK_EQ(strftime(str, sizeof(str), "%Y%m%d-%H%M%S", &tm), 15u);
+  return str;
+}
+
+string SetupLogFile(const string& kLogsRoot) {
+  const string kLogSymlink = kLogsRoot + "/update_engine.log";
+  const string kLogsDir = kLogsRoot + "/update_engine";
+  const string kLogPath =
+      base::StringPrintf("%s/update_engine.%s",
+                         kLogsDir.c_str(),
+                         GetTimeAsString(::time(nullptr)).c_str());
+  mkdir(kLogsDir.c_str(), 0755);
+  SetupLogSymlink(kLogSymlink, kLogPath);
+  return kLogSymlink;
+}
+
+void SetupLogging(bool log_to_std_err) {
+  string log_file;
+  logging::LoggingSettings log_settings;
+  log_settings.lock_log = logging::DONT_LOCK_LOG_FILE;
+  log_settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE;
+
+  if (log_to_std_err) {
+    // Log to stderr initially.
+    log_settings.log_file = nullptr;
+    log_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+  } else {
+    log_file = SetupLogFile("/var/log");
+    log_settings.log_file = log_file.c_str();
+    log_settings.logging_dest = logging::LOG_TO_FILE;
+  }
+
+  logging::InitLogging(log_settings);
+}
+
+}  // namespace
+}  // namespace chromeos_update_engine
+
+int main(int argc, char** argv) {
+  DEFINE_bool(logtostderr, false,
+              "Write logs to stderr instead of to a file in log_dir.");
+  DEFINE_bool(foreground, false,
+              "Don't daemon()ize; run in foreground.");
+
+  chromeos_update_engine::Terminator::Init();
+  brillo::FlagHelper::Init(argc, argv, "Chromium OS Update Engine");
+  chromeos_update_engine::SetupLogging(FLAGS_logtostderr);
+  if (!FLAGS_foreground)
+    PLOG_IF(FATAL, daemon(0, 0) == 1) << "daemon() failed";
+
+  LOG(INFO) << "Chrome OS Update Engine starting";
+
+  // xz-embedded requires to initialize its CRC-32 table once on startup.
+  xz_crc32_init();
+
+  // Ensure that all written files have safe permissions.
+  // This is a mask, so we _block_ all permissions for the group owner and other
+  // users but allow all permissions for the user owner. We allow execution
+  // for the owner so we can create directories.
+  // Done _after_ log file creation.
+  umask(S_IRWXG | S_IRWXO);
+
+  chromeos_update_engine::UpdateEngineDaemon update_engine_daemon;
+  int exit_code = update_engine_daemon.Run();
+
+  LOG(INFO) << "Chrome OS Update Engine terminating with exit code "
+            << exit_code;
+  return exit_code;
+}
diff --git a/metrics.cc b/metrics.cc
new file mode 100644
index 0000000..742ba7e
--- /dev/null
+++ b/metrics.cc
@@ -0,0 +1,526 @@
+//
+// 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/metrics.h"
+
+#include <string>
+
+#include <base/logging.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/utils.h"
+#include "update_engine/metrics_utils.h"
+#include "update_engine/system_state.h"
+
+using std::string;
+
+namespace chromeos_update_engine {
+
+namespace metrics {
+
+// UpdateEngine.Daily.* metrics.
+const char kMetricDailyOSAgeDays[] = "UpdateEngine.Daily.OSAgeDays";
+
+// UpdateEngine.Check.* metrics.
+const char kMetricCheckDownloadErrorCode[] =
+    "UpdateEngine.Check.DownloadErrorCode";
+const char kMetricCheckReaction[] = "UpdateEngine.Check.Reaction";
+const char kMetricCheckResult[] = "UpdateEngine.Check.Result";
+const char kMetricCheckTimeSinceLastCheckMinutes[] =
+    "UpdateEngine.Check.TimeSinceLastCheckMinutes";
+const char kMetricCheckTimeSinceLastCheckUptimeMinutes[] =
+    "UpdateEngine.Check.TimeSinceLastCheckUptimeMinutes";
+
+// UpdateEngine.Attempt.* metrics.
+const char kMetricAttemptNumber[] = "UpdateEngine.Attempt.Number";
+const char kMetricAttemptPayloadType[] =
+    "UpdateEngine.Attempt.PayloadType";
+const char kMetricAttemptPayloadSizeMiB[] =
+    "UpdateEngine.Attempt.PayloadSizeMiB";
+const char kMetricAttemptConnectionType[] =
+    "UpdateEngine.Attempt.ConnectionType";
+const char kMetricAttemptDurationMinutes[] =
+    "UpdateEngine.Attempt.DurationMinutes";
+const char kMetricAttemptDurationUptimeMinutes[] =
+    "UpdateEngine.Attempt.DurationUptimeMinutes";
+const char kMetricAttemptTimeSinceLastAttemptMinutes[] =
+    "UpdateEngine.Attempt.TimeSinceLastAttemptMinutes";
+const char kMetricAttemptTimeSinceLastAttemptUptimeMinutes[] =
+    "UpdateEngine.Attempt.TimeSinceLastAttemptUptimeMinutes";
+const char kMetricAttemptPayloadBytesDownloadedMiB[] =
+    "UpdateEngine.Attempt.PayloadBytesDownloadedMiB";
+const char kMetricAttemptPayloadDownloadSpeedKBps[] =
+    "UpdateEngine.Attempt.PayloadDownloadSpeedKBps";
+const char kMetricAttemptDownloadSource[] =
+    "UpdateEngine.Attempt.DownloadSource";
+const char kMetricAttemptResult[] =
+    "UpdateEngine.Attempt.Result";
+const char kMetricAttemptInternalErrorCode[] =
+    "UpdateEngine.Attempt.InternalErrorCode";
+const char kMetricAttemptDownloadErrorCode[] =
+    "UpdateEngine.Attempt.DownloadErrorCode";
+
+// UpdateEngine.SuccessfulUpdate.* metrics.
+const char kMetricSuccessfulUpdateAttemptCount[] =
+    "UpdateEngine.SuccessfulUpdate.AttemptCount";
+const char kMetricSuccessfulUpdateBytesDownloadedMiB[] =
+    "UpdateEngine.SuccessfulUpdate.BytesDownloadedMiB";
+const char kMetricSuccessfulUpdateDownloadOverheadPercentage[] =
+    "UpdateEngine.SuccessfulUpdate.DownloadOverheadPercentage";
+const char kMetricSuccessfulUpdateDownloadSourcesUsed[] =
+    "UpdateEngine.SuccessfulUpdate.DownloadSourcesUsed";
+const char kMetricSuccessfulUpdatePayloadType[] =
+    "UpdateEngine.SuccessfulUpdate.PayloadType";
+const char kMetricSuccessfulUpdatePayloadSizeMiB[] =
+    "UpdateEngine.SuccessfulUpdate.PayloadSizeMiB";
+const char kMetricSuccessfulUpdateRebootCount[] =
+    "UpdateEngine.SuccessfulUpdate.RebootCount";
+const char kMetricSuccessfulUpdateTotalDurationMinutes[] =
+    "UpdateEngine.SuccessfulUpdate.TotalDurationMinutes";
+const char kMetricSuccessfulUpdateUpdatesAbandonedCount[] =
+    "UpdateEngine.SuccessfulUpdate.UpdatesAbandonedCount";
+const char kMetricSuccessfulUpdateUrlSwitchCount[] =
+    "UpdateEngine.SuccessfulUpdate.UrlSwitchCount";
+
+// UpdateEngine.Rollback.* metric.
+const char kMetricRollbackResult[] = "UpdateEngine.Rollback.Result";
+
+// UpdateEngine.CertificateCheck.* metrics.
+const char kMetricCertificateCheckUpdateCheck[] =
+    "UpdateEngine.CertificateCheck.UpdateCheck";
+const char kMetricCertificateCheckDownload[] =
+    "UpdateEngine.CertificateCheck.Download";
+
+// UpdateEngine.* metrics.
+const char kMetricFailedUpdateCount[] = "UpdateEngine.FailedUpdateCount";
+const char kMetricInstallDateProvisioningSource[] =
+    "UpdateEngine.InstallDateProvisioningSource";
+const char kMetricTimeToRebootMinutes[] =
+    "UpdateEngine.TimeToRebootMinutes";
+
+void ReportDailyMetrics(SystemState *system_state,
+                        base::TimeDelta os_age) {
+  string metric = metrics::kMetricDailyOSAgeDays;
+  LOG(INFO) << "Uploading " << utils::FormatTimeDelta(os_age)
+            << " for metric " <<  metric;
+  system_state->metrics_lib()->SendToUMA(
+      metric,
+      static_cast<int>(os_age.InDays()),
+      0,     // min: 0 days
+      6*30,  // max: 6 months (approx)
+      50);   // num_buckets
+}
+
+void ReportUpdateCheckMetrics(SystemState *system_state,
+                              CheckResult result,
+                              CheckReaction reaction,
+                              DownloadErrorCode download_error_code) {
+  string metric;
+  int value;
+  int max_value;
+
+  if (result != metrics::CheckResult::kUnset) {
+    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)";
+    system_state->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)";
+    system_state->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)";
+    system_state->metrics_lib()->SendSparseToUMA(metric, value);
+  }
+
+  base::TimeDelta time_since_last;
+  if (metrics_utils::WallclockDurationHelper(
+          system_state,
+          kPrefsMetricsCheckLastReportingTime,
+          &time_since_last)) {
+    metric = kMetricCheckTimeSinceLastCheckMinutes;
+    LOG(INFO) << "Sending " << utils::FormatTimeDelta(time_since_last)
+              << " for metric " << metric;
+    system_state->metrics_lib()->SendToUMA(
+        metric,
+        time_since_last.InMinutes(),
+        0,         // min: 0 min
+        30*24*60,  // max: 30 days
+        50);       // num_buckets
+  }
+
+  base::TimeDelta uptime_since_last;
+  static int64_t uptime_since_last_storage = 0;
+  if (metrics_utils::MonotonicDurationHelper(system_state,
+                                             &uptime_since_last_storage,
+                                             &uptime_since_last)) {
+    metric = kMetricCheckTimeSinceLastCheckUptimeMinutes;
+    LOG(INFO) << "Sending " << utils::FormatTimeDelta(uptime_since_last)
+              << " for metric " << metric;
+    system_state->metrics_lib()->SendToUMA(
+        metric,
+        uptime_since_last.InMinutes(),
+        0,         // min: 0 min
+        30*24*60,  // max: 30 days
+        50);       // num_buckets
+  }
+}
+
+void ReportAbnormallyTerminatedUpdateAttemptMetrics(
+    SystemState *system_state) {
+
+  string metric = metrics::kMetricAttemptResult;
+  AttemptResult attempt_result = AttemptResult::kAbnormalTermination;
+
+  LOG(INFO) << "Uploading " << static_cast<int>(attempt_result)
+            << " for metric " <<  metric;
+  system_state->metrics_lib()->SendEnumToUMA(
+      metric,
+      static_cast<int>(attempt_result),
+      static_cast<int>(AttemptResult::kNumConstants));
+}
+
+void ReportUpdateAttemptMetrics(
+    SystemState *system_state,
+    int attempt_number,
+    PayloadType payload_type,
+    base::TimeDelta duration,
+    base::TimeDelta duration_uptime,
+    int64_t payload_size,
+    int64_t payload_bytes_downloaded,
+    int64_t payload_download_speed_bps,
+    DownloadSource download_source,
+    AttemptResult attempt_result,
+    ErrorCode internal_error_code,
+    DownloadErrorCode payload_download_error_code,
+    ConnectionType connection_type) {
+  string metric;
+
+  metric = metrics::kMetricAttemptNumber;
+  LOG(INFO) << "Uploading " << attempt_number << " for metric " <<  metric;
+  system_state->metrics_lib()->SendToUMA(metric,
+                                         attempt_number,
+                                         0,    // min: 0 attempts
+                                         49,   // max: 49 attempts
+                                         50);  // num_buckets
+
+  metric = metrics::kMetricAttemptPayloadType;
+  LOG(INFO) << "Uploading " << utils::ToString(payload_type)
+            << " for metric " <<  metric;
+  system_state->metrics_lib()->SendEnumToUMA(metric,
+                                             payload_type,
+                                             kNumPayloadTypes);
+
+  metric = metrics::kMetricAttemptDurationMinutes;
+  LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration)
+            << " for metric " <<  metric;
+  system_state->metrics_lib()->SendToUMA(metric,
+                                         duration.InMinutes(),
+                                         0,         // min: 0 min
+                                         10*24*60,  // max: 10 days
+                                         50);       // num_buckets
+
+  metric = metrics::kMetricAttemptDurationUptimeMinutes;
+  LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration_uptime)
+            << " for metric " <<  metric;
+  system_state->metrics_lib()->SendToUMA(metric,
+                                         duration_uptime.InMinutes(),
+                                         0,         // min: 0 min
+                                         10*24*60,  // max: 10 days
+                                         50);       // num_buckets
+
+  metric = metrics::kMetricAttemptPayloadSizeMiB;
+  int64_t payload_size_mib = payload_size / kNumBytesInOneMiB;
+  LOG(INFO) << "Uploading " << payload_size_mib << " for metric " <<  metric;
+  system_state->metrics_lib()->SendToUMA(metric,
+                                         payload_size_mib,
+                                         0,     // min: 0 MiB
+                                         1024,  // max: 1024 MiB = 1 GiB
+                                         50);   // num_buckets
+
+  metric = metrics::kMetricAttemptPayloadBytesDownloadedMiB;
+  int64_t payload_bytes_downloaded_mib =
+       payload_bytes_downloaded / kNumBytesInOneMiB;
+  LOG(INFO) << "Uploading " << payload_bytes_downloaded_mib
+            << " for metric " <<  metric;
+  system_state->metrics_lib()->SendToUMA(metric,
+                                         payload_bytes_downloaded_mib,
+                                         0,     // min: 0 MiB
+                                         1024,  // max: 1024 MiB = 1 GiB
+                                         50);   // num_buckets
+
+  metric = metrics::kMetricAttemptPayloadDownloadSpeedKBps;
+  int64_t payload_download_speed_kbps = payload_download_speed_bps / 1000;
+  LOG(INFO) << "Uploading " << payload_download_speed_kbps
+            << " for metric " <<  metric;
+  system_state->metrics_lib()->SendToUMA(metric,
+                                         payload_download_speed_kbps,
+                                         0,        // min: 0 kB/s
+                                         10*1000,  // max: 10000 kB/s = 10 MB/s
+                                         50);      // num_buckets
+
+  metric = metrics::kMetricAttemptDownloadSource;
+  LOG(INFO) << "Uploading " << download_source
+            << " for metric " <<  metric;
+  system_state->metrics_lib()->SendEnumToUMA(metric,
+                                             download_source,
+                                             kNumDownloadSources);
+
+  metric = metrics::kMetricAttemptResult;
+  LOG(INFO) << "Uploading " << static_cast<int>(attempt_result)
+            << " for metric " <<  metric;
+  system_state->metrics_lib()->SendEnumToUMA(
+      metric,
+      static_cast<int>(attempt_result),
+      static_cast<int>(AttemptResult::kNumConstants));
+
+  if (internal_error_code != ErrorCode::kSuccess) {
+    metric = metrics::kMetricAttemptInternalErrorCode;
+    LOG(INFO) << "Uploading " << internal_error_code
+              << " for metric " <<  metric;
+    system_state->metrics_lib()->SendEnumToUMA(
+        metric,
+        static_cast<int>(internal_error_code),
+        static_cast<int>(ErrorCode::kUmaReportedMax));
+  }
+
+  if (payload_download_error_code != DownloadErrorCode::kUnset) {
+    metric = metrics::kMetricAttemptDownloadErrorCode;
+    LOG(INFO) << "Uploading " << static_cast<int>(payload_download_error_code)
+              << " for metric " <<  metric << " (sparse)";
+    system_state->metrics_lib()->SendSparseToUMA(
+        metric,
+        static_cast<int>(payload_download_error_code));
+  }
+
+  base::TimeDelta time_since_last;
+  if (metrics_utils::WallclockDurationHelper(
+          system_state,
+          kPrefsMetricsAttemptLastReportingTime,
+          &time_since_last)) {
+    metric = kMetricAttemptTimeSinceLastAttemptMinutes;
+    LOG(INFO) << "Sending " << utils::FormatTimeDelta(time_since_last)
+              << " for metric " << metric;
+    system_state->metrics_lib()->SendToUMA(
+        metric,
+        time_since_last.InMinutes(),
+        0,         // min: 0 min
+        30*24*60,  // max: 30 days
+        50);       // num_buckets
+  }
+
+  static int64_t uptime_since_last_storage = 0;
+  base::TimeDelta uptime_since_last;
+  if (metrics_utils::MonotonicDurationHelper(system_state,
+                                             &uptime_since_last_storage,
+                                             &uptime_since_last)) {
+    metric = kMetricAttemptTimeSinceLastAttemptUptimeMinutes;
+    LOG(INFO) << "Sending " << utils::FormatTimeDelta(uptime_since_last)
+              << " for metric " << metric;
+    system_state->metrics_lib()->SendToUMA(
+        metric,
+        uptime_since_last.InMinutes(),
+        0,         // min: 0 min
+        30*24*60,  // max: 30 days
+        50);       // num_buckets
+  }
+
+  metric = metrics::kMetricAttemptConnectionType;
+  LOG(INFO) << "Uploading " << static_cast<int>(connection_type)
+            << " for metric " <<  metric;
+  system_state->metrics_lib()->SendEnumToUMA(
+      metric,
+      static_cast<int>(connection_type),
+      static_cast<int>(ConnectionType::kNumConstants));
+}
+
+
+void ReportSuccessfulUpdateMetrics(
+         SystemState *system_state,
+         int attempt_count,
+         int updates_abandoned_count,
+         PayloadType payload_type,
+         int64_t payload_size,
+         int64_t num_bytes_downloaded[kNumDownloadSources],
+         int download_overhead_percentage,
+         base::TimeDelta total_duration,
+         int reboot_count,
+         int url_switch_count) {
+  string metric;
+  int64_t mbs;
+
+  metric = kMetricSuccessfulUpdatePayloadSizeMiB;
+  mbs = payload_size / kNumBytesInOneMiB;
+  LOG(INFO) << "Uploading " << mbs << " (MiBs) for metric " << metric;
+  system_state->metrics_lib()->SendToUMA(metric,
+                                         mbs,
+                                         0,     // min: 0 MiB
+                                         1024,  // max: 1024 MiB = 1 GiB
+                                         50);   // num_buckets
+
+  int64_t total_bytes = 0;
+  int download_sources_used = 0;
+  for (int i = 0; i < kNumDownloadSources + 1; i++) {
+    DownloadSource source = static_cast<DownloadSource>(i);
+
+    // Only consider this download source (and send byte counts) as
+    // having been used if we downloaded a non-trivial amount of bytes
+    // (e.g. at least 1 MiB) that contributed to the
+    // update. Otherwise we're going to end up with a lot of zero-byte
+    // events in the histogram.
+
+    metric = metrics::kMetricSuccessfulUpdateBytesDownloadedMiB;
+    if (i < kNumDownloadSources) {
+      metric += utils::ToString(source);
+      mbs = num_bytes_downloaded[i] / kNumBytesInOneMiB;
+      total_bytes += num_bytes_downloaded[i];
+      if (mbs > 0)
+        download_sources_used |= (1 << i);
+    } else {
+      mbs = total_bytes / kNumBytesInOneMiB;
+    }
+
+    if (mbs > 0) {
+      LOG(INFO) << "Uploading " << mbs << " (MiBs) for metric " << metric;
+      system_state->metrics_lib()->SendToUMA(metric,
+                                             mbs,
+                                             0,     // min: 0 MiB
+                                             1024,  // max: 1024 MiB = 1 GiB
+                                             50);   // num_buckets
+    }
+  }
+
+  metric = metrics::kMetricSuccessfulUpdateDownloadSourcesUsed;
+  LOG(INFO) << "Uploading 0x" << std::hex << download_sources_used
+            << " (bit flags) for metric " << metric;
+  system_state->metrics_lib()->SendToUMA(
+      metric,
+      download_sources_used,
+      0,                               // min
+      (1 << kNumDownloadSources) - 1,  // max
+      1 << kNumDownloadSources);       // num_buckets
+
+  metric = metrics::kMetricSuccessfulUpdateDownloadOverheadPercentage;
+  LOG(INFO) << "Uploading " << download_overhead_percentage
+            << "% for metric " << metric;
+  system_state->metrics_lib()->SendToUMA(metric,
+                                         download_overhead_percentage,
+                                         0,     // min: 0% overhead
+                                         1000,  // max: 1000% overhead
+                                         50);   // num_buckets
+
+  metric = metrics::kMetricSuccessfulUpdateUrlSwitchCount;
+  LOG(INFO) << "Uploading " << url_switch_count
+            << " (count) for metric " <<  metric;
+  system_state->metrics_lib()->SendToUMA(metric,
+                                         url_switch_count,
+                                         0,    // min: 0 URL switches
+                                         49,   // max: 49 URL switches
+                                         50);  // num_buckets
+
+  metric = metrics::kMetricSuccessfulUpdateTotalDurationMinutes;
+  LOG(INFO) << "Uploading " << utils::FormatTimeDelta(total_duration)
+            << " for metric " <<  metric;
+  system_state->metrics_lib()->SendToUMA(
+       metric,
+       static_cast<int>(total_duration.InMinutes()),
+       0,          // min: 0 min
+       365*24*60,  // max: 365 days ~= 1 year
+       50);        // num_buckets
+
+  metric = metrics::kMetricSuccessfulUpdateRebootCount;
+  LOG(INFO) << "Uploading reboot count of " << reboot_count << " for metric "
+            <<  metric;
+  system_state->metrics_lib()->SendToUMA(metric,
+                                         reboot_count,
+                                         0,    // min: 0 reboots
+                                         49,   // max: 49 reboots
+                                         50);  // num_buckets
+
+  metric = metrics::kMetricSuccessfulUpdatePayloadType;
+  system_state->metrics_lib()->SendEnumToUMA(metric,
+                                             payload_type,
+                                             kNumPayloadTypes);
+  LOG(INFO) << "Uploading " << utils::ToString(payload_type)
+            << " for metric " <<  metric;
+
+  metric = metrics::kMetricSuccessfulUpdateAttemptCount;
+  system_state->metrics_lib()->SendToUMA(metric,
+                                         attempt_count,
+                                         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;
+  system_state->metrics_lib()->SendToUMA(metric,
+                                         updates_abandoned_count,
+                                         0,    // min: 0 counts
+                                         49,   // max: 49 counts
+                                         50);  // num_buckets
+}
+
+void ReportRollbackMetrics(SystemState *system_state,
+                           RollbackResult result) {
+  string metric;
+  int value;
+
+  metric = metrics::kMetricRollbackResult;
+  value = static_cast<int>(result);
+  LOG(INFO) << "Sending " << value << " for metric " << metric << " (enum)";
+  system_state->metrics_lib()->SendEnumToUMA(
+      metric,
+      value,
+      static_cast<int>(metrics::RollbackResult::kNumConstants));
+}
+
+void ReportCertificateCheckMetrics(SystemState* system_state,
+                                   ServerToCheck server_to_check,
+                                   CertificateCheckResult result) {
+  string metric;
+  switch (server_to_check) {
+    case ServerToCheck::kUpdate:
+      metric = kMetricCertificateCheckUpdateCheck;
+      break;
+    case ServerToCheck::kDownload:
+      metric = kMetricCertificateCheckDownload;
+      break;
+    case ServerToCheck::kNone:
+      return;
+  }
+  LOG(INFO) << "Uploading " << static_cast<int>(result) << " for metric "
+            << metric;
+  system_state->metrics_lib()->SendEnumToUMA(
+      metric, static_cast<int>(result),
+      static_cast<int>(CertificateCheckResult::kNumConstants));
+}
+
+}  // namespace metrics
+
+}  // namespace chromeos_update_engine
diff --git a/metrics.h b/metrics.h
new file mode 100644
index 0000000..ec9d880
--- /dev/null
+++ b/metrics.h
@@ -0,0 +1,320 @@
+//
+// 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_METRICS_H_
+#define UPDATE_ENGINE_METRICS_H_
+
+#include <base/time/time.h>
+
+#include "update_engine/common/certificate_checker.h"
+#include "update_engine/common/constants.h"
+#include "update_engine/common/error_code.h"
+
+namespace chromeos_update_engine {
+
+class SystemState;
+
+namespace metrics {
+
+// UpdateEngine.Daily.* metrics.
+extern const char kMetricDailyOSAgeDays[];
+
+// UpdateEngine.Check.* metrics.
+extern const char kMetricCheckDownloadErrorCode[];
+extern const char kMetricCheckReaction[];
+extern const char kMetricCheckResult[];
+extern const char kMetricCheckTimeSinceLastCheckMinutes[];
+extern const char kMetricCheckTimeSinceLastCheckUptimeMinutes[];
+
+// UpdateEngine.Attempt.* metrics.
+extern const char kMetricAttemptNumber[];
+extern const char kMetricAttemptPayloadType[];
+extern const char kMetricAttemptPayloadSizeMiB[];
+extern const char kMetricAttemptConnectionType[];
+extern const char kMetricAttemptDurationMinutes[];
+extern const char kMetricAttemptDurationUptimeMinutes[];
+extern const char kMetricAttemptTimeSinceLastAttemptSeconds[];
+extern const char kMetricAttemptTimeSinceLastAttemptUptimeSeconds[];
+extern const char kMetricAttemptPayloadBytesDownloaded[];
+extern const char kMetricAttemptPayloadDownloadSpeedKBps[];
+extern const char kMetricAttemptDownloadSource[];
+extern const char kMetricAttemptResult[];
+extern const char kMetricAttemptInternalErrorCode[];
+extern const char kMetricAttemptDownloadErrorCode[];
+
+// UpdateEngine.SuccessfulUpdate.* metrics.
+extern const char kMetricSuccessfulUpdateAttemptCount[];
+extern const char kMetricSuccessfulUpdateBytesDownloadedMiB[];
+extern const char kMetricSuccessfulUpdateDownloadOverheadPercentage[];
+extern const char kMetricSuccessfulUpdateDownloadSourcesUsed[];
+extern const char kMetricSuccessfulUpdatePayloadType[];
+extern const char kMetricSuccessfulUpdatePayloadSizeMiB[];
+extern const char kMetricSuccessfulUpdateTotalDurationMinutes[];
+extern const char kMetricSuccessfulUpdateRebootCount[];
+extern const char kMetricSuccessfulUpdateUpdatesAbandonedCount[];
+extern const char kMetricSuccessfulUpdateUrlSwitchCount[];
+
+// UpdateEngine.Rollback.* metric.
+extern const char kMetricRollbackResult[];
+
+// UpdateEngine.* metrics.
+extern const char kMetricFailedUpdateCount[];
+extern const char kMetricInstallDateProvisioningSource[];
+extern const char kMetricTimeToRebootMinutes[];
+
+// The possible outcomes when checking for updates.
+//
+// This is used in the UpdateEngine.Check.Result histogram.
+enum class CheckResult {
+  kUpdateAvailable,    // Response indicates an update is available.
+  kNoUpdateAvailable,  // Response indicates no updates are available.
+  kDownloadError,      // Error downloading response from Omaha.
+  kParsingError,       // Error parsing response.
+  kRebootPending,      // No update check was performed a reboot is pending.
+
+  kNumConstants,
+  kUnset = -1
+};
+
+// Possible ways a device can react to a new update being available.
+//
+// This is used in the UpdateEngine.Check.Reaction histogram.
+enum class CheckReaction {
+  kUpdating,    // Device proceeds to download and apply update.
+  kIgnored  ,   // Device-policy dictates ignoring the update.
+  kDeferring,   // Device-policy dictates waiting.
+  kBackingOff,  // Previous errors dictates waiting.
+
+  kNumConstants,
+  kUnset = -1
+};
+
+// The possible ways that downloading from a HTTP or HTTPS server can fail.
+//
+// This is used in the UpdateEngine.Check.DownloadErrorCode and
+// UpdateEngine.Attempt.DownloadErrorCode histograms.
+enum class DownloadErrorCode {
+  // Errors that can happen in the field. See http://crbug.com/355745
+  // for how we plan to add more detail in the future.
+  kDownloadError = 0,  // Error downloading data from server.
+
+  // IMPORTANT: When adding a new error code, add at the bottom of the
+  // above block and before the kInputMalformed field. This
+  // is to ensure that error codes are not reordered.
+
+  // This error code is used to convey that malformed input was given
+  // to the utils::GetDownloadErrorCode() function. This should never
+  // happen but if it does it's because of an internal update_engine
+  // error and we're interested in knowing this.
+  kInputMalformed = 100,
+
+  // Bucket for capturing HTTP status codes not in the 200-599
+  // range. This should never happen in practice but if it does we
+  // want to know.
+  kHttpStatusOther = 101,
+
+  // Above 200 and below 600, the value is the HTTP status code.
+  kHttpStatus200 = 200,
+
+  kNumConstants = 600,
+
+  kUnset = -1
+};
+
+// Possible ways an update attempt can end.
+//
+// This is used in the UpdateEngine.Attempt.Result histogram.
+enum class AttemptResult {
+  kUpdateSucceeded,             // The update succeeded.
+  kInternalError,               // An internal error occurred.
+  kPayloadDownloadError,        // Failure while downloading payload.
+  kMetadataMalformed,           // Metadata was malformed.
+  kOperationMalformed,          // An operation was malformed.
+  kOperationExecutionError,     // An operation failed to execute.
+  kMetadataVerificationFailed,  // Metadata verification failed.
+  kPayloadVerificationFailed,   // Payload verification failed.
+  kVerificationFailed,          // Root or Kernel partition verification failed.
+  kPostInstallFailed,           // The postinstall step failed.
+  kAbnormalTermination,         // The attempt ended abnormally.
+
+  kNumConstants,
+
+  kUnset = -1
+};
+
+// Possible ways the device is connected to the Internet.
+//
+// This is used in the UpdateEngine.Attempt.ConnectionType histogram.
+enum class ConnectionType {
+  kUnknown,           // Unknown.
+  kEthernet,          // Ethernet.
+  kWifi,              // Wireless.
+  kWimax,             // WiMax.
+  kBluetooth,         // Bluetooth.
+  kCellular,          // Cellular.
+  kTetheredEthernet,  // Tethered (Ethernet).
+  kTetheredWifi,      // Tethered (Wifi).
+
+  kNumConstants,
+  kUnset = -1
+};
+
+// Possible ways a rollback can end.
+//
+// This is used in the UpdateEngine.Rollback histogram.
+enum class RollbackResult {
+  kFailed,
+  kSuccess,
+
+  kNumConstants
+};
+
+// Helper function to report metrics related to rollback. The
+// following metrics are reported:
+//
+//  |kMetricRollbackResult|
+void ReportRollbackMetrics(SystemState *system_state,
+                           RollbackResult result);
+
+// Helper function to report metrics reported once a day. The
+// following metrics are reported:
+//
+//  |kMetricDailyOSAgeDays|
+void ReportDailyMetrics(SystemState *system_state,
+                        base::TimeDelta os_age);
+
+// Helper function to report metrics after completing an update check
+// with the ChromeOS update server ("Omaha"). The following metrics
+// are reported:
+//
+//  |kMetricCheckResult|
+//  |kMetricCheckReaction|
+//  |kMetricCheckDownloadErrorCode|
+//  |kMetricCheckTimeSinceLastCheckMinutes|
+//  |kMetricCheckTimeSinceLastCheckUptimeMinutes|
+//
+// The |kMetricCheckResult| metric will only be reported if |result|
+// is not |kUnset|.
+//
+// The |kMetricCheckReaction| metric will only be reported if
+// |reaction| is not |kUnset|.
+//
+// The |kMetricCheckDownloadErrorCode| will only be reported if
+// |download_error_code| is not |kUnset|.
+//
+// The values for the |kMetricCheckTimeSinceLastCheckMinutes| and
+// |kMetricCheckTimeSinceLastCheckUptimeMinutes| metrics are
+// automatically reported and calculated by maintaining persistent
+// and process-local state variables.
+void ReportUpdateCheckMetrics(SystemState *system_state,
+                              CheckResult result,
+                              CheckReaction reaction,
+                              DownloadErrorCode download_error_code);
+
+
+// Helper function to report metrics after the completion of each
+// update attempt. The following metrics are reported:
+//
+//  |kMetricAttemptNumber|
+//  |kMetricAttemptPayloadType|
+//  |kMetricAttemptPayloadSizeMiB|
+//  |kMetricAttemptDurationSeconds|
+//  |kMetricAttemptDurationUptimeSeconds|
+//  |kMetricAttemptTimeSinceLastAttemptMinutes|
+//  |kMetricAttemptTimeSinceLastAttemptUptimeMinutes|
+//  |kMetricAttemptPayloadBytesDownloadedMiB|
+//  |kMetricAttemptPayloadDownloadSpeedKBps|
+//  |kMetricAttemptDownloadSource|
+//  |kMetricAttemptResult|
+//  |kMetricAttemptInternalErrorCode|
+//  |kMetricAttemptDownloadErrorCode|
+//
+// The |kMetricAttemptInternalErrorCode| metric will only be reported
+// if |internal_error_code| is not |kErrorSuccess|.
+//
+// The |kMetricAttemptDownloadErrorCode| metric will only be
+// reported if |payload_download_error_code| is not |kUnset|.
+//
+// The values for the |kMetricAttemptTimeSinceLastAttemptMinutes| and
+// |kMetricAttemptTimeSinceLastAttemptUptimeMinutes| metrics are
+// automatically calculated and reported by maintaining persistent and
+// process-local state variables.
+void ReportUpdateAttemptMetrics(
+    SystemState *system_state,
+    int attempt_number,
+    PayloadType payload_type,
+    base::TimeDelta duration,
+    base::TimeDelta duration_uptime,
+    int64_t payload_size,
+    int64_t payload_bytes_downloaded,
+    int64_t payload_download_speed_bps,
+    DownloadSource download_source,
+    AttemptResult attempt_result,
+    ErrorCode internal_error_code,
+    DownloadErrorCode payload_download_error_code,
+    ConnectionType connection_type);
+
+// Reports the |kAbnormalTermination| for the |kMetricAttemptResult|
+// metric. No other metrics in the UpdateEngine.Attempt.* namespace
+// will be reported.
+void ReportAbnormallyTerminatedUpdateAttemptMetrics(SystemState *system_state);
+
+// Helper function to report the after the completion of a successful
+// update attempt. The following metrics are reported:
+//
+//  |kMetricSuccessfulUpdateAttemptCount|
+//  |kMetricSuccessfulUpdateUpdatesAbandonedCount|
+//  |kMetricSuccessfulUpdatePayloadType|
+//  |kMetricSuccessfulUpdatePayloadSizeMiB|
+//  |kMetricSuccessfulUpdateBytesDownloadedMiBHttpsServer|
+//  |kMetricSuccessfulUpdateBytesDownloadedMiBHttpServer|
+//  |kMetricSuccessfulUpdateBytesDownloadedMiBHttpPeer|
+//  |kMetricSuccessfulUpdateBytesDownloadedMiB|
+//  |kMetricSuccessfulUpdateDownloadSourcesUsed|
+//  |kMetricSuccessfulUpdateDownloadOverheadPercentage|
+//  |kMetricSuccessfulUpdateTotalDurationMinutes|
+//  |kMetricSuccessfulUpdateRebootCount|
+//  |kMetricSuccessfulUpdateUrlSwitchCount|
+//
+// The values for the |kMetricSuccessfulUpdateDownloadSourcesUsed| are
+// |kMetricSuccessfulUpdateBytesDownloadedMiB| metrics automatically
+// calculated from examining the |num_bytes_downloaded| array.
+void ReportSuccessfulUpdateMetrics(
+    SystemState *system_state,
+    int attempt_count,
+    int updates_abandoned_count,
+    PayloadType payload_type,
+    int64_t payload_size,
+    int64_t num_bytes_downloaded[kNumDownloadSources],
+    int download_overhead_percentage,
+    base::TimeDelta total_duration,
+    int reboot_count,
+    int url_switch_count);
+
+// Helper function to report the after the completion of a SSL certificate
+// check. One of the following metrics is reported:
+//
+//  |kMetricCertificateCheckUpdateCheck|
+//  |kMetricCertificateCheckDownload|
+void ReportCertificateCheckMetrics(SystemState* system_state,
+                                   ServerToCheck server_to_check,
+                                   CertificateCheckResult result);
+
+}  // namespace metrics
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_METRICS_H_
diff --git a/metrics_utils.cc b/metrics_utils.cc
new file mode 100644
index 0000000..eb99c7d
--- /dev/null
+++ b/metrics_utils.cc
@@ -0,0 +1,301 @@
+//
+// 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/metrics_utils.h"
+
+#include <string>
+
+#include <base/time/time.h>
+
+#include "update_engine/common/clock_interface.h"
+#include "update_engine/common/prefs_interface.h"
+#include "update_engine/system_state.h"
+
+using base::Time;
+using base::TimeDelta;
+
+namespace chromeos_update_engine {
+namespace metrics_utils {
+
+metrics::AttemptResult GetAttemptResult(ErrorCode code) {
+  ErrorCode base_code = static_cast<ErrorCode>(
+      static_cast<int>(code) & ~static_cast<int>(ErrorCode::kSpecialFlags));
+
+  switch (base_code) {
+    case ErrorCode::kSuccess:
+      return metrics::AttemptResult::kUpdateSucceeded;
+
+    case ErrorCode::kDownloadTransferError:
+      return metrics::AttemptResult::kPayloadDownloadError;
+
+    case ErrorCode::kDownloadInvalidMetadataSize:
+    case ErrorCode::kDownloadInvalidMetadataMagicString:
+    case ErrorCode::kDownloadMetadataSignatureError:
+    case ErrorCode::kDownloadMetadataSignatureVerificationError:
+    case ErrorCode::kPayloadMismatchedType:
+    case ErrorCode::kUnsupportedMajorPayloadVersion:
+    case ErrorCode::kUnsupportedMinorPayloadVersion:
+    case ErrorCode::kDownloadNewPartitionInfoError:
+    case ErrorCode::kDownloadSignatureMissingInManifest:
+    case ErrorCode::kDownloadManifestParseError:
+    case ErrorCode::kDownloadOperationHashMissingError:
+      return metrics::AttemptResult::kMetadataMalformed;
+
+    case ErrorCode::kDownloadOperationHashMismatch:
+    case ErrorCode::kDownloadOperationHashVerificationError:
+      return metrics::AttemptResult::kOperationMalformed;
+
+    case ErrorCode::kDownloadOperationExecutionError:
+    case ErrorCode::kInstallDeviceOpenError:
+    case ErrorCode::kKernelDeviceOpenError:
+    case ErrorCode::kDownloadWriteError:
+    case ErrorCode::kFilesystemCopierError:
+    case ErrorCode::kFilesystemVerifierError:
+      return metrics::AttemptResult::kOperationExecutionError;
+
+    case ErrorCode::kDownloadMetadataSignatureMismatch:
+      return metrics::AttemptResult::kMetadataVerificationFailed;
+
+    case ErrorCode::kPayloadSizeMismatchError:
+    case ErrorCode::kPayloadHashMismatchError:
+    case ErrorCode::kDownloadPayloadVerificationError:
+    case ErrorCode::kSignedDeltaPayloadExpectedError:
+    case ErrorCode::kDownloadPayloadPubKeyVerificationError:
+      return metrics::AttemptResult::kPayloadVerificationFailed;
+
+    case ErrorCode::kNewRootfsVerificationError:
+    case ErrorCode::kNewKernelVerificationError:
+      return metrics::AttemptResult::kVerificationFailed;
+
+    case ErrorCode::kPostinstallRunnerError:
+    case ErrorCode::kPostinstallBootedFromFirmwareB:
+    case ErrorCode::kPostinstallFirmwareRONotUpdatable:
+      return metrics::AttemptResult::kPostInstallFailed;
+
+    // We should never get these errors in the update-attempt stage so
+    // return internal error if this happens.
+    case ErrorCode::kError:
+    case ErrorCode::kOmahaRequestXMLParseError:
+    case ErrorCode::kOmahaRequestError:
+    case ErrorCode::kOmahaResponseHandlerError:
+    case ErrorCode::kDownloadStateInitializationError:
+    case ErrorCode::kOmahaRequestEmptyResponseError:
+    case ErrorCode::kDownloadInvalidMetadataSignature:
+    case ErrorCode::kOmahaResponseInvalid:
+    case ErrorCode::kOmahaUpdateIgnoredPerPolicy:
+    case ErrorCode::kOmahaUpdateDeferredPerPolicy:
+    case ErrorCode::kOmahaErrorInHTTPResponse:
+    case ErrorCode::kDownloadMetadataSignatureMissingError:
+    case ErrorCode::kOmahaUpdateDeferredForBackoff:
+    case ErrorCode::kPostinstallPowerwashError:
+    case ErrorCode::kUpdateCanceledByChannelChange:
+    case ErrorCode::kOmahaRequestXMLHasEntityDecl:
+      return metrics::AttemptResult::kInternalError;
+
+    // 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|.
+    case ErrorCode::kUmaReportedMax:
+    case ErrorCode::kOmahaRequestHTTPResponseBase:
+    case ErrorCode::kDevModeFlag:
+    case ErrorCode::kResumedFlag:
+    case ErrorCode::kTestImageFlag:
+    case ErrorCode::kTestOmahaUrlFlag:
+    case ErrorCode::kSpecialFlags:
+      break;
+  }
+
+  LOG(ERROR) << "Unexpected error code " << base_code;
+  return metrics::AttemptResult::kInternalError;
+}
+
+metrics::DownloadErrorCode GetDownloadErrorCode(ErrorCode code) {
+  ErrorCode base_code = static_cast<ErrorCode>(
+      static_cast<int>(code) & ~static_cast<int>(ErrorCode::kSpecialFlags));
+
+  if (base_code >= ErrorCode::kOmahaRequestHTTPResponseBase) {
+    int http_status =
+        static_cast<int>(base_code) -
+        static_cast<int>(ErrorCode::kOmahaRequestHTTPResponseBase);
+    if (http_status >= 200 && http_status <= 599) {
+      return static_cast<metrics::DownloadErrorCode>(
+          static_cast<int>(metrics::DownloadErrorCode::kHttpStatus200) +
+          http_status - 200);
+    } else if (http_status == 0) {
+      // The code is using HTTP Status 0 for "Unable to get http
+      // response code."
+      return metrics::DownloadErrorCode::kDownloadError;
+    }
+    LOG(WARNING) << "Unexpected HTTP status code " << http_status;
+    return metrics::DownloadErrorCode::kHttpStatusOther;
+  }
+
+  switch (base_code) {
+    // Unfortunately, ErrorCode::kDownloadTransferError is returned for a wide
+    // variety of errors (proxy errors, host not reachable, timeouts etc.).
+    //
+    // For now just map that to kDownloading. See http://crbug.com/355745
+    // for how we plan to add more detail in the future.
+    case ErrorCode::kDownloadTransferError:
+      return metrics::DownloadErrorCode::kDownloadError;
+
+    // All of these error codes are not related to downloading so break
+    // out so we can warn and return InputMalformed.
+    case ErrorCode::kSuccess:
+    case ErrorCode::kError:
+    case ErrorCode::kOmahaRequestError:
+    case ErrorCode::kOmahaResponseHandlerError:
+    case ErrorCode::kFilesystemCopierError:
+    case ErrorCode::kPostinstallRunnerError:
+    case ErrorCode::kPayloadMismatchedType:
+    case ErrorCode::kInstallDeviceOpenError:
+    case ErrorCode::kKernelDeviceOpenError:
+    case ErrorCode::kPayloadHashMismatchError:
+    case ErrorCode::kPayloadSizeMismatchError:
+    case ErrorCode::kDownloadPayloadVerificationError:
+    case ErrorCode::kDownloadNewPartitionInfoError:
+    case ErrorCode::kDownloadWriteError:
+    case ErrorCode::kNewRootfsVerificationError:
+    case ErrorCode::kNewKernelVerificationError:
+    case ErrorCode::kSignedDeltaPayloadExpectedError:
+    case ErrorCode::kDownloadPayloadPubKeyVerificationError:
+    case ErrorCode::kPostinstallBootedFromFirmwareB:
+    case ErrorCode::kDownloadStateInitializationError:
+    case ErrorCode::kDownloadInvalidMetadataMagicString:
+    case ErrorCode::kDownloadSignatureMissingInManifest:
+    case ErrorCode::kDownloadManifestParseError:
+    case ErrorCode::kDownloadMetadataSignatureError:
+    case ErrorCode::kDownloadMetadataSignatureVerificationError:
+    case ErrorCode::kDownloadMetadataSignatureMismatch:
+    case ErrorCode::kDownloadOperationHashVerificationError:
+    case ErrorCode::kDownloadOperationExecutionError:
+    case ErrorCode::kDownloadOperationHashMismatch:
+    case ErrorCode::kOmahaRequestEmptyResponseError:
+    case ErrorCode::kOmahaRequestXMLParseError:
+    case ErrorCode::kDownloadInvalidMetadataSize:
+    case ErrorCode::kDownloadInvalidMetadataSignature:
+    case ErrorCode::kOmahaResponseInvalid:
+    case ErrorCode::kOmahaUpdateIgnoredPerPolicy:
+    case ErrorCode::kOmahaUpdateDeferredPerPolicy:
+    case ErrorCode::kOmahaErrorInHTTPResponse:
+    case ErrorCode::kDownloadOperationHashMissingError:
+    case ErrorCode::kDownloadMetadataSignatureMissingError:
+    case ErrorCode::kOmahaUpdateDeferredForBackoff:
+    case ErrorCode::kPostinstallPowerwashError:
+    case ErrorCode::kUpdateCanceledByChannelChange:
+    case ErrorCode::kPostinstallFirmwareRONotUpdatable:
+    case ErrorCode::kUnsupportedMajorPayloadVersion:
+    case ErrorCode::kUnsupportedMinorPayloadVersion:
+    case ErrorCode::kOmahaRequestXMLHasEntityDecl:
+    case ErrorCode::kFilesystemVerifierError:
+      break;
+
+    // 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 |kInputMalformed|.
+    case ErrorCode::kUmaReportedMax:
+    case ErrorCode::kOmahaRequestHTTPResponseBase:
+    case ErrorCode::kDevModeFlag:
+    case ErrorCode::kResumedFlag:
+    case ErrorCode::kTestImageFlag:
+    case ErrorCode::kTestOmahaUrlFlag:
+    case ErrorCode::kSpecialFlags:
+      LOG(ERROR) << "Unexpected error code " << base_code;
+      break;
+  }
+
+  return metrics::DownloadErrorCode::kInputMalformed;
+}
+
+metrics::ConnectionType GetConnectionType(NetworkConnectionType type,
+                                          NetworkTethering tethering) {
+  switch (type) {
+    case NetworkConnectionType::kUnknown:
+      return metrics::ConnectionType::kUnknown;
+
+    case NetworkConnectionType::kEthernet:
+      if (tethering == NetworkTethering::kConfirmed)
+        return metrics::ConnectionType::kTetheredEthernet;
+      else
+        return metrics::ConnectionType::kEthernet;
+
+    case NetworkConnectionType::kWifi:
+      if (tethering == NetworkTethering::kConfirmed)
+        return metrics::ConnectionType::kTetheredWifi;
+      else
+        return metrics::ConnectionType::kWifi;
+
+    case NetworkConnectionType::kWimax:
+      return metrics::ConnectionType::kWimax;
+
+    case NetworkConnectionType::kBluetooth:
+      return metrics::ConnectionType::kBluetooth;
+
+    case NetworkConnectionType::kCellular:
+      return metrics::ConnectionType::kCellular;
+  }
+
+  LOG(ERROR) << "Unexpected network connection type: type="
+             << static_cast<int>(type)
+             << ", tethering=" << static_cast<int>(tethering);
+
+  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;
+}
+
+}  // namespace metrics_utils
+}  // namespace chromeos_update_engine
diff --git a/metrics_utils.h b/metrics_utils.h
new file mode 100644
index 0000000..7c3b02d
--- /dev/null
+++ b/metrics_utils.h
@@ -0,0 +1,71 @@
+//
+// 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_METRICS_UTILS_H_
+#define UPDATE_ENGINE_METRICS_UTILS_H_
+
+#include "update_engine/connection_manager.h"
+#include "update_engine/metrics.h"
+
+namespace chromeos_update_engine {
+
+class SystemState;
+
+namespace metrics_utils {
+
+// Transforms a ErrorCode value into a metrics::DownloadErrorCode.
+// This obviously only works for errors related to downloading so if |code|
+// is e.g. |ErrorCode::kFilesystemCopierError| then
+// |kDownloadErrorCodeInputMalformed| is returned.
+metrics::DownloadErrorCode GetDownloadErrorCode(ErrorCode code);
+
+// Transforms a ErrorCode value into a metrics::AttemptResult.
+//
+// If metrics::AttemptResult::kPayloadDownloadError is returned, you
+// can use utils::GetDownloadError() to get more detail.
+metrics::AttemptResult GetAttemptResult(ErrorCode code);
+
+// Calculates the internet connection type given |type| and |tethering|.
+metrics::ConnectionType GetConnectionType(NetworkConnectionType type,
+                                          NetworkTethering 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);
+
+}  // namespace metrics_utils
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_METRICS_UTILS_H_
diff --git a/metrics_utils_unittest.cc b/metrics_utils_unittest.cc
new file mode 100644
index 0000000..e702c17
--- /dev/null
+++ b/metrics_utils_unittest.cc
@@ -0,0 +1,210 @@
+//
+// 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/metrics_utils.h"
+
+#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 {
+
+class MetricsUtilsTest : public ::testing::Test {};
+
+TEST(MetricsUtilsTest, GetConnectionType) {
+  // Check that expected combinations map to the right value.
+  EXPECT_EQ(metrics::ConnectionType::kUnknown,
+            GetConnectionType(NetworkConnectionType::kUnknown,
+                              NetworkTethering::kUnknown));
+  EXPECT_EQ(metrics::ConnectionType::kEthernet,
+            GetConnectionType(NetworkConnectionType::kEthernet,
+                              NetworkTethering::kUnknown));
+  EXPECT_EQ(metrics::ConnectionType::kWifi,
+            GetConnectionType(NetworkConnectionType::kWifi,
+                              NetworkTethering::kUnknown));
+  EXPECT_EQ(metrics::ConnectionType::kWimax,
+            GetConnectionType(NetworkConnectionType::kWimax,
+                              NetworkTethering::kUnknown));
+  EXPECT_EQ(metrics::ConnectionType::kBluetooth,
+            GetConnectionType(NetworkConnectionType::kBluetooth,
+                              NetworkTethering::kUnknown));
+  EXPECT_EQ(metrics::ConnectionType::kCellular,
+            GetConnectionType(NetworkConnectionType::kCellular,
+                              NetworkTethering::kUnknown));
+  EXPECT_EQ(metrics::ConnectionType::kTetheredEthernet,
+            GetConnectionType(NetworkConnectionType::kEthernet,
+                              NetworkTethering::kConfirmed));
+  EXPECT_EQ(metrics::ConnectionType::kTetheredWifi,
+            GetConnectionType(NetworkConnectionType::kWifi,
+                              NetworkTethering::kConfirmed));
+
+  // Ensure that we don't report tethered ethernet unless it's confirmed.
+  EXPECT_EQ(metrics::ConnectionType::kEthernet,
+            GetConnectionType(NetworkConnectionType::kEthernet,
+                              NetworkTethering::kNotDetected));
+  EXPECT_EQ(metrics::ConnectionType::kEthernet,
+            GetConnectionType(NetworkConnectionType::kEthernet,
+                              NetworkTethering::kSuspected));
+  EXPECT_EQ(metrics::ConnectionType::kEthernet,
+            GetConnectionType(NetworkConnectionType::kEthernet,
+                              NetworkTethering::kUnknown));
+
+  // Ditto for tethered wifi.
+  EXPECT_EQ(metrics::ConnectionType::kWifi,
+            GetConnectionType(NetworkConnectionType::kWifi,
+                              NetworkTethering::kNotDetected));
+  EXPECT_EQ(metrics::ConnectionType::kWifi,
+            GetConnectionType(NetworkConnectionType::kWifi,
+                              NetworkTethering::kSuspected));
+  EXPECT_EQ(metrics::ConnectionType::kWifi,
+            GetConnectionType(NetworkConnectionType::kWifi,
+                              NetworkTethering::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_action.h b/mock_action.h
new file mode 100644
index 0000000..0ba796d
--- /dev/null
+++ b/mock_action.h
@@ -0,0 +1,45 @@
+//
+// 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_MOCK_ACTION_H_
+#define UPDATE_ENGINE_MOCK_ACTION_H_
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include "update_engine/common/action.h"
+
+namespace chromeos_update_engine {
+
+class MockAction;
+
+template<>
+class ActionTraits<MockAction> {
+ public:
+  typedef NoneType OutputObjectType;
+  typedef NoneType InputObjectType;
+};
+
+class MockAction : public Action<MockAction> {
+ public:
+  MOCK_METHOD0(PerformAction, void());
+  MOCK_CONST_METHOD0(Type, std::string());
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_MOCK_ACTION_H_
diff --git a/mock_action_processor.h b/mock_action_processor.h
new file mode 100644
index 0000000..c98ff3c
--- /dev/null
+++ b/mock_action_processor.h
@@ -0,0 +1,34 @@
+//
+// 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_MOCK_ACTION_PROCESSOR_H_
+#define UPDATE_ENGINE_MOCK_ACTION_PROCESSOR_H_
+
+#include <gmock/gmock.h>
+
+#include "update_engine/common/action.h"
+
+namespace chromeos_update_engine {
+
+class MockActionProcessor : public ActionProcessor {
+ public:
+  MOCK_METHOD0(StartProcessing, void());
+  MOCK_METHOD1(EnqueueAction, void(AbstractAction* action));
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_MOCK_ACTION_PROCESSOR_H_
diff --git a/mock_connection_manager.h b/mock_connection_manager.h
new file mode 100644
index 0000000..109c529
--- /dev/null
+++ b/mock_connection_manager.h
@@ -0,0 +1,43 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_MOCK_CONNECTION_MANAGER_H_
+#define UPDATE_ENGINE_MOCK_CONNECTION_MANAGER_H_
+
+#include <gmock/gmock.h>
+
+#include "update_engine/connection_manager_interface.h"
+
+namespace chromeos_update_engine {
+
+// This class mocks the generic interface to the connection manager
+// (e.g FlimFlam, Shill, etc.) to consolidate all connection-related
+// logic in update_engine.
+class MockConnectionManager : public ConnectionManagerInterface {
+ public:
+  MockConnectionManager() = default;
+
+  MOCK_METHOD2(GetConnectionProperties,
+               bool(NetworkConnectionType* out_type,
+                    NetworkTethering* out_tethering));
+
+  MOCK_CONST_METHOD2(IsUpdateAllowedOver, bool(NetworkConnectionType type,
+                                               NetworkTethering tethering));
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_MOCK_CONNECTION_MANAGER_H_
diff --git a/mock_file_writer.h b/mock_file_writer.h
new file mode 100644
index 0000000..72d6a86
--- /dev/null
+++ b/mock_file_writer.h
@@ -0,0 +1,33 @@
+//
+// 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_MOCK_FILE_WRITER_H_
+#define UPDATE_ENGINE_MOCK_FILE_WRITER_H_
+
+#include <gmock/gmock.h>
+#include "update_engine/payload_consumer/file_writer.h"
+
+namespace chromeos_update_engine {
+
+class MockFileWriter : public FileWriter {
+ public:
+  MOCK_METHOD2(Write, ssize_t(const void* bytes, size_t count));
+  MOCK_METHOD0(Close, int());
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_MOCK_FILE_WRITER_H_
diff --git a/mock_omaha_request_params.h b/mock_omaha_request_params.h
new file mode 100644
index 0000000..5d5d47b
--- /dev/null
+++ b/mock_omaha_request_params.h
@@ -0,0 +1,91 @@
+//
+// 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_MOCK_OMAHA_REQUEST_PARAMS_H_
+#define UPDATE_ENGINE_MOCK_OMAHA_REQUEST_PARAMS_H_
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include "update_engine/omaha_request_params.h"
+
+namespace chromeos_update_engine {
+
+class MockOmahaRequestParams : public OmahaRequestParams {
+ public:
+  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.
+    ON_CALL(*this, to_more_stable_channel())
+        .WillByDefault(testing::Invoke(
+            this, &MockOmahaRequestParams::fake_to_more_stable_channel));
+    ON_CALL(*this, GetAppId())
+        .WillByDefault(testing::Invoke(
+            this, &MockOmahaRequestParams::FakeGetAppId));
+    ON_CALL(*this, SetTargetChannel(testing::_, testing::_, testing::_))
+        .WillByDefault(testing::Invoke(
+            this, &MockOmahaRequestParams::FakeSetTargetChannel));
+    ON_CALL(*this, UpdateDownloadChannel())
+        .WillByDefault(testing::Invoke(
+            this, &MockOmahaRequestParams::FakeUpdateDownloadChannel));
+    ON_CALL(*this, is_powerwash_allowed())
+        .WillByDefault(testing::Invoke(
+            this, &MockOmahaRequestParams::fake_is_powerwash_allowed));
+  }
+
+  MOCK_CONST_METHOD0(to_more_stable_channel, bool(void));
+  MOCK_CONST_METHOD0(GetAppId, std::string(void));
+  MOCK_METHOD3(SetTargetChannel, bool(const std::string& channel,
+                                      bool is_powerwash_allowed,
+                                      std::string* error));
+  MOCK_METHOD0(UpdateDownloadChannel, void(void));
+  MOCK_CONST_METHOD0(is_powerwash_allowed, bool(void));
+  MOCK_CONST_METHOD0(IsUpdateUrlOfficial, bool(void));
+
+ private:
+  // Wrappers to call the parent class and behave like the real object by
+  // default. See "Delegating Calls to a Parent Class" in gmock's documentation.
+  bool fake_to_more_stable_channel() const {
+    return OmahaRequestParams::to_more_stable_channel();
+  }
+
+  std::string FakeGetAppId() const {
+    return OmahaRequestParams::GetAppId();
+  }
+
+  bool FakeSetTargetChannel(const std::string& channel,
+                            bool is_powerwash_allowed,
+                            std::string* error) {
+    return OmahaRequestParams::SetTargetChannel(channel,
+                                                is_powerwash_allowed,
+                                                error);
+  }
+
+  void FakeUpdateDownloadChannel() {
+    return OmahaRequestParams::UpdateDownloadChannel();
+  }
+
+  bool fake_is_powerwash_allowed() const {
+    return OmahaRequestParams::is_powerwash_allowed();
+  }
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_MOCK_OMAHA_REQUEST_PARAMS_H_
diff --git a/mock_p2p_manager.h b/mock_p2p_manager.h
new file mode 100644
index 0000000..5f4418e
--- /dev/null
+++ b/mock_p2p_manager.h
@@ -0,0 +1,109 @@
+//
+// 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_MOCK_P2P_MANAGER_H_
+#define UPDATE_ENGINE_MOCK_P2P_MANAGER_H_
+
+#include <string>
+
+#include "update_engine/fake_p2p_manager.h"
+
+#include <gmock/gmock.h>
+
+namespace chromeos_update_engine {
+
+// A mocked, fake implementation of P2PManager.
+class MockP2PManager : public P2PManager {
+ public:
+  MockP2PManager() {
+    // Delegate all calls to the fake instance
+    ON_CALL(*this, SetDevicePolicy(testing::_))
+      .WillByDefault(testing::Invoke(&fake_,
+            &FakeP2PManager::SetDevicePolicy));
+    ON_CALL(*this, IsP2PEnabled())
+      .WillByDefault(testing::Invoke(&fake_,
+            &FakeP2PManager::IsP2PEnabled));
+    ON_CALL(*this, EnsureP2PRunning())
+      .WillByDefault(testing::Invoke(&fake_,
+            &FakeP2PManager::EnsureP2PRunning));
+    ON_CALL(*this, EnsureP2PNotRunning())
+      .WillByDefault(testing::Invoke(&fake_,
+            &FakeP2PManager::EnsureP2PNotRunning));
+    ON_CALL(*this, PerformHousekeeping())
+      .WillByDefault(testing::Invoke(&fake_,
+            &FakeP2PManager::PerformHousekeeping));
+    ON_CALL(*this, LookupUrlForFile(testing::_, testing::_, testing::_,
+                                    testing::_))
+      .WillByDefault(testing::Invoke(&fake_,
+            &FakeP2PManager::LookupUrlForFile));
+    ON_CALL(*this, FileShare(testing::_, testing::_))
+      .WillByDefault(testing::Invoke(&fake_,
+            &FakeP2PManager::FileShare));
+    ON_CALL(*this, FileGetPath(testing::_))
+      .WillByDefault(testing::Invoke(&fake_,
+            &FakeP2PManager::FileGetPath));
+    ON_CALL(*this, FileGetSize(testing::_))
+      .WillByDefault(testing::Invoke(&fake_,
+            &FakeP2PManager::FileGetSize));
+    ON_CALL(*this, FileGetExpectedSize(testing::_))
+      .WillByDefault(testing::Invoke(&fake_,
+            &FakeP2PManager::FileGetExpectedSize));
+    ON_CALL(*this, FileGetVisible(testing::_, testing::_))
+      .WillByDefault(testing::Invoke(&fake_,
+            &FakeP2PManager::FileGetVisible));
+    ON_CALL(*this, FileMakeVisible(testing::_))
+      .WillByDefault(testing::Invoke(&fake_,
+            &FakeP2PManager::FileMakeVisible));
+    ON_CALL(*this, CountSharedFiles())
+      .WillByDefault(testing::Invoke(&fake_,
+            &FakeP2PManager::CountSharedFiles));
+  }
+
+  ~MockP2PManager() override {}
+
+  // P2PManager overrides.
+  MOCK_METHOD1(SetDevicePolicy, void(const policy::DevicePolicy*));
+  MOCK_METHOD0(IsP2PEnabled, bool());
+  MOCK_METHOD0(EnsureP2PRunning, bool());
+  MOCK_METHOD0(EnsureP2PNotRunning, bool());
+  MOCK_METHOD0(PerformHousekeeping, bool());
+  MOCK_METHOD4(LookupUrlForFile, void(const std::string&,
+                                      size_t,
+                                      base::TimeDelta,
+                                      LookupCallback));
+  MOCK_METHOD2(FileShare, bool(const std::string&, size_t));
+  MOCK_METHOD1(FileGetPath, base::FilePath(const std::string&));
+  MOCK_METHOD1(FileGetSize, ssize_t(const std::string&));
+  MOCK_METHOD1(FileGetExpectedSize, ssize_t(const std::string&));
+  MOCK_METHOD2(FileGetVisible, bool(const std::string&, bool*));
+  MOCK_METHOD1(FileMakeVisible, bool(const std::string&));
+  MOCK_METHOD0(CountSharedFiles, int());
+
+  // Returns a reference to the underlying FakeP2PManager.
+  FakeP2PManager& fake() {
+    return fake_;
+  }
+
+ private:
+  // The underlying FakeP2PManager.
+  FakeP2PManager fake_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockP2PManager);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_MOCK_P2P_MANAGER_H_
diff --git a/mock_payload_state.h b/mock_payload_state.h
new file mode 100644
index 0000000..728f274
--- /dev/null
+++ b/mock_payload_state.h
@@ -0,0 +1,81 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_MOCK_PAYLOAD_STATE_H_
+#define UPDATE_ENGINE_MOCK_PAYLOAD_STATE_H_
+
+#include <string>
+
+#include <gmock/gmock.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(SystemState* system_state) {
+    return true;
+  }
+
+  // Significant methods.
+  MOCK_METHOD1(SetResponse, void(const OmahaResponse& response));
+  MOCK_METHOD0(DownloadComplete, void());
+  MOCK_METHOD1(DownloadProgress, void(size_t count));
+  MOCK_METHOD0(UpdateResumed, void());
+  MOCK_METHOD0(UpdateRestarted, void());
+  MOCK_METHOD0(UpdateSucceeded, void());
+  MOCK_METHOD1(UpdateFailed, void(ErrorCode error));
+  MOCK_METHOD0(ResetUpdateStatus, void());
+  MOCK_METHOD0(ShouldBackoffDownload, bool());
+  MOCK_METHOD0(UpdateEngineStarted, void());
+  MOCK_METHOD0(Rollback, void());
+  MOCK_METHOD1(ExpectRebootInNewVersion,
+               void(const std::string& target_version_uid));
+  MOCK_METHOD0(P2PNewAttempt, void());
+  MOCK_METHOD0(P2PAttemptAllowed, bool());
+  MOCK_METHOD1(SetUsingP2PForDownloading, void(bool value));
+  MOCK_METHOD1(SetUsingP2PForSharing, void(bool value));
+  MOCK_METHOD1(SetScatteringWaitPeriod, void(base::TimeDelta));
+  MOCK_METHOD1(SetP2PUrl, void(const std::string&));
+
+  // Getters.
+  MOCK_METHOD0(GetResponseSignature, std::string());
+  MOCK_METHOD0(GetPayloadAttemptNumber, int());
+  MOCK_METHOD0(GetFullPayloadAttemptNumber, int());
+  MOCK_METHOD0(GetCurrentUrl, std::string());
+  MOCK_METHOD0(GetUrlFailureCount, uint32_t());
+  MOCK_METHOD0(GetUrlSwitchCount, uint32_t());
+  MOCK_METHOD0(GetNumResponsesSeen, int());
+  MOCK_METHOD0(GetBackoffExpiryTime, base::Time());
+  MOCK_METHOD0(GetUpdateDuration, base::TimeDelta());
+  MOCK_METHOD0(GetUpdateDurationUptime, base::TimeDelta());
+  MOCK_METHOD1(GetCurrentBytesDownloaded, uint64_t(DownloadSource source));
+  MOCK_METHOD1(GetTotalBytesDownloaded, uint64_t(DownloadSource source));
+  MOCK_METHOD0(GetNumReboots, uint32_t());
+  MOCK_METHOD0(GetRollbackVersion, std::string());
+  MOCK_METHOD0(GetP2PNumAttempts, int());
+  MOCK_METHOD0(GetP2PFirstAttemptTimestamp, base::Time());
+  MOCK_CONST_METHOD0(GetUsingP2PForDownloading, bool());
+  MOCK_CONST_METHOD0(GetUsingP2PForSharing, bool());
+  MOCK_METHOD0(GetScatteringWaitPeriod, base::TimeDelta());
+  MOCK_CONST_METHOD0(GetP2PUrl, std::string());
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_MOCK_PAYLOAD_STATE_H_
diff --git a/mock_update_attempter.h b/mock_update_attempter.h
new file mode 100644
index 0000000..89f163e
--- /dev/null
+++ b/mock_update_attempter.h
@@ -0,0 +1,64 @@
+//
+// 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_MOCK_UPDATE_ATTEMPTER_H_
+#define UPDATE_ENGINE_MOCK_UPDATE_ATTEMPTER_H_
+
+#include <string>
+
+#include "update_engine/update_attempter.h"
+
+#include <gmock/gmock.h>
+
+namespace chromeos_update_engine {
+
+class MockUpdateAttempter : public UpdateAttempter {
+ public:
+  using UpdateAttempter::UpdateAttempter;
+
+  MOCK_METHOD6(Update, void(const std::string& app_version,
+                            const std::string& omaha_url,
+                            const std::string& target_channel,
+                            const std::string& target_version_prefix,
+                            bool obey_proxies,
+                            bool interactive));
+
+  MOCK_METHOD5(GetStatus, bool(int64_t* last_checked_time,
+                               double* progress,
+                               std::string* current_operation,
+                               std::string* new_version,
+                               int64_t* new_size));
+
+  MOCK_METHOD1(GetBootTimeAtUpdate, bool(base::Time* out_boot_time));
+
+  MOCK_METHOD0(ResetStatus, bool(void));
+
+  MOCK_METHOD3(CheckForUpdate, void(const std::string& app_version,
+                                    const std::string& omaha_url,
+                                    bool is_interactive));
+
+  MOCK_METHOD0(RefreshDevicePolicy, void(void));
+
+  MOCK_CONST_METHOD0(consecutive_failed_update_checks, unsigned int(void));
+
+  MOCK_CONST_METHOD0(server_dictated_poll_interval, unsigned int(void));
+
+  MOCK_METHOD0(IsAnyUpdateSourceAllowed, bool(void));
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_MOCK_UPDATE_ATTEMPTER_H_
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
new file mode 100644
index 0000000..f2cd032
--- /dev/null
+++ b/omaha_request_action.cc
@@ -0,0 +1,1484 @@
+//
+// 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 <map>
+#include <sstream>
+#include <string>
+#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_util.h>
+#include <base/strings/stringprintf.h>
+#include <base/time/time.h>
+#include <expat.h>
+#include <metrics/metrics_library.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.h"
+#include "update_engine/metrics.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 std::map;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+// List of custom pair tags that we interpret in the Omaha Response:
+static const char* kTagDeadline = "deadline";
+static const char* kTagDisablePayloadBackoff = "DisablePayloadBackoff";
+static const char* kTagVersion = "version";
+// Deprecated: "IsDelta"
+static const char* kTagIsDeltaPayload = "IsDeltaPayload";
+static const char* kTagMaxFailureCountPerUrl = "MaxFailureCountPerUrl";
+static const char* kTagMaxDaysToScatter = "MaxDaysToScatter";
+// Deprecated: "ManifestSignatureRsa"
+// Deprecated: "ManifestSize"
+static const char* kTagMetadataSignatureRsa = "MetadataSignatureRsa";
+static const char* kTagMetadataSize = "MetadataSize";
+static const char* kTagMoreInfo = "MoreInfo";
+// Deprecated: "NeedsAdmin"
+static const char* kTagPrompt = "Prompt";
+static const char* kTagSha256 = "sha256";
+static const char* kTagDisableP2PForDownloading = "DisableP2PForDownloading";
+static const char* kTagDisableP2PForSharing = "DisableP2PForSharing";
+static const char* kTagPublicKeyRsa = "PublicKeyRsa";
+
+static const char* kOmahaUpdaterVersion = "0.1.0.0";
+
+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,
+                  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) {
+      app_body += base::StringPrintf(
+          "        <updatecheck targetversionprefix=\"%s\""
+          "></updatecheck>\n",
+          XmlEncodeWithDefault(params->target_version_prefix(), "").c_str());
+
+      // 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
+  // existance 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());
+}
+
+// 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,
+                 bool ping_only,
+                 bool include_ping,
+                 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,
+                               ping_active_days, ping_roll_call_days,
+                               system_state->prefs());
+  string app_versions;
+
+  // If we are upgrading 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->to_more_stable_channel() && params->is_powerwash_allowed()) {
+    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(params->app_version(), "0.0.0.0") + "\" ";
+  } else {
+    app_versions = "version=\"" +
+        XmlEncodeWithDefault(params->app_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 app_xml = "    <app "
+      "appid=\"" + XmlEncodeWithDefault(params->GetAppId(), "") + "\" " +
+      app_cohort_args +
+      app_versions +
+      app_channels +
+      "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";
+
+  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);
+  string app_xml = GetAppXml(event, params, ping_only, include_ping,
+                             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 app_cohort;
+  string app_cohorthint;
+  string app_cohortname;
+  bool app_cohort_set = false;
+  bool app_cohorthint_set = false;
+  bool app_cohortname_set = false;
+  string updatecheck_status;
+  string updatecheck_poll_interval;
+  string daystart_elapsed_days;
+  string daystart_elapsed_seconds;
+  vector<string> url_codebase;
+  string package_name;
+  string package_size;
+  string manifest_version;
+  map<string, string> action_postinstall_attrs;
+};
+
+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") {
+    if (attrs.find("cohort") != attrs.end()) {
+      data->app_cohort_set = true;
+      data->app_cohort = attrs["cohort"];
+    }
+    if (attrs.find("cohorthint") != attrs.end()) {
+      data->app_cohorthint_set = true;
+      data->app_cohorthint = attrs["cohorthint"];
+    }
+    if (attrs.find("cohortname") != attrs.end()) {
+      data->app_cohortname_set = true;
+      data->app_cohortname = attrs["cohortname"];
+    }
+  } else if (data->current_path == "/response/app/updatecheck") {
+    // There is only supposed to be a single <updatecheck> element.
+    data->updatecheck_status = attrs["status"];
+    data->updatecheck_poll_interval = attrs["PollInterval"];
+  } else if (data->current_path == "/response/daystart") {
+    // Get the install-date.
+    data->daystart_elapsed_days = attrs["elapsed_days"];
+    data->daystart_elapsed_seconds = attrs["elapsed_seconds"];
+  } else if (data->current_path == "/response/app/updatecheck/urls/url") {
+    // Look at all <url> elements.
+    data->url_codebase.push_back(attrs["codebase"]);
+  } else if (data->package_name.empty() && data->current_path ==
+             "/response/app/updatecheck/manifest/packages/package") {
+    // Only look at the first <package>.
+    data->package_name = attrs["name"];
+    data->package_size = attrs["size"];
+  } else if (data->current_path == "/response/app/updatecheck/manifest") {
+    // Get the version.
+    data->manifest_version = attrs[kTagVersion];
+  } else if (data->current_path ==
+             "/response/app/updatecheck/manifest/actions/action") {
+    // We only care about the postinstall action.
+    if (attrs["event"] == "postinstall") {
+      data->action_postinstall_attrs = 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, true)) {
+    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),
+      event_(event),
+      http_fetcher_(std::move(http_fetcher)),
+      ping_only_(ping_only),
+      ping_active_days_(0),
+      ping_roll_call_days_(0) {
+  params_ = system_state->request_params();
+}
+
+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;
+    }
+    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()->IsOOBEComplete(&time_of_oobe)) {
+    LOG(INFO) << "Not generating Omaha InstallData as we have "
+              << "no prefs file and OOBE is not complete.";
+    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_));
+
+  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.
+void 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);
+}
+
+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;
+}
+}  // namespace
+
+bool OmahaRequestAction::ParseResponse(OmahaParserData* parser_data,
+                                       OmahaResponse* output_object,
+                                       ScopedActionCompleter* completer) {
+  if (parser_data->updatecheck_status.empty()) {
+    completer->set_code(ErrorCode::kOmahaResponseInvalid);
+    return false;
+  }
+
+  // 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".
+  if (parser_data->app_cohort_set)
+    PersistCohortData(kPrefsOmahaCohort, parser_data->app_cohort);
+  if (parser_data->app_cohorthint_set)
+    PersistCohortData(kPrefsOmahaCohortHint, parser_data->app_cohorthint);
+  if (parser_data->app_cohortname_set)
+    PersistCohortData(kPrefsOmahaCohortName, parser_data->app_cohortname);
+
+  if (!ParseStatus(parser_data, output_object, completer))
+    return false;
+
+  // Note: ParseUrls MUST be called before ParsePackage as ParsePackage
+  // appends the package name to the URLs populated in this method.
+  if (!ParseUrls(parser_data, output_object, completer))
+    return false;
+
+  if (!ParsePackage(parser_data, output_object, completer))
+    return false;
+
+  if (!ParseParams(parser_data, output_object, completer))
+    return false;
+
+  return true;
+}
+
+bool OmahaRequestAction::ParseStatus(OmahaParserData* parser_data,
+                                     OmahaResponse* output_object,
+                                     ScopedActionCompleter* completer) {
+  const string& status = parser_data->updatecheck_status;
+  if (status == "noupdate") {
+    LOG(INFO) << "No update.";
+    output_object->update_exists = false;
+    SetOutputObject(*output_object);
+    completer->set_code(ErrorCode::kSuccess);
+    return false;
+  }
+
+  if (status != "ok") {
+    LOG(ERROR) << "Unknown Omaha response status: " << status;
+    completer->set_code(ErrorCode::kOmahaResponseInvalid);
+    return false;
+  }
+
+  return true;
+}
+
+bool OmahaRequestAction::ParseUrls(OmahaParserData* parser_data,
+                                   OmahaResponse* output_object,
+                                   ScopedActionCompleter* completer) {
+  if (parser_data->url_codebase.empty()) {
+    LOG(ERROR) << "No Omaha Response URLs";
+    completer->set_code(ErrorCode::kOmahaResponseInvalid);
+    return false;
+  }
+
+  LOG(INFO) << "Found " << parser_data->url_codebase.size() << " url(s)";
+  output_object->payload_urls.clear();
+  for (const auto& codebase : parser_data->url_codebase) {
+    if (codebase.empty()) {
+      LOG(ERROR) << "Omaha Response URL has empty codebase";
+      completer->set_code(ErrorCode::kOmahaResponseInvalid);
+      return false;
+    }
+    output_object->payload_urls.push_back(codebase);
+  }
+
+  return true;
+}
+
+bool OmahaRequestAction::ParsePackage(OmahaParserData* parser_data,
+                                      OmahaResponse* output_object,
+                                      ScopedActionCompleter* completer) {
+  if (parser_data->package_name.empty()) {
+    LOG(ERROR) << "Omaha Response has empty package name";
+    completer->set_code(ErrorCode::kOmahaResponseInvalid);
+    return false;
+  }
+
+  // Append the package name to each URL in our list so that we don't
+  // propagate the urlBase vs packageName distinctions beyond this point.
+  // From now on, we only need to use payload_urls.
+  for (auto& payload_url : output_object->payload_urls)
+    payload_url += parser_data->package_name;
+
+  // Parse the payload size.
+  off_t size = ParseInt(parser_data->package_size);
+  if (size <= 0) {
+    LOG(ERROR) << "Omaha Response has invalid payload size: " << size;
+    completer->set_code(ErrorCode::kOmahaResponseInvalid);
+    return false;
+  }
+  output_object->size = size;
+
+  LOG(INFO) << "Payload size = " << output_object->size << " bytes";
+
+  return true;
+}
+
+bool OmahaRequestAction::ParseParams(OmahaParserData* parser_data,
+                                     OmahaResponse* output_object,
+                                     ScopedActionCompleter* completer) {
+  output_object->version = parser_data->manifest_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;
+
+  map<string, string> attrs = parser_data->action_postinstall_attrs;
+  if (attrs.empty()) {
+    LOG(ERROR) << "Omaha Response has no postinstall event action";
+    completer->set_code(ErrorCode::kOmahaResponseInvalid);
+    return false;
+  }
+
+  output_object->hash = attrs[kTagSha256];
+  if (output_object->hash.empty()) {
+    LOG(ERROR) << "Omaha Response has empty sha256 value";
+    completer->set_code(ErrorCode::kOmahaResponseInvalid);
+    return false;
+  }
+
+  // Get the optional properties one by one.
+  output_object->more_info_url = attrs[kTagMoreInfo];
+  output_object->metadata_size = ParseInt(attrs[kTagMetadataSize]);
+  output_object->metadata_signature = attrs[kTagMetadataSignatureRsa];
+  output_object->prompt = ParseBool(attrs[kTagPrompt]);
+  output_object->deadline = attrs[kTagDeadline];
+  output_object->max_days_to_scatter = ParseInt(attrs[kTagMaxDaysToScatter]);
+  output_object->disable_p2p_for_downloading =
+      ParseBool(attrs[kTagDisableP2PForDownloading]);
+  output_object->disable_p2p_for_sharing =
+      ParseBool(attrs[kTagDisableP2PForSharing]);
+  output_object->public_key_rsa = attrs[kTagPublicKeyRsa];
+
+  string max = attrs[kTagMaxFailureCountPerUrl];
+  if (!base::StringToUint(max, &output_object->max_failure_count_per_url))
+    output_object->max_failure_count_per_url = kDefaultMaxFailureCountPerUrl;
+
+  output_object->is_delta_payload = ParseBool(attrs[kTagIsDeltaPayload]);
+
+  output_object->disable_payload_backoff =
+      ParseBool(attrs[kTagDisablePayloadBackoff]);
+
+  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();
+
+  // Events are best effort transactions -- assume they always succeed.
+  if (IsEvent()) {
+    CHECK(!HasOutputPipe()) << "No output pipe allowed for event requests.";
+    if (event_->result == OmahaEvent::kResultError && successful &&
+        system_state_->hardware()->IsOfficialBuild()) {
+      LOG(INFO) << "Signalling Crash Reporter.";
+      utils::ScheduleCrashReporterUpload();
+    }
+    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);
+  XML_ParserFree(parser);
+
+  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);
+    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;
+  }
+
+  // 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!";
+
+  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);
+
+  if (ShouldIgnoreUpdate(output_object)) {
+    output_object.update_exists = false;
+    completer.set_code(ErrorCode::kOmahaUpdateIgnoredPerPolicy);
+    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 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(kPrefsUpdateStateNextDataOffset,
+                                       &next_data_offset) &&
+      next_data_offset != -1 &&
+      system_state_->prefs()->GetInt64(kPrefsUpdateStateNextDataLength,
+                                       &next_data_length)) {
+    minimum_size = manifest_metadata_size + next_data_offset + next_data_length;
+  }
+
+  string file_id = utils::CalculateP2PFileId(response.hash, response.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;
+  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. Just skip scattering in this
+      // case to be safe.
+     LOG(INFO) << "Not scattering as UpdateFirstSeenAt value cannot be read";
+     return kWallClockWaitDoneAndUpdateCheckWaitNotRequired;
+    }
+  } else {
+    update_first_seen_at = Time::Now();
+    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. Just skip scattering in this
+      // case to be safe.
+      LOG(INFO) << "Not scattering as UpdateFirstSeenAt value "
+                << utils::ToString(update_first_seen_at)
+                << " cannot be persisted";
+     return kWallClockWaitDoneAndUpdateCheckWaitNotRequired;
+    }
+  }
+
+  TimeDelta elapsed_time = Time::Now() - update_first_seen_at;
+  TimeDelta max_scatter_period = TimeDelta::FromDays(
+      output_object->max_days_to_scatter);
+
+  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;
+
+  string metric_name = metrics::kMetricInstallDateProvisioningSource;
+  system_state->metrics_lib()->SendEnumToUMA(
+      metric_name,
+      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;
+}
+
+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:
+    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;
+  }
+
+  metrics::ReportUpdateCheckMetrics(system_state_,
+                                    result, reaction, download_error_code);
+}
+
+bool OmahaRequestAction::ShouldIgnoreUpdate(
+    const OmahaResponse& response) 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.";
+      return true;
+    }
+  }
+
+  if (!IsUpdateAllowedOverCurrentConnection()) {
+    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::IsUpdateAllowedOverCurrentConnection() const {
+  NetworkConnectionType type;
+  NetworkTethering 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);
+  LOG(INFO) << "We are connected via "
+            << ConnectionManager::StringForConnectionType(type)
+            << ", Updates allowed: " << (is_allowed ? "Yes" : "No");
+  return is_allowed;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/omaha_request_action.h b/omaha_request_action.h
new file mode 100644
index 0000000..1aeaf8a
--- /dev/null
+++ b/omaha_request_action.h
@@ -0,0 +1,332 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_OMAHA_REQUEST_ACTION_H_
+#define UPDATE_ENGINE_OMAHA_REQUEST_ACTION_H_
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#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/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.
+
+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;
+
+template<>
+class ActionTraits<OmahaRequestAction> {
+ public:
+  // Takes parameters on the input pipe.
+  typedef NoneType InputObjectType;
+  // On UpdateCheck success, puts the Omaha response on output. Event
+  // requests do not have an output pipe.
+  typedef OmahaResponse OutputObjectType;
+};
+
+class OmahaRequestAction : public Action<OmahaRequestAction>,
+                           public HttpFetcherDelegate {
+ public:
+  static const int kNeverPinged = -1;
+  static const int kPingTimeJump = -2;
+  // 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 - 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;
+
+  // These are the possible outcome upon checking whether we satisfied
+  // the wall-clock-based-wait.
+  enum WallClockWaitResult {
+    kWallClockWaitNotSatisfied,
+    kWallClockWaitDoneButUpdateCheckWaitRequired,
+    kWallClockWaitDoneAndUpdateCheckWaitNotRequired,
+  };
+
+  // The ctor takes in all the parameters that will be used for making
+  // the request to Omaha. For some of them we have constants that
+  // should be used.
+  //
+  // Takes ownership of the passed in HttpFetcher. Useful for testing.
+  //
+  // Takes ownership of the passed in OmahaEvent. If |event| is null,
+  // this is an UpdateCheck request, otherwise it's an Event request.
+  // Event requests always succeed.
+  //
+  // A good calling pattern is:
+  // OmahaRequestAction(..., new OmahaEvent(...), new WhateverHttpFetcher);
+  // or
+  // OmahaRequestAction(..., nullptr, new WhateverHttpFetcher);
+  OmahaRequestAction(SystemState* system_state,
+                     OmahaEvent* event,
+                     std::unique_ptr<HttpFetcher> http_fetcher,
+                     bool ping_only);
+  ~OmahaRequestAction() override;
+  typedef ActionTraits<OmahaRequestAction>::InputObjectType InputObjectType;
+  typedef ActionTraits<OmahaRequestAction>::OutputObjectType OutputObjectType;
+  void PerformAction() override;
+  void TerminateProcessing() override;
+  void ActionCompleted(ErrorCode code) override;
+
+  int GetHTTPResponseCode() { return http_fetcher_->http_response_code(); }
+
+  // Debugging/logging
+  static std::string StaticType() { return "OmahaRequestAction"; }
+  std::string Type() const override { return StaticType(); }
+
+  // Delegate methods (see http_fetcher.h)
+  void ReceivedBytes(HttpFetcher *fetcher,
+                     const void* bytes, size_t length) override;
+
+  void TransferComplete(HttpFetcher *fetcher, bool successful) override;
+
+  // Returns true if this is an Event request, false if it's an UpdateCheck.
+  bool IsEvent() const { return event_.get() != nullptr; }
+
+ private:
+  FRIEND_TEST(OmahaRequestActionTest, GetInstallDateWhenNoPrefsNorOOBE);
+  FRIEND_TEST(OmahaRequestActionTest,
+              GetInstallDateWhenOOBECompletedWithInvalidDate);
+  FRIEND_TEST(OmahaRequestActionTest,
+              GetInstallDateWhenOOBECompletedWithValidDate);
+  FRIEND_TEST(OmahaRequestActionTest,
+              GetInstallDateWhenOOBECompletedDateChanges);
+
+  // Enumeration used in PersistInstallDate().
+  enum InstallDateProvisioningSource {
+    kProvisionedFromOmahaResponse,
+    kProvisionedFromOOBEMarker,
+
+    // kProvisionedMax is the count of the number of enums above. Add
+    // any new enums above this line only.
+    kProvisionedMax
+  };
+
+  // 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(SystemState* system_state);
+
+  // Parses the Omaha Response in |doc| and sets the
+  // |install_date_days| field of |output_object| to the value of the
+  // elapsed_days attribute of the daystart element. Returns True if
+  // the value was set, False if it wasn't found.
+  static bool ParseInstallDate(OmahaParserData* parser_data,
+                               OmahaResponse* output_object);
+
+  // Returns True if the kPrefsInstallDateDays state variable is set,
+  // False otherwise.
+  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(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, 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);
+
+  // If this is an update check request, initializes
+  // |ping_active_days_| and |ping_roll_call_days_| to values that may
+  // be sent as pings to Omaha.
+  void InitPingDays();
+
+  // Based on the persistent preference store values, calculates the
+  // number of days since the last ping sent for |key|.
+  int CalculatePingDays(const std::string& key);
+
+  // Returns whether we have "active_days" or "roll_call_days" ping values to
+  // send to Omaha and thus we should include them in the response.
+  bool ShouldPing() 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);
+
+  // Returns true if the basic wall-clock-based waiting period has been
+  // satisfied based on the scattering policy setting. False otherwise.
+  // If true, it also indicates whether the additional update-check-count-based
+  // waiting period also needs to be satisfied before the download can begin.
+  WallClockWaitResult IsWallClockBasedWaitingSatisfied(
+      OmahaResponse* output_object);
+
+  // Returns true if the update-check-count-based waiting period has been
+  // satisfied. False otherwise.
+  bool IsUpdateCheckCountBasedWaitingSatisfied();
+
+  // Parses the response from Omaha that's available in |doc| using the other
+  // helper methods below and populates the |output_object| with the relevant
+  // values. Returns true if we should continue the parsing.  False otherwise,
+  // in which case it sets any error code using |completer|.
+  bool ParseResponse(OmahaParserData* parser_data,
+                     OmahaResponse* output_object,
+                     ScopedActionCompleter* completer);
+
+  // Parses the status property in the given update_check_node 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 ParseStatus(OmahaParserData* parser_data,
+                   OmahaResponse* output_object,
+                   ScopedActionCompleter* completer);
+
+  // Parses the URL nodes 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 ParseUrls(OmahaParserData* parser_data,
+                 OmahaResponse* output_object,
+                 ScopedActionCompleter* completer);
+
+  // 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* parser_data,
+                    OmahaResponse* output_object,
+                    ScopedActionCompleter* completer);
+
+  // Parses the other parameters 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 ParseParams(OmahaParserData* parser_data,
+                   OmahaResponse* output_object,
+                   ScopedActionCompleter* completer);
+
+  // Called by TransferComplete() to complete processing, either
+  // asynchronously after looking up resources via p2p or directly.
+  void CompleteProcessing();
+
+  // Helper to asynchronously look up payload on the LAN.
+  void LookupPayloadViaP2P(const OmahaResponse& response);
+
+  // Callback used by LookupPayloadViaP2P().
+  void OnLookupPayloadViaP2PCompleted(const std::string& url);
+
+  // Returns true if the current update should be ignored.
+  bool ShouldIgnoreUpdate(const OmahaResponse& response) const;
+
+  // Returns true if updates are allowed over the current type of connection.
+  // False otherwise.
+  bool IsUpdateAllowedOverCurrentConnection() 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_;
+
+  // pointer to the HttpFetcher that does the http work
+  std::unique_ptr<HttpFetcher> http_fetcher_;
+
+  // If true, only include the <ping> element in the request.
+  bool ping_only_;
+
+  // Stores the response from the omaha server
+  brillo::Blob response_buffer_;
+
+  // Initialized by InitPingDays to values that may be sent to Omaha
+  // as part of a ping message. Note that only positive values and -1
+  // are sent to Omaha.
+  int ping_active_days_;
+  int ping_roll_call_days_;
+
+  DISALLOW_COPY_AND_ASSIGN(OmahaRequestAction);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_OMAHA_REQUEST_ACTION_H_
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
new file mode 100644
index 0000000..69585c8
--- /dev/null
+++ b/omaha_request_action_unittest.cc
@@ -0,0 +1,2176 @@
+//
+// 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 <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/files/file_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/bind_lambda.h>
+#include <brillo/make_unique_ptr.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 "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/common/utils.h"
+#include "update_engine/fake_system_state.h"
+#include "update_engine/metrics.h"
+#include "update_engine/mock_connection_manager.h"
+#include "update_engine/mock_payload_state.h"
+#include "update_engine/omaha_request_params.h"
+
+using base::Time;
+using base::TimeDelta;
+using chromeos_update_engine::test_utils::System;
+using chromeos_update_engine::test_utils::WriteFileString;
+using std::string;
+using std::vector;
+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::SaveArg;
+using testing::SetArgumentPointee;
+using testing::_;
+
+namespace {
+
+const char kTestAppId[] = "test-app-id";
+
+// This is a helper struct to allow unit tests build an update response with the
+// values they care about.
+struct FakeUpdateResponse {
+  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></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\">"
+        "<urls><url codebase=\"" + codebase + "\"/></urls>"
+        "<manifest version=\"" + version + "\">"
+        "<packages><package hash=\"not-used\" name=\"" + filename +  "\" "
+        "size=\"" + base::Int64ToString(size) + "\"/></packages>"
+        "<actions><action event=\"postinstall\" "
+        "ChromeOSVersion=\"" + version + "\" "
+        "MoreInfo=\"" + more_info_url + "\" Prompt=\"" + prompt + "\" "
+        "IsDelta=\"true\" "
+        "IsDeltaPayload=\"true\" "
+        "MaxDaysToScatter=\"" + max_days_to_scatter + "\" "
+        "sha256=\"" + hash + "\" "
+        "needsadmin=\"" + needsadmin + "\" " +
+        (deadline.empty() ? "" : ("deadline=\"" + deadline + "\" ")) +
+        (disable_p2p_for_downloading ?
+            "DisableP2PForDownloading=\"true\" " : "") +
+        (disable_p2p_for_sharing ? "DisableP2PForSharing=\"true\" " : "") +
+        "/></actions></manifest></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 version = "1.2.3.4";
+  string more_info_url = "http://more/info";
+  string prompt = "true";
+  string codebase = "http://code/base/";
+  string filename = "file.signed";
+  string hash = "HASH1234=";
+  string needsadmin = "false";
+  int64_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;
+
+  // 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;
+};
+
+}  // namespace
+
+namespace chromeos_update_engine {
+
+class OmahaRequestActionTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    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(OmahaRequestParams* request_params,
+                       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);
+
+  // 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_;
+
+  // By default, all tests use these objects unless they replace them in the
+  // fake_system_state_.
+  OmahaRequestParams request_params_ = OmahaRequestParams{
+      &fake_system_state_,
+      constants::kOmahaPlatformName,
+      OmahaRequestParams::kOsVersion,
+      "service_pack",
+      "x86-generic",
+      kTestAppId,
+      "0.1.0.0",
+      "en-US",
+      "unittest",
+      "OEM MODEL 09235 7471",
+      "ChromeOSFirmware.1.0",
+      "0X0A1",
+      false,   // delta okay
+      false,   // interactive
+      "http://url",
+      ""};     // target_version_prefix
+
+  FakePrefs fake_prefs_;
+};
+
+namespace {
+class OmahaRequestActionTestProcessorDelegate : public ActionProcessorDelegate {
+ public:
+  OmahaRequestActionTestProcessorDelegate()
+      : expected_code_(ErrorCode::kSuccess) {}
+  ~OmahaRequestActionTestProcessorDelegate() override {
+  }
+  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);
+    else
+      EXPECT_EQ(ErrorCode::kSuccess, code);
+  }
+  ErrorCode expected_code_;
+};
+}  // namespace
+
+class OutputObjectCollectorAction;
+
+template<>
+class ActionTraits<OutputObjectCollectorAction> {
+ public:
+  // Does not take an object for input
+  typedef OmahaResponse InputObjectType;
+  // On success, puts the output path on output
+  typedef NoneType OutputObjectType;
+};
+
+class OutputObjectCollectorAction : public Action<OutputObjectCollectorAction> {
+ public:
+  OutputObjectCollectorAction() : has_input_object_(false) {}
+  void PerformAction() {
+    // copy input object
+    has_input_object_ = HasInputObject();
+    if (has_input_object_)
+      omaha_response_ = GetInputObject();
+    processor_->ActionComplete(this, ErrorCode::kSuccess);
+  }
+  // Should never be called
+  void TerminateProcessing() {
+    CHECK(false);
+  }
+  // Debugging/logging
+  static string StaticType() {
+    return "OutputObjectCollectorAction";
+  }
+  string Type() const { return StaticType(); }
+  bool has_input_object_;
+  OmahaResponse omaha_response_;
+};
+
+bool OmahaRequestActionTest::TestUpdateCheck(
+    OmahaRequestParams* request_params,
+    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) {
+  brillo::FakeMessageLoop loop(nullptr);
+  loop.SetAsCurrent();
+  MockHttpFetcher* fetcher = new MockHttpFetcher(http_response.data(),
+                                                 http_response.size(),
+                                                 nullptr);
+  if (fail_http_response_code >= 0) {
+    fetcher->FailTransfer(fail_http_response_code);
+  }
+  if (request_params)
+    fake_system_state_.set_request_params(request_params);
+  OmahaRequestAction action(&fake_system_state_,
+                            nullptr,
+                            brillo::make_unique_ptr(fetcher),
+                            ping_only);
+  OmahaRequestActionTestProcessorDelegate delegate;
+  delegate.expected_code_ = expected_code;
+
+  ActionProcessor processor;
+  processor.set_delegate(&delegate);
+  processor.EnqueueAction(&action);
+
+  OutputObjectCollectorAction collector_action;
+  BondActions(&action, &collector_action);
+  processor.EnqueueAction(&collector_action);
+
+  EXPECT_CALL(*fake_system_state_.mock_metrics_lib(), SendEnumToUMA(_, _, _))
+      .Times(AnyNumber());
+  EXPECT_CALL(*fake_system_state_.mock_metrics_lib(),
+      SendEnumToUMA(metrics::kMetricCheckResult,
+          static_cast<int>(expected_check_result),
+          static_cast<int>(metrics::CheckResult::kNumConstants) - 1))
+      .Times(expected_check_result == metrics::CheckResult::kUnset ? 0 : 1);
+  EXPECT_CALL(*fake_system_state_.mock_metrics_lib(),
+      SendEnumToUMA(metrics::kMetricCheckReaction,
+          static_cast<int>(expected_check_reaction),
+          static_cast<int>(metrics::CheckReaction::kNumConstants) - 1))
+      .Times(expected_check_reaction == metrics::CheckReaction::kUnset ? 0 : 1);
+  EXPECT_CALL(*fake_system_state_.mock_metrics_lib(),
+      SendSparseToUMA(metrics::kMetricCheckDownloadErrorCode,
+          static_cast<int>(expected_download_error_code)))
+      .Times(expected_download_error_code == metrics::DownloadErrorCode::kUnset
+             ? 0 : 1);
+
+  loop.PostTask(base::Bind([&processor] { processor.StartProcessing(); }));
+  LOG(INFO) << "loop.PendingTasks() = " << loop.PendingTasks();
+  loop.Run();
+  LOG(INFO) << "loop.PendingTasks() = " << loop.PendingTasks();
+  EXPECT_FALSE(loop.PendingTasks());
+  if (collector_action.has_input_object_ && out_response)
+    *out_response = collector_action.omaha_response_;
+  if (out_post_data)
+    *out_post_data = fetcher->post_data();
+  return collector_action.has_input_object_;
+}
+
+// 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 TestEvent(OmahaRequestParams params,
+               OmahaEvent* event,
+               const string& http_response,
+               brillo::Blob* out_post_data) {
+  brillo::FakeMessageLoop loop(nullptr);
+  loop.SetAsCurrent();
+  MockHttpFetcher* fetcher = new MockHttpFetcher(http_response.data(),
+                                                 http_response.size(),
+                                                 nullptr);
+  FakeSystemState fake_system_state;
+  fake_system_state.set_request_params(&params);
+  OmahaRequestAction action(&fake_system_state,
+                            event,
+                            brillo::make_unique_ptr(fetcher),
+                            false);
+  OmahaRequestActionTestProcessorDelegate delegate;
+  ActionProcessor processor;
+  processor.set_delegate(&delegate);
+  processor.EnqueueAction(&action);
+
+  loop.PostTask(base::Bind([&processor] { processor.StartProcessing(); }));
+  loop.Run();
+
+  // This test should schedule a callback to notify the crash reporter if
+  // the passed event is an error.
+  EXPECT_EQ(event->result == OmahaEvent::kResultError, loop.PendingTasks());
+
+  if (out_post_data)
+    *out_post_data = fetcher->post_data();
+}
+
+TEST_F(OmahaRequestActionTest, RejectEntities) {
+  OmahaResponse response;
+  fake_update_response_.include_entity = true;
+  ASSERT_FALSE(
+      TestUpdateCheck(nullptr,  // request_params
+                      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(nullptr,  // request_params
+                      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 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(nullptr,  // request_params
+                      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.update_exists);
+  EXPECT_EQ(fake_update_response_.version, response.version);
+  EXPECT_EQ(fake_update_response_.GetPayloadUrl(), response.payload_urls[0]);
+  EXPECT_EQ(fake_update_response_.more_info_url, response.more_info_url);
+  EXPECT_EQ(fake_update_response_.hash, response.hash);
+  EXPECT_EQ(fake_update_response_.size, response.size);
+  EXPECT_EQ(fake_update_response_.prompt == "true", response.prompt);
+  EXPECT_EQ(fake_update_response_.deadline, response.deadline);
+  // Omaha cohort attribets 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, 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(SetArgumentPointee<0>(NetworkConnectionType::kEthernet),
+                SetArgumentPointee<1>(NetworkTethering::kUnknown),
+                Return(true)));
+  EXPECT_CALL(mock_cm, IsUpdateAllowedOver(NetworkConnectionType::kEthernet, _))
+    .WillRepeatedly(Return(false));
+
+  ASSERT_FALSE(
+      TestUpdateCheck(nullptr,  // request_params
+                      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, 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(nullptr,  // request_params
+                      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, WallClockBasedWaitAloneCausesScattering) {
+  OmahaResponse response;
+  OmahaRequestParams params = request_params_;
+  params.set_wall_clock_based_wait_enabled(true);
+  params.set_update_check_count_wait_enabled(false);
+  params.set_waiting_period(TimeDelta::FromDays(2));
+
+  ASSERT_FALSE(
+      TestUpdateCheck(&params,
+                      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.
+  params.set_interactive(true);
+  ASSERT_TRUE(
+      TestUpdateCheck(&params,
+                      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;
+  OmahaRequestParams params = request_params_;
+  params.set_wall_clock_based_wait_enabled(false);
+  params.set_waiting_period(TimeDelta::FromDays(2));
+
+  params.set_update_check_count_wait_enabled(true);
+  params.set_min_update_checks_needed(1);
+  params.set_max_update_checks_allowed(8);
+
+  ASSERT_TRUE(
+      TestUpdateCheck(&params,
+                      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;
+  OmahaRequestParams params = request_params_;
+  params.set_wall_clock_based_wait_enabled(true);
+  params.set_waiting_period(TimeDelta::FromDays(2));
+
+  params.set_update_check_count_wait_enabled(true);
+  params.set_min_update_checks_needed(1);
+  params.set_max_update_checks_allowed(8);
+
+  fake_update_response_.max_days_to_scatter = "0";
+  ASSERT_TRUE(
+      TestUpdateCheck(&params,
+                      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;
+  OmahaRequestParams params = request_params_;
+  params.set_wall_clock_based_wait_enabled(true);
+  params.set_waiting_period(TimeDelta());
+
+  params.set_update_check_count_wait_enabled(true);
+  params.set_min_update_checks_needed(0);
+  params.set_max_update_checks_allowed(0);
+
+  ASSERT_TRUE(TestUpdateCheck(
+                      &params,
+                      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;
+  OmahaRequestParams params = request_params_;
+  params.set_wall_clock_based_wait_enabled(true);
+  params.set_waiting_period(TimeDelta());
+
+  params.set_update_check_count_wait_enabled(true);
+  params.set_min_update_checks_needed(1);
+  params.set_max_update_checks_allowed(8);
+
+  ASSERT_FALSE(TestUpdateCheck(
+                      &params,
+                      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.
+  params.set_interactive(true);
+  ASSERT_TRUE(
+      TestUpdateCheck(&params,
+                      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;
+  OmahaRequestParams params = request_params_;
+  params.set_wall_clock_based_wait_enabled(true);
+  params.set_waiting_period(TimeDelta());
+
+  params.set_update_check_count_wait_enabled(true);
+  params.set_min_update_checks_needed(1);
+  params.set_max_update_checks_allowed(8);
+
+  ASSERT_TRUE(fake_prefs_.SetInt64(kPrefsUpdateCheckCount, 5));
+
+  ASSERT_FALSE(TestUpdateCheck(
+                      &params,
+                      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.
+  params.set_interactive(true);
+  ASSERT_TRUE(
+      TestUpdateCheck(&params,
+                      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;
+  OmahaRequestParams params = request_params_;
+  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(&params,
+                              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;
+  OmahaRequestParams params = request_params_;
+  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(&params,
+                              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;
+  OmahaRequestParams params = request_params_;
+  EXPECT_TRUE(fake_prefs_.SetString(kPrefsOmahaCohort, "old_value"));
+
+  ASSERT_TRUE(TestUpdateCheck(&params,
+                              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;
+  OmahaRequestParams params = request_params_;
+  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(&params,
+                              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, NoOutputPipeTest) {
+  const string http_response(fake_update_response_.GetNoUpdateResponse());
+
+  brillo::FakeMessageLoop loop(nullptr);
+  loop.SetAsCurrent();
+
+  OmahaRequestParams params = request_params_;
+  fake_system_state_.set_request_params(&params);
+  OmahaRequestAction action(&fake_system_state_, nullptr,
+                            brillo::make_unique_ptr(
+                                new MockHttpFetcher(http_response.data(),
+                                                    http_response.size(),
+                                                    nullptr)),
+                            false);
+  OmahaRequestActionTestProcessorDelegate delegate;
+  ActionProcessor processor;
+  processor.set_delegate(&delegate);
+  processor.EnqueueAction(&action);
+
+  loop.PostTask(base::Bind([&processor] { processor.StartProcessing(); }));
+  loop.Run();
+  EXPECT_FALSE(loop.PendingTasks());
+  EXPECT_FALSE(processor.IsRunning());
+}
+
+TEST_F(OmahaRequestActionTest, InvalidXmlTest) {
+  OmahaResponse response;
+  ASSERT_FALSE(
+      TestUpdateCheck(nullptr,  // request_params
+                      "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(nullptr,  // request_params
+                      "",
+                      -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(
+      nullptr,  // request_params
+      "<?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(
+      nullptr,  // request_params
+      "<?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(
+      nullptr,  // request_params
+      "<?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\"/>"
+      "<app appid=\"xyz\" 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\"/></packages>"
+      "<actions><action event=\"postinstall\" "
+      "ChromeOSVersion=\"10.2.3.4\" "
+      "Prompt=\"false\" "
+      "IsDelta=\"true\" "
+      "IsDeltaPayload=\"false\" "
+      "sha256=\"lkq34j5345\" "
+      "needsadmin=\"true\" "
+      "/></actions></manifest></updatecheck></app></response>";
+  LOG(INFO) << "Input Response = " << input_response;
+
+  OmahaResponse response;
+  ASSERT_TRUE(TestUpdateCheck(nullptr,  // request_params
+                              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.payload_urls[0]);
+  EXPECT_EQ("", response.more_info_url);
+  EXPECT_EQ("lkq34j5345", response.hash);
+  EXPECT_EQ(587, response.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");
+  OmahaRequestAction action(&fake_system_state_, nullptr,
+                            brillo::make_unique_ptr(
+                                new MockHttpFetcher(http_response.data(),
+                                                    http_response.size(),
+                                                    nullptr)),
+                            false);
+  TerminateEarlyTestProcessorDelegate delegate;
+  ActionProcessor processor;
+  processor.set_delegate(&delegate);
+  processor.EnqueueAction(&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
+  OmahaRequestParams params(&fake_system_state_,
+                            constants::kOmahaPlatformName,
+                            OmahaRequestParams::kOsVersion,
+                            "testtheservice_pack>",
+                            "x86 generic<id",
+                            kTestAppId,
+                            "0.1.0.0",
+                            "en-US",
+                            "unittest_track&lt;",
+                            "<OEM MODEL>",
+                            "ChromeOSFirmware.1.0",
+                            "EC100",
+                            false,   // delta okay
+                            false,   // interactive
+                            "http://url",
+                            "");     // target_version_prefix
+  fake_prefs_.SetString(kPrefsOmahaCohort, "evil\nstring");
+  fake_prefs_.SetString(kPrefsOmahaCohortHint, "evil&string\\");
+  fake_prefs_.SetString(kPrefsOmahaCohortName,
+                        JoinString(vector<string>(100, "My spoon is too big."),
+                                   ' '));
+  OmahaResponse response;
+  ASSERT_FALSE(
+      TestUpdateCheck(&params,
+                      "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(nullptr,  // request_params
+                      fake_update_response_.GetUpdateResponse(),
+                      -1,
+                      false,  // ping_only
+                      ErrorCode::kSuccess,
+                      metrics::CheckResult::kUpdateAvailable,
+                      metrics::CheckReaction::kUpdating,
+                      metrics::DownloadErrorCode::kUnset,
+                      &response,
+                      nullptr));
+
+  EXPECT_EQ(response.more_info_url, "testthe<url");
+  EXPECT_EQ(response.payload_urls[0], "testthe&codebase/file.signed");
+  EXPECT_EQ(response.deadline, "<20110101");
+}
+
+TEST_F(OmahaRequestActionTest, ParseIntTest) {
+  OmahaResponse response;
+  // overflows int32_t:
+  fake_update_response_.size = 123123123123123ll;
+  ASSERT_TRUE(
+      TestUpdateCheck(nullptr,  // request_params
+                      fake_update_response_.GetUpdateResponse(),
+                      -1,
+                      false,  // ping_only
+                      ErrorCode::kSuccess,
+                      metrics::CheckResult::kUpdateAvailable,
+                      metrics::CheckReaction::kUpdating,
+                      metrics::DownloadErrorCode::kUnset,
+                      &response,
+                      nullptr));
+
+  EXPECT_EQ(response.size, 123123123123123ll);
+}
+
+TEST_F(OmahaRequestActionTest, FormatUpdateCheckOutputTest) {
+  brillo::Blob post_data;
+  NiceMock<MockPrefs> prefs;
+  fake_system_state_.set_prefs(&prefs);
+
+  EXPECT_CALL(prefs, GetString(kPrefsPreviousVersion, _))
+      .WillOnce(DoAll(SetArgumentPointee<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(nullptr,  // request_params
+                               "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 targetversionprefix=\"\"></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(request_params_,
+            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(request_params_,
+            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");
+  // Create a copy of the OmahaRequestParams to reuse it later.
+  OmahaRequestParams params = request_params_;
+  fake_system_state_.set_request_params(&params);
+  OmahaRequestAction update_check_action(
+      &fake_system_state_,
+      nullptr,
+      brillo::make_unique_ptr(
+          new MockHttpFetcher(http_response.data(),
+                              http_response.size(),
+                              nullptr)),
+      false);
+  EXPECT_FALSE(update_check_action.IsEvent());
+
+  params = request_params_;
+  fake_system_state_.set_request_params(&params);
+  OmahaRequestAction event_action(
+      &fake_system_state_,
+      new OmahaEvent(OmahaEvent::kTypeUpdateComplete),
+      brillo::make_unique_ptr(
+          new 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;
+    OmahaRequestParams params(&fake_system_state_,
+                              constants::kOmahaPlatformName,
+                              OmahaRequestParams::kOsVersion,
+                              "service_pack",
+                              "x86-generic",
+                              kTestAppId,
+                              "0.1.0.0",
+                              "en-US",
+                              "unittest_track",
+                              "OEM MODEL REV 1234",
+                              "ChromeOSFirmware.1.0",
+                              "EC100",
+                              delta_okay,
+                              false,  // interactive
+                              "http://url",
+                              "");    // target_version_prefix
+    ASSERT_FALSE(TestUpdateCheck(&params,
+                                 "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;
+    OmahaRequestParams params(&fake_system_state_,
+                              constants::kOmahaPlatformName,
+                              OmahaRequestParams::kOsVersion,
+                              "service_pack",
+                              "x86-generic",
+                              kTestAppId,
+                              "0.1.0.0",
+                              "en-US",
+                              "unittest_track",
+                              "OEM MODEL REV 1234",
+                              "ChromeOSFirmware.1.0",
+                              "EC100",
+                              true,   // delta_okay
+                              interactive,
+                              "http://url",
+                              "");    // target_version_prefix
+    ASSERT_FALSE(TestUpdateCheck(&params,
+                                 "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, 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(SetArgumentPointee<1>(0), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastActivePingDay, _))
+      .WillOnce(DoAll(SetArgumentPointee<1>(six_days_ago), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastRollCallPingDay, _))
+      .WillOnce(DoAll(SetArgumentPointee<1>(five_days_ago), Return(true)));
+  brillo::Blob post_data;
+  ASSERT_TRUE(
+      TestUpdateCheck(nullptr,  // request_params
+                      fake_update_response_.GetNoUpdateResponse(),
+                      -1,
+                      ping_only,
+                      ErrorCode::kSuccess,
+                      metrics::CheckResult::kUnset,
+                      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(SetArgumentPointee<1>(0), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastActivePingDay, _))
+      .WillOnce(DoAll(SetArgumentPointee<1>(three_days_ago), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastRollCallPingDay, _))
+      .WillOnce(DoAll(SetArgumentPointee<1>(now), Return(true)));
+  brillo::Blob post_data;
+  ASSERT_TRUE(
+      TestUpdateCheck(nullptr,  // request_params
+                      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(SetArgumentPointee<1>(0), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastActivePingDay, _))
+      .WillOnce(DoAll(SetArgumentPointee<1>(now), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastRollCallPingDay, _))
+      .WillOnce(DoAll(SetArgumentPointee<1>(four_days_ago), Return(true)));
+  brillo::Blob post_data;
+  ASSERT_TRUE(
+      TestUpdateCheck(nullptr,  // request_params
+                      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(SetArgumentPointee<1>(0), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastActivePingDay, _))
+      .WillOnce(DoAll(SetArgumentPointee<1>(one_hour_ago), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastRollCallPingDay, _))
+      .WillOnce(DoAll(SetArgumentPointee<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(nullptr,  // request_params
+                      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(SetArgumentPointee<1>(now), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastRollCallPingDay, _))
+      .WillOnce(DoAll(SetArgumentPointee<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(nullptr,  // request_params
+                      fake_update_response_.GetNoUpdateResponse(),
+                      -1,
+                      true,  // ping_only
+                      ErrorCode::kSuccess,
+                      metrics::CheckResult::kUnset,
+                      metrics::CheckReaction::kUnset,
+                      metrics::DownloadErrorCode::kUnset,
+                      nullptr,
+                      &post_data));
+  EXPECT_EQ(post_data.size(), 0);
+}
+
+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(SetArgumentPointee<1>(0), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastActivePingDay, _))
+      .WillOnce(DoAll(SetArgumentPointee<1>(future), Return(true)));
+  EXPECT_CALL(prefs, GetInt64(kPrefsLastRollCallPingDay, _))
+      .WillOnce(DoAll(SetArgumentPointee<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(nullptr,  // request_params
+                      "<?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(nullptr,  // request_params
+                      "<?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(nullptr,  // request_params
+                      "<?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(nullptr,  // request_params
+                      "<?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, NoUniqueIDTest) {
+  brillo::Blob post_data;
+  ASSERT_FALSE(TestUpdateCheck(nullptr,  // request_params
+                               "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(nullptr,  // request_params
+                      "",
+                      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(nullptr,  // request_params
+                      "",
+                      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;
+  OmahaRequestParams params = request_params_;
+  params.set_wall_clock_based_wait_enabled(true);
+  params.set_waiting_period(TimeDelta().FromDays(1));
+  params.set_update_check_count_wait_enabled(false);
+
+  ASSERT_FALSE(TestUpdateCheck(
+                      &params,
+                      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));
+  ASSERT_GT(timestamp, 0);
+  EXPECT_FALSE(response.update_exists);
+
+  // Verify if we are interactive check we don't defer.
+  params.set_interactive(true);
+  ASSERT_TRUE(
+      TestUpdateCheck(&params,
+                      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;
+  OmahaRequestParams params = request_params_;
+  params.set_wall_clock_based_wait_enabled(true);
+  params.set_waiting_period(TimeDelta().FromDays(1));
+  params.set_update_check_count_wait_enabled(false);
+
+  // Set the timestamp to a very old value such that it exceeds the
+  // waiting period set above.
+  Time t1;
+  Time::FromString("1/1/2012", &t1);
+  ASSERT_TRUE(fake_prefs_.SetInt64(
+      kPrefsUpdateFirstSeenAt, t1.ToInternalValue()));
+  ASSERT_TRUE(TestUpdateCheck(
+                      &params,
+                      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.
+  string test_dir;
+  ASSERT_TRUE(utils::MakeTempDirectory(
+          "omaha_request_action-test-XXXXXX", &test_dir));
+
+  ASSERT_EQ(0, System(string("mkdir -p ") + test_dir + "/etc"));
+  ASSERT_EQ(0, System(string("mkdir -p ") + test_dir +
+                      kStatefulPartition + "/etc"));
+  brillo::Blob post_data;
+  NiceMock<MockPrefs> prefs;
+  fake_system_state_.set_prefs(&prefs);
+  ASSERT_TRUE(WriteFileString(
+      test_dir + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_APPID={11111111-1111-1111-1111-111111111111}\n"
+      "CHROMEOS_BOARD_APPID={22222222-2222-2222-2222-222222222222}\n"
+      "CHROMEOS_RELEASE_TRACK=canary-channel\n"));
+  ASSERT_TRUE(WriteFileString(
+      test_dir + kStatefulPartition + "/etc/lsb-release",
+      "CHROMEOS_IS_POWERWASH_ALLOWED=true\n"
+      "CHROMEOS_RELEASE_TRACK=stable-channel\n"));
+  OmahaRequestParams params = request_params_;
+  params.set_root(test_dir);
+  params.Init("1.2.3.4", "", 0);
+  EXPECT_EQ("canary-channel", params.current_channel());
+  EXPECT_EQ("stable-channel", params.target_channel());
+  EXPECT_TRUE(params.to_more_stable_channel());
+  EXPECT_TRUE(params.is_powerwash_allowed());
+  ASSERT_FALSE(TestUpdateCheck(&params,
+                               "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\" "));
+
+  ASSERT_TRUE(base::DeleteFile(base::FilePath(test_dir), true));
+}
+
+TEST_F(OmahaRequestActionTest, TestChangingToLessStableChannel) {
+  // Create a uniquely named test directory.
+  string test_dir;
+  ASSERT_TRUE(utils::MakeTempDirectory(
+          "omaha_request_action-test-XXXXXX", &test_dir));
+
+  ASSERT_EQ(0, System(string("mkdir -p ") + test_dir + "/etc"));
+  ASSERT_EQ(0, System(string("mkdir -p ") + test_dir +
+                      kStatefulPartition + "/etc"));
+  brillo::Blob post_data;
+  NiceMock<MockPrefs> prefs;
+  fake_system_state_.set_prefs(&prefs);
+  ASSERT_TRUE(WriteFileString(
+      test_dir + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_APPID={11111111-1111-1111-1111-111111111111}\n"
+      "CHROMEOS_BOARD_APPID={22222222-2222-2222-2222-222222222222}\n"
+      "CHROMEOS_RELEASE_TRACK=stable-channel\n"));
+  ASSERT_TRUE(WriteFileString(
+      test_dir + kStatefulPartition + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_TRACK=canary-channel\n"));
+  OmahaRequestParams params = request_params_;
+  params.set_root(test_dir);
+  params.Init("5.6.7.8", "", 0);
+  EXPECT_EQ("stable-channel", params.current_channel());
+  EXPECT_EQ("canary-channel", params.target_channel());
+  EXPECT_FALSE(params.to_more_stable_channel());
+  EXPECT_FALSE(params.is_powerwash_allowed());
+  ASSERT_FALSE(TestUpdateCheck(&params,
+                               "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"));
+}
+
+// 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(nullptr,  // request_params
+                      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 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(nullptr,  // request_params
+                      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;
+  OmahaRequestParams request_params = request_params_;
+  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(&request_params,
+                      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(nullptr,  // request_params
+                      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;
+
+  // 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) {
+  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);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/omaha_request_params.cc b/omaha_request_params.cc
new file mode 100644
index 0000000..d0011f7
--- /dev/null
+++ b/omaha_request_params.cc
@@ -0,0 +1,215 @@
+//
+// 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_request_params.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/utsname.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/key_value_store.h>
+#include <brillo/strings/string_utils.h>
+#include <policy/device_policy.h>
+
+#include "update_engine/common/constants.h"
+#include "update_engine/common/hardware_interface.h"
+#include "update_engine/common/platform_constants.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/system_state.h"
+
+#define CALL_MEMBER_FN(object, member) ((object).*(member))
+
+using std::map;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+const char OmahaRequestParams::kOsVersion[] = "Indy";
+
+const char* kChannelsByStability[] = {
+    // This list has to be sorted from least stable to most stable channel.
+    "canary-channel",
+    "dev-channel",
+    "beta-channel",
+    "stable-channel",
+};
+
+OmahaRequestParams::~OmahaRequestParams() {
+  if (!root_.empty())
+    test::SetImagePropertiesRootPrefix(nullptr);
+}
+
+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(system_state_);
+  mutable_image_props_ = LoadMutableImageProperties(system_state_);
+
+  // Sanity check the channel names.
+  if (!IsValidChannel(image_props_.current_channel))
+    image_props_.current_channel = "stable-channel";
+  if (!IsValidChannel(mutable_image_props_.target_channel))
+    mutable_image_props_.target_channel = image_props_.current_channel;
+  UpdateDownloadChannel();
+
+  LOG(INFO) << "Running from channel " << image_props_.current_channel;
+
+  os_platform_ = constants::kOmahaPlatformName;
+  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_ = 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
+    // know (i.e. stat() returns some unexpected error), then err on the side of
+    // caution and say deltas are not okay.
+    struct stat stbuf;
+    delta_okay_ = (stat((root_ + "/.nodelta").c_str(), &stbuf) < 0) &&
+                  (errno == ENOENT);
+  } else {
+    LOG(INFO) << "Disabling deltas as a channel change to "
+              << mutable_image_props_.target_channel
+              << " is pending, with is_powerwash_allowed="
+              << utils::ToString(mutable_image_props_.is_powerwash_allowed);
+    // For now, disable delta updates if the current channel is different from
+    // the channel that we're sending to the update server because such updates
+    // are destined to fail -- the current rootfs hash will be different than
+    // the expected hash due to the different channel in /etc/lsb-release.
+    delta_okay_ = false;
+  }
+
+  if (in_update_url.empty())
+    update_url_ = image_props_.omaha_url;
+  else
+    update_url_ = in_update_url;
+
+  // Set the interactive flag accordingly.
+  interactive_ = in_interactive;
+  return true;
+}
+
+bool OmahaRequestParams::IsUpdateUrlOfficial() const {
+  return (update_url_ == constants::kOmahaDefaultAUTestURL ||
+          update_url_ == image_props_.omaha_url);
+}
+
+bool OmahaRequestParams::CollectECFWVersions() const {
+  return base::StartsWithASCII(hwid_, string("SAMS ALEX"), true) ||
+         base::StartsWithASCII(hwid_, string("BUTTERFLY"), true) ||
+         base::StartsWithASCII(hwid_, string("LUMPY"), true) ||
+         base::StartsWithASCII(hwid_, string("PARROT"), true) ||
+         base::StartsWithASCII(hwid_, string("SPRING"), true) ||
+         base::StartsWithASCII(hwid_, string("SNOW"), true);
+}
+
+bool OmahaRequestParams::SetTargetChannel(const string& new_target_channel,
+                                          bool is_powerwash_allowed,
+                                          string* error_message) {
+  LOG(INFO) << "SetTargetChannel called with " << new_target_channel
+            << ", Is Powerwash Allowed = "
+            << utils::ToString(is_powerwash_allowed)
+            << ". Current channel = " << image_props_.current_channel
+            << ", existing target channel = "
+            << mutable_image_props_.target_channel
+            << ", download channel = " << download_channel_;
+  if (!IsValidChannel(new_target_channel)) {
+    string valid_channels = brillo::string_utils::JoinRange(
+        ", ",
+        std::begin(kChannelsByStability),
+        std::end(kChannelsByStability));
+    if (error_message) {
+      *error_message = base::StringPrintf(
+          "Invalid channel name \"%s\", valid names are: %s",
+          new_target_channel.c_str(), valid_channels.c_str());
+    }
+    return false;
+  }
+
+  MutableImageProperties new_props;
+  new_props.target_channel = new_target_channel;
+  new_props.is_powerwash_allowed = is_powerwash_allowed;
+
+  if (!StoreMutableImageProperties(system_state_, new_props)) {
+    if (error_message)
+      *error_message = "Error storing the new channel value.";
+    return false;
+  }
+  mutable_image_props_ = new_props;
+  return true;
+}
+
+void OmahaRequestParams::UpdateDownloadChannel() {
+  if (download_channel_ != mutable_image_props_.target_channel) {
+    download_channel_ = mutable_image_props_.target_channel;
+    LOG(INFO) << "Download channel for this attempt = " << download_channel_;
+  }
+}
+
+string OmahaRequestParams::GetMachineType() const {
+  struct utsname buf;
+  string ret;
+  if (uname(&buf) == 0)
+    ret = buf.machine;
+  return ret;
+}
+
+bool OmahaRequestParams::IsValidChannel(const string& channel) const {
+  return GetChannelIndex(channel) >= 0;
+}
+
+void OmahaRequestParams::set_root(const string& root) {
+  root_ = root;
+  test::SetImagePropertiesRootPrefix(root_.c_str());
+}
+
+int OmahaRequestParams::GetChannelIndex(const string& channel) const {
+  for (size_t t = 0; t < arraysize(kChannelsByStability); ++t)
+    if (channel == kChannelsByStability[t])
+      return t;
+
+  return -1;
+}
+
+bool OmahaRequestParams::to_more_stable_channel() const {
+  int current_channel_index = GetChannelIndex(image_props_.current_channel);
+  int download_channel_index = GetChannelIndex(download_channel_);
+
+  return download_channel_index > current_channel_index;
+}
+
+string OmahaRequestParams::GetAppId() const {
+  return download_channel_ == "canary-channel" ? image_props_.canary_product_id
+                                               : image_props_.product_id;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/omaha_request_params.h b/omaha_request_params.h
new file mode 100644
index 0000000..b4534a1
--- /dev/null
+++ b/omaha_request_params.h
@@ -0,0 +1,336 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_OMAHA_REQUEST_PARAMS_H_
+#define UPDATE_ENGINE_OMAHA_REQUEST_PARAMS_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include <base/macros.h>
+#include <base/time/time.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "update_engine/common/platform_constants.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.
+//
+// TODO(jaysri): chromium-os:39752 tracks the need to rename this class to
+// reflect its lifetime more appropriately.
+class OmahaRequestParams {
+ public:
+  explicit OmahaRequestParams(SystemState* system_state)
+      : system_state_(system_state),
+        os_platform_(constants::kOmahaPlatformName),
+        os_version_(kOsVersion),
+        delta_okay_(true),
+        interactive_(false),
+        wall_clock_based_wait_enabled_(false),
+        update_check_count_wait_enabled_(false),
+        min_update_checks_needed_(kDefaultMinUpdateChecks),
+        max_update_checks_allowed_(kDefaultMaxUpdateChecks) {}
+
+  OmahaRequestParams(SystemState* system_state,
+                     const std::string& in_os_platform,
+                     const std::string& in_os_version,
+                     const std::string& in_os_sp,
+                     const std::string& in_os_board,
+                     const std::string& in_app_id,
+                     const std::string& in_app_version,
+                     const std::string& in_app_lang,
+                     const std::string& in_target_channel,
+                     const std::string& in_hwid,
+                     const std::string& in_fw_version,
+                     const std::string& in_ec_version,
+                     bool in_delta_okay,
+                     bool in_interactive,
+                     const std::string& in_update_url,
+                     const std::string& in_target_version_prefix)
+      : system_state_(system_state),
+        os_platform_(in_os_platform),
+        os_version_(in_os_version),
+        os_sp_(in_os_sp),
+        app_lang_(in_app_lang),
+        hwid_(in_hwid),
+        fw_version_(in_fw_version),
+        ec_version_(in_ec_version),
+        delta_okay_(in_delta_okay),
+        interactive_(in_interactive),
+        update_url_(in_update_url),
+        target_version_prefix_(in_target_version_prefix),
+        wall_clock_based_wait_enabled_(false),
+        update_check_count_wait_enabled_(false),
+        min_update_checks_needed_(kDefaultMinUpdateChecks),
+        max_update_checks_allowed_(kDefaultMaxUpdateChecks) {
+    image_props_.board = in_os_board;
+    image_props_.product_id = in_app_id;
+    image_props_.canary_product_id = in_app_id;
+    image_props_.version = in_app_version;
+    image_props_.current_channel = in_target_channel;
+    mutable_image_props_.target_channel = in_target_channel;
+    mutable_image_props_.is_powerwash_allowed = false;
+  }
+
+  virtual ~OmahaRequestParams();
+
+  // 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_; }
+  inline std::string os_sp() const { return os_sp_; }
+  inline std::string os_board() const { return image_props_.board; }
+  inline std::string board_app_id() const { return image_props_.product_id; }
+  inline std::string canary_app_id() const {
+    return image_props_.canary_product_id;
+  }
+  inline std::string app_lang() const { return app_lang_; }
+  inline std::string hwid() const { return hwid_; }
+  inline std::string fw_version() const { return fw_version_; }
+  inline std::string ec_version() const { return ec_version_; }
+
+  inline 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 current_channel() const {
+    return image_props_.current_channel;
+  }
+  inline std::string target_channel() const {
+    return mutable_image_props_.target_channel;
+  }
+  inline std::string download_channel() const { return download_channel_; }
+
+  // Can client accept a delta ?
+  inline void set_delta_okay(bool ok) { delta_okay_ = ok; }
+  inline bool delta_okay() const { return delta_okay_; }
+
+  // True if this is a user-initiated update check.
+  inline void set_interactive(bool interactive) { interactive_ = interactive; }
+  inline bool interactive() const { return interactive_; }
+
+  inline void set_update_url(const std::string& url) { update_url_ = url; }
+  inline std::string update_url() const { return update_url_; }
+
+  inline void set_target_version_prefix(const std::string& prefix) {
+    target_version_prefix_ = prefix;
+  }
+
+  inline std::string target_version_prefix() const {
+    return target_version_prefix_;
+  }
+
+  inline void set_wall_clock_based_wait_enabled(bool enabled) {
+    wall_clock_based_wait_enabled_ = enabled;
+  }
+  inline bool wall_clock_based_wait_enabled() const {
+    return wall_clock_based_wait_enabled_;
+  }
+
+  inline void set_waiting_period(base::TimeDelta period) {
+    waiting_period_ = period;
+  }
+  base::TimeDelta waiting_period() const { return waiting_period_; }
+
+  inline void set_update_check_count_wait_enabled(bool enabled) {
+    update_check_count_wait_enabled_ = enabled;
+  }
+
+  inline bool update_check_count_wait_enabled() const {
+    return update_check_count_wait_enabled_;
+  }
+
+  inline void set_min_update_checks_needed(int64_t min) {
+    min_update_checks_needed_ = min;
+  }
+  inline int64_t min_update_checks_needed() const {
+    return min_update_checks_needed_;
+  }
+
+  inline void set_max_update_checks_allowed(int64_t max) {
+    max_update_checks_allowed_ = max;
+  }
+  inline int64_t max_update_checks_allowed() const {
+    return max_update_checks_allowed_;
+  }
+
+  // True if we're trying to update to a more stable channel.
+  // i.e. index(target_channel) > index(current_channel).
+  virtual bool to_more_stable_channel() const;
+
+  // Returns the app id corresponding to the current value of the
+  // download channel.
+  virtual std::string GetAppId() const;
+
+  // Suggested defaults
+  static const char kOsVersion[];
+  static const char kIsPowerwashAllowedKey[];
+  static const int64_t kDefaultMinUpdateChecks = 0;
+  static const int64_t kDefaultMaxUpdateChecks = 8;
+
+  // Initializes all the data in the object. Non-empty
+  // |in_app_version| or |in_update_url| prevents automatic detection
+  // of the parameter. Returns true on success, false otherwise.
+  bool Init(const std::string& in_app_version,
+            const std::string& in_update_url,
+            bool in_interactive);
+
+  // Permanently changes the release channel to |channel|. Performs a
+  // powerwash, if required and allowed.
+  // Returns true on success, false otherwise. Note: This call will fail if
+  // there's a channel change pending already. This is to serialize all the
+  // channel changes done by the user in order to avoid having to solve
+  // numerous edge cases around ensuring the powerwash happens as intended in
+  // all such cases.
+  virtual bool SetTargetChannel(const std::string& channel,
+                                bool is_powerwash_allowed,
+                                std::string* error_message);
+
+  // Updates the download channel for this particular attempt from the current
+  // value of target channel.  This method takes a "snapshot" of the current
+  // value of target channel and uses it for all subsequent Omaha requests for
+  // this attempt (i.e. initial request as well as download progress/error
+  // event requests). The snapshot will be updated only when either this method
+  // or Init is called again.
+  virtual void UpdateDownloadChannel();
+
+  virtual bool is_powerwash_allowed() const {
+    return mutable_image_props_.is_powerwash_allowed;
+  }
+
+  // Check if the provided update URL is official, meaning either the default
+  // autoupdate server or the autoupdate autotest server.
+  virtual bool IsUpdateUrlOfficial() const;
+
+  // For unit-tests.
+  void set_root(const std::string& root);
+  void set_current_channel(const std::string& channel) {
+    image_props_.current_channel = channel;
+  }
+  void set_target_channel(const std::string& channel) {
+    mutable_image_props_.target_channel = channel;
+  }
+
+ private:
+  FRIEND_TEST(OmahaRequestParamsTest, IsValidChannelTest);
+  FRIEND_TEST(OmahaRequestParamsTest, ShouldLockDownTest);
+  FRIEND_TEST(OmahaRequestParamsTest, ChannelIndexTest);
+  FRIEND_TEST(OmahaRequestParamsTest, LsbPreserveTest);
+  FRIEND_TEST(OmahaRequestParamsTest, CollectECFWVersionsTest);
+
+  // Returns true if |channel| is a valid channel, false otherwise.
+  bool IsValidChannel(const std::string& channel) const;
+
+  // Returns the index of the given channel.
+  int GetChannelIndex(const std::string& channel) 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;
+
+  // These are individual helper methods to initialize the said properties from
+  // the LSB value.
+  void SetTargetChannelFromLsbValue();
+  void SetCurrentChannelFromLsbValue();
+  void SetIsPowerwashAllowedFromLsbValue();
+
+  // Initializes the required properties from the LSB value.
+  void InitFromLsbValue();
+
+  // 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_;
+
+  // Basic properties of the OS and Application that go into the Omaha request.
+  std::string os_platform_;
+  std::string os_version_;
+  std::string os_sp_;
+  std::string app_lang_;
+
+  // There are three channel values we deal with:
+  // * The channel we got the image we are running from or "current channel"
+  //   stored in |image_props_.current_channel|.
+  //
+  // * The release channel we are tracking, where we should get updates from,
+  //   stored in |mutable_image_props_.target_channel|. This channel is
+  //   normally the same as the current_channel, except when the user changes
+  //   the channel. In that case it'll have the release channel the user
+  //   switched to, regardless of whether we downloaded an update from that
+  //   channel or not, or if we are in the middle of a download from a
+  //   previously selected channel  (as opposed to download channel
+  //   which gets updated only at the start of next download).
+  //
+  // * The channel from which we're downloading the payload. This should
+  //   normally be the same as target channel. But if the user made another
+  //   channel change after we started the download, then they'd be different,
+  //   in which case, we'd detect elsewhere that the target channel has been
+  //   changed and cancel the current download attempt.
+  std::string download_channel_;
+
+  std::string hwid_;  // Hardware Qualification ID of the client
+  std::string fw_version_;  // Chrome OS Firmware Version.
+  std::string ec_version_;  // Chrome OS EC Version.
+  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_;
+
+  // Prefix of the target OS version that the enterprise wants this device
+  // to be pinned to. It's empty otherwise.
+  std::string target_version_prefix_;
+
+  // True if scattering is enabled, in which case waiting_period_ specifies the
+  // amount of absolute time that we've to wait for before sending a request to
+  // Omaha.
+  bool wall_clock_based_wait_enabled_;
+  base::TimeDelta waiting_period_;
+
+  // True if scattering is enabled to denote the number of update checks
+  // we've to skip before we can send a request to Omaha. The min and max
+  // values establish the bounds for a random number to be chosen within that
+  // range to enable such a wait.
+  bool update_check_count_wait_enabled_;
+  int64_t min_update_checks_needed_;
+  int64_t max_update_checks_allowed_;
+
+  // When reading files, prepend root_ to the paths. Useful for testing.
+  std::string root_;
+
+  // TODO(jaysri): Uncomment this after fixing unit tests, as part of
+  // chromium-os:39752
+  // DISALLOW_COPY_AND_ASSIGN(OmahaRequestParams);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_OMAHA_REQUEST_PARAMS_H_
diff --git a/omaha_request_params_unittest.cc b/omaha_request_params_unittest.cc
new file mode 100644
index 0000000..33dd6d5
--- /dev/null
+++ b/omaha_request_params_unittest.cc
@@ -0,0 +1,598 @@
+//
+// 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_request_params.h"
+
+#include <stdio.h>
+
+#include <string>
+
+#include <base/files/file_util.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/payload_consumer/install_plan.h"
+
+using chromeos_update_engine::test_utils::WriteFileString;
+using std::string;
+
+namespace chromeos_update_engine {
+
+class OmahaRequestParamsTest : public ::testing::Test {
+ public:
+  OmahaRequestParamsTest() : params_(&fake_system_state_) {}
+
+ protected:
+  // Return true iff the OmahaRequestParams::Init succeeded. If
+  // out is non-null, it's set w/ the generated data.
+  bool DoTest(OmahaRequestParams* out, const string& app_version,
+              const string& omaha_url);
+
+  void SetUp() override {
+    // Create a uniquely named test directory.
+    ASSERT_TRUE(utils::MakeTempDirectory(kTestDirTemplate, &test_dir_));
+    EXPECT_TRUE(base::CreateDirectory(base::FilePath(test_dir_ + "/etc")));
+    EXPECT_TRUE(base::CreateDirectory(
+        base::FilePath(test_dir_ + kStatefulPartition + "/etc")));
+    // Create a fresh copy of the params for each test, so there's no
+    // unintended reuse of state across tests.
+    OmahaRequestParams new_params(&fake_system_state_);
+    params_ = new_params;
+    params_.set_root(test_dir_);
+    SetLockDown(false);
+  }
+
+  void TearDown() override {
+    EXPECT_TRUE(base::DeleteFile(base::FilePath(test_dir_), true));
+  }
+
+  void SetLockDown(bool locked_down) {
+    fake_system_state_.fake_hardware()->SetIsOfficialBuild(locked_down);
+    fake_system_state_.fake_hardware()->SetIsNormalBootMode(locked_down);
+  }
+
+  OmahaRequestParams params_;
+  FakeSystemState fake_system_state_;
+
+  static const char* kTestDirTemplate;
+  string test_dir_;
+};
+
+const char* OmahaRequestParamsTest::kTestDirTemplate =
+  "omaha_request_params-test-XXXXXX";
+
+bool OmahaRequestParamsTest::DoTest(OmahaRequestParams* out,
+                                    const string& app_version,
+                                    const string& omaha_url) {
+  bool success = params_.Init(app_version, omaha_url, false);
+  if (out)
+    *out = params_;
+  return success;
+}
+
+namespace {
+string GetMachineType() {
+  string machine_type;
+  if (!utils::ReadPipe("uname -m", &machine_type))
+    return "";
+  // Strip anything from the first newline char.
+  size_t newline_pos = machine_type.find('\n');
+  if (newline_pos != string::npos)
+    machine_type.erase(newline_pos);
+  return machine_type;
+}
+}  // namespace
+
+TEST_F(OmahaRequestParamsTest, SimpleTest) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=arm-generic\n"
+      "CHROMEOS_RELEASE_FOO=bar\n"
+      "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel\n"
+      "CHROMEOS_AUSERVER=http://www.google.com"));
+  OmahaRequestParams out(&fake_system_state_);
+  EXPECT_TRUE(DoTest(&out, "", ""));
+  EXPECT_EQ("Chrome OS", out.os_platform());
+  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp());
+  EXPECT_EQ("arm-generic", out.os_board());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.GetAppId());
+  EXPECT_EQ("0.2.2.3", out.app_version());
+  EXPECT_EQ("en-US", out.app_lang());
+  EXPECT_EQ(fake_system_state_.hardware()->GetHardwareClass(), out.hwid());
+  EXPECT_TRUE(out.delta_okay());
+  EXPECT_EQ("dev-channel", out.target_channel());
+  EXPECT_EQ("http://www.google.com", out.update_url());
+}
+
+TEST_F(OmahaRequestParamsTest, AppIDTest) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=arm-generic\n"
+      "CHROMEOS_RELEASE_FOO=bar\n"
+      "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel\n"
+      "CHROMEOS_RELEASE_APPID={58c35cef-9d30-476e-9098-ce20377d535d}\n"
+      "CHROMEOS_AUSERVER=http://www.google.com"));
+  OmahaRequestParams out(&fake_system_state_);
+  EXPECT_TRUE(DoTest(&out, "", ""));
+  EXPECT_EQ("Chrome OS", out.os_platform());
+  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp());
+  EXPECT_EQ("arm-generic", out.os_board());
+  EXPECT_EQ("{58c35cef-9d30-476e-9098-ce20377d535d}", out.GetAppId());
+  EXPECT_EQ("0.2.2.3", out.app_version());
+  EXPECT_EQ("en-US", out.app_lang());
+  EXPECT_EQ(fake_system_state_.hardware()->GetHardwareClass(), out.hwid());
+  EXPECT_TRUE(out.delta_okay());
+  EXPECT_EQ("dev-channel", out.target_channel());
+  EXPECT_EQ("http://www.google.com", out.update_url());
+}
+
+TEST_F(OmahaRequestParamsTest, MissingChannelTest) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_FOO=bar\n"
+      "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
+      "CHROMEOS_RELEASE_TRXCK=dev-channel"));
+  OmahaRequestParams out(&fake_system_state_);
+  EXPECT_TRUE(DoTest(&out, "", ""));
+  EXPECT_EQ("Chrome OS", out.os_platform());
+  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.GetAppId());
+  EXPECT_EQ("0.2.2.3", out.app_version());
+  EXPECT_EQ("en-US", out.app_lang());
+  // By default, if no channel is set, we should track the stable-channel.
+  EXPECT_EQ("stable-channel", out.target_channel());
+}
+
+TEST_F(OmahaRequestParamsTest, ConfusingReleaseTest) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_FOO=CHROMEOS_RELEASE_VERSION=1.2.3.4\n"
+      "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
+      "CHROMEOS_RELEASE_TRXCK=dev-channel"));
+  OmahaRequestParams out(&fake_system_state_);
+  EXPECT_TRUE(DoTest(&out, "", ""));
+  EXPECT_EQ("Chrome OS", out.os_platform());
+  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.GetAppId());
+  EXPECT_EQ("0.2.2.3", out.app_version());
+  EXPECT_EQ("en-US", out.app_lang());
+  EXPECT_EQ("stable-channel", out.target_channel());
+}
+
+TEST_F(OmahaRequestParamsTest, MissingVersionTest) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=arm-generic\n"
+      "CHROMEOS_RELEASE_FOO=bar\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel"));
+  OmahaRequestParams out(&fake_system_state_);
+  EXPECT_TRUE(DoTest(&out, "", ""));
+  EXPECT_EQ("Chrome OS", out.os_platform());
+  EXPECT_EQ(string("_") + GetMachineType(), out.os_sp());
+  EXPECT_EQ("arm-generic", out.os_board());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.GetAppId());
+  EXPECT_EQ("", out.app_version());
+  EXPECT_EQ("en-US", out.app_lang());
+  EXPECT_TRUE(out.delta_okay());
+  EXPECT_EQ("dev-channel", out.target_channel());
+}
+
+TEST_F(OmahaRequestParamsTest, ForceVersionTest) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=arm-generic\n"
+      "CHROMEOS_RELEASE_FOO=bar\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel"));
+  OmahaRequestParams out(&fake_system_state_);
+  EXPECT_TRUE(DoTest(&out, "ForcedVersion", ""));
+  EXPECT_EQ("Chrome OS", out.os_platform());
+  EXPECT_EQ(string("ForcedVersion_") + GetMachineType(), out.os_sp());
+  EXPECT_EQ("arm-generic", out.os_board());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.GetAppId());
+  EXPECT_EQ("ForcedVersion", out.app_version());
+  EXPECT_EQ("en-US", out.app_lang());
+  EXPECT_TRUE(out.delta_okay());
+  EXPECT_EQ("dev-channel", out.target_channel());
+}
+
+TEST_F(OmahaRequestParamsTest, ForcedURLTest) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=arm-generic\n"
+      "CHROMEOS_RELEASE_FOO=bar\n"
+      "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel"));
+  OmahaRequestParams out(&fake_system_state_);
+  EXPECT_TRUE(DoTest(&out, "", "http://forced.google.com"));
+  EXPECT_EQ("Chrome OS", out.os_platform());
+  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp());
+  EXPECT_EQ("arm-generic", out.os_board());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.GetAppId());
+  EXPECT_EQ("0.2.2.3", out.app_version());
+  EXPECT_EQ("en-US", out.app_lang());
+  EXPECT_TRUE(out.delta_okay());
+  EXPECT_EQ("dev-channel", out.target_channel());
+  EXPECT_EQ("http://forced.google.com", out.update_url());
+}
+
+TEST_F(OmahaRequestParamsTest, MissingURLTest) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=arm-generic\n"
+      "CHROMEOS_RELEASE_FOO=bar\n"
+      "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel"));
+  OmahaRequestParams out(&fake_system_state_);
+  EXPECT_TRUE(DoTest(&out, "", ""));
+  EXPECT_EQ("Chrome OS", out.os_platform());
+  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp());
+  EXPECT_EQ("arm-generic", out.os_board());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.GetAppId());
+  EXPECT_EQ("0.2.2.3", out.app_version());
+  EXPECT_EQ("en-US", out.app_lang());
+  EXPECT_TRUE(out.delta_okay());
+  EXPECT_EQ("dev-channel", out.target_channel());
+  EXPECT_EQ(constants::kOmahaDefaultProductionURL, out.update_url());
+}
+
+TEST_F(OmahaRequestParamsTest, NoDeltasTest) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_FOO=CHROMEOS_RELEASE_VERSION=1.2.3.4\n"
+      "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
+      "CHROMEOS_RELEASE_TRXCK=dev-channel"));
+  ASSERT_TRUE(WriteFileString(test_dir_ + "/.nodelta", ""));
+  OmahaRequestParams out(&fake_system_state_);
+  EXPECT_TRUE(DoTest(&out, "", ""));
+  EXPECT_FALSE(out.delta_okay());
+}
+
+TEST_F(OmahaRequestParamsTest, OverrideTest) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=arm-generic\n"
+      "CHROMEOS_RELEASE_FOO=bar\n"
+      "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel\n"
+      "CHROMEOS_AUSERVER=http://www.google.com"));
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + kStatefulPartition + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=x86-generic\n"
+      "CHROMEOS_RELEASE_TRACK=beta-channel\n"
+      "CHROMEOS_AUSERVER=https://www.google.com"));
+  OmahaRequestParams out(&fake_system_state_);
+  EXPECT_TRUE(DoTest(&out, "", ""));
+  EXPECT_EQ("Chrome OS", out.os_platform());
+  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp());
+  EXPECT_EQ("x86-generic", out.os_board());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.GetAppId());
+  EXPECT_EQ("0.2.2.3", out.app_version());
+  EXPECT_EQ("en-US", out.app_lang());
+  EXPECT_EQ(fake_system_state_.hardware()->GetHardwareClass(), out.hwid());
+  EXPECT_FALSE(out.delta_okay());
+  EXPECT_EQ("beta-channel", out.target_channel());
+  EXPECT_EQ("https://www.google.com", out.update_url());
+}
+
+TEST_F(OmahaRequestParamsTest, OverrideLockDownTest) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=arm-generic\n"
+      "CHROMEOS_RELEASE_FOO=bar\n"
+      "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel\n"
+      "CHROMEOS_AUSERVER=https://www.google.com"));
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + kStatefulPartition + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=x86-generic\n"
+      "CHROMEOS_RELEASE_TRACK=stable-channel\n"
+      "CHROMEOS_AUSERVER=http://www.google.com"));
+  SetLockDown(true);
+  OmahaRequestParams out(&fake_system_state_);
+  EXPECT_TRUE(DoTest(&out, "", ""));
+  EXPECT_EQ("arm-generic", out.os_board());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.GetAppId());
+  EXPECT_EQ("0.2.2.3", out.app_version());
+  EXPECT_EQ(fake_system_state_.hardware()->GetHardwareClass(), out.hwid());
+  EXPECT_FALSE(out.delta_okay());
+  EXPECT_EQ("stable-channel", out.target_channel());
+  EXPECT_EQ("https://www.google.com", out.update_url());
+}
+
+TEST_F(OmahaRequestParamsTest, OverrideSameChannelTest) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=arm-generic\n"
+      "CHROMEOS_RELEASE_FOO=bar\n"
+      "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel\n"
+      "CHROMEOS_AUSERVER=http://www.google.com"));
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + kStatefulPartition + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=x86-generic\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel"));
+  OmahaRequestParams out(&fake_system_state_);
+  EXPECT_TRUE(DoTest(&out, "", ""));
+  EXPECT_EQ("x86-generic", out.os_board());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.GetAppId());
+  EXPECT_EQ("0.2.2.3", out.app_version());
+  EXPECT_EQ(fake_system_state_.hardware()->GetHardwareClass(), out.hwid());
+  EXPECT_TRUE(out.delta_okay());
+  EXPECT_EQ("dev-channel", out.target_channel());
+  EXPECT_EQ("http://www.google.com", out.update_url());
+}
+
+TEST_F(OmahaRequestParamsTest, SetTargetChannelTest) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=arm-generic\n"
+      "CHROMEOS_RELEASE_FOO=bar\n"
+      "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel\n"
+      "CHROMEOS_AUSERVER=http://www.google.com"));
+  {
+    OmahaRequestParams params(&fake_system_state_);
+    params.set_root(test_dir_);
+    EXPECT_TRUE(params.Init("", "", false));
+    params.SetTargetChannel("canary-channel", false, nullptr);
+    EXPECT_FALSE(params.is_powerwash_allowed());
+  }
+  OmahaRequestParams out(&fake_system_state_);
+  out.set_root(test_dir_);
+  EXPECT_TRUE(DoTest(&out, "", ""));
+  EXPECT_EQ("canary-channel", out.target_channel());
+  EXPECT_FALSE(out.is_powerwash_allowed());
+}
+
+TEST_F(OmahaRequestParamsTest, SetIsPowerwashAllowedTest) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=arm-generic\n"
+      "CHROMEOS_RELEASE_FOO=bar\n"
+      "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel\n"
+      "CHROMEOS_AUSERVER=http://www.google.com"));
+  {
+    OmahaRequestParams params(&fake_system_state_);
+    params.set_root(test_dir_);
+    EXPECT_TRUE(params.Init("", "", false));
+    params.SetTargetChannel("canary-channel", true, nullptr);
+    EXPECT_TRUE(params.is_powerwash_allowed());
+  }
+  OmahaRequestParams out(&fake_system_state_);
+  out.set_root(test_dir_);
+  EXPECT_TRUE(DoTest(&out, "", ""));
+  EXPECT_EQ("canary-channel", out.target_channel());
+  EXPECT_TRUE(out.is_powerwash_allowed());
+}
+
+TEST_F(OmahaRequestParamsTest, SetTargetChannelInvalidTest) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=arm-generic\n"
+      "CHROMEOS_RELEASE_FOO=bar\n"
+      "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel\n"
+      "CHROMEOS_AUSERVER=http://www.google.com"));
+  {
+    OmahaRequestParams params(&fake_system_state_);
+    params.set_root(test_dir_);
+    SetLockDown(true);
+    EXPECT_TRUE(params.Init("", "", false));
+    string error_message;
+    EXPECT_FALSE(
+        params.SetTargetChannel("dogfood-channel", true, &error_message));
+    // The error message should include a message about the valid channels.
+    EXPECT_NE(string::npos, error_message.find("stable-channel"));
+    EXPECT_FALSE(params.is_powerwash_allowed());
+  }
+  OmahaRequestParams out(&fake_system_state_);
+  out.set_root(test_dir_);
+  EXPECT_TRUE(DoTest(&out, "", ""));
+  EXPECT_EQ("arm-generic", out.os_board());
+  EXPECT_EQ("dev-channel", out.target_channel());
+  EXPECT_FALSE(out.is_powerwash_allowed());
+}
+
+TEST_F(OmahaRequestParamsTest, IsValidChannelTest) {
+  EXPECT_TRUE(params_.IsValidChannel("canary-channel"));
+  EXPECT_TRUE(params_.IsValidChannel("stable-channel"));
+  EXPECT_TRUE(params_.IsValidChannel("beta-channel"));
+  EXPECT_TRUE(params_.IsValidChannel("dev-channel"));
+  EXPECT_FALSE(params_.IsValidChannel("testimage-channel"));
+  EXPECT_FALSE(params_.IsValidChannel("dogfood-channel"));
+  EXPECT_FALSE(params_.IsValidChannel("some-channel"));
+  EXPECT_FALSE(params_.IsValidChannel(""));
+}
+
+TEST_F(OmahaRequestParamsTest, ValidChannelTest) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=arm-generic\n"
+      "CHROMEOS_RELEASE_FOO=bar\n"
+      "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel\n"
+      "CHROMEOS_AUSERVER=http://www.google.com"));
+  SetLockDown(true);
+  OmahaRequestParams out(&fake_system_state_);
+  EXPECT_TRUE(DoTest(&out, "", ""));
+  EXPECT_EQ("Chrome OS", out.os_platform());
+  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp());
+  EXPECT_EQ("arm-generic", out.os_board());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.GetAppId());
+  EXPECT_EQ("0.2.2.3", out.app_version());
+  EXPECT_EQ("en-US", out.app_lang());
+  EXPECT_EQ(fake_system_state_.hardware()->GetHardwareClass(), out.hwid());
+  EXPECT_TRUE(out.delta_okay());
+  EXPECT_EQ("dev-channel", out.target_channel());
+  EXPECT_EQ("http://www.google.com", out.update_url());
+}
+
+TEST_F(OmahaRequestParamsTest, SetTargetChannelWorks) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=arm-generic\n"
+      "CHROMEOS_RELEASE_FOO=bar\n"
+      "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel\n"
+      "CHROMEOS_AUSERVER=http://www.google.com"));
+
+  // Check LSB value is used by default when SetTargetChannel is not called.
+  params_.Init("", "", false);
+  EXPECT_EQ("dev-channel", params_.target_channel());
+
+  // When an invalid value is set, it should be ignored and the
+  // value from lsb-release should be used instead.
+  params_.Init("", "", false);
+  EXPECT_FALSE(params_.SetTargetChannel("invalid-channel", false, nullptr));
+  EXPECT_EQ("dev-channel", params_.target_channel());
+
+  // When set to a valid value, it should take effect.
+  params_.Init("", "", false);
+  EXPECT_TRUE(params_.SetTargetChannel("beta-channel", true, nullptr));
+  EXPECT_EQ("beta-channel", params_.target_channel());
+
+  // When set to the same value, it should be idempotent.
+  params_.Init("", "", false);
+  EXPECT_TRUE(params_.SetTargetChannel("beta-channel", true, nullptr));
+  EXPECT_EQ("beta-channel", params_.target_channel());
+
+  // When set to a valid value while a change is already pending, it should
+  // succeed.
+  params_.Init("", "", false);
+  EXPECT_TRUE(params_.SetTargetChannel("stable-channel", true, nullptr));
+  EXPECT_EQ("stable-channel", params_.target_channel());
+
+  // Set a different channel in stateful LSB release.
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + kStatefulPartition + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_TRACK=stable-channel\n"
+      "CHROMEOS_IS_POWERWASH_ALLOWED=true\n"));
+
+  // When set to a valid value while a change is already pending, it should
+  // succeed.
+  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 ...
+  EXPECT_EQ("beta-channel", params_.target_channel());
+  EXPECT_EQ("stable-channel", params_.download_channel());
+
+  // ... until we update the download channel explicitly.
+  params_.UpdateDownloadChannel();
+  EXPECT_EQ("beta-channel", params_.download_channel());
+  EXPECT_EQ("beta-channel", params_.target_channel());
+}
+
+TEST_F(OmahaRequestParamsTest, ChannelIndexTest) {
+  int canary = params_.GetChannelIndex("canary-channel");
+  int dev = params_.GetChannelIndex("dev-channel");
+  int beta = params_.GetChannelIndex("beta-channel");
+  int stable = params_.GetChannelIndex("stable-channel");
+  EXPECT_LE(canary, dev);
+  EXPECT_LE(dev, beta);
+  EXPECT_LE(beta, stable);
+
+  // testimage-channel or other names are not recognized, so index will be -1.
+  int testimage = params_.GetChannelIndex("testimage-channel");
+  int bogus = params_.GetChannelIndex("bogus-channel");
+  EXPECT_EQ(-1, testimage);
+  EXPECT_EQ(-1, bogus);
+}
+
+TEST_F(OmahaRequestParamsTest, ToMoreStableChannelFlagTest) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=arm-generic\n"
+      "CHROMEOS_RELEASE_FOO=bar\n"
+      "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
+      "CHROMEOS_RELEASE_TRACK=canary-channel\n"
+      "CHROMEOS_AUSERVER=http://www.google.com"));
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + kStatefulPartition + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=x86-generic\n"
+      "CHROMEOS_RELEASE_TRACK=stable-channel\n"
+      "CHROMEOS_AUSERVER=https://www.google.com"));
+  OmahaRequestParams out(&fake_system_state_);
+  EXPECT_TRUE(DoTest(&out, "", ""));
+  EXPECT_EQ("https://www.google.com", out.update_url());
+  EXPECT_FALSE(out.delta_okay());
+  EXPECT_EQ("stable-channel", out.target_channel());
+  EXPECT_TRUE(out.to_more_stable_channel());
+}
+
+TEST_F(OmahaRequestParamsTest, BoardAppIdUsedForNonCanaryChannelTest) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_APPID=r\n"
+      "CHROMEOS_BOARD_APPID=b\n"
+      "CHROMEOS_CANARY_APPID=c\n"
+      "CHROMEOS_RELEASE_TRACK=stable-channel\n"));
+  OmahaRequestParams out(&fake_system_state_);
+  EXPECT_TRUE(DoTest(&out, "", ""));
+  EXPECT_EQ("stable-channel", out.download_channel());
+  EXPECT_EQ("b", out.GetAppId());
+}
+
+TEST_F(OmahaRequestParamsTest, CanaryAppIdUsedForCanaryChannelTest) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_APPID=r\n"
+      "CHROMEOS_BOARD_APPID=b\n"
+      "CHROMEOS_CANARY_APPID=c\n"
+      "CHROMEOS_RELEASE_TRACK=canary-channel\n"));
+  OmahaRequestParams out(&fake_system_state_);
+  EXPECT_TRUE(DoTest(&out, "", ""));
+  EXPECT_EQ("canary-channel", out.download_channel());
+  EXPECT_EQ("c", out.GetAppId());
+}
+
+TEST_F(OmahaRequestParamsTest, ReleaseAppIdUsedAsDefaultTest) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_APPID=r\n"
+      "CHROMEOS_CANARY_APPID=c\n"
+      "CHROMEOS_RELEASE_TRACK=stable-channel\n"));
+  OmahaRequestParams out(&fake_system_state_);
+  EXPECT_TRUE(DoTest(&out, "", ""));
+  EXPECT_EQ("stable-channel", out.download_channel());
+  EXPECT_EQ("r", out.GetAppId());
+}
+
+TEST_F(OmahaRequestParamsTest, CollectECFWVersionsTest) {
+  ASSERT_TRUE(WriteFileString(
+      test_dir_ + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_APPID=r\n"
+      "CHROMEOS_CANARY_APPID=c\n"
+      "CHROMEOS_RELEASE_TRACK=stable-channel\n"));
+  OmahaRequestParams out(&fake_system_state_);
+  out.hwid_ = string("STUMPY ALEX 12345");
+  EXPECT_FALSE(out.CollectECFWVersions());
+
+  out.hwid_ = string("SNOW 12345");
+  EXPECT_TRUE(out.CollectECFWVersions());
+
+  out.hwid_ = string("SAMS ALEX 12345");
+  EXPECT_TRUE(out.CollectECFWVersions());
+}
+
+}  // namespace chromeos_update_engine
diff --git a/omaha_response.h b/omaha_response.h
new file mode 100644
index 0000000..3a5a889
--- /dev/null
+++ b/omaha_response.h
@@ -0,0 +1,85 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_OMAHA_RESPONSE_H_
+#define UPDATE_ENGINE_OMAHA_RESPONSE_H_
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+namespace chromeos_update_engine {
+
+// This struct encapsulates the data Omaha's response for the request.
+// The strings in this struct are not XML escaped.
+struct OmahaResponse {
+  // True iff there is an update to be downloaded.
+  bool update_exists = false;
+
+  // If non-zero, server-dictated poll interval in seconds.
+  int poll_interval = 0;
+
+  // These are only valid if update_exists is true:
+  std::string version;
+
+  // The ordered list of URLs in the Omaha response. Each item is a complete
+  // URL (i.e. in terms of Omaha XML, each value is a urlBase + packageName)
+  std::vector<std::string> payload_urls;
+
+  std::string more_info_url;
+  std::string hash;
+  std::string metadata_signature;
+  std::string deadline;
+  off_t size = 0;
+  off_t metadata_size = 0;
+  int max_days_to_scatter = 0;
+  // The number of URL-related failures to tolerate before moving on to the
+  // next URL in the current pass. This is a configurable value from the
+  // Omaha Response attribute, if ever we need to fine tune the behavior.
+  uint32_t max_failure_count_per_url = 0;
+  bool prompt = false;
+
+  // True if the payload described in this response is a delta payload.
+  // False if it's a full payload.
+  bool is_delta_payload = false;
+
+  // True if the Omaha rule instructs us to disable the back-off logic
+  // on the client altogether. False otherwise.
+  bool disable_payload_backoff = false;
+
+  // True if the Omaha rule instructs us to disable p2p for downloading.
+  bool disable_p2p_for_downloading = false;
+
+  // True if the Omaha rule instructs us to disable p2p for sharing.
+  bool disable_p2p_for_sharing = false;
+
+  // If not blank, a base-64 encoded representation of the PEM-encoded
+  // public key in the response.
+  std::string public_key_rsa;
+
+  // If not -1, the number of days since the epoch Jan 1, 2007 0:00
+  // PST, according to the Omaha Server's clock and timezone (PST8PDT,
+  // aka "Pacific Time".)
+  int install_date_days = -1;
+};
+COMPILE_ASSERT(sizeof(off_t) == 8, off_t_not_64bit);
+
+}  // namespace chromeos_update_engine
+
+#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..3fa9348
--- /dev/null
+++ b/omaha_response_handler_action.cc
@@ -0,0 +1,206 @@
+//
+// 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 <string>
+
+#include <base/logging.h>
+#include <base/strings/string_util.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"
+
+using std::string;
+
+namespace chromeos_update_engine {
+
+OmahaResponseHandlerAction::OmahaResponseHandlerAction(
+    SystemState* system_state)
+    : OmahaResponseHandlerAction(system_state,
+                                 constants::kOmahaResponseDeadlineFile) {}
+
+OmahaResponseHandlerAction::OmahaResponseHandlerAction(
+    SystemState* system_state, const string& deadline_file)
+    : system_state_(system_state),
+      got_no_update_response_(false),
+      key_path_(constants::kUpdatePayloadPublicKeyPath),
+      deadline_file_(deadline_file) {}
+
+void OmahaResponseHandlerAction::PerformAction() {
+  CHECK(HasInputObject());
+  ScopedActionCompleter completer(processor_, this);
+  const OmahaResponse& response = GetInputObject();
+  if (!response.update_exists) {
+    got_no_update_response_ = true;
+    LOG(INFO) << "There are no updates. Aborting.";
+    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;
+  }
+
+  install_plan_.download_url = current_url;
+  install_plan_.version = response.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.
+  install_plan_.payload_size = response.size;
+  install_plan_.payload_hash = response.hash;
+  install_plan_.metadata_size = response.metadata_size;
+  install_plan_.metadata_signature = response.metadata_signature;
+  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(), 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, response.hash))
+        << "Unable to save the update check response hash.";
+  }
+  install_plan_.is_full_update = !response.is_delta_payload;
+
+  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());
+
+  if (params->to_more_stable_channel() && params->is_powerwash_allowed())
+    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()) {
+    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);
+  }
+
+  completer.set_code(ErrorCode::kSuccess);
+}
+
+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;
+    }
+  }
+
+  // If we're using p2p, |install_plan_.download_url| may contain a
+  // HTTP URL even if |response.payload_urls| contain only HTTPS URLs.
+  if (!base::StartsWithASCII(install_plan_.download_url, "https://", false)) {
+    LOG(INFO) << "Mandating hash checks since download_url is not HTTPS.";
+    return true;
+  }
+
+  // TODO(jaysri): VALIDATION: For official builds, we currently waive hash
+  // checks for HTTPS until we have rolled out at least once and are confident
+  // nothing breaks. chromium-os:37082 tracks turning this on for HTTPS
+  // eventually.
+
+  // Even if there's a single non-HTTPS URL, make the hash checks as
+  // mandatory because we could be downloading the payload from any URL later
+  // on. It's really hard to do book-keeping based on each byte being
+  // downloaded to see whether we only used HTTPS throughout.
+  for (size_t i = 0; i < response.payload_urls.size(); i++) {
+    if (!base::StartsWithASCII(response.payload_urls[i], "https://", false)) {
+      LOG(INFO) << "Mandating payload hash checks since Omaha response "
+                << "contains non-HTTPS URL(s)";
+      return true;
+    }
+  }
+
+  LOG(INFO) << "Waiving payload hash checks since Omaha response "
+            << "only has HTTPS URL(s)";
+  return false;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/omaha_response_handler_action.h b/omaha_response_handler_action.h
new file mode 100644
index 0000000..51dfa7a
--- /dev/null
+++ b/omaha_response_handler_action.h
@@ -0,0 +1,98 @@
+//
+// 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.
+//
+
+#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/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.
+
+namespace chromeos_update_engine {
+
+class OmahaResponseHandlerAction;
+
+template<>
+class ActionTraits<OmahaResponseHandlerAction> {
+ public:
+  typedef OmahaResponse InputObjectType;
+  typedef InstallPlan OutputObjectType;
+};
+
+class OmahaResponseHandlerAction : public Action<OmahaResponseHandlerAction> {
+ public:
+  explicit OmahaResponseHandlerAction(SystemState* system_state);
+
+  typedef ActionTraits<OmahaResponseHandlerAction>::InputObjectType
+      InputObjectType;
+  typedef ActionTraits<OmahaResponseHandlerAction>::OutputObjectType
+      OutputObjectType;
+  void PerformAction() override;
+
+  // This is a synchronous action, and thus TerminateProcessing() should
+  // never be called
+  void TerminateProcessing() override { CHECK(false); }
+
+  bool GotNoUpdateResponse() const { return got_no_update_response_; }
+  const InstallPlan& install_plan() const { return install_plan_; }
+
+  // Debugging/logging
+  static std::string StaticType() { return "OmahaResponseHandlerAction"; }
+  std::string Type() const override { return StaticType(); }
+  void set_key_path(const std::string& path) { key_path_ = path; }
+
+ private:
+  // Returns true if payload hash checks are mandatory based on the state
+  // 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_;
+
+  // True only if we got a response and the response said no updates
+  bool got_no_update_response_;
+
+  // Public key path to use for payload verification.
+  std::string key_path_;
+
+  // File used for communication deadline to Chrome.
+  const std::string deadline_file_;
+
+  // Special ctor + friend declarations for testing purposes.
+  OmahaResponseHandlerAction(SystemState* system_state,
+                             const std::string& deadline_file);
+
+  friend class OmahaResponseHandlerActionTest;
+
+  FRIEND_TEST(UpdateAttempterTest, CreatePendingErrorEventResumedTest);
+
+  DISALLOW_COPY_AND_ASSIGN(OmahaResponseHandlerAction);
+};
+
+}  // namespace chromeos_update_engine
+
+#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..b996d38
--- /dev/null
+++ b/omaha_response_handler_action_unittest.cc
@@ -0,0 +1,435 @@
+//
+// 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 <string>
+
+#include <base/files/file_util.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"
+
+using chromeos_update_engine::test_utils::System;
+using chromeos_update_engine::test_utils::WriteFileString;
+using std::string;
+using testing::Return;
+
+namespace chromeos_update_engine {
+
+class OmahaResponseHandlerActionTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    FakeBootControl* fake_boot_control = fake_system_state_.fake_boot_control();
+    fake_boot_control->SetPartitionDevice(
+        kLegacyPartitionNameKernel, 0, "/dev/sdz2");
+    fake_boot_control->SetPartitionDevice(
+        kLegacyPartitionNameRoot, 0, "/dev/sdz3");
+    fake_boot_control->SetPartitionDevice(
+        kLegacyPartitionNameKernel, 1, "/dev/sdz4");
+    fake_boot_control->SetPartitionDevice(
+        kLegacyPartitionNameRoot, 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);
+
+  FakeSystemState fake_system_state_;
+};
+
+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()) {
+      code_ = code;
+      code_set_ = true;
+    }
+  }
+  ErrorCode code_;
+  bool code_set_;
+};
+
+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";
+}  // namespace
+
+bool OmahaResponseHandlerActionTest::DoTest(
+    const OmahaResponse& in,
+    const string& test_deadline_file,
+    InstallPlan* out) {
+  ActionProcessor processor;
+  OmahaResponseHandlerActionProcessorDelegate delegate;
+  processor.set_delegate(&delegate);
+
+  ObjectFeederAction<OmahaResponse> feeder_action;
+  feeder_action.set_obj(in);
+  if (in.update_exists && in.version != kBadVersion) {
+    EXPECT_CALL(*(fake_system_state_.mock_prefs()),
+                SetString(kPrefsUpdateCheckResponseHash, in.hash))
+        .WillOnce(Return(true));
+
+    int slot = 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.payload_urls.size() ? in.payload_urls[0] : "";
+  EXPECT_CALL(*(fake_system_state_.mock_payload_state()), GetCurrentUrl())
+      .WillRepeatedly(Return(current_url));
+
+  OmahaResponseHandlerAction response_handler_action(
+      &fake_system_state_,
+      (test_deadline_file.empty() ?
+       constants::kOmahaResponseDeadlineFile : test_deadline_file));
+  BondActions(&feeder_action, &response_handler_action);
+  ObjectCollectorAction<InstallPlan> collector_action;
+  BondActions(&response_handler_action, &collector_action);
+  processor.EnqueueAction(&feeder_action);
+  processor.EnqueueAction(&response_handler_action);
+  processor.EnqueueAction(&collector_action);
+  processor.StartProcessing();
+  EXPECT_TRUE(!processor.IsRunning())
+      << "Update test to handle non-async actions";
+  if (out)
+    *out = collector_action.object();
+  EXPECT_TRUE(delegate.code_set_);
+  return delegate.code_ == ErrorCode::kSuccess;
+}
+
+TEST_F(OmahaResponseHandlerActionTest, SimpleTest) {
+  string test_deadline_file;
+  CHECK(utils::MakeTempFile(
+          "omaha_response_handler_action_unittest-XXXXXX",
+          &test_deadline_file, nullptr));
+  ScopedPathUnlinker deadline_unlinker(test_deadline_file);
+  {
+    OmahaResponse in;
+    in.update_exists = true;
+    in.version = "a.b.c.d";
+    in.payload_urls.push_back("http://foo/the_update_a.b.c.d.tgz");
+    in.more_info_url = "http://more/info";
+    in.hash = "HASH+";
+    in.size = 12;
+    in.prompt = false;
+    in.deadline = "20101020";
+    InstallPlan install_plan;
+    EXPECT_TRUE(DoTest(in, test_deadline_file, &install_plan));
+    EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
+    EXPECT_EQ(in.hash, install_plan.payload_hash);
+    EXPECT_EQ(1, install_plan.target_slot);
+    string deadline;
+    EXPECT_TRUE(utils::ReadFile(test_deadline_file, &deadline));
+    EXPECT_EQ("20101020", deadline);
+    struct stat deadline_stat;
+    EXPECT_EQ(0, stat(test_deadline_file.c_str(), &deadline_stat));
+    EXPECT_EQ(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.payload_urls.push_back("http://foo/the_update_a.b.c.d.tgz");
+    in.more_info_url = "http://more/info";
+    in.hash = "HASHj+";
+    in.size = 12;
+    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, &install_plan));
+    EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
+    EXPECT_EQ(in.hash, install_plan.payload_hash);
+    EXPECT_EQ(0, install_plan.target_slot);
+    string deadline;
+    EXPECT_TRUE(utils::ReadFile(test_deadline_file, &deadline) &&
+                deadline.empty());
+    EXPECT_EQ(in.version, install_plan.version);
+  }
+  {
+    OmahaResponse in;
+    in.update_exists = true;
+    in.version = "a.b.c.d";
+    in.payload_urls.push_back(kLongName);
+    in.more_info_url = "http://more/info";
+    in.hash = "HASHj+";
+    in.size = 12;
+    in.prompt = true;
+    in.deadline = "some-deadline";
+    InstallPlan install_plan;
+    fake_system_state_.fake_boot_control()->SetCurrentSlot(0);
+    EXPECT_TRUE(DoTest(in, test_deadline_file, &install_plan));
+    EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
+    EXPECT_EQ(in.hash, install_plan.payload_hash);
+    EXPECT_EQ(1, install_plan.target_slot);
+    string deadline;
+    EXPECT_TRUE(utils::ReadFile(test_deadline_file, &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, HashChecksForHttpTest) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.version = "a.b.c.d";
+  in.payload_urls.push_back("http://test.should/need/hash.checks.signed");
+  in.more_info_url = "http://more/info";
+  in.hash = "HASHj+";
+  in.size = 12;
+  // 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.payload_urls[0], install_plan.download_url);
+  EXPECT_EQ(in.hash, install_plan.payload_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.payload_urls.push_back("http://url.normally/needs/hash.checks.signed");
+  in.more_info_url = "http://more/info";
+  in.hash = "HASHj+";
+  in.size = 12;
+  EXPECT_CALL(*(fake_system_state_.mock_request_params()),
+              IsUpdateUrlOfficial())
+      .WillRepeatedly(Return(false));
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
+  EXPECT_EQ(in.hash, install_plan.payload_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.payload_urls.push_back("http://url.normally/needs/hash.checks.signed");
+  in.more_info_url = "http://more/info";
+  in.hash = "HASHj+";
+  in.size = 12;
+  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.payload_urls[0], install_plan.download_url);
+  EXPECT_EQ(in.hash, install_plan.payload_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.payload_urls.push_back("https://test.should.not/need/hash.checks.signed");
+  in.more_info_url = "http://more/info";
+  in.hash = "HASHj+";
+  in.size = 12;
+  EXPECT_CALL(*(fake_system_state_.mock_request_params()),
+              IsUpdateUrlOfficial())
+      .WillRepeatedly(Return(true));
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
+  EXPECT_EQ(in.hash, install_plan.payload_hash);
+  EXPECT_FALSE(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.payload_urls.push_back("http://test.should.still/need/hash.checks");
+  in.payload_urls.push_back("https://test.should.still/need/hash.checks");
+  in.more_info_url = "http://more/info";
+  in.hash = "HASHj+";
+  in.size = 12;
+  EXPECT_CALL(*(fake_system_state_.mock_request_params()),
+              IsUpdateUrlOfficial())
+      .WillRepeatedly(Return(true));
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
+  EXPECT_EQ(in.hash, install_plan.payload_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.payload_urls.push_back("https://MoreStableChannelTest");
+  in.more_info_url = "http://more/info";
+  in.hash = "HASHjk";
+  in.size = 15;
+
+  // Create a uniquely named test directory.
+  string test_dir;
+  ASSERT_TRUE(utils::MakeTempDirectory(
+          "omaha_response_handler_action-test-XXXXXX", &test_dir));
+
+  ASSERT_EQ(0, System(string("mkdir -p ") + test_dir + "/etc"));
+  ASSERT_EQ(0, System(string("mkdir -p ") + test_dir +
+                      kStatefulPartition + "/etc"));
+  ASSERT_TRUE(WriteFileString(
+      test_dir + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_TRACK=canary-channel\n"));
+  ASSERT_TRUE(WriteFileString(
+      test_dir + kStatefulPartition + "/etc/lsb-release",
+      "CHROMEOS_IS_POWERWASH_ALLOWED=true\n"
+      "CHROMEOS_RELEASE_TRACK=stable-channel\n"));
+
+  OmahaRequestParams params(&fake_system_state_);
+  fake_system_state_.fake_hardware()->SetIsOfficialBuild(false);
+  params.set_root(test_dir);
+  params.Init("1.2.3.4", "", 0);
+  EXPECT_EQ("canary-channel", params.current_channel());
+  EXPECT_EQ("stable-channel", params.target_channel());
+  EXPECT_TRUE(params.to_more_stable_channel());
+  EXPECT_TRUE(params.is_powerwash_allowed());
+
+  fake_system_state_.set_request_params(&params);
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_TRUE(install_plan.powerwash_required);
+
+  ASSERT_TRUE(base::DeleteFile(base::FilePath(test_dir), true));
+}
+
+TEST_F(OmahaResponseHandlerActionTest, ChangeToLessStableChannelTest) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.version = "a.b.c.d";
+  in.payload_urls.push_back("https://LessStableChannelTest");
+  in.more_info_url = "http://more/info";
+  in.hash = "HASHjk";
+  in.size = 15;
+
+  // Create a uniquely named test directory.
+  string test_dir;
+  ASSERT_TRUE(utils::MakeTempDirectory(
+          "omaha_response_handler_action-test-XXXXXX", &test_dir));
+
+  ASSERT_EQ(0, System(string("mkdir -p ") + test_dir + "/etc"));
+  ASSERT_EQ(0, System(string("mkdir -p ") + test_dir +
+                      kStatefulPartition + "/etc"));
+  ASSERT_TRUE(WriteFileString(
+      test_dir + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_TRACK=stable-channel\n"));
+  ASSERT_TRUE(WriteFileString(
+      test_dir + kStatefulPartition + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_TRACK=canary-channel\n"));
+
+  OmahaRequestParams params(&fake_system_state_);
+  fake_system_state_.fake_hardware()->SetIsOfficialBuild(false);
+  params.set_root(test_dir);
+  params.Init("5.6.7.8", "", 0);
+  EXPECT_EQ("stable-channel", params.current_channel());
+  params.SetTargetChannel("canary-channel", false, nullptr);
+  EXPECT_EQ("canary-channel", params.target_channel());
+  EXPECT_FALSE(params.to_more_stable_channel());
+  EXPECT_FALSE(params.is_powerwash_allowed());
+
+  fake_system_state_.set_request_params(&params);
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTest(in, "", &install_plan));
+  EXPECT_FALSE(install_plan.powerwash_required);
+
+  ASSERT_TRUE(base::DeleteFile(base::FilePath(test_dir), true));
+}
+
+TEST_F(OmahaResponseHandlerActionTest, P2PUrlIsUsedAndHashChecksMandatory) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.version = "a.b.c.d";
+  in.payload_urls.push_back("https://would.not/cause/hash/checks");
+  in.more_info_url = "http://more/info";
+  in.hash = "HASHj+";
+  in.size = 12;
+
+  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(in.hash, install_plan.payload_hash);
+  EXPECT_EQ(install_plan.download_url, p2p_url);
+  EXPECT_TRUE(install_plan.hash_checks_mandatory);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/p2p_manager.cc b/p2p_manager.cc
new file mode 100644
index 0000000..658630c
--- /dev/null
+++ b/p2p_manager.cc
@@ -0,0 +1,736 @@
+//
+// 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.
+//
+
+// This provides access to timestamps with nanosecond resolution in
+// struct stat, See NOTES in stat(2) for details.
+#ifndef _BSD_SOURCE
+#define _BSD_SOURCE
+#endif
+
+#include "update_engine/p2p_manager.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/falloc.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/files/file_enumerator.h>
+#include <base/files/file_path.h>
+#include <base/format_macros.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+
+#include "update_engine/common/subprocess.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/update_manager/policy.h"
+#include "update_engine/update_manager/update_manager.h"
+
+using base::Bind;
+using base::Callback;
+using base::FilePath;
+using base::StringPrintf;
+using base::Time;
+using base::TimeDelta;
+using brillo::MessageLoop;
+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;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+// The default p2p directory.
+const char kDefaultP2PDir[] = "/var/cache/p2p";
+
+// The p2p xattr used for conveying the final size of a file - see the
+// p2p ddoc for details.
+const char kCrosP2PFileSizeXAttrName[] = "user.cros-p2p-filesize";
+
+}  // namespace
+
+// The default P2PManager::Configuration implementation.
+class ConfigurationImpl : public P2PManager::Configuration {
+ public:
+  ConfigurationImpl() {}
+
+  FilePath GetP2PDir() override {
+    return FilePath(kDefaultP2PDir);
+  }
+
+  vector<string> GetInitctlArgs(bool is_start) override {
+    vector<string> args;
+    args.push_back("initctl");
+    args.push_back(is_start ? "start" : "stop");
+    args.push_back("p2p");
+    return args;
+  }
+
+  vector<string> GetP2PClientArgs(const string &file_id,
+                                  size_t minimum_size) override {
+    vector<string> args;
+    args.push_back("p2p-client");
+    args.push_back(string("--get-url=") + file_id);
+    args.push_back(StringPrintf("--minimum-size=%" PRIuS, minimum_size));
+    return args;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ConfigurationImpl);
+};
+
+// The default P2PManager implementation.
+class P2PManagerImpl : public P2PManager {
+ public:
+  P2PManagerImpl(Configuration *configuration,
+                 ClockInterface *clock,
+                 UpdateManager* update_manager,
+                 const string& file_extension,
+                 const int num_files_to_keep,
+                 const TimeDelta& max_file_age);
+
+  // P2PManager methods.
+  void SetDevicePolicy(const policy::DevicePolicy* device_policy) override;
+  bool IsP2PEnabled() override;
+  bool EnsureP2PRunning() override;
+  bool EnsureP2PNotRunning() override;
+  bool PerformHousekeeping() override;
+  void LookupUrlForFile(const string& file_id,
+                        size_t minimum_size,
+                        TimeDelta max_time_to_wait,
+                        LookupCallback callback) override;
+  bool FileShare(const string& file_id,
+                 size_t expected_size) override;
+  FilePath FileGetPath(const string& file_id) override;
+  ssize_t FileGetSize(const string& file_id) override;
+  ssize_t FileGetExpectedSize(const string& file_id) override;
+  bool FileGetVisible(const string& file_id,
+                      bool *out_result) override;
+  bool FileMakeVisible(const string& file_id) override;
+  int CountSharedFiles() override;
+
+ private:
+  // Enumeration for specifying visibility.
+  enum Visibility {
+    kVisible,
+    kNonVisible
+  };
+
+  // Returns "." + |file_extension_| + ".p2p" if |visibility| is
+  // |kVisible|. Returns the same concatenated with ".tmp" otherwise.
+  string GetExt(Visibility visibility);
+
+  // Gets the on-disk path for |file_id| depending on if the file
+  // is visible or not.
+  FilePath GetPath(const string& file_id, Visibility visibility);
+
+  // Utility function used by EnsureP2PRunning() and EnsureP2PNotRunning().
+  bool EnsureP2P(bool should_be_running);
+
+  // Utility function to delete a file given by |path| and log the
+  // path as well as |reason|. Returns false on failure.
+  bool DeleteP2PFile(const FilePath& path, const string& reason);
+
+  // Schedules an async request for tracking changes in P2P enabled status.
+  void ScheduleEnabledStatusChange();
+
+  // An async callback used by the above.
+  void OnEnabledStatusChange(EvalStatus status, const bool& result);
+
+  // The device policy being used or null if no policy is being used.
+  const policy::DevicePolicy* device_policy_ = nullptr;
+
+  // Configuration object.
+  unique_ptr<Configuration> configuration_;
+
+  // Object for telling the time.
+  ClockInterface* clock_;
+
+  // A pointer to the global Update Manager.
+  UpdateManager* update_manager_;
+
+  // A short string unique to the application (for example "cros_au")
+  // used to mark a file as being owned by a particular application.
+  const string file_extension_;
+
+  // If non-zero, this number denotes how many files in /var/cache/p2p
+  // owned by the application (cf. |file_extension_|) to keep after
+  // performing housekeeping.
+  const int num_files_to_keep_;
+
+  // If non-zero, files older than this will not be kept after
+  // performing housekeeping.
+  const TimeDelta max_file_age_;
+
+  // The string ".p2p".
+  static const char kP2PExtension[];
+
+  // The string ".tmp".
+  static const char kTmpExtension[];
+
+  // Whether P2P service may be running; initially, we assume it may be.
+  bool may_be_running_ = true;
+
+  // The current known enabled status of the P2P feature (initialized lazily),
+  // and whether an async status check has been scheduled.
+  bool is_enabled_;
+  bool waiting_for_enabled_status_change_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(P2PManagerImpl);
+};
+
+const char P2PManagerImpl::kP2PExtension[] = ".p2p";
+
+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)
+  : clock_(clock),
+    update_manager_(update_manager),
+    file_extension_(file_extension),
+    num_files_to_keep_(num_files_to_keep),
+    max_file_age_(max_file_age) {
+  configuration_.reset(configuration != nullptr ? configuration :
+                       new ConfigurationImpl());
+}
+
+void P2PManagerImpl::SetDevicePolicy(
+    const policy::DevicePolicy* device_policy) {
+  device_policy_ = device_policy;
+}
+
+bool P2PManagerImpl::IsP2PEnabled() {
+  if (!waiting_for_enabled_status_change_) {
+    // Get and store an initial value.
+    if (update_manager_->PolicyRequest(&Policy::P2PEnabled, &is_enabled_) ==
+        EvalStatus::kFailed) {
+      is_enabled_ = false;
+      LOG(ERROR) << "Querying P2P enabled status failed, disabling.";
+    }
+
+    // Track future changes (async).
+    ScheduleEnabledStatusChange();
+  }
+
+  return is_enabled_;
+}
+
+bool P2PManagerImpl::EnsureP2P(bool should_be_running) {
+  int return_code = 0;
+  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, &output)) {
+    LOG(ERROR) << "Error spawning " << utils::StringVectorToString(args);
+    return false;
+  }
+
+  // If initctl(8) does not exit normally (exit status other than zero), ensure
+  // that the error message is not benign by scanning stderr; this is a
+  // necessity because initctl does not offer actions such as "start if not
+  // running" or "stop if running".
+  // TODO(zeuthen,chromium:277051): Avoid doing this.
+  if (return_code != 0) {
+    const char *expected_error_message = should_be_running ?
+      "initctl: Job is already running: p2p\n" :
+      "initctl: Unknown instance \n";
+    if (output != expected_error_message)
+      return false;
+  }
+
+  may_be_running_ = should_be_running;  // Successful after all.
+  return true;
+}
+
+bool P2PManagerImpl::EnsureP2PRunning() {
+  return EnsureP2P(true);
+}
+
+bool P2PManagerImpl::EnsureP2PNotRunning() {
+  return EnsureP2P(false);
+}
+
+// Returns True if the timestamp in the first pair is greater than the
+// timestamp in the latter. If used with std::sort() this will yield a
+// sequence of elements where newer (high timestamps) elements precede
+// older ones (low timestamps).
+static bool MatchCompareFunc(const pair<FilePath, Time>& a,
+                             const pair<FilePath, Time>& b) {
+  return a.second > b.second;
+}
+
+string P2PManagerImpl::GetExt(Visibility visibility) {
+  string ext = string(".") + file_extension_ + kP2PExtension;
+  switch (visibility) {
+  case kVisible:
+    break;
+  case kNonVisible:
+    ext += kTmpExtension;
+    break;
+  // Don't add a default case to let the compiler warn about newly
+  // added enum values.
+  }
+  return ext;
+}
+
+FilePath P2PManagerImpl::GetPath(const string& file_id, Visibility visibility) {
+  return configuration_->GetP2PDir().Append(file_id + GetExt(visibility));
+}
+
+bool P2PManagerImpl::DeleteP2PFile(const FilePath& path,
+                                   const string& reason) {
+  LOG(INFO) << "Deleting p2p file " << path.value()
+            << " (reason: " << reason << ")";
+  if (unlink(path.value().c_str()) != 0) {
+    PLOG(ERROR) << "Error deleting p2p file " << path.value();
+    return false;
+  }
+  return true;
+}
+
+
+bool P2PManagerImpl::PerformHousekeeping() {
+  // Open p2p dir.
+  FilePath p2p_dir = configuration_->GetP2PDir();
+  const string ext_visible = GetExt(kVisible);
+  const string ext_non_visible = GetExt(kNonVisible);
+
+  bool deletion_failed = false;
+  vector<pair<FilePath, Time>> matches;
+
+  base::FileEnumerator dir(p2p_dir, false, base::FileEnumerator::FILES);
+  // Go through all files and collect their mtime.
+  for (FilePath name = dir.Next(); !name.empty(); name = dir.Next()) {
+    if (!(base::EndsWith(name.value(), ext_visible, true) ||
+          base::EndsWith(name.value(), ext_non_visible, true)))
+      continue;
+
+    Time time = dir.GetInfo().GetLastModifiedTime();
+
+    // 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 (clock_ != nullptr && max_file_age_ != TimeDelta() &&
+        clock_->GetWallclockTime() - time > max_file_age_) {
+      if (!DeleteP2PFile(name, "file too old"))
+        deletion_failed = true;
+    } else {
+      matches.push_back(std::make_pair(name, time));
+    }
+  }
+
+  // If instructed to only keep N files (|max_files_to_keep_ != 0),
+  // sort list of matches, newest (biggest time) to oldest (lowest
+  // time). Then delete starting at element |num_files_to_keep_|.
+  if (num_files_to_keep_ > 0) {
+    std::sort(matches.begin(), matches.end(), MatchCompareFunc);
+    vector<pair<FilePath, Time>>::const_iterator i;
+    for (i = matches.begin() + num_files_to_keep_; i < matches.end(); ++i) {
+      if (!DeleteP2PFile(i->first, "too many files"))
+        deletion_failed = true;
+    }
+  }
+
+  return !deletion_failed;
+}
+
+// Helper class for implementing LookupUrlForFile().
+class LookupData {
+ public:
+  explicit LookupData(P2PManager::LookupCallback callback)
+    : callback_(callback) {}
+
+  ~LookupData() {
+    if (timeout_task_ != MessageLoop::kTaskIdNull)
+      MessageLoop::current()->CancelTask(timeout_task_);
+    if (child_pid_)
+      Subprocess::Get().KillExec(child_pid_);
+  }
+
+  void InitiateLookup(const vector<string>& cmd, TimeDelta timeout) {
+    // NOTE: if we fail early (i.e. in this method), we need to schedule
+    // an idle to report the error. This is because we guarantee that
+    // the callback is always called from the message loop (this
+    // guarantee is useful for testing).
+
+    // We expect to run just "p2p-client" and find it in the path.
+    child_pid_ = Subprocess::Get().ExecFlags(
+        cmd, Subprocess::kSearchPath,
+        Bind(&LookupData::OnLookupDone, base::Unretained(this)));
+
+    if (!child_pid_) {
+      LOG(ERROR) << "Error spawning " << utils::StringVectorToString(cmd);
+      ReportErrorAndDeleteInIdle();
+      return;
+    }
+
+    if (timeout > TimeDelta()) {
+      timeout_task_ = MessageLoop::current()->PostDelayedTask(
+          FROM_HERE,
+          Bind(&LookupData::OnTimeout, base::Unretained(this)),
+          timeout);
+    }
+  }
+
+ private:
+  void ReportErrorAndDeleteInIdle() {
+    MessageLoop::current()->PostTask(FROM_HERE, Bind(
+        &LookupData::OnIdleForReportErrorAndDelete,
+        base::Unretained(this)));
+  }
+
+  void OnIdleForReportErrorAndDelete() {
+    ReportError();
+    delete this;
+  }
+
+  void IssueCallback(const string& url) {
+    if (!callback_.is_null())
+      callback_.Run(url);
+  }
+
+  void ReportError() {
+    if (reported_)
+      return;
+    IssueCallback("");
+    reported_ = true;
+  }
+
+  void ReportSuccess(const string& output) {
+    if (reported_)
+      return;
+    string url = output;
+    size_t newline_pos = url.find('\n');
+    if (newline_pos != string::npos)
+      url.resize(newline_pos);
+
+    // Since p2p-client(1) is constructing this URL itself strictly
+    // speaking there's no need to validate it... but, anyway, can't
+    // hurt.
+    if (url.compare(0, 7, "http://") == 0) {
+      IssueCallback(url);
+    } else {
+      LOG(ERROR) << "p2p URL '" << url << "' does not look right. Ignoring.";
+      ReportError();
+    }
+    reported_ = true;
+  }
+
+  void OnLookupDone(int return_code, const string& output) {
+    child_pid_ = 0;
+    if (return_code != 0) {
+      LOG(INFO) << "Child exited with non-zero exit code "
+                << return_code;
+      ReportError();
+    } else {
+      ReportSuccess(output);
+    }
+    delete this;
+  }
+
+  void OnTimeout() {
+    timeout_task_ = MessageLoop::kTaskIdNull;
+    ReportError();
+    delete this;
+  }
+
+  P2PManager::LookupCallback callback_;
+
+  // The Subprocess tag of the running process. A value of 0 means that the
+  // process is not running.
+  pid_t child_pid_{0};
+
+  // The timeout task_id we are waiting on, if any.
+  MessageLoop::TaskId timeout_task_{MessageLoop::kTaskIdNull};
+
+  bool reported_{false};
+};
+
+void P2PManagerImpl::LookupUrlForFile(const string& file_id,
+                                      size_t minimum_size,
+                                      TimeDelta max_time_to_wait,
+                                      LookupCallback callback) {
+  LookupData *lookup_data = new LookupData(callback);
+  string file_id_with_ext = file_id + "." + file_extension_;
+  vector<string> args = configuration_->GetP2PClientArgs(file_id_with_ext,
+                                                         minimum_size);
+  lookup_data->InitiateLookup(args, max_time_to_wait);
+}
+
+bool P2PManagerImpl::FileShare(const string& file_id,
+                               size_t expected_size) {
+  // Check if file already exist.
+  FilePath path = FileGetPath(file_id);
+  if (!path.empty()) {
+    // File exists - double check its expected size though.
+    ssize_t file_expected_size = FileGetExpectedSize(file_id);
+    if (file_expected_size == -1 ||
+        static_cast<size_t>(file_expected_size) != expected_size) {
+      LOG(ERROR) << "Existing p2p file " << path.value()
+                 << " with expected_size=" << file_expected_size
+                 << " does not match the passed in"
+                 << " expected_size=" << expected_size;
+      return false;
+    }
+    return true;
+  }
+
+  // Before creating the file, bail if statvfs(3) indicates that at
+  // least twice the size is not available in P2P_DIR.
+  struct statvfs statvfsbuf;
+  FilePath p2p_dir = configuration_->GetP2PDir();
+  if (statvfs(p2p_dir.value().c_str(), &statvfsbuf) != 0) {
+    PLOG(ERROR) << "Error calling statvfs() for dir " << p2p_dir.value();
+    return false;
+  }
+  size_t free_bytes =
+      static_cast<size_t>(statvfsbuf.f_bsize) * statvfsbuf.f_bavail;
+  if (free_bytes < 2 * expected_size) {
+    // This can easily happen and is worth reporting.
+    LOG(INFO) << "Refusing to allocate p2p file of " << expected_size
+              << " bytes since the directory " << p2p_dir.value()
+              << " only has " << free_bytes
+              << " bytes available and this is less than twice the"
+              << " requested size.";
+    return false;
+  }
+
+  // Okie-dokey looks like enough space is available - create the file.
+  path = GetPath(file_id, kNonVisible);
+  int fd = open(path.value().c_str(), O_CREAT | O_RDWR, 0644);
+  if (fd == -1) {
+    PLOG(ERROR) << "Error creating file with path " << path.value();
+    return false;
+  }
+  ScopedFdCloser fd_closer(&fd);
+
+  // If the final size is known, allocate the file (e.g. reserve disk
+  // space) and set the user.cros-p2p-filesize xattr.
+  if (expected_size != 0) {
+    if (fallocate(fd,
+                  FALLOC_FL_KEEP_SIZE,  // Keep file size as 0.
+                  0,
+                  expected_size) != 0) {
+      if (errno == ENOSYS || errno == EOPNOTSUPP) {
+        // If the filesystem doesn't support the fallocate, keep
+        // going. This is helpful when running unit tests on build
+        // machines with ancient filesystems and/or OSes.
+        PLOG(WARNING) << "Ignoring fallocate(2) failure";
+      } else {
+        // ENOSPC can happen (funky race though, cf. the statvfs() check
+        // above), handle it gracefully, e.g. use logging level INFO.
+        PLOG(INFO) << "Error allocating " << expected_size
+                   << " bytes for file " << path.value();
+        if (unlink(path.value().c_str()) != 0) {
+          PLOG(ERROR) << "Error deleting file with path " << path.value();
+        }
+        return false;
+      }
+    }
+
+    string decimal_size = std::to_string(expected_size);
+    if (fsetxattr(fd, kCrosP2PFileSizeXAttrName,
+                  decimal_size.c_str(), decimal_size.size(), 0) != 0) {
+      PLOG(ERROR) << "Error setting xattr " << path.value();
+      return false;
+    }
+  }
+
+  return true;
+}
+
+FilePath P2PManagerImpl::FileGetPath(const string& file_id) {
+  struct stat statbuf;
+  FilePath path;
+
+  path = GetPath(file_id, kVisible);
+  if (stat(path.value().c_str(), &statbuf) == 0) {
+    return path;
+  }
+
+  path = GetPath(file_id, kNonVisible);
+  if (stat(path.value().c_str(), &statbuf) == 0) {
+    return path;
+  }
+
+  path.clear();
+  return path;
+}
+
+bool P2PManagerImpl::FileGetVisible(const string& file_id,
+                                    bool *out_result) {
+  FilePath path = FileGetPath(file_id);
+  if (path.empty()) {
+    LOG(ERROR) << "No file for id " << file_id;
+    return false;
+  }
+  if (out_result != nullptr)
+    *out_result = path.MatchesExtension(kP2PExtension);
+  return true;
+}
+
+bool P2PManagerImpl::FileMakeVisible(const string& file_id) {
+  FilePath path = FileGetPath(file_id);
+  if (path.empty()) {
+    LOG(ERROR) << "No file for id " << file_id;
+    return false;
+  }
+
+  // Already visible?
+  if (path.MatchesExtension(kP2PExtension))
+    return true;
+
+  LOG_ASSERT(path.MatchesExtension(kTmpExtension));
+  FilePath new_path = path.RemoveExtension();
+  LOG_ASSERT(new_path.MatchesExtension(kP2PExtension));
+  if (rename(path.value().c_str(), new_path.value().c_str()) != 0) {
+    PLOG(ERROR) << "Error renaming " << path.value()
+                << " to " << new_path.value();
+    return false;
+  }
+
+  return true;
+}
+
+ssize_t P2PManagerImpl::FileGetSize(const string& file_id) {
+  FilePath path = FileGetPath(file_id);
+  if (path.empty())
+    return -1;
+
+  return utils::FileSize(path.value());
+}
+
+ssize_t P2PManagerImpl::FileGetExpectedSize(const string& file_id) {
+  FilePath path = FileGetPath(file_id);
+  if (path.empty())
+    return -1;
+
+  char ea_value[64] = { 0 };
+  ssize_t ea_size;
+  ea_size = getxattr(path.value().c_str(), kCrosP2PFileSizeXAttrName,
+                     &ea_value, sizeof(ea_value) - 1);
+  if (ea_size == -1) {
+    PLOG(ERROR) << "Error calling getxattr() on file " << path.value();
+    return -1;
+  }
+
+  char* endp = nullptr;
+  long long int val = strtoll(ea_value, &endp, 0);  // NOLINT(runtime/int)
+  if (*endp != '\0') {
+    LOG(ERROR) << "Error parsing the value '" << ea_value
+               << "' of the xattr " << kCrosP2PFileSizeXAttrName
+               << " as an integer";
+    return -1;
+  }
+
+  return val;
+}
+
+int P2PManagerImpl::CountSharedFiles() {
+  int num_files = 0;
+
+  FilePath p2p_dir = configuration_->GetP2PDir();
+  const string ext_visible = GetExt(kVisible);
+  const string ext_non_visible = GetExt(kNonVisible);
+
+  base::FileEnumerator dir(p2p_dir, false, base::FileEnumerator::FILES);
+  for (FilePath name = dir.Next(); !name.empty(); name = dir.Next()) {
+    if (base::EndsWith(name.value(), ext_visible, true) ||
+        base::EndsWith(name.value(), ext_non_visible, true))
+      num_files += 1;
+  }
+
+  return num_files;
+}
+
+void P2PManagerImpl::ScheduleEnabledStatusChange() {
+  if (waiting_for_enabled_status_change_)
+    return;
+
+  Callback<void(EvalStatus, const bool&)> callback = Bind(
+      &P2PManagerImpl::OnEnabledStatusChange, base::Unretained(this));
+  update_manager_->AsyncPolicyRequest(callback, &Policy::P2PEnabledChanged,
+                                      is_enabled_);
+  waiting_for_enabled_status_change_ = true;
+}
+
+void P2PManagerImpl::OnEnabledStatusChange(EvalStatus status,
+                                           const bool& result) {
+  waiting_for_enabled_status_change_ = false;
+
+  if (status == EvalStatus::kSucceeded) {
+    if (result == is_enabled_) {
+      LOG(WARNING) << "P2P enabled status did not change, which means that it "
+                      "is permanent; not scheduling further checks.";
+      waiting_for_enabled_status_change_ = true;
+      return;
+    }
+
+    is_enabled_ = result;
+
+    // If P2P is running but shouldn't be, make sure it isn't.
+    if (may_be_running_ && !is_enabled_ && !EnsureP2PNotRunning()) {
+      LOG(WARNING) << "Failed to stop P2P service.";
+    }
+  } else {
+    LOG(WARNING)
+        << "P2P enabled tracking failed (possibly timed out); retrying.";
+  }
+
+  ScheduleEnabledStatusChange();
+}
+
+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,
+                            max_file_age);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/p2p_manager.h b/p2p_manager.h
new file mode 100644
index 0000000..4ffab9a
--- /dev/null
+++ b/p2p_manager.h
@@ -0,0 +1,188 @@
+//
+// 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_P2P_MANAGER_H_
+#define UPDATE_ENGINE_P2P_MANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include <base/callback.h>
+#include <base/files/file_path.h>
+#include <base/memory/ref_counted.h>
+#include <base/time/time.h>
+#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"
+
+namespace chromeos_update_engine {
+
+// Interface for sharing and discovering files via p2p.
+class P2PManager {
+ public:
+  // Interface used for P2PManager implementations. The sole reason
+  // for this interface is unit testing.
+  class Configuration {
+   public:
+    virtual ~Configuration() {}
+
+    // Gets the path to the p2p dir being used, e.g. /var/cache/p2p.
+    virtual base::FilePath GetP2PDir() = 0;
+
+    // Gets the argument vector for starting (if |is_start| is True)
+    // resp. stopping (if |is_start| is False) the p2p service
+    // e.g. ["initctl", "start", "p2p"] or ["initctl", "stop", "p2p"].
+    virtual std::vector<std::string> GetInitctlArgs(bool is_start) = 0;
+
+    // Gets the argument vector for invoking p2p-client, e.g.
+    // "p2p-client --get-url=file_id_we_want --minimum-size=42123".
+    virtual std::vector<std::string> GetP2PClientArgs(
+        const std::string& file_id, size_t minimum_size) = 0;
+  };
+
+  virtual ~P2PManager() {}
+
+  // The type for the callback used in LookupUrlForFile().
+  // If the lookup failed, |url| is empty.
+  typedef base::Callback<void(const std::string& url)> LookupCallback;
+
+  // Use the device policy specified by |device_policy|. If this is
+  // null, then no device policy is used.
+  virtual void SetDevicePolicy(const policy::DevicePolicy* device_policy) = 0;
+
+  // Returns true iff P2P is currently allowed for use on this device. This
+  // value is determined and maintained by the Update Manager.
+  virtual bool IsP2PEnabled() = 0;
+
+  // Ensures that the p2p subsystem is running (e.g. starts it if it's
+  // not already running) and blocks until this is so. Returns false
+  // if an error occurred.
+  virtual bool EnsureP2PRunning() = 0;
+
+  // Ensures that the p2p subsystem is not running (e.g. stops it if
+  // it's running) and blocks until this is so. Returns false if an
+  // error occurred.
+  virtual bool EnsureP2PNotRunning() = 0;
+
+  // Cleans up files in /var/cache/p2p owned by this application as
+  // per the |file_extension| and |num_files_to_keep| values passed
+  // when the object was constructed. This may be called even if
+  // the p2p subsystem is not running.
+  virtual bool PerformHousekeeping() = 0;
+
+  // Asynchronously finds a peer that serves the file identified by
+  // |file_id|. If |minimum_size| is non-zero, will find a peer that
+  // has at least that many bytes. When the result is ready |callback|
+  // is called from the current message loop.
+  //
+  // This operation may take a very long time to complete because part
+  // of the p2p protocol involves waiting for the LAN-wide sum of all
+  // num-connections to drop below a given threshold. However, if
+  // |max_time_to_wait| is non-zero, the operation is guaranteed to
+  // not exceed this duration.
+  //
+  // If the file is not available on the LAN (or if mDNS/DNS-SD is
+  // filtered), this is guaranteed to not take longer than 5 seconds.
+  virtual void LookupUrlForFile(const std::string& file_id,
+                                size_t minimum_size,
+                                base::TimeDelta max_time_to_wait,
+                                LookupCallback callback) = 0;
+
+  // Shares a file identified by |file_id| in the directory
+  // /var/cache/p2p. Initially the file will not be visible, that is,
+  // it will have a .tmp extension and not be shared via p2p. Use the
+  // FileMakeVisible() method to change this.
+  //
+  // If you know the final size of the file, pass it in the
+  // |expected_size| parameter. Otherwise pass zero. If non-zero, the
+  // amount of free space in /var/cache/p2p is checked and if there is
+  // less than twice the amount of space available, this method
+  // fails. Additionally, disk space will be reserved via fallocate(2)
+  // and |expected_size| is written to the user.cros-p2p-filesize
+  // xattr of the created file.
+  //
+  // If the file already exists, true is returned. Any on-disk xattr
+  // is not updated.
+  virtual bool FileShare(const std::string& file_id,
+                         size_t expected_size) = 0;
+
+  // Gets a fully qualified path for the file identified by |file_id|.
+  // If the file has not been shared already using the FileShare()
+  // method, an empty base::FilePath is returned - use FilePath::empty() to
+  // find out.
+  virtual base::FilePath FileGetPath(const std::string& file_id) = 0;
+
+  // Gets the actual size of the file identified by |file_id|. This is
+  // equivalent to reading the value of the st_size field of the
+  // struct stat on the file given by FileGetPath(). Returns -1 if an
+  // error occurs.
+  //
+  // For a file just created with FileShare() this will return 0.
+  virtual ssize_t FileGetSize(const std::string& file_id) = 0;
+
+  // Gets the expected size of the file identified by |file_id|. This
+  // is equivalent to reading the value of the user.cros-p2p-filesize
+  // xattr on the file given by FileGetPath(). Returns -1 if an error
+  // occurs.
+  //
+  // For a file just created with FileShare() this will return the
+  // value of the |expected_size| parameter passed to that method.
+  virtual ssize_t FileGetExpectedSize(const std::string& file_id) = 0;
+
+  // Gets whether the file identified by |file_id| is publicly
+  // visible. If |out_result| is not null, the result is returned
+  // there. Returns false if an error occurs.
+  virtual bool FileGetVisible(const std::string& file_id,
+                              bool *out_result) = 0;
+
+  // Makes the file identified by |file_id| publicly visible
+  // (e.g. removes the .tmp extension). If the file is already
+  // visible, this method does nothing. Returns False if
+  // the method fails or there is no file for |file_id|.
+  virtual bool FileMakeVisible(const std::string& file_id) = 0;
+
+  // Counts the number of shared files used by this application
+  // (cf. the |file_extension parameter|. Returns -1 if an error
+  // occurred.
+  virtual int CountSharedFiles() = 0;
+
+  // Creates a suitable P2PManager instance and initializes the object
+  // so it's ready for use. The |file_extension| parameter is used to
+  // identify your application, use e.g. "cros_au".  If
+  // |configuration| is non-null, the P2PManager will take ownership
+  // of the Configuration object and use it (hence, it must be
+  // heap-allocated).
+  //
+  // The |num_files_to_keep| parameter specifies how many files to
+  // keep after performing housekeeping (cf. the PerformHousekeeping()
+  // method) - pass zero to allow infinitely many files. The
+  // |max_file_age| parameter specifies the maximum file age after
+  // 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,
+      const base::TimeDelta& max_file_age);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_P2P_MANAGER_H_
diff --git a/p2p_manager_unittest.cc b/p2p_manager_unittest.cc
new file mode 100644
index 0000000..463c0e2
--- /dev/null
+++ b/p2p_manager_unittest.cc
@@ -0,0 +1,515 @@
+//
+// 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/p2p_manager.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/files/file_util.h>
+#include <base/message_loop/message_loop.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/asynchronous_signal_handler.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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+#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/test_utils.h"
+#include "update_engine/common/utils.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"
+
+using base::TimeDelta;
+using brillo::MessageLoop;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+using testing::DoAll;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::_;
+
+namespace chromeos_update_engine {
+
+// Test fixture that sets up a testing configuration (with e.g. a
+// temporary p2p dir) for P2PManager and cleans up when the test is
+// done.
+class P2PManagerTest : public testing::Test {
+ protected:
+  P2PManagerTest() : fake_um_(&fake_clock_) {}
+  ~P2PManagerTest() override {}
+
+  // Derived from testing::Test.
+  void SetUp() override {
+    loop_.SetAsCurrent();
+    async_signal_handler_.Init();
+    subprocess_.Init(&async_signal_handler_);
+    test_conf_ = new FakeP2PManagerConfiguration();
+
+    // 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(&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)));
+  }
+
+  base::MessageLoopForIO base_loop_;
+  brillo::BaseMessageLoop loop_{&base_loop_};
+  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_;
+
+  unique_ptr<P2PManager> manager_;
+};
+
+
+// Check that IsP2PEnabled() polls the policy correctly, with the value not
+// changing between calls.
+TEST_F(P2PManagerTest, P2PEnabledInitAndNotChanged) {
+  EXPECT_CALL(*mock_policy_, P2PEnabled(_, _, _, _));
+  EXPECT_CALL(*mock_policy_, P2PEnabledChanged(_, _, _, _, false));
+
+  EXPECT_FALSE(manager_->IsP2PEnabled());
+  brillo::MessageLoopRunMaxIterations(MessageLoop::current(), 100);
+  EXPECT_FALSE(manager_->IsP2PEnabled());
+}
+
+// Check that IsP2PEnabled() polls the policy correctly, with the value changing
+// between calls.
+TEST_F(P2PManagerTest, P2PEnabledInitAndChanged) {
+  EXPECT_CALL(*mock_policy_, P2PEnabled(_, _, _, _))
+      .WillOnce(DoAll(
+              SetArgPointee<3>(true),
+              Return(chromeos_update_manager::EvalStatus::kSucceeded)));
+  EXPECT_CALL(*mock_policy_, P2PEnabledChanged(_, _, _, _, true));
+  EXPECT_CALL(*mock_policy_, P2PEnabledChanged(_, _, _, _, false));
+
+  EXPECT_TRUE(manager_->IsP2PEnabled());
+  brillo::MessageLoopRunMaxIterations(MessageLoop::current(), 100);
+  EXPECT_FALSE(manager_->IsP2PEnabled());
+}
+
+// Check that we keep the $N newest files with the .$EXT.p2p extension.
+TEST_F(P2PManagerTest, HousekeepingCountLimit) {
+  // Specifically pass 0 for |max_file_age| to allow files of any age. 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", 3,
+      TimeDelta() /* max_file_age */));
+  EXPECT_EQ(manager_->CountSharedFiles(), 0);
+
+  base::Time start_time = base::Time::FromDoubleT(1246996800.);
+  // Generate files with different timestamps matching our pattern and generate
+  // other files not matching the pattern.
+  for (int n = 0; n < 5; n++) {
+    base::FilePath path = test_conf_->GetP2PDir().Append(base::StringPrintf(
+        "file_%d.cros_au.p2p", n));
+    base::Time file_time = start_time + TimeDelta::FromMinutes(n);
+    EXPECT_EQ(0, base::WriteFile(path, nullptr, 0));
+    EXPECT_TRUE(base::TouchFile(path, file_time, file_time));
+
+    path = test_conf_->GetP2PDir().Append(base::StringPrintf(
+        "file_%d.OTHER.p2p", n));
+    EXPECT_EQ(0, base::WriteFile(path, nullptr, 0));
+    EXPECT_TRUE(base::TouchFile(path, file_time, file_time));
+  }
+  // CountSharedFiles() only counts 'cros_au' files.
+  EXPECT_EQ(manager_->CountSharedFiles(), 5);
+
+  EXPECT_TRUE(manager_->PerformHousekeeping());
+
+  // At this point - after HouseKeeping - we should only have
+  // eight files left.
+  for (int n = 0; n < 5; n++) {
+    string file_name;
+    bool expect;
+
+    expect = (n >= 2);
+    file_name = base::StringPrintf(
+        "%s/file_%d.cros_au.p2p",
+         test_conf_->GetP2PDir().value().c_str(), n);
+    EXPECT_EQ(expect, utils::FileExists(file_name.c_str()));
+
+    file_name = base::StringPrintf(
+        "%s/file_%d.OTHER.p2p",
+        test_conf_->GetP2PDir().value().c_str(), n);
+    EXPECT_TRUE(utils::FileExists(file_name.c_str()));
+  }
+  // CountSharedFiles() only counts 'cros_au' files.
+  EXPECT_EQ(manager_->CountSharedFiles(), 3);
+}
+
+// Check that we keep files with the .$EXT.p2p extension not older
+// than some specificed age (5 days, in this test).
+TEST_F(P2PManagerTest, HousekeepingAgeLimit) {
+  // We set the cutoff time to be 1 billion seconds (01:46:40 UTC on 9
+  // September 2001 - arbitrary number, but constant to avoid test
+  // flakiness) since the epoch and then we put two files before that
+  // date and three files after.
+  base::Time cutoff_time = base::Time::FromTimeT(1000000000);
+  TimeDelta age_limit = TimeDelta::FromDays(5);
+
+  // Set the clock just so files with a timestamp before |cutoff_time|
+  // will be deleted at housekeeping.
+  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 */, age_limit));
+  EXPECT_EQ(manager_->CountSharedFiles(), 0);
+
+  // Generate files with different timestamps matching our pattern and generate
+  // other files not matching the pattern.
+  for (int n = 0; n < 5; n++) {
+    base::FilePath path = test_conf_->GetP2PDir().Append(base::StringPrintf(
+        "file_%d.cros_au.p2p", n));
+
+    // With five files and aiming for two of them to be before
+    // |cutoff_time|, we distribute it like this:
+    //
+    //  -------- 0 -------- 1 -------- 2 -------- 3 -------- 4 --------
+    //                            |
+    //                       cutoff_time
+    //
+    base::Time file_date = cutoff_time + (n - 2) * TimeDelta::FromDays(1)
+        + TimeDelta::FromHours(12);
+
+    EXPECT_EQ(0, base::WriteFile(path, nullptr, 0));
+    EXPECT_TRUE(base::TouchFile(path, file_date, file_date));
+
+    path = test_conf_->GetP2PDir().Append(base::StringPrintf(
+        "file_%d.OTHER.p2p", n));
+    EXPECT_EQ(0, base::WriteFile(path, nullptr, 0));
+    EXPECT_TRUE(base::TouchFile(path, file_date, file_date));
+  }
+  // CountSharedFiles() only counts 'cros_au' files.
+  EXPECT_EQ(manager_->CountSharedFiles(), 5);
+
+  EXPECT_TRUE(manager_->PerformHousekeeping());
+
+  // At this point - after HouseKeeping - we should only have
+  // eight files left.
+  for (int n = 0; n < 5; n++) {
+    string file_name;
+    bool expect;
+
+    expect = (n >= 2);
+    file_name = base::StringPrintf(
+        "%s/file_%d.cros_au.p2p",
+         test_conf_->GetP2PDir().value().c_str(), n);
+    EXPECT_EQ(expect, utils::FileExists(file_name.c_str()));
+
+    file_name = base::StringPrintf(
+        "%s/file_%d.OTHER.p2p",
+        test_conf_->GetP2PDir().value().c_str(), n);
+    EXPECT_TRUE(utils::FileExists(file_name.c_str()));
+  }
+  // CountSharedFiles() only counts 'cros_au' files.
+  EXPECT_EQ(manager_->CountSharedFiles(), 3);
+}
+
+static bool CheckP2PFile(const string& p2p_dir, const string& file_name,
+                         ssize_t expected_size, ssize_t expected_size_xattr) {
+  string path = p2p_dir + "/" + file_name;
+  char ea_value[64] = { 0 };
+  ssize_t ea_size;
+
+  off_t p2p_size = utils::FileSize(path);
+  if (p2p_size < 0) {
+    LOG(ERROR) << "File " << path << " does not exist";
+    return false;
+  }
+
+  if (expected_size != 0) {
+    if (p2p_size != expected_size) {
+      LOG(ERROR) << "Expected size " << expected_size
+                 << " but size was " << p2p_size;
+      return false;
+    }
+  }
+
+  if (expected_size_xattr == 0) {
+    ea_size = getxattr(path.c_str(), "user.cros-p2p-filesize",
+                       &ea_value, sizeof ea_value - 1);
+    if (ea_size == -1 && errno == ENODATA) {
+      // This is valid behavior as we support files without the xattr set.
+    } else {
+      PLOG(ERROR) << "getxattr() didn't fail with ENODATA as expected, "
+                  << "ea_size=" << ea_size << ", errno=" << errno;
+      return false;
+    }
+  } else {
+    ea_size = getxattr(path.c_str(), "user.cros-p2p-filesize",
+                       &ea_value, sizeof ea_value - 1);
+    if (ea_size < 0) {
+      LOG(ERROR) << "Error getting xattr attribute";
+      return false;
+    }
+    char* endp = nullptr;
+    long long int val = strtoll(ea_value, &endp, 0);  // NOLINT(runtime/int)
+    if (endp == nullptr || *endp != '\0') {
+      LOG(ERROR) << "Error parsing xattr '" << ea_value
+                 << "' as an integer";
+      return false;
+    }
+    if (val != expected_size_xattr) {
+      LOG(ERROR) << "Expected xattr size " << expected_size_xattr
+                 << " but size was " << val;
+      return false;
+    }
+  }
+
+  return true;
+}
+
+static bool CreateP2PFile(string p2p_dir, string file_name,
+                          size_t size, size_t size_xattr) {
+  string path = p2p_dir + "/" + file_name;
+
+  int fd = open(path.c_str(), O_CREAT|O_RDWR, 0644);
+  if (fd == -1) {
+    PLOG(ERROR) << "Error creating file with path " << path;
+    return false;
+  }
+  if (ftruncate(fd, size) != 0) {
+    PLOG(ERROR) << "Error truncating " << path << " to size " << size;
+    close(fd);
+    return false;
+  }
+
+  if (size_xattr != 0) {
+    string decimal_size = std::to_string(size_xattr);
+    if (fsetxattr(fd, "user.cros-p2p-filesize",
+                  decimal_size.c_str(), decimal_size.size(), 0) != 0) {
+      PLOG(ERROR) << "Error setting xattr on " << path;
+      close(fd);
+      return false;
+    }
+  }
+
+  close(fd);
+  return true;
+}
+
+// Check that sharing a *new* file works.
+TEST_F(P2PManagerTest, ShareFile) {
+  if (!test_utils::IsXAttrSupported(base::FilePath("/tmp"))) {
+    LOG(WARNING) << "Skipping test because /tmp does not support xattr. "
+                 << "Please update your system to support this feature.";
+    return;
+  }
+  const int kP2PTestFileSize = 1000 * 1000;  // 1 MB
+
+  EXPECT_TRUE(manager_->FileShare("foo", kP2PTestFileSize));
+  EXPECT_EQ(manager_->FileGetPath("foo"),
+            test_conf_->GetP2PDir().Append("foo.cros_au.p2p.tmp"));
+  EXPECT_TRUE(CheckP2PFile(test_conf_->GetP2PDir().value(),
+                           "foo.cros_au.p2p.tmp", 0, kP2PTestFileSize));
+
+  // Sharing it again - with the same expected size - should return true
+  EXPECT_TRUE(manager_->FileShare("foo", kP2PTestFileSize));
+
+  // ... but if we use the wrong size, it should fail
+  EXPECT_FALSE(manager_->FileShare("foo", kP2PTestFileSize + 1));
+}
+
+// Check that making a shared file visible, does what is expected.
+TEST_F(P2PManagerTest, MakeFileVisible) {
+  if (!test_utils::IsXAttrSupported(base::FilePath("/tmp"))) {
+    LOG(WARNING) << "Skipping test because /tmp does not support xattr. "
+                 << "Please update your system to support this feature.";
+    return;
+  }
+  const int kP2PTestFileSize = 1000 * 1000;  // 1 MB
+
+  // First, check that it's not visible.
+  manager_->FileShare("foo", kP2PTestFileSize);
+  EXPECT_EQ(manager_->FileGetPath("foo"),
+            test_conf_->GetP2PDir().Append("foo.cros_au.p2p.tmp"));
+  EXPECT_TRUE(CheckP2PFile(test_conf_->GetP2PDir().value(),
+                           "foo.cros_au.p2p.tmp", 0, kP2PTestFileSize));
+  // Make the file visible and check that it changed its name. Do it
+  // twice to check that FileMakeVisible() is idempotent.
+  for (int n = 0; n < 2; n++) {
+    manager_->FileMakeVisible("foo");
+    EXPECT_EQ(manager_->FileGetPath("foo"),
+              test_conf_->GetP2PDir().Append("foo.cros_au.p2p"));
+    EXPECT_TRUE(CheckP2PFile(test_conf_->GetP2PDir().value(),
+                             "foo.cros_au.p2p", 0, kP2PTestFileSize));
+  }
+}
+
+// Check that we return the right values for existing files in P2P_DIR.
+TEST_F(P2PManagerTest, ExistingFiles) {
+  if (!test_utils::IsXAttrSupported(base::FilePath("/tmp"))) {
+    LOG(WARNING) << "Skipping test because /tmp does not support xattr. "
+                 << "Please update your system to support this feature.";
+    return;
+  }
+
+  bool visible;
+
+  // Check that errors are returned if the file does not exist
+  EXPECT_EQ(manager_->FileGetPath("foo"), base::FilePath());
+  EXPECT_EQ(manager_->FileGetSize("foo"), -1);
+  EXPECT_EQ(manager_->FileGetExpectedSize("foo"), -1);
+  EXPECT_FALSE(manager_->FileGetVisible("foo", nullptr));
+  // ... then create the file ...
+  EXPECT_TRUE(CreateP2PFile(test_conf_->GetP2PDir().value(),
+                            "foo.cros_au.p2p", 42, 43));
+  // ... and then check that the expected values are returned
+  EXPECT_EQ(manager_->FileGetPath("foo"),
+            test_conf_->GetP2PDir().Append("foo.cros_au.p2p"));
+  EXPECT_EQ(manager_->FileGetSize("foo"), 42);
+  EXPECT_EQ(manager_->FileGetExpectedSize("foo"), 43);
+  EXPECT_TRUE(manager_->FileGetVisible("foo", &visible));
+  EXPECT_TRUE(visible);
+
+  // One more time, this time with a .tmp variant. First ensure it errors out..
+  EXPECT_EQ(manager_->FileGetPath("bar"), base::FilePath());
+  EXPECT_EQ(manager_->FileGetSize("bar"), -1);
+  EXPECT_EQ(manager_->FileGetExpectedSize("bar"), -1);
+  EXPECT_FALSE(manager_->FileGetVisible("bar", nullptr));
+  // ... then create the file ...
+  EXPECT_TRUE(CreateP2PFile(test_conf_->GetP2PDir().value(),
+                            "bar.cros_au.p2p.tmp", 44, 45));
+  // ... and then check that the expected values are returned
+  EXPECT_EQ(manager_->FileGetPath("bar"),
+            test_conf_->GetP2PDir().Append("bar.cros_au.p2p.tmp"));
+  EXPECT_EQ(manager_->FileGetSize("bar"), 44);
+  EXPECT_EQ(manager_->FileGetExpectedSize("bar"), 45);
+  EXPECT_TRUE(manager_->FileGetVisible("bar", &visible));
+  EXPECT_FALSE(visible);
+}
+
+// This is a little bit ugly but short of mocking a 'p2p' service this
+// will have to do. E.g. we essentially simulate the various
+// behaviours of initctl(8) that we rely on.
+TEST_F(P2PManagerTest, StartP2P) {
+  // Check that we can start the service
+  test_conf_->SetInitctlStartCommand({"true"});
+  EXPECT_TRUE(manager_->EnsureP2PRunning());
+  test_conf_->SetInitctlStartCommand({"false"});
+  EXPECT_FALSE(manager_->EnsureP2PRunning());
+  test_conf_->SetInitctlStartCommand({
+      "sh", "-c", "echo \"initctl: Job is already running: p2p\" >&2; false"});
+  EXPECT_TRUE(manager_->EnsureP2PRunning());
+  test_conf_->SetInitctlStartCommand({
+      "sh", "-c", "echo something else >&2; false"});
+  EXPECT_FALSE(manager_->EnsureP2PRunning());
+}
+
+// Same comment as for StartP2P
+TEST_F(P2PManagerTest, StopP2P) {
+  // Check that we can start the service
+  test_conf_->SetInitctlStopCommand({"true"});
+  EXPECT_TRUE(manager_->EnsureP2PNotRunning());
+  test_conf_->SetInitctlStopCommand({"false"});
+  EXPECT_FALSE(manager_->EnsureP2PNotRunning());
+  test_conf_->SetInitctlStopCommand({
+      "sh", "-c", "echo \"initctl: Unknown instance \" >&2; false"});
+  EXPECT_TRUE(manager_->EnsureP2PNotRunning());
+  test_conf_->SetInitctlStopCommand({
+      "sh", "-c", "echo something else >&2; false"});
+  EXPECT_FALSE(manager_->EnsureP2PNotRunning());
+}
+
+static void ExpectUrl(const string& expected_url,
+                      const string& url) {
+  EXPECT_EQ(url, expected_url);
+  MessageLoop::current()->BreakLoop();
+}
+
+// Like StartP2P, we're mocking the different results that p2p-client
+// can return. It's not pretty but it works.
+TEST_F(P2PManagerTest, LookupURL) {
+  // Emulate p2p-client returning valid URL with "fooX", 42 and "cros_au"
+  // being propagated in the right places.
+  test_conf_->SetP2PClientCommand({
+      "echo", "http://1.2.3.4/{file_id}_{minsize}"});
+  manager_->LookupUrlForFile("fooX", 42, TimeDelta(),
+                             base::Bind(ExpectUrl,
+                                        "http://1.2.3.4/fooX.cros_au_42"));
+  loop_.Run();
+
+  // Emulate p2p-client returning invalid URL.
+  test_conf_->SetP2PClientCommand({"echo", "not_a_valid_url"});
+  manager_->LookupUrlForFile("foobar", 42, TimeDelta(),
+                             base::Bind(ExpectUrl, ""));
+  loop_.Run();
+
+  // Emulate p2p-client conveying failure.
+  test_conf_->SetP2PClientCommand({"false"});
+  manager_->LookupUrlForFile("foobar", 42, TimeDelta(),
+                             base::Bind(ExpectUrl, ""));
+  loop_.Run();
+
+  // Emulate p2p-client not existing.
+  test_conf_->SetP2PClientCommand({"/path/to/non/existent/helper/program"});
+  manager_->LookupUrlForFile("foobar", 42,
+                             TimeDelta(),
+                             base::Bind(ExpectUrl, ""));
+  loop_.Run();
+
+  // Emulate p2p-client crashing.
+  test_conf_->SetP2PClientCommand({"sh", "-c", "kill -SEGV $$"});
+  manager_->LookupUrlForFile("foobar", 42, TimeDelta(),
+                             base::Bind(ExpectUrl, ""));
+  loop_.Run();
+
+  // Emulate p2p-client exceeding its timeout.
+  test_conf_->SetP2PClientCommand({
+      "sh", "-c", "echo http://1.2.3.4/; sleep 2"});
+  manager_->LookupUrlForFile("foobar", 42, TimeDelta::FromMilliseconds(500),
+                             base::Bind(ExpectUrl, ""));
+  loop_.Run();
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/bzip_extent_writer.cc b/payload_consumer/bzip_extent_writer.cc
new file mode 100644
index 0000000..0fcc8ba
--- /dev/null
+++ b/payload_consumer/bzip_extent_writer.cc
@@ -0,0 +1,91 @@
+//
+// 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_consumer/bzip_extent_writer.h"
+
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+const brillo::Blob::size_type kOutputBufferLength = 16 * 1024;
+}
+
+bool BzipExtentWriter::Init(FileDescriptorPtr fd,
+                            const vector<Extent>& extents,
+                            uint32_t block_size) {
+  // Init bzip2 stream
+  int rc = BZ2_bzDecompressInit(&stream_,
+                                0,   // verbosity. (0 == silent)
+                                0);  // 0 = faster algo, more memory
+
+  TEST_AND_RETURN_FALSE(rc == BZ_OK);
+
+  return next_->Init(fd, extents, block_size);
+}
+
+bool BzipExtentWriter::Write(const void* bytes, size_t count) {
+  brillo::Blob output_buffer(kOutputBufferLength);
+
+  // Copy the input data into |input_buffer_| only if |input_buffer_| already
+  // contains unconsumed data. Otherwise, process the data directly from the
+  // source.
+  const uint8_t* input = reinterpret_cast<const uint8_t*>(bytes);
+  const uint8_t* input_end = input + count;
+  if (!input_buffer_.empty()) {
+    input_buffer_.insert(input_buffer_.end(), input, input_end);
+    input = input_buffer_.data();
+    input_end = input + input_buffer_.size();
+  }
+  stream_.next_in = reinterpret_cast<char*>(const_cast<uint8_t*>(input));
+  stream_.avail_in = input_end - input;
+
+  for (;;) {
+    stream_.next_out = reinterpret_cast<char*>(output_buffer.data());
+    stream_.avail_out = output_buffer.size();
+
+    int rc = BZ2_bzDecompress(&stream_);
+    TEST_AND_RETURN_FALSE(rc == BZ_OK || rc == BZ_STREAM_END);
+
+    if (stream_.avail_out == output_buffer.size())
+      break;  // got no new bytes
+
+    TEST_AND_RETURN_FALSE(
+        next_->Write(output_buffer.data(),
+                     output_buffer.size() - stream_.avail_out));
+
+    if (rc == BZ_STREAM_END)
+      CHECK_EQ(stream_.avail_in, 0u);
+    if (stream_.avail_in == 0)
+      break;  // no more input to process
+  }
+
+  // Store unconsumed data (if any) in |input_buffer_|.
+  if (stream_.avail_in || !input_buffer_.empty()) {
+    brillo::Blob new_input_buffer(input_end - stream_.avail_in, input_end);
+    new_input_buffer.swap(input_buffer_);
+  }
+
+  return true;
+}
+
+bool BzipExtentWriter::EndImpl() {
+  TEST_AND_RETURN_FALSE(input_buffer_.empty());
+  TEST_AND_RETURN_FALSE(BZ2_bzDecompressEnd(&stream_) == BZ_OK);
+  return next_->End();
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/bzip_extent_writer.h b/payload_consumer/bzip_extent_writer.h
new file mode 100644
index 0000000..0ad542e
--- /dev/null
+++ b/payload_consumer/bzip_extent_writer.h
@@ -0,0 +1,57 @@
+//
+// 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_CONSUMER_BZIP_EXTENT_WRITER_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_BZIP_EXTENT_WRITER_H_
+
+#include <bzlib.h>
+#include <memory>
+#include <vector>
+
+#include <brillo/secure_blob.h>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/extent_writer.h"
+
+// BzipExtentWriter is a concrete ExtentWriter subclass that bzip-decompresses
+// what it's given in Write. It passes the decompressed data to an underlying
+// ExtentWriter.
+
+namespace chromeos_update_engine {
+
+class BzipExtentWriter : public ExtentWriter {
+ public:
+  explicit BzipExtentWriter(std::unique_ptr<ExtentWriter> next)
+      : next_(std::move(next)) {
+    memset(&stream_, 0, sizeof(stream_));
+  }
+  ~BzipExtentWriter() override = default;
+
+  bool Init(FileDescriptorPtr fd,
+            const std::vector<Extent>& extents,
+            uint32_t block_size) override;
+  bool Write(const void* bytes, size_t count) override;
+  bool EndImpl() override;
+
+ private:
+  std::unique_ptr<ExtentWriter> next_;  // The underlying ExtentWriter.
+  bz_stream stream_;  // the libbz2 stream
+  brillo::Blob input_buffer_;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_BZIP_EXTENT_WRITER_H_
diff --git a/payload_consumer/bzip_extent_writer_unittest.cc b/payload_consumer/bzip_extent_writer_unittest.cc
new file mode 100644
index 0000000..a52a286
--- /dev/null
+++ b/payload_consumer/bzip_extent_writer_unittest.cc
@@ -0,0 +1,145 @@
+//
+// 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_consumer/bzip_extent_writer.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <brillo/make_unique_ptr.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+
+using std::min;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+const char kPathTemplate[] = "./BzipExtentWriterTest-file.XXXXXX";
+const uint32_t kBlockSize = 4096;
+}
+
+class BzipExtentWriterTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memcpy(path_, kPathTemplate, sizeof(kPathTemplate));
+    fd_.reset(new EintrSafeFileDescriptor);
+    int fd = mkstemp(path_);
+    ASSERT_TRUE(fd_->Open(path_, O_RDWR, 0600));
+    close(fd);
+  }
+  void TearDown() override {
+    fd_->Close();
+    unlink(path_);
+  }
+  void WriteAlignedExtents(size_t chunk_size, size_t first_chunk_size);
+  void TestZeroPad(bool aligned_size);
+
+  FileDescriptorPtr fd_;
+  char path_[sizeof(kPathTemplate)];
+};
+
+TEST_F(BzipExtentWriterTest, SimpleTest) {
+  vector<Extent> extents;
+  Extent extent;
+  extent.set_start_block(0);
+  extent.set_num_blocks(1);
+  extents.push_back(extent);
+
+  // 'echo test | bzip2 | hexdump' yields:
+  static const char test_uncompressed[] = "test\n";
+  static const uint8_t test[] = {
+    0x42, 0x5a, 0x68, 0x39, 0x31, 0x41, 0x59, 0x26, 0x53, 0x59, 0xcc, 0xc3,
+    0x71, 0xd4, 0x00, 0x00, 0x02, 0x41, 0x80, 0x00, 0x10, 0x02, 0x00, 0x0c,
+    0x00, 0x20, 0x00, 0x21, 0x9a, 0x68, 0x33, 0x4d, 0x19, 0x97, 0x8b, 0xb9,
+    0x22, 0x9c, 0x28, 0x48, 0x66, 0x61, 0xb8, 0xea, 0x00,
+  };
+
+  BzipExtentWriter bzip_writer(
+      brillo::make_unique_ptr(new DirectExtentWriter()));
+  EXPECT_TRUE(bzip_writer.Init(fd_, extents, kBlockSize));
+  EXPECT_TRUE(bzip_writer.Write(test, sizeof(test)));
+  EXPECT_TRUE(bzip_writer.End());
+
+  brillo::Blob buf;
+  EXPECT_TRUE(utils::ReadFile(path_, &buf));
+  EXPECT_EQ(strlen(test_uncompressed), buf.size());
+  EXPECT_EQ(string(buf.begin(), buf.end()), string(test_uncompressed));
+}
+
+TEST_F(BzipExtentWriterTest, ChunkedTest) {
+  const brillo::Blob::size_type kDecompressedLength = 2048 * 1024;  // 2 MiB
+  string decompressed_path;
+  ASSERT_TRUE(utils::MakeTempFile("BzipExtentWriterTest-decompressed-XXXXXX",
+                                  &decompressed_path, nullptr));
+  string compressed_path;
+  ASSERT_TRUE(utils::MakeTempFile("BzipExtentWriterTest-compressed-XXXXXX",
+                                  &compressed_path, nullptr));
+  const size_t kChunkSize = 3;
+
+  vector<Extent> extents;
+  Extent extent;
+  extent.set_start_block(0);
+  extent.set_num_blocks(kDecompressedLength / kBlockSize + 1);
+  extents.push_back(extent);
+
+  brillo::Blob decompressed_data(kDecompressedLength);
+  test_utils::FillWithData(&decompressed_data);
+
+  EXPECT_TRUE(test_utils::WriteFileVector(
+      decompressed_path, decompressed_data));
+
+  EXPECT_EQ(0, test_utils::System(
+      string("cat ") + decompressed_path + "|bzip2>" + compressed_path));
+
+  brillo::Blob compressed_data;
+  EXPECT_TRUE(utils::ReadFile(compressed_path, &compressed_data));
+
+  BzipExtentWriter bzip_writer(
+      brillo::make_unique_ptr(new DirectExtentWriter()));
+  EXPECT_TRUE(bzip_writer.Init(fd_, extents, kBlockSize));
+
+  brillo::Blob original_compressed_data = compressed_data;
+  for (brillo::Blob::size_type i = 0; i < compressed_data.size();
+       i += kChunkSize) {
+    size_t this_chunk_size = min(kChunkSize, compressed_data.size() - i);
+    EXPECT_TRUE(bzip_writer.Write(&compressed_data[i], this_chunk_size));
+  }
+  EXPECT_TRUE(bzip_writer.End());
+
+  // Check that the const input has not been clobbered.
+  test_utils::ExpectVectorsEq(original_compressed_data, compressed_data);
+
+  brillo::Blob output;
+  EXPECT_TRUE(utils::ReadFile(path_, &output));
+  EXPECT_EQ(kDecompressedLength, output.size());
+  test_utils::ExpectVectorsEq(decompressed_data, output);
+
+  unlink(decompressed_path.c_str());
+  unlink(compressed_path.c_str());
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
new file mode 100644
index 0000000..6261cb1
--- /dev/null
+++ b/payload_consumer/delta_performer.cc
@@ -0,0 +1,1794 @@
+//
+// 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_consumer/delta_performer.h"
+
+#include <endian.h>
+#include <errno.h>
+#include <linux/fs.h>
+
+#include <algorithm>
+#include <cstring>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/format_macros.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/data_encoding.h>
+#include <brillo/make_unique_ptr.h>
+#include <google/protobuf/repeated_field.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/subprocess.h"
+#include "update_engine/common/terminator.h"
+#include "update_engine/payload_consumer/bzip_extent_writer.h"
+#include "update_engine/payload_consumer/download_action.h"
+#include "update_engine/payload_consumer/extent_writer.h"
+#if USE_MTD
+#include "update_engine/payload_consumer/mtd_file_descriptor.h"
+#endif
+#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"
+
+using google::protobuf::RepeatedPtrField;
+using std::min;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+const uint64_t DeltaPerformer::kDeltaVersionOffset = sizeof(kDeltaMagic);
+const uint64_t DeltaPerformer::kDeltaVersionSize = 8;
+const uint64_t DeltaPerformer::kDeltaManifestSizeOffset =
+    kDeltaVersionOffset + kDeltaVersionSize;
+const uint64_t DeltaPerformer::kDeltaManifestSizeSize = 8;
+const uint64_t DeltaPerformer::kDeltaMetadataSignatureSizeSize = 4;
+const uint64_t DeltaPerformer::kMaxPayloadHeaderSize = 24;
+const uint64_t DeltaPerformer::kSupportedMajorPayloadVersion = 2;
+const uint32_t DeltaPerformer::kSupportedMinorPayloadVersion = 3;
+
+const unsigned DeltaPerformer::kProgressLogMaxChunks = 10;
+const unsigned DeltaPerformer::kProgressLogTimeoutSeconds = 30;
+const unsigned DeltaPerformer::kProgressDownloadWeight = 50;
+const unsigned DeltaPerformer::kProgressOperationsWeight = 50;
+
+namespace {
+const int kUpdateStateOperationInvalid = -1;
+const int kMaxResumedUpdateFailures = 10;
+#if USE_MTD
+const int kUbiVolumeAttachTimeout = 5 * 60;
+#endif
+
+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, int* err) {
+  FileDescriptorPtr fd = CreateFileDescriptor(path);
+#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;
+}
+}  // namespace
+
+
+// Computes the ratio of |part| and |total|, scaled to |norm|, using integer
+// arithmetic.
+static uint64_t IntRatio(uint64_t part, uint64_t total, uint64_t norm) {
+  return part * norm / total;
+}
+
+void DeltaPerformer::LogProgress(const char* message_prefix) {
+  // Format operations total count and percentage.
+  string total_operations_str("?");
+  string completed_percentage_str("");
+  if (num_total_operations_) {
+    total_operations_str = std::to_string(num_total_operations_);
+    // Upcasting to 64-bit to avoid overflow, back to size_t for formatting.
+    completed_percentage_str =
+        base::StringPrintf(" (%" PRIu64 "%%)",
+                           IntRatio(next_operation_num_, num_total_operations_,
+                                    100));
+  }
+
+  // Format download total count and percentage.
+  size_t payload_size = install_plan_->payload_size;
+  string payload_size_str("?");
+  string downloaded_percentage_str("");
+  if (payload_size) {
+    payload_size_str = std::to_string(payload_size);
+    // Upcasting to 64-bit to avoid overflow, back to size_t for formatting.
+    downloaded_percentage_str =
+        base::StringPrintf(" (%" PRIu64 "%%)",
+                           IntRatio(total_bytes_received_, payload_size, 100));
+  }
+
+  LOG(INFO) << (message_prefix ? message_prefix : "") << next_operation_num_
+            << "/" << total_operations_str << " operations"
+            << completed_percentage_str << ", " << total_bytes_received_
+            << "/" << payload_size_str << " bytes downloaded"
+            << downloaded_percentage_str << ", overall progress "
+            << overall_progress_ << "%";
+}
+
+void DeltaPerformer::UpdateOverallProgress(bool force_log,
+                                           const char* message_prefix) {
+  // Compute our download and overall progress.
+  unsigned new_overall_progress = 0;
+  COMPILE_ASSERT(kProgressDownloadWeight + kProgressOperationsWeight == 100,
+                 progress_weight_dont_add_up);
+  // Only consider download progress if its total size is known; otherwise
+  // adjust the operations weight to compensate for the absence of download
+  // progress. Also, make sure to cap the download portion at
+  // kProgressDownloadWeight, in case we end up downloading more than we
+  // initially expected (this indicates a problem, but could generally happen).
+  // TODO(garnold) the correction of operations weight when we do not have the
+  // total payload size, as well as the conditional guard below, should both be
+  // eliminated once we ensure that the payload_size in the install plan is
+  // always given and is non-zero. This currently isn't the case during unit
+  // tests (see chromium-os:37969).
+  size_t payload_size = install_plan_->payload_size;
+  unsigned actual_operations_weight = kProgressOperationsWeight;
+  if (payload_size)
+    new_overall_progress += min(
+        static_cast<unsigned>(IntRatio(total_bytes_received_, payload_size,
+                                       kProgressDownloadWeight)),
+        kProgressDownloadWeight);
+  else
+    actual_operations_weight += kProgressDownloadWeight;
+
+  // Only add completed operations if their total number is known; we definitely
+  // expect an update to have at least one operation, so the expectation is that
+  // this will eventually reach |actual_operations_weight|.
+  if (num_total_operations_)
+    new_overall_progress += IntRatio(next_operation_num_, num_total_operations_,
+                                     actual_operations_weight);
+
+  // Progress ratio cannot recede, unless our assumptions about the total
+  // payload size, total number of operations, or the monotonicity of progress
+  // is breached.
+  if (new_overall_progress < overall_progress_) {
+    LOG(WARNING) << "progress counter receded from " << overall_progress_
+                 << "% down to " << new_overall_progress << "%; this is a bug";
+    force_log = true;
+  }
+  overall_progress_ = new_overall_progress;
+
+  // Update chunk index, log as needed: if forced by called, or we completed a
+  // progress chunk, or a timeout has expired.
+  base::Time curr_time = base::Time::Now();
+  unsigned curr_progress_chunk =
+      overall_progress_ * kProgressLogMaxChunks / 100;
+  if (force_log || curr_progress_chunk > last_progress_chunk_ ||
+      curr_time > forced_progress_log_time_) {
+    forced_progress_log_time_ = curr_time + forced_progress_log_wait_;
+    LogProgress(message_prefix);
+  }
+  last_progress_chunk_ = curr_progress_chunk;
+}
+
+
+size_t DeltaPerformer::CopyDataToBuffer(const char** bytes_p, size_t* count_p,
+                                        size_t max) {
+  const size_t count = *count_p;
+  if (!count)
+    return 0;  // Special case shortcut.
+  size_t read_len = min(count, max - buffer_.size());
+  const char* bytes_start = *bytes_p;
+  const char* bytes_end = bytes_start + read_len;
+  buffer_.insert(buffer_.end(), bytes_start, bytes_end);
+  *bytes_p = bytes_end;
+  *count_p = count - read_len;
+  return read_len;
+}
+
+
+bool DeltaPerformer::HandleOpResult(bool op_result, const char* op_type_name,
+                                    ErrorCode* error) {
+  if (op_result)
+    return true;
+
+  LOG(ERROR) << "Failed to perform " << op_type_name << " operation "
+             << next_operation_num_;
+  *error = ErrorCode::kDownloadOperationExecutionError;
+  return false;
+}
+
+int DeltaPerformer::Close() {
+  int err = -CloseCurrentPartition();
+  LOG_IF(ERROR, !payload_hash_calculator_.Finalize() ||
+                !signed_hash_calculator_.Finalize())
+      << "Unable to finalize the hash.";
+  if (!buffer_.empty()) {
+    LOG(INFO) << "Discarding " << buffer_.size() << " unused downloaded bytes";
+    if (err >= 0)
+      err = 1;
+  }
+  return -err;
+}
+
+int DeltaPerformer::CloseCurrentPartition() {
+  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();
+  return -err;
+}
+
+bool DeltaPerformer::OpenCurrentPartition() {
+  if (current_partition_ >= partitions_.size())
+    return false;
+
+  const PartitionUpdate& partition = partitions_[current_partition_];
+  // Open source fds if we have a delta payload with minor version >= 2.
+  if (!install_plan_->is_full_update &&
+      GetMinorVersion() != kInPlaceMinorPayloadVersion) {
+    source_path_ = install_plan_->partitions[current_partition_].source_path;
+    int err;
+    source_fd_ = OpenFile(source_path_.c_str(), O_RDONLY, &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;
+    }
+  }
+
+  target_path_ = install_plan_->partitions[current_partition_].target_path;
+  int err;
+  target_fd_ = OpenFile(target_path_.c_str(), O_RDWR, &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;
+  }
+  return true;
+}
+
+namespace {
+
+void LogPartitionInfoHash(const PartitionInfo& info, const string& tag) {
+  string sha256 = brillo::data_encoding::Base64Encode(info.hash());
+  LOG(INFO) << "PartitionInfo " << tag << " sha256: " << sha256
+            << " size: " << info.size();
+}
+
+void LogPartitionInfo(const vector<PartitionUpdate>& partitions) {
+  for (const PartitionUpdate& partition : partitions) {
+    LogPartitionInfoHash(partition.old_partition_info(),
+                         "old " + partition.partition_name());
+    LogPartitionInfoHash(partition.new_partition_info(),
+                         "new " + partition.partition_name());
+  }
+}
+
+}  // namespace
+
+bool DeltaPerformer::GetMetadataSignatureSizeOffset(
+    uint64_t* out_offset) const {
+  if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
+    *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
+    return true;
+  }
+  return false;
+}
+
+bool DeltaPerformer::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;
+}
+
+uint64_t DeltaPerformer::GetMetadataSize() const {
+  return metadata_size_;
+}
+
+uint64_t DeltaPerformer::GetMajorVersion() const {
+  return major_payload_version_;
+}
+
+uint32_t DeltaPerformer::GetMinorVersion() const {
+  if (manifest_.has_minor_version()) {
+    return manifest_.minor_version();
+  } else {
+    return (install_plan_->is_full_update ?
+            kFullPayloadMinorVersion :
+            kSupportedMinorPayloadVersion);
+  }
+}
+
+bool DeltaPerformer::GetManifest(DeltaArchiveManifest* out_manifest_p) const {
+  if (!manifest_parsed_)
+    return false;
+  *out_manifest_p = manifest_;
+  return true;
+}
+
+bool DeltaPerformer::IsHeaderParsed() const {
+  return metadata_size_ != 0;
+}
+
+DeltaPerformer::MetadataParseResult DeltaPerformer::ParsePayloadMetadata(
+    const brillo::Blob& payload, ErrorCode* error) {
+  *error = ErrorCode::kSuccess;
+  uint64_t manifest_offset;
+
+  if (!IsHeaderParsed()) {
+    // Ensure we have data to cover the major payload version.
+    if (payload.size() < kDeltaManifestSizeOffset)
+      return kMetadataParseInsufficientData;
+
+    // Validate the magic string.
+    if (memcmp(payload.data(), kDeltaMagic, sizeof(kDeltaMagic)) != 0) {
+      LOG(ERROR) << "Bad payload format -- invalid delta magic.";
+      *error = ErrorCode::kDownloadInvalidMetadataMagicString;
+      return kMetadataParseError;
+    }
+
+    // Extract the payload version from the metadata.
+    COMPILE_ASSERT(sizeof(major_payload_version_) == kDeltaVersionSize,
+                   major_payload_version_size_mismatch);
+    memcpy(&major_payload_version_,
+           &payload[kDeltaVersionOffset],
+           kDeltaVersionSize);
+    // switch big endian to host
+    major_payload_version_ = be64toh(major_payload_version_);
+
+    if (major_payload_version_ != supported_major_version_ &&
+        major_payload_version_ != kChromeOSMajorPayloadVersion) {
+      LOG(ERROR) << "Bad payload format -- unsupported payload version: "
+          << major_payload_version_;
+      *error = ErrorCode::kUnsupportedMajorPayloadVersion;
+      return kMetadataParseError;
+    }
+
+    // Get the manifest offset now that we have payload version.
+    if (!GetManifestOffset(&manifest_offset)) {
+      *error = ErrorCode::kUnsupportedMajorPayloadVersion;
+      return kMetadataParseError;
+    }
+    // Check again with the manifest offset.
+    if (payload.size() < manifest_offset)
+      return kMetadataParseInsufficientData;
+
+    // Next, parse the manifest size.
+    COMPILE_ASSERT(sizeof(manifest_size_) == kDeltaManifestSizeSize,
+                   manifest_size_size_mismatch);
+    memcpy(&manifest_size_,
+           &payload[kDeltaManifestSizeOffset],
+           kDeltaManifestSizeSize);
+    manifest_size_ = be64toh(manifest_size_);  // switch big endian to host
+
+    if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
+      // Parse the metadata signature size.
+      COMPILE_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 kMetadataParseError;
+      }
+      memcpy(&metadata_signature_size_,
+             &payload[metadata_signature_size_offset],
+             kDeltaMetadataSignatureSizeSize);
+      metadata_signature_size_ = be32toh(metadata_signature_size_);
+    }
+
+    // If the metadata size is present in install plan, check for it immediately
+    // even before waiting for that many number of bytes to be downloaded in the
+    // payload. This will prevent any attack which relies on us downloading data
+    // beyond the expected metadata size.
+    metadata_size_ = manifest_offset + manifest_size_;
+    if (install_plan_->hash_checks_mandatory) {
+      if (install_plan_->metadata_size != metadata_size_) {
+        LOG(ERROR) << "Mandatory metadata size in Omaha response ("
+                   << install_plan_->metadata_size
+                   << ") is missing/incorrect, actual = " << metadata_size_;
+        *error = ErrorCode::kDownloadInvalidMetadataSize;
+        return kMetadataParseError;
+      }
+    }
+  }
+
+  // Now that we have validated the metadata size, we should wait for the full
+  // metadata and its signature (if exist) to be read in before we can parse it.
+  if (payload.size() < metadata_size_ + metadata_signature_size_)
+    return kMetadataParseInsufficientData;
+
+  // Log whether we validated the size or simply trusting what's in the payload
+  // here. This is logged here (after we received the full metadata data) so
+  // that we just log once (instead of logging n times) if it takes n
+  // DeltaPerformer::Write calls to download the full manifest.
+  if (install_plan_->metadata_size == metadata_size_) {
+    LOG(INFO) << "Manifest size in payload matches expected value from Omaha";
+  } else {
+    // For mandatory-cases, we'd have already returned a kMetadataParseError
+    // above. We'll be here only for non-mandatory cases. Just send a UMA stat.
+    LOG(WARNING) << "Ignoring missing/incorrect metadata size ("
+                 << install_plan_->metadata_size
+                 << ") in Omaha response as validation is not mandatory. "
+                 << "Trusting metadata size in payload = " << metadata_size_;
+  }
+
+  // We have the full metadata in |payload|. Verify its integrity
+  // and authenticity based on the information we have in Omaha response.
+  *error = ValidateMetadataSignature(payload);
+  if (*error != ErrorCode::kSuccess) {
+    if (install_plan_->hash_checks_mandatory) {
+      // The autoupdate_CatchBadSignatures test checks for this string
+      // in log-files. Keep in sync.
+      LOG(ERROR) << "Mandatory metadata signature validation failed";
+      return kMetadataParseError;
+    }
+
+    // For non-mandatory cases, just send a UMA stat.
+    LOG(WARNING) << "Ignoring metadata signature validation failures";
+    *error = ErrorCode::kSuccess;
+  }
+
+  if (!GetManifestOffset(&manifest_offset)) {
+    *error = ErrorCode::kUnsupportedMajorPayloadVersion;
+    return kMetadataParseError;
+  }
+  // The payload metadata is deemed valid, it's safe to parse the protobuf.
+  if (!manifest_.ParseFromArray(&payload[manifest_offset], manifest_size_)) {
+    LOG(ERROR) << "Unable to parse manifest in update file.";
+    *error = ErrorCode::kDownloadManifestParseError;
+    return kMetadataParseError;
+  }
+
+  manifest_parsed_ = true;
+  return kMetadataParseSuccess;
+}
+
+// Wrapper around write. Returns true if all requested bytes
+// were written, or false on any error, regardless of progress
+// and stores an action exit code in |error|.
+bool DeltaPerformer::Write(const void* bytes, size_t count, ErrorCode *error) {
+  *error = ErrorCode::kSuccess;
+
+  const char* c_bytes = reinterpret_cast<const char*>(bytes);
+
+  // Update the total byte downloaded count and the progress logs.
+  total_bytes_received_ += count;
+  UpdateOverallProgress(false, "Completed ");
+
+  while (!manifest_valid_) {
+    // Read data up to the needed limit; this is either maximium payload header
+    // size, or the full metadata size (once it becomes known).
+    const bool do_read_header = !IsHeaderParsed();
+    CopyDataToBuffer(&c_bytes, &count,
+                     (do_read_header ? kMaxPayloadHeaderSize :
+                      metadata_size_ + metadata_signature_size_));
+
+    MetadataParseResult result = ParsePayloadMetadata(buffer_, error);
+    if (result == kMetadataParseError)
+      return false;
+    if (result == kMetadataParseInsufficientData) {
+      // If we just processed the header, make an attempt on the manifest.
+      if (do_read_header && IsHeaderParsed())
+        continue;
+
+      return true;
+    }
+
+    // Checks the integrity of the payload manifest.
+    if ((*error = ValidateManifest()) != ErrorCode::kSuccess)
+      return false;
+    manifest_valid_ = true;
+
+    // Clear the download buffer.
+    DiscardBuffer(false, metadata_size_);
+
+    // This populates |partitions_| and the |install_plan.partitions| with the
+    // list of partitions from the manifest.
+    if (!ParseManifestPartitions(error))
+      return false;
+
+    num_total_operations_ = 0;
+    for (const auto& partition : partitions_) {
+      num_total_operations_ += partition.operations_size();
+      acc_num_operations_.push_back(num_total_operations_);
+    }
+
+    LOG_IF(WARNING, !prefs_->SetInt64(kPrefsManifestMetadataSize,
+                                      metadata_size_))
+        << "Unable to save the manifest metadata size.";
+
+    if (!PrimeUpdateState()) {
+      *error = ErrorCode::kDownloadStateInitializationError;
+      LOG(ERROR) << "Unable to prime the update state.";
+      return false;
+    }
+
+    if (!OpenCurrentPartition()) {
+      *error = ErrorCode::kInstallDeviceOpenError;
+      return false;
+    }
+
+    if (next_operation_num_ > 0)
+      UpdateOverallProgress(true, "Resuming after ");
+    LOG(INFO) << "Starting to apply update payload operations";
+  }
+
+  while (next_operation_num_ < num_total_operations_) {
+    // Check if we should cancel the current attempt for any reason.
+    // In this case, *error will have already been populated with the reason
+    // why we're canceling.
+    if (download_delegate_ && download_delegate_->ShouldCancel(error))
+      return false;
+
+    // We know there are more operations to perform because we didn't reach the
+    // |num_total_operations_| limit yet.
+    while (next_operation_num_ >= acc_num_operations_[current_partition_]) {
+      CloseCurrentPartition();
+      current_partition_++;
+      if (!OpenCurrentPartition()) {
+        *error = ErrorCode::kInstallDeviceOpenError;
+        return false;
+      }
+    }
+    const size_t partition_operation_num = next_operation_num_ - (
+        current_partition_ ? acc_num_operations_[current_partition_ - 1] : 0);
+
+    const InstallOperation& op =
+        partitions_[current_partition_].operations(partition_operation_num);
+
+    CopyDataToBuffer(&c_bytes, &count, op.data_length());
+
+    // Check whether we received all of the next operation's data payload.
+    if (!CanPerformInstallOperation(op))
+      return true;
+
+    // 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 (!install_plan_->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;
+      }
+    }
+
+    // Makes sure we unblock exit when this operation completes.
+    ScopedTerminatorExitUnblocker exit_unblocker =
+        ScopedTerminatorExitUnblocker();  // Avoids a compiler unused var bug.
+
+    bool op_result;
+    switch (op.type()) {
+      case InstallOperation::REPLACE:
+      case InstallOperation::REPLACE_BZ:
+      case InstallOperation::REPLACE_XZ:
+        op_result = PerformReplaceOperation(op);
+        break;
+      case InstallOperation::ZERO:
+      case InstallOperation::DISCARD:
+        op_result = PerformZeroOrDiscardOperation(op);
+        break;
+      case InstallOperation::MOVE:
+        op_result = PerformMoveOperation(op);
+        break;
+      case InstallOperation::BSDIFF:
+        op_result = PerformBsdiffOperation(op);
+        break;
+      case InstallOperation::SOURCE_COPY:
+        op_result = PerformSourceCopyOperation(op);
+        break;
+      case InstallOperation::SOURCE_BSDIFF:
+        op_result = PerformSourceBsdiffOperation(op);
+        break;
+      default:
+       op_result = false;
+    }
+    if (!HandleOpResult(op_result, InstallOperationTypeName(op.type()), error))
+      return false;
+
+    next_operation_num_++;
+    UpdateOverallProgress(false, "Completed ");
+    CheckpointUpdateProgress();
+  }
+
+  // In major version 2, we don't add dummy operation to the payload.
+  if (major_payload_version_ == kBrilloMajorPayloadVersion &&
+      manifest_.has_signatures_offset() && manifest_.has_signatures_size()) {
+    if (manifest_.signatures_offset() != buffer_offset_) {
+      LOG(ERROR) << "Payload signatures offset points to blob offset "
+                 << manifest_.signatures_offset()
+                 << " but signatures are expected at offset "
+                 << buffer_offset_;
+      *error = ErrorCode::kDownloadPayloadVerificationError;
+      return false;
+    }
+    CopyDataToBuffer(&c_bytes, &count, manifest_.signatures_size());
+    // Needs more data to cover entire signature.
+    if (buffer_.size() < manifest_.signatures_size())
+      return true;
+    if (!ExtractSignatureMessage()) {
+      LOG(ERROR) << "Extract payload signature failed.";
+      *error = ErrorCode::kDownloadPayloadVerificationError;
+      return false;
+    }
+    DiscardBuffer(true, 0);
+  }
+
+  return true;
+}
+
+bool DeltaPerformer::IsManifestValid() {
+  return manifest_valid_;
+}
+
+bool DeltaPerformer::ParseManifestPartitions(ErrorCode* error) {
+  if (major_payload_version_ == kBrilloMajorPayloadVersion) {
+    partitions_.clear();
+    for (const PartitionUpdate& partition : manifest_.partitions()) {
+      partitions_.push_back(partition);
+    }
+    manifest_.clear_partitions();
+  } else if (major_payload_version_ == kChromeOSMajorPayloadVersion) {
+    LOG(INFO) << "Converting update information from old format.";
+    PartitionUpdate root_part;
+    root_part.set_partition_name(kLegacyPartitionNameRoot);
+#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();
+    }
+    if (manifest_.has_new_rootfs_info()) {
+      *root_part.mutable_new_partition_info() = manifest_.new_rootfs_info();
+      manifest_.clear_new_rootfs_info();
+    }
+    *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(kLegacyPartitionNameKernel);
+    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));
+  }
+
+  // TODO(deymo): Remove this block of code once we switched to optional
+  // source partition verification. This list of partitions in the InstallPlan
+  // is initialized with the expected hashes in the payload major version 1,
+  // so we need to check those now if already set. See b/23182225.
+  if (!install_plan_->partitions.empty()) {
+    if (!VerifySourcePartitions()) {
+      *error = ErrorCode::kDownloadStateInitializationError;
+      return false;
+    }
+  }
+
+  // Fill in the InstallPlan::partitions based on the partitions from the
+  // payload.
+  install_plan_->partitions.clear();
+  for (const auto& partition : partitions_) {
+    InstallPlan::Partition install_part;
+    install_part.name = partition.partition_name();
+    install_part.run_postinstall =
+        partition.has_run_postinstall() && partition.run_postinstall();
+
+    if (partition.has_old_partition_info()) {
+      const PartitionInfo& info = partition.old_partition_info();
+      install_part.source_size = info.size();
+      install_part.source_hash.assign(info.hash().begin(), info.hash().end());
+    }
+
+    if (!partition.has_new_partition_info()) {
+      LOG(ERROR) << "Unable to get new partition hash info on partition "
+                 << install_part.name << ".";
+      *error = ErrorCode::kDownloadNewPartitionInfoError;
+      return false;
+    }
+    const PartitionInfo& info = partition.new_partition_info();
+    install_part.target_size = info.size();
+    install_part.target_hash.assign(info.hash().begin(), info.hash().end());
+
+    install_plan_->partitions.push_back(install_part);
+  }
+
+  if (!install_plan_->LoadPartitionsFromSlots(boot_control_)) {
+    LOG(ERROR) << "Unable to determine all the partition devices.";
+    *error = ErrorCode::kInstallDeviceOpenError;
+    return false;
+  }
+  LogPartitionInfo(partitions_);
+  return true;
+}
+
+bool DeltaPerformer::CanPerformInstallOperation(
+    const chromeos_update_engine::InstallOperation& operation) {
+  // Move and source_copy operations don't require any data blob, so they can
+  // always be performed.
+  if (operation.type() == InstallOperation::MOVE ||
+      operation.type() == InstallOperation::SOURCE_COPY)
+    return true;
+
+  // See if we have the entire data blob in the buffer
+  if (operation.data_offset() < buffer_offset_) {
+    LOG(ERROR) << "we threw away data it seems?";
+    return false;
+  }
+
+  return (operation.data_offset() + operation.data_length() <=
+          buffer_offset_ + buffer_.size());
+}
+
+bool DeltaPerformer::PerformReplaceOperation(
+    const InstallOperation& operation) {
+  CHECK(operation.type() == InstallOperation::REPLACE ||
+        operation.type() == InstallOperation::REPLACE_BZ ||
+        operation.type() == InstallOperation::REPLACE_XZ);
+
+  // 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());
+
+  // 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 =
+    brillo::make_unique_ptr(new ZeroPadExtentWriter(
+      brillo::make_unique_ptr(new 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)));
+  }
+
+  // Create a vector of extents to pass to the ExtentWriter.
+  vector<Extent> extents;
+  for (int i = 0; i < operation.dst_extents_size(); i++) {
+    extents.push_back(operation.dst_extents(i));
+  }
+
+  TEST_AND_RETURN_FALSE(writer->Init(target_fd_, extents, block_size_));
+  TEST_AND_RETURN_FALSE(writer->Write(buffer_.data(), operation.data_length()));
+  TEST_AND_RETURN_FALSE(writer->End());
+
+  // Update buffer
+  DiscardBuffer(true, buffer_.size());
+  return true;
+}
+
+bool DeltaPerformer::PerformZeroOrDiscardOperation(
+    const InstallOperation& operation) {
+  CHECK(operation.type() == InstallOperation::DISCARD ||
+        operation.type() == InstallOperation::ZERO);
+
+  // These operations have no blob.
+  TEST_AND_RETURN_FALSE(!operation.has_data_offset());
+  TEST_AND_RETURN_FALSE(!operation.has_data_length());
+
+  int request =
+      (operation.type() == InstallOperation::ZERO ? BLKZEROOUT : BLKDISCARD);
+
+  bool attempt_ioctl = true;
+  brillo::Blob zeros;
+  for (int i = 0; i < operation.dst_extents_size(); i++) {
+    Extent extent = operation.dst_extents(i);
+    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;
+      zeros.resize(16 * block_size_);
+    }
+    // In case of failure, we fall back to writing 0 to the selected region.
+    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 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;
+}
+
+namespace {
+
+// Takes |extents| and fills an empty vector |blocks| with a block index for
+// each block in |extents|. For example, [(3, 2), (8, 1)] would give [3, 4, 8].
+void ExtentsToBlocks(const RepeatedPtrField<Extent>& extents,
+                     vector<uint64_t>* blocks) {
+  for (Extent ext : extents) {
+    for (uint64_t j = 0; j < ext.num_blocks(); j++)
+      blocks->push_back(ext.start_block() + j);
+  }
+}
+
+// Takes |extents| and returns the number of blocks in those extents.
+uint64_t GetBlockCount(const RepeatedPtrField<Extent>& extents) {
+  uint64_t sum = 0;
+  for (Extent ext : extents) {
+    sum += ext.num_blocks();
+  }
+  return sum;
+}
+
+// Compare |calculated_hash| with source hash in |operation|, return false and
+// dump hash if don't match.
+bool ValidateSourceHash(const brillo::Blob& calculated_hash,
+                        const InstallOperation& operation) {
+  brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
+                                    operation.src_sha256_hash().end());
+  if (calculated_hash != expected_source_hash) {
+    LOG(ERROR) << "Hash verification failed. Expected hash = ";
+    utils::HexDumpVector(expected_source_hash);
+    LOG(ERROR) << "Calculated hash = ";
+    utils::HexDumpVector(calculated_hash);
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+bool DeltaPerformer::PerformSourceCopyOperation(
+    const InstallOperation& operation) {
+  if (operation.has_src_length())
+    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);
+
+  uint64_t blocks_to_read = GetBlockCount(operation.src_extents());
+  uint64_t blocks_to_write = GetBlockCount(operation.dst_extents());
+  TEST_AND_RETURN_FALSE(blocks_to_write ==  blocks_to_read);
+
+  // Create vectors of all the individual src/dst blocks.
+  vector<uint64_t> src_blocks;
+  vector<uint64_t> dst_blocks;
+  ExtentsToBlocks(operation.src_extents(), &src_blocks);
+  ExtentsToBlocks(operation.dst_extents(), &dst_blocks);
+  DCHECK_EQ(src_blocks.size(), blocks_to_read);
+  DCHECK_EQ(src_blocks.size(), dst_blocks.size());
+
+  brillo::Blob buf(block_size_);
+  ssize_t bytes_read = 0;
+  HashCalculator source_hasher;
+  // Read/write one block at a time.
+  for (uint64_t i = 0; i < blocks_to_read; i++) {
+    ssize_t bytes_read_this_iteration = 0;
+    uint64_t src_block = src_blocks[i];
+    uint64_t dst_block = dst_blocks[i];
+
+    // Read in bytes.
+    TEST_AND_RETURN_FALSE(
+        utils::PReadAll(source_fd_,
+                        buf.data(),
+                        block_size_,
+                        src_block * block_size_,
+                        &bytes_read_this_iteration));
+
+    // Write bytes out.
+    TEST_AND_RETURN_FALSE(
+        utils::PWriteAll(target_fd_,
+                         buf.data(),
+                         block_size_,
+                         dst_block * block_size_));
+
+    bytes_read += bytes_read_this_iteration;
+    TEST_AND_RETURN_FALSE(bytes_read_this_iteration ==
+                          static_cast<ssize_t>(block_size_));
+
+    if (operation.has_src_sha256_hash())
+      TEST_AND_RETURN_FALSE(source_hasher.Update(buf.data(), buf.size()));
+  }
+
+  if (operation.has_src_sha256_hash()) {
+    TEST_AND_RETURN_FALSE(source_hasher.Finalize());
+    TEST_AND_RETURN_FALSE(
+        ValidateSourceHash(source_hasher.raw_hash(), operation));
+  }
+
+  DCHECK_EQ(bytes_read, static_cast<ssize_t>(blocks_to_read * block_size_));
+  return true;
+}
+
+bool DeltaPerformer::ExtentsToBsdiffPositionsString(
+    const RepeatedPtrField<Extent>& extents,
+    uint64_t block_size,
+    uint64_t full_length,
+    string* positions_string) {
+  string ret;
+  uint64_t length = 0;
+  for (int i = 0; i < extents.size(); i++) {
+    Extent extent = extents.Get(i);
+    int64_t start = extent.start_block() * block_size;
+    uint64_t this_length = min(full_length - length,
+                               extent.num_blocks() * block_size);
+    ret += base::StringPrintf("%" PRIi64 ":%" PRIu64 ",", start, this_length);
+    length += this_length;
+  }
+  TEST_AND_RETURN_FALSE(length == full_length);
+  if (!ret.empty())
+    ret.resize(ret.size() - 1);  // Strip trailing comma off
+  *positions_string = ret;
+  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));
+
+  string temp_filename;
+  TEST_AND_RETURN_FALSE(utils::MakeTempFile("au_patch.XXXXXX",
+                                            &temp_filename,
+                                            nullptr));
+  ScopedPathUnlinker path_unlinker(temp_filename);
+  {
+    int fd = open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
+    ScopedFdCloser fd_closer(&fd);
+    TEST_AND_RETURN_FALSE(
+        utils::WriteAll(fd, buffer_.data(), operation.data_length()));
+  }
+
+  // Update the buffer to release the patch data memory as soon as the patch
+  // file is written out.
+  DiscardBuffer(true, buffer_.size());
+
+  vector<string> cmd{kBspatchPath, target_path_, target_path_, temp_filename,
+                     input_positions, output_positions};
+
+  int return_code = 0;
+  TEST_AND_RETURN_FALSE(
+      Subprocess::SynchronousExecFlags(cmd, Subprocess::kSearchPath,
+                                       &return_code, nullptr));
+  TEST_AND_RETURN_FALSE(return_code == 0);
+
+  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;
+}
+
+bool DeltaPerformer::PerformSourceBsdiffOperation(
+    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());
+  if (operation.has_src_length())
+    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);
+
+  if (operation.has_src_sha256_hash()) {
+    HashCalculator source_hasher;
+    const uint64_t kMaxBlocksToRead = 512;  // 2MB if block size is 4KB
+    brillo::Blob buf(kMaxBlocksToRead * block_size_);
+    for (const Extent& extent : operation.src_extents()) {
+      for (uint64_t i = 0; i < extent.num_blocks(); i += kMaxBlocksToRead) {
+        uint64_t blocks_to_read =
+            min(kMaxBlocksToRead, extent.num_blocks() - i);
+        ssize_t bytes_to_read = blocks_to_read * block_size_;
+        ssize_t bytes_read_this_iteration = 0;
+        TEST_AND_RETURN_FALSE(
+            utils::PReadAll(source_fd_, buf.data(), bytes_to_read,
+                            (extent.start_block() + i) * block_size_,
+                            &bytes_read_this_iteration));
+        TEST_AND_RETURN_FALSE(bytes_read_this_iteration == bytes_to_read);
+        TEST_AND_RETURN_FALSE(source_hasher.Update(buf.data(), bytes_to_read));
+      }
+    }
+    TEST_AND_RETURN_FALSE(source_hasher.Finalize());
+    TEST_AND_RETURN_FALSE(
+        ValidateSourceHash(source_hasher.raw_hash(), operation));
+  }
+
+  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));
+
+  string temp_filename;
+  TEST_AND_RETURN_FALSE(utils::MakeTempFile("au_patch.XXXXXX",
+                                            &temp_filename,
+                                            nullptr));
+  ScopedPathUnlinker path_unlinker(temp_filename);
+  {
+    int fd = open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
+    ScopedFdCloser fd_closer(&fd);
+    TEST_AND_RETURN_FALSE(
+        utils::WriteAll(fd, buffer_.data(), operation.data_length()));
+  }
+
+  // Update the buffer to release the patch data memory as soon as the patch
+  // file is written out.
+  DiscardBuffer(true, buffer_.size());
+
+  vector<string> cmd{kBspatchPath, source_path_, target_path_, temp_filename,
+                     input_positions, output_positions};
+
+  int return_code = 0;
+  TEST_AND_RETURN_FALSE(
+      Subprocess::SynchronousExecFlags(cmd, Subprocess::kSearchPath,
+                                       &return_code, nullptr));
+  TEST_AND_RETURN_FALSE(return_code == 0);
+  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());
+  TEST_AND_RETURN_FALSE(buffer_.size() >= manifest_.signatures_size());
+  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,
+                                     string(signatures_message_data_.begin(),
+                                            signatures_message_data_.end())))
+      << "Unable to store the signature blob.";
+
+  LOG(INFO) << "Extracted signature data of size "
+            << manifest_.signatures_size() << " at "
+            << manifest_.signatures_offset();
+  return true;
+}
+
+bool DeltaPerformer::GetPublicKeyFromResponse(base::FilePath *out_tmp_key) {
+  if (hardware_->IsOfficialBuild() ||
+      utils::FileExists(public_key_path_.c_str()) ||
+      install_plan_->public_key_rsa.empty())
+    return false;
+
+  if (!utils::DecodeAndStoreBase64String(install_plan_->public_key_rsa,
+                                         out_tmp_key))
+    return false;
+
+  return true;
+}
+
+ErrorCode DeltaPerformer::ValidateMetadataSignature(
+    const brillo::Blob& payload) {
+  if (payload.size() < metadata_size_ + metadata_signature_size_)
+    return ErrorCode::kDownloadMetadataSignatureError;
+
+  brillo::Blob metadata_signature_blob, metadata_signature_protobuf_blob;
+  if (!install_plan_->metadata_signature.empty()) {
+    // Convert base64-encoded signature to raw bytes.
+    if (!brillo::data_encoding::Base64Decode(
+        install_plan_->metadata_signature, &metadata_signature_blob)) {
+      LOG(ERROR) << "Unable to decode base64 metadata signature: "
+                 << install_plan_->metadata_signature;
+      return ErrorCode::kDownloadMetadataSignatureError;
+    }
+  } else if (major_payload_version_ == kBrilloMajorPayloadVersion) {
+    metadata_signature_protobuf_blob.assign(payload.begin() + metadata_size_,
+                                            payload.begin() + metadata_size_ +
+                                            metadata_signature_size_);
+  }
+
+  if (metadata_signature_blob.empty() &&
+      metadata_signature_protobuf_blob.empty()) {
+    if (install_plan_->hash_checks_mandatory) {
+      LOG(ERROR) << "Missing mandatory metadata signature in both Omaha "
+                 << "response and payload.";
+      return ErrorCode::kDownloadMetadataSignatureMissingError;
+    }
+
+    LOG(WARNING) << "Cannot validate metadata as the signature is empty";
+    return ErrorCode::kSuccess;
+  }
+
+  // See if we should use the public RSA key in the Omaha response.
+  base::FilePath path_to_public_key(public_key_path_);
+  base::FilePath tmp_key;
+  if (GetPublicKeyFromResponse(&tmp_key))
+    path_to_public_key = tmp_key;
+  ScopedPathUnlinker tmp_key_remover(tmp_key.value());
+  if (tmp_key.empty())
+    tmp_key_remover.set_should_remove(false);
+
+  LOG(INFO) << "Verifying metadata hash signature using public key: "
+            << path_to_public_key.value();
+
+  HashCalculator metadata_hasher;
+  metadata_hasher.Update(payload.data(), metadata_size_);
+  if (!metadata_hasher.Finalize()) {
+    LOG(ERROR) << "Unable to compute actual hash of manifest";
+    return ErrorCode::kDownloadMetadataSignatureVerificationError;
+  }
+
+  brillo::Blob calculated_metadata_hash = metadata_hasher.raw_hash();
+  PayloadVerifier::PadRSA2048SHA256Hash(&calculated_metadata_hash);
+  if (calculated_metadata_hash.empty()) {
+    LOG(ERROR) << "Computed actual hash of metadata is empty.";
+    return ErrorCode::kDownloadMetadataSignatureVerificationError;
+  }
+
+  if (!metadata_signature_blob.empty()) {
+    brillo::Blob expected_metadata_hash;
+    if (!PayloadVerifier::GetRawHashFromSignature(metadata_signature_blob,
+                                                  path_to_public_key.value(),
+                                                  &expected_metadata_hash)) {
+      LOG(ERROR) << "Unable to compute expected hash from metadata signature";
+      return ErrorCode::kDownloadMetadataSignatureError;
+    }
+    if (calculated_metadata_hash != expected_metadata_hash) {
+      LOG(ERROR) << "Manifest hash verification failed. Expected hash = ";
+      utils::HexDumpVector(expected_metadata_hash);
+      LOG(ERROR) << "Calculated hash = ";
+      utils::HexDumpVector(calculated_metadata_hash);
+      return ErrorCode::kDownloadMetadataSignatureMismatch;
+    }
+  } else {
+    if (!PayloadVerifier::VerifySignature(metadata_signature_protobuf_blob,
+                                          path_to_public_key.value(),
+                                          calculated_metadata_hash)) {
+      LOG(ERROR) << "Manifest hash verification failed.";
+      return ErrorCode::kDownloadMetadataSignatureMismatch;
+    }
+  }
+
+  // The autoupdate_CatchBadSignatures test checks for this string in
+  // log-files. Keep in sync.
+  LOG(INFO) << "Metadata hash signature matches value in Omaha response.";
+  return ErrorCode::kSuccess;
+}
+
+ErrorCode DeltaPerformer::ValidateManifest() {
+  // Perform assorted checks to sanity check the manifest, make sure it
+  // matches data from other sources, and that it is a supported version.
+  //
+  // TODO(garnold) in general, the presence of an old partition hash should be
+  // the sole indicator for a delta update, as we would generally like update
+  // payloads to be self contained and not assume an Omaha response to tell us
+  // that. However, since this requires some massive reengineering of the update
+  // flow (making filesystem copying happen conditionally only *after*
+  // downloading and parsing of the update manifest) we'll put it off for now.
+  // See chromium-os:7597 for further discussion.
+  if (install_plan_->is_full_update) {
+    if (manifest_.has_old_kernel_info() || manifest_.has_old_rootfs_info()) {
+      LOG(ERROR) << "Purported full payload contains old partition "
+                    "hash(es), aborting update";
+      return ErrorCode::kPayloadMismatchedType;
+    }
+
+    for (const PartitionUpdate& partition : manifest_.partitions()) {
+      if (partition.has_old_partition_info()) {
+        LOG(ERROR) << "Purported full payload contains old partition "
+                      "hash(es), aborting update";
+        return ErrorCode::kPayloadMismatchedType;
+      }
+    }
+
+    if (manifest_.minor_version() != kFullPayloadMinorVersion) {
+      LOG(ERROR) << "Manifest contains minor version "
+                 << manifest_.minor_version()
+                 << ", but all full payloads should have version "
+                 << kFullPayloadMinorVersion << ".";
+      return ErrorCode::kUnsupportedMinorPayloadVersion;
+    }
+  } else {
+    if (manifest_.minor_version() != supported_minor_version_) {
+      LOG(ERROR) << "Manifest contains minor version "
+                 << manifest_.minor_version()
+                 << " not the supported "
+                 << supported_minor_version_;
+      return ErrorCode::kUnsupportedMinorPayloadVersion;
+    }
+  }
+
+  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(garnold) we should be adding more and more manifest checks, such as
+  // partition boundaries etc (see chromium-os:37661).
+
+  return ErrorCode::kSuccess;
+}
+
+ErrorCode DeltaPerformer::ValidateOperationHash(
+    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.
+      return ErrorCode::kSuccess;
+    }
+
+    // No hash is present for an operation that has data blobs. This shouldn't
+    // happen normally for any client that has this code, because the
+    // 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 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.
+    if (manifest_.signatures_offset() &&
+        manifest_.signatures_offset() == operation.data_offset()) {
+      LOG(INFO) << "Skipping hash verification for signature operation "
+                << next_operation_num_ + 1;
+    } else {
+      if (install_plan_->hash_checks_mandatory) {
+        LOG(ERROR) << "Missing mandatory operation hash for operation "
+                   << next_operation_num_ + 1;
+        return ErrorCode::kDownloadOperationHashMissingError;
+      }
+
+      LOG(WARNING) << "Cannot validate operation " << next_operation_num_ + 1
+                   << " as there's no operation hash in manifest";
+    }
+    return ErrorCode::kSuccess;
+  }
+
+  brillo::Blob expected_op_hash;
+  expected_op_hash.assign(operation.data_sha256_hash().data(),
+                          (operation.data_sha256_hash().data() +
+                           operation.data_sha256_hash().size()));
+
+  HashCalculator operation_hasher;
+  operation_hasher.Update(buffer_.data(), operation.data_length());
+  if (!operation_hasher.Finalize()) {
+    LOG(ERROR) << "Unable to compute actual hash of operation "
+               << next_operation_num_;
+    return ErrorCode::kDownloadOperationHashVerificationError;
+  }
+
+  brillo::Blob calculated_op_hash = operation_hasher.raw_hash();
+  if (calculated_op_hash != expected_op_hash) {
+    LOG(ERROR) << "Hash verification failed for operation "
+               << next_operation_num_ << ". Expected hash = ";
+    utils::HexDumpVector(expected_op_hash);
+    LOG(ERROR) << "Calculated hash over " << operation.data_length()
+               << " bytes at offset: " << operation.data_offset() << " = ";
+    utils::HexDumpVector(calculated_op_hash);
+    return ErrorCode::kDownloadOperationHashMismatch;
+  }
+
+  return ErrorCode::kSuccess;
+}
+
+#define TEST_AND_RETURN_VAL(_retval, _condition)                \
+  do {                                                          \
+    if (!(_condition)) {                                        \
+      LOG(ERROR) << "VerifyPayload failure: " << #_condition;   \
+      return _retval;                                           \
+    }                                                           \
+  } while (0);
+
+ErrorCode DeltaPerformer::VerifyPayload(
+    const string& update_check_response_hash,
+    const uint64_t update_check_response_size) {
+
+  // See if we should use the public RSA key in the Omaha response.
+  base::FilePath path_to_public_key(public_key_path_);
+  base::FilePath tmp_key;
+  if (GetPublicKeyFromResponse(&tmp_key))
+    path_to_public_key = tmp_key;
+  ScopedPathUnlinker tmp_key_remover(tmp_key.value());
+  if (tmp_key.empty())
+    tmp_key_remover.set_should_remove(false);
+
+  LOG(INFO) << "Verifying payload using public key: "
+            << path_to_public_key.value();
+
+  // Verifies the download size.
+  TEST_AND_RETURN_VAL(ErrorCode::kPayloadSizeMismatchError,
+                      update_check_response_size ==
+                      metadata_size_ + metadata_signature_size_ +
+                      buffer_offset_);
+
+  // Verifies the payload hash.
+  const string& payload_hash_data = payload_hash_calculator_.hash();
+  TEST_AND_RETURN_VAL(ErrorCode::kDownloadPayloadVerificationError,
+                      !payload_hash_data.empty());
+  TEST_AND_RETURN_VAL(ErrorCode::kPayloadHashMismatchError,
+                      payload_hash_data == update_check_response_hash);
+
+  // Verifies the signed payload hash.
+  if (!utils::FileExists(path_to_public_key.value().c_str())) {
+    LOG(WARNING) << "Not verifying signed delta payload -- missing public key.";
+    return ErrorCode::kSuccess;
+  }
+  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,
+                      PayloadVerifier::PadRSA2048SHA256Hash(&hash_data));
+  TEST_AND_RETURN_VAL(ErrorCode::kDownloadPayloadPubKeyVerificationError,
+                      !hash_data.empty());
+
+  if (!PayloadVerifier::VerifySignature(
+      signatures_message_data_, path_to_public_key.value(), hash_data)) {
+    // The autoupdate_CatchBadSignatures test checks for this string
+    // in log-files. Keep in sync.
+    LOG(ERROR) << "Public key verification failed, thus update failed.";
+    return ErrorCode::kDownloadPayloadPubKeyVerificationError;
+  }
+
+  LOG(INFO) << "Payload hash matches value in payload.";
+
+  // At this point, we are guaranteed to have downloaded a full payload, i.e
+  // the one whose size matches the size mentioned in Omaha response. If any
+  // errors happen after this, it's likely a problem with the payload itself or
+  // the state of the system and not a problem with the URL or network.  So,
+  // indicate that to the download delegate so that AU can backoff
+  // appropriately.
+  if (download_delegate_)
+    download_delegate_->DownloadComplete();
+
+  return ErrorCode::kSuccess;
+}
+
+namespace {
+void LogVerifyError(const string& type,
+                    const string& device,
+                    uint64_t size,
+                    const string& local_hash,
+                    const string& expected_hash) {
+  LOG(ERROR) << "This is a server-side error due to "
+             << "mismatched delta update image!";
+  LOG(ERROR) << "The delta I've been given contains a " << type << " delta "
+             << "update that must be applied over a " << type << " with "
+             << "a specific checksum, but the " << type << " we're starting "
+             << "with doesn't have that checksum! This means that "
+             << "the delta I've been given doesn't match my existing "
+             << "system. The " << type << " partition I have has hash: "
+             << local_hash << " but the update expected me to have "
+             << expected_hash << " .";
+  LOG(INFO) << "To get the checksum of the " << type << " partition run this"
+               "command: dd if=" << device << " bs=1M count=" << size
+            << " iflag=count_bytes 2>/dev/null | openssl dgst -sha256 -binary "
+               "| openssl base64";
+  LOG(INFO) << "To get the checksum of partitions in a bin file, "
+            << "run: .../src/scripts/sha256_partitions.sh .../file.bin";
+}
+
+string StringForHashBytes(const void* bytes, size_t size) {
+  return brillo::data_encoding::Base64Encode(bytes, size);
+}
+}  // namespace
+
+bool DeltaPerformer::VerifySourcePartitions() {
+  LOG(INFO) << "Verifying source partitions.";
+  CHECK(manifest_valid_);
+  CHECK(install_plan_);
+  if (install_plan_->partitions.size() != partitions_.size()) {
+    DLOG(ERROR) << "The list of partitions in the InstallPlan doesn't match the "
+                   "list received in the payload. The InstallPlan has "
+                << install_plan_->partitions.size()
+                << " partitions while the payload has " << partitions_.size()
+                << " partitions.";
+    return false;
+  }
+  for (size_t i = 0; i < partitions_.size(); ++i) {
+    if (partitions_[i].partition_name() != install_plan_->partitions[i].name) {
+      DLOG(ERROR) << "The InstallPlan's partition " << i << " is \""
+                  << install_plan_->partitions[i].name
+                  << "\" but the payload expects it to be \""
+                  << partitions_[i].partition_name()
+                  << "\". This is an error in the DeltaPerformer setup.";
+      return false;
+    }
+    if (!partitions_[i].has_old_partition_info())
+      continue;
+    const PartitionInfo& info = partitions_[i].old_partition_info();
+    const InstallPlan::Partition& plan_part = install_plan_->partitions[i];
+    bool valid =
+        !plan_part.source_hash.empty() &&
+        plan_part.source_hash.size() == info.hash().size() &&
+        memcmp(plan_part.source_hash.data(),
+               info.hash().data(),
+               plan_part.source_hash.size()) == 0;
+    if (!valid) {
+      LogVerifyError(partitions_[i].partition_name(),
+                     plan_part.source_path,
+                     info.hash().size(),
+                     StringForHashBytes(plan_part.source_hash.data(),
+                                        plan_part.source_hash.size()),
+                     StringForHashBytes(info.hash().data(),
+                                        info.hash().size()));
+      return false;
+    }
+  }
+  return true;
+}
+
+void DeltaPerformer::DiscardBuffer(bool do_advance_offset,
+                                   size_t signed_hash_buffer_size) {
+  // Update the buffer offset.
+  if (do_advance_offset)
+    buffer_offset_ += buffer_.size();
+
+  // Hash the content.
+  payload_hash_calculator_.Update(buffer_.data(), buffer_.size());
+  signed_hash_calculator_.Update(buffer_.data(), signed_hash_buffer_size);
+
+  // Swap content with an empty vector to ensure that all memory is released.
+  brillo::Blob().swap(buffer_);
+}
+
+bool DeltaPerformer::CanResumeUpdate(PrefsInterface* prefs,
+                                     string update_check_response_hash) {
+  int64_t next_operation = kUpdateStateOperationInvalid;
+  if (!(prefs->GetInt64(kPrefsUpdateStateNextOperation, &next_operation) &&
+        next_operation != kUpdateStateOperationInvalid &&
+        next_operation > 0))
+    return false;
+
+  string interrupted_hash;
+  if (!(prefs->GetString(kPrefsUpdateCheckResponseHash, &interrupted_hash) &&
+        !interrupted_hash.empty() &&
+        interrupted_hash == update_check_response_hash))
+    return false;
+
+  int64_t resumed_update_failures;
+  if (!(prefs->GetInt64(kPrefsResumedUpdateFailures, &resumed_update_failures)
+        && resumed_update_failures > kMaxResumedUpdateFailures))
+    return false;
+
+  // Sanity check the rest.
+  int64_t next_data_offset = -1;
+  if (!(prefs->GetInt64(kPrefsUpdateStateNextDataOffset, &next_data_offset) &&
+        next_data_offset >= 0))
+    return false;
+
+  string sha256_context;
+  if (!(prefs->GetString(kPrefsUpdateStateSHA256Context, &sha256_context) &&
+        !sha256_context.empty()))
+    return false;
+
+  int64_t manifest_metadata_size = 0;
+  if (!(prefs->GetInt64(kPrefsManifestMetadataSize, &manifest_metadata_size) &&
+        manifest_metadata_size > 0))
+    return false;
+
+  return true;
+}
+
+bool DeltaPerformer::ResetUpdateProgress(PrefsInterface* prefs, bool quick) {
+  TEST_AND_RETURN_FALSE(prefs->SetInt64(kPrefsUpdateStateNextOperation,
+                                        kUpdateStateOperationInvalid));
+  if (!quick) {
+    prefs->SetString(kPrefsUpdateCheckResponseHash, "");
+    prefs->SetInt64(kPrefsUpdateStateNextDataOffset, -1);
+    prefs->SetInt64(kPrefsUpdateStateNextDataLength, 0);
+    prefs->SetString(kPrefsUpdateStateSHA256Context, "");
+    prefs->SetString(kPrefsUpdateStateSignedSHA256Context, "");
+    prefs->SetString(kPrefsUpdateStateSignatureBlob, "");
+    prefs->SetInt64(kPrefsManifestMetadataSize, -1);
+    prefs->SetInt64(kPrefsResumedUpdateFailures, 0);
+  }
+  return true;
+}
+
+bool DeltaPerformer::CheckpointUpdateProgress() {
+  Terminator::set_exit_blocked(true);
+  if (last_updated_buffer_offset_ != buffer_offset_) {
+    // Resets the progress in case we die in the middle of the state update.
+    ResetUpdateProgress(prefs_, true);
+    TEST_AND_RETURN_FALSE(
+        prefs_->SetString(kPrefsUpdateStateSHA256Context,
+                          payload_hash_calculator_.GetContext()));
+    TEST_AND_RETURN_FALSE(
+        prefs_->SetString(kPrefsUpdateStateSignedSHA256Context,
+                          signed_hash_calculator_.GetContext()));
+    TEST_AND_RETURN_FALSE(prefs_->SetInt64(kPrefsUpdateStateNextDataOffset,
+                                           buffer_offset_));
+    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])
+        partition_index++;
+      const size_t partition_operation_num = next_operation_num_ - (
+          partition_index ? acc_num_operations_[partition_index - 1] : 0);
+      const InstallOperation& op =
+          partitions_[partition_index].operations(partition_operation_num);
+      TEST_AND_RETURN_FALSE(prefs_->SetInt64(kPrefsUpdateStateNextDataLength,
+                                             op.data_length()));
+    } else {
+      TEST_AND_RETURN_FALSE(prefs_->SetInt64(kPrefsUpdateStateNextDataLength,
+                                             0));
+    }
+  }
+  TEST_AND_RETURN_FALSE(prefs_->SetInt64(kPrefsUpdateStateNextOperation,
+                                         next_operation_num_));
+  return true;
+}
+
+bool DeltaPerformer::PrimeUpdateState() {
+  CHECK(manifest_valid_);
+  block_size_ = manifest_.block_size();
+
+  int64_t next_operation = kUpdateStateOperationInvalid;
+  if (!prefs_->GetInt64(kPrefsUpdateStateNextOperation, &next_operation) ||
+      next_operation == kUpdateStateOperationInvalid ||
+      next_operation <= 0) {
+    // Initiating a new update, no more state needs to be initialized.
+    return true;
+  }
+  next_operation_num_ = next_operation;
+
+  // Resuming an update -- load the rest of the update state.
+  int64_t next_data_offset = -1;
+  TEST_AND_RETURN_FALSE(prefs_->GetInt64(kPrefsUpdateStateNextDataOffset,
+                                         &next_data_offset) &&
+                        next_data_offset >= 0);
+  buffer_offset_ = next_data_offset;
+
+  // The signed hash context and the signature blob may be empty if the
+  // interrupted update didn't reach the signature.
+  string signed_hash_context;
+  if (prefs_->GetString(kPrefsUpdateStateSignedSHA256Context,
+                        &signed_hash_context)) {
+    TEST_AND_RETURN_FALSE(
+        signed_hash_calculator_.SetContext(signed_hash_context));
+  }
+
+  string signature_blob;
+  if (prefs_->GetString(kPrefsUpdateStateSignatureBlob, &signature_blob)) {
+    signatures_message_data_.assign(signature_blob.begin(),
+                                    signature_blob.end());
+  }
+
+  string hash_context;
+  TEST_AND_RETURN_FALSE(prefs_->GetString(kPrefsUpdateStateSHA256Context,
+                                          &hash_context) &&
+                        payload_hash_calculator_.SetContext(hash_context));
+
+  int64_t manifest_metadata_size = 0;
+  TEST_AND_RETURN_FALSE(prefs_->GetInt64(kPrefsManifestMetadataSize,
+                                         &manifest_metadata_size) &&
+                        manifest_metadata_size > 0);
+  metadata_size_ = manifest_metadata_size;
+
+  // Advance the download progress to reflect what doesn't need to be
+  // re-downloaded.
+  total_bytes_received_ += buffer_offset_;
+
+  // Speculatively count the resume as a failure.
+  int64_t resumed_update_failures;
+  if (prefs_->GetInt64(kPrefsResumedUpdateFailures, &resumed_update_failures)) {
+    resumed_update_failures++;
+  } else {
+    resumed_update_failures = 1;
+  }
+  prefs_->SetInt64(kPrefsResumedUpdateFailures, resumed_update_failures);
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
new file mode 100644
index 0000000..47ecfd8
--- /dev/null
+++ b/payload_consumer/delta_performer.h
@@ -0,0 +1,407 @@
+//
+// 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_CONSUMER_DELTA_PERFORMER_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_DELTA_PERFORMER_H_
+
+#include <inttypes.h>
+
+#include <string>
+#include <vector>
+
+#include <base/time/time.h>
+#include <brillo/secure_blob.h>
+#include <google/protobuf/repeated_field.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "update_engine/common/hash_calculator.h"
+#include "update_engine/common/platform_constants.h"
+#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/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+class DownloadActionDelegate;
+class BootControlInterface;
+class HardwareInterface;
+class PrefsInterface;
+
+// 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:
+  enum MetadataParseResult {
+    kMetadataParseSuccess,
+    kMetadataParseError,
+    kMetadataParseInsufficientData,
+  };
+
+  static const uint64_t kDeltaVersionOffset;
+  static const uint64_t kDeltaVersionSize;
+  static const uint64_t kDeltaManifestSizeOffset;
+  static const uint64_t kDeltaManifestSizeSize;
+  static const uint64_t kDeltaMetadataSignatureSizeSize;
+  static const uint64_t kMaxPayloadHeaderSize;
+  static const uint64_t kSupportedMajorPayloadVersion;
+  static const uint32_t kSupportedMinorPayloadVersion;
+
+  // Defines the granularity of progress logging in terms of how many "completed
+  // chunks" we want to report at the most.
+  static const unsigned kProgressLogMaxChunks;
+  // Defines a timeout since the last progress was logged after which we want to
+  // force another log message (even if the current chunk was not completed).
+  static const unsigned kProgressLogTimeoutSeconds;
+  // These define the relative weights (0-100) we give to the different work
+  // components associated with an update when computing an overall progress.
+  // Currently they include the download progress and the number of completed
+  // operations. They must add up to one hundred (100).
+  static const unsigned kProgressDownloadWeight;
+  static const unsigned kProgressOperationsWeight;
+
+  DeltaPerformer(PrefsInterface* prefs,
+                 BootControlInterface* boot_control,
+                 HardwareInterface* hardware,
+                 DownloadActionDelegate* download_delegate,
+                 InstallPlan* install_plan)
+      : prefs_(prefs),
+        boot_control_(boot_control),
+        hardware_(hardware),
+        download_delegate_(download_delegate),
+        install_plan_(install_plan) {}
+
+  // FileWriter's Write implementation where caller doesn't care about
+  // error codes.
+  bool Write(const void* bytes, size_t count) override {
+    ErrorCode error;
+    return Write(bytes, count, &error);
+  }
+
+  // FileWriter's Write implementation that returns a more specific |error| code
+  // in case of failures in Write operation.
+  bool Write(const void* bytes, size_t count, ErrorCode *error) override;
+
+  // Wrapper around close. Returns 0 on success or -errno on error.
+  // Closes both 'path' given to Open() and the kernel path.
+  int Close() override;
+
+  // Open the target and source (if delta payload) file descriptors for the
+  // |current_partition_|. The manifest needs to be already parsed for this to
+  // work. Returns whether the required file descriptors were successfully open.
+  bool OpenCurrentPartition();
+
+  // Closes the current partition file descriptors if open. Returns 0 on success
+  // or -errno on error.
+  int CloseCurrentPartition();
+
+  // Returns |true| only if the manifest has been processed and it's valid.
+  bool IsManifestValid();
+
+  // Verifies the downloaded payload against the signed hash included in the
+  // payload, against the update check hash (which is in base64 format)  and
+  // size using the public key and returns ErrorCode::kSuccess on success, an
+  // error code on failure.  This method should be called after closing the
+  // stream. Note this method skips the signed hash check if the public key is
+  // unavailable; it returns ErrorCode::kSignedDeltaPayloadExpectedError if the
+  // public key is available but the delta payload doesn't include a signature.
+  ErrorCode VerifyPayload(const std::string& update_check_response_hash,
+                          const uint64_t update_check_response_size);
+
+  // Converts an ordered collection of Extent objects which contain data of
+  // length full_length to a comma-separated string. For each Extent, the
+  // string will have the start offset and then the length in bytes.
+  // The length value of the last extent in the string may be short, since
+  // the full length of all extents in the string is capped to full_length.
+  // Also, an extent starting at kSparseHole, appears as -1 in the string.
+  // For example, if the Extents are {1, 1}, {4, 2}, {kSparseHole, 1},
+  // {0, 1}, block_size is 4096, and full_length is 5 * block_size - 13,
+  // the resulting string will be: "4096:4096,16384:8192,-1:4096,0:4083"
+  static bool ExtentsToBsdiffPositionsString(
+      const google::protobuf::RepeatedPtrField<Extent>& extents,
+      uint64_t block_size,
+      uint64_t full_length,
+      std::string* positions_string);
+
+  // Returns true if a previous update attempt can be continued based on the
+  // persistent preferences and the new update check response hash.
+  static bool CanResumeUpdate(PrefsInterface* prefs,
+                              std::string update_check_response_hash);
+
+  // Resets the persistent update progress state to indicate that an update
+  // can't be resumed. Performs a quick update-in-progress reset if |quick| is
+  // true, otherwise resets all progress-related update state. Returns true on
+  // success, false otherwise.
+  static bool ResetUpdateProgress(PrefsInterface* prefs, bool quick);
+
+  // Attempts to parse the update metadata starting from the beginning of
+  // |payload|. On success, returns kMetadataParseSuccess. Returns
+  // kMetadataParseInsufficientData if more data is needed to parse the complete
+  // metadata. Returns kMetadataParseError if the metadata can't be parsed given
+  // the payload.
+  MetadataParseResult ParsePayloadMetadata(const brillo::Blob& payload,
+                                           ErrorCode* error);
+
+  void set_public_key_path(const std::string& public_key_path) {
+    public_key_path_ = public_key_path;
+  }
+
+  // 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;
+
+  // 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 size of the payload metadata, which includes the payload header
+  // and the manifest. If the header was not yet parsed, returns zero.
+  uint64_t GetMetadataSize() const;
+
+  // If the manifest was successfully parsed, copies it to |*out_manifest_p|.
+  // Returns true on success.
+  bool GetManifest(DeltaArchiveManifest* out_manifest_p) const;
+
+  // Return true if header parsing is finished and no errors occurred.
+  bool IsHeaderParsed() const;
+
+  // Returns the major payload version. If the version was not yet parsed,
+  // returns zero.
+  uint64_t GetMajorVersion() const;
+
+  // Returns the delta minor version. If this value is defined in the manifest,
+  // it returns that value, otherwise it returns the default value.
+  uint32_t GetMinorVersion() const;
+
+ private:
+  friend class DeltaPerformerTest;
+  friend class DeltaPerformerIntegrationTest;
+  FRIEND_TEST(DeltaPerformerTest, BrilloMetadataSignatureSizeTest);
+  FRIEND_TEST(DeltaPerformerTest, BrilloVerifyMetadataSignatureTest);
+  FRIEND_TEST(DeltaPerformerTest, UsePublicKeyFromResponse);
+
+  // Parse and move the update instructions of all partitions into our local
+  // |partitions_| variable based on the version of the payload. Requires the
+  // manifest to be parsed and valid.
+  bool ParseManifestPartitions(ErrorCode* error);
+
+  // Appends up to |*count_p| bytes from |*bytes_p| to |buffer_|, but only to
+  // the extent that the size of |buffer_| does not exceed |max|. Advances
+  // |*cbytes_p| and decreases |*count_p| by the actual number of bytes copied,
+  // and returns this number.
+  size_t CopyDataToBuffer(const char** bytes_p, size_t* count_p, size_t max);
+
+  // If |op_result| is false, emits an error message using |op_type_name| and
+  // sets |*error| accordingly. Otherwise does nothing. Returns |op_result|.
+  bool HandleOpResult(bool op_result, const char* op_type_name,
+                      ErrorCode* error);
+
+  // Logs the progress of downloading/applying an update.
+  void LogProgress(const char* message_prefix);
+
+  // Update overall progress metrics, log as necessary.
+  void UpdateOverallProgress(bool force_log, const char* message_prefix);
+
+  // Verifies that the expected source partition hashes (if present) match the
+  // hashes for the current partitions. Returns true if there are no expected
+  // hashes in the payload (e.g., if it's a new-style full update) or if the
+  // hashes match; returns false otherwise.
+  bool VerifySourcePartitions();
+
+  // Returns true if enough of the delta file has been passed via Write()
+  // to be able to perform a given install operation.
+  bool CanPerformInstallOperation(const InstallOperation& operation);
+
+  // Checks the integrity of the payload manifest. Returns true upon success,
+  // false otherwise.
+  ErrorCode ValidateManifest();
+
+  // Validates that the hash of the blobs corresponding to the given |operation|
+  // matches what's specified in the manifest in the payload.
+  // Returns ErrorCode::kSuccess on match or a suitable error code otherwise.
+  ErrorCode ValidateOperationHash(const InstallOperation& operation);
+
+  // Given the |payload|, verifies that the signed hash of its metadata matches
+  // what's specified in the install plan from Omaha (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 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(const brillo::Blob& payload);
+
+  // Returns true on success.
+  bool PerformInstallOperation(const InstallOperation& operation);
+
+  // These perform a specific type of operation and return true on success.
+  bool PerformReplaceOperation(const InstallOperation& operation);
+  bool PerformZeroOrDiscardOperation(const InstallOperation& operation);
+  bool PerformMoveOperation(const InstallOperation& operation);
+  bool PerformBsdiffOperation(const InstallOperation& operation);
+  bool PerformSourceCopyOperation(const InstallOperation& operation);
+  bool PerformSourceBsdiffOperation(const InstallOperation& operation);
+
+  // 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.
+  bool ExtractSignatureMessage();
+
+  // Updates the payload hash calculator with the bytes in |buffer_|, also
+  // updates the signed hash calculator with the first |signed_hash_buffer_size|
+  // bytes in |buffer_|. Then discard the content, ensuring that memory is being
+  // deallocated. If |do_advance_offset|, advances the internal offset counter
+  // 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.
+  bool CheckpointUpdateProgress();
+
+  // 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.
+  bool PrimeUpdateState();
+
+  // If the Omaha response contains a public RSA key and we're allowed
+  // to use it (e.g. if we're in developer mode), extract the key from
+  // the response and store it in a temporary file and return true. In
+  // the affirmative the path to the temporary file is stored in
+  // |out_tmp_key| and it is the responsibility of the caller to clean
+  // it up.
+  bool GetPublicKeyFromResponse(base::FilePath *out_tmp_key);
+
+  // Update Engine preference store.
+  PrefsInterface* prefs_;
+
+  // BootControl and Hardware interface references.
+  BootControlInterface* boot_control_;
+  HardwareInterface* hardware_;
+
+  // The DownloadActionDelegate instance monitoring the DownloadAction, or a
+  // nullptr if not used.
+  DownloadActionDelegate* download_delegate_;
+
+  // Install Plan based on Omaha Response.
+  InstallPlan* install_plan_;
+
+  // 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 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_;
+
+  // Parsed manifest. Set after enough bytes to parse the manifest were
+  // downloaded.
+  DeltaArchiveManifest manifest_;
+  bool manifest_parsed_{false};
+  bool manifest_valid_{false};
+  uint64_t metadata_size_{0};
+  uint64_t manifest_size_{0};
+  uint32_t metadata_signature_size_{0};
+  uint64_t major_payload_version_{0};
+
+  // Accumulated number of operations per partition. The i-th element is the
+  // sum of the number of operations for all the partitions from 0 to i
+  // inclusive. Valid when |manifest_valid_| is true.
+  std::vector<size_t> acc_num_operations_;
+
+  // The total operations in a payload. Valid when |manifest_valid_| is true,
+  // 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.
+  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.
+  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.
+  brillo::Blob buffer_;
+  // Offset of buffer_ in the binary blobs section of the update.
+  uint64_t buffer_offset_{0};
+
+  // Last |buffer_offset_| value updated as part of the progress update.
+  uint64_t last_updated_buffer_offset_{kuint64max};
+
+  // The block size (parsed from the manifest).
+  uint32_t block_size_{0};
+
+  // Calculates the whole payload file hash, including headers and signatures.
+  HashCalculator payload_hash_calculator_;
+
+  // Calculates the hash of the portion of the payload signed by the payload
+  // signature. This hash skips the metadata signature portion, located after
+  // the metadata and doesn't include the payload signature itself.
+  HashCalculator signed_hash_calculator_;
+
+  // Signatures message blob extracted directly from the payload.
+  brillo::Blob signatures_message_data_;
+
+  // The public key to be used. Provided as a member so that tests can
+  // override with test keys.
+  std::string public_key_path_{constants::kUpdatePayloadPublicKeyPath};
+
+  // The number of bytes received so far, used for progress tracking.
+  size_t total_bytes_received_{0};
+
+  // An overall progress counter, which should reflect both download progress
+  // and the ratio of applied operations. Range is 0-100.
+  unsigned overall_progress_{0};
+
+  // The last progress chunk recorded.
+  unsigned last_progress_chunk_{0};
+
+  // 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::Time forced_progress_log_time_;
+
+  // The payload major payload version supported by DeltaPerformer.
+  uint64_t supported_major_version_{kSupportedMajorPayloadVersion};
+
+  // The delta minor payload version supported by DeltaPerformer.
+  uint32_t supported_minor_version_{kSupportedMinorPayloadVersion};
+
+  DISALLOW_COPY_AND_ASSIGN(DeltaPerformer);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_DELTA_PERFORMER_H_
diff --git a/payload_consumer/delta_performer_integration_test.cc b/payload_consumer/delta_performer_integration_test.cc
new file mode 100644
index 0000000..bcf7db2
--- /dev/null
+++ b/payload_consumer/delta_performer_integration_test.cc
@@ -0,0 +1,1035 @@
+//
+// 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_consumer/delta_performer.h"
+
+#include <inttypes.h>
+#include <sys/mount.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <google/protobuf/repeated_field.h>
+#include <gtest/gtest.h>
+
+#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/mock_prefs.h"
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/mock_download_action.h"
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_consumer/payload_verifier.h"
+#include "update_engine/payload_generator/delta_diff_generator.h"
+#include "update_engine/payload_generator/payload_signer.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+using std::string;
+using std::vector;
+using test_utils::ScopedLoopMounter;
+using test_utils::System;
+using test_utils::kRandomString;
+using testing::Return;
+using testing::_;
+
+extern const char* kUnittestPrivateKeyPath;
+extern const char* kUnittestPublicKeyPath;
+extern const char* kUnittestPrivateKey2Path;
+extern const char* kUnittestPublicKey2Path;
+
+static const int kDefaultKernelSize = 4096;  // Something small for a test
+static const uint8_t kNewData[] = {'T', 'h', 'i', 's', ' ', 'i', 's', ' ',
+                                   'n', 'e', 'w', ' ', 'd', 'a', 't', 'a', '.'};
+
+namespace {
+struct DeltaState {
+  string a_img;
+  string b_img;
+  string result_img;
+  size_t image_size;
+
+  string delta_path;
+  uint64_t metadata_size;
+
+  string old_kernel;
+  brillo::Blob old_kernel_data;
+
+  string new_kernel;
+  brillo::Blob new_kernel_data;
+
+  string result_kernel;
+  brillo::Blob result_kernel_data;
+  size_t kernel_size;
+
+  // The InstallPlan referenced by the DeltaPerformer. This needs to outlive
+  // 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_;
+  MockDownloadActionDelegate mock_delegate_;
+};
+
+enum SignatureTest {
+  kSignatureNone,  // No payload signing.
+  kSignatureGenerator,  // Sign the payload at generation time.
+  kSignatureGenerated,  // Sign the payload after it's generated.
+  kSignatureGeneratedPlaceholder,  // Insert placeholder signatures, then real.
+  kSignatureGeneratedPlaceholderMismatch,  // Insert a wrong sized placeholder.
+  kSignatureGeneratedShell,  // Sign the generated payload through shell cmds.
+  kSignatureGeneratedShellBadKey,  // Sign with a bad key through shell cmds.
+  kSignatureGeneratedShellRotateCl1,  // Rotate key, test client v1
+  kSignatureGeneratedShellRotateCl2,  // Rotate key, test client v2
+};
+
+enum OperationHashTest {
+  kInvalidOperationData,
+  kValidOperationData,
+};
+
+}  // namespace
+
+class DeltaPerformerIntegrationTest : public ::testing::Test {
+ public:
+  static void SetSupportedVersion(DeltaPerformer* performer,
+                                  uint64_t minor_version) {
+    performer->supported_minor_version_ = minor_version;
+  }
+};
+
+static void CompareFilesByBlock(const string& a_file, const string& b_file,
+                                size_t image_size) {
+  EXPECT_EQ(0, image_size % kBlockSize);
+
+  brillo::Blob a_data, b_data;
+  EXPECT_TRUE(utils::ReadFile(a_file, &a_data)) << "file failed: " << a_file;
+  EXPECT_TRUE(utils::ReadFile(b_file, &b_data)) << "file failed: " << b_file;
+
+  EXPECT_GE(a_data.size(), image_size);
+  EXPECT_GE(b_data.size(), image_size);
+  for (size_t i = 0; i < image_size; i += kBlockSize) {
+    EXPECT_EQ(0, i % kBlockSize);
+    brillo::Blob a_sub(&a_data[i], &a_data[i + kBlockSize]);
+    brillo::Blob b_sub(&b_data[i], &b_data[i + kBlockSize]);
+    EXPECT_TRUE(a_sub == b_sub) << "Block " << (i/kBlockSize) << " differs";
+  }
+  if (::testing::Test::HasNonfatalFailure()) {
+    LOG(INFO) << "Compared filesystems with size " << image_size
+              << ", partition A " << a_file << " size: " << a_data.size()
+              << ", partition B " << b_file << " size: " << b_data.size();
+  }
+}
+
+static bool WriteSparseFile(const string& path, off_t size) {
+  int fd = open(path.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644);
+  TEST_AND_RETURN_FALSE_ERRNO(fd >= 0);
+  ScopedFdCloser fd_closer(&fd);
+  off_t rc = lseek(fd, size + 1, SEEK_SET);
+  TEST_AND_RETURN_FALSE_ERRNO(rc != static_cast<off_t>(-1));
+  int return_code = ftruncate(fd, size);
+  TEST_AND_RETURN_FALSE_ERRNO(return_code == 0);
+  return true;
+}
+
+static size_t GetSignatureSize(const string& private_key_path) {
+  const brillo::Blob data(1, 'x');
+  brillo::Blob hash;
+  EXPECT_TRUE(HashCalculator::RawHashOfData(data, &hash));
+  brillo::Blob signature;
+  EXPECT_TRUE(PayloadSigner::SignHash(hash,
+                                      private_key_path,
+                                      &signature));
+  return signature.size();
+}
+
+static bool InsertSignaturePlaceholder(int signature_size,
+                                       const string& payload_path,
+                                       uint64_t* out_metadata_size) {
+  vector<brillo::Blob> signatures;
+  signatures.push_back(brillo::Blob(signature_size, 0));
+
+  return PayloadSigner::AddSignatureToPayload(
+      payload_path,
+      signatures,
+      {},
+      payload_path,
+      out_metadata_size);
+}
+
+static void SignGeneratedPayload(const string& payload_path,
+                                 uint64_t* out_metadata_size) {
+  int signature_size = GetSignatureSize(kUnittestPrivateKeyPath);
+  brillo::Blob hash;
+  ASSERT_TRUE(PayloadSigner::HashPayloadForSigning(
+      payload_path,
+      vector<int>(1, signature_size),
+      &hash,
+      nullptr));
+  brillo::Blob signature;
+  ASSERT_TRUE(PayloadSigner::SignHash(hash,
+                                      kUnittestPrivateKeyPath,
+                                      &signature));
+  ASSERT_TRUE(PayloadSigner::AddSignatureToPayload(
+      payload_path,
+      vector<brillo::Blob>(1, signature),
+      {},
+      payload_path,
+      out_metadata_size));
+  EXPECT_TRUE(PayloadSigner::VerifySignedPayload(
+      payload_path,
+      kUnittestPublicKeyPath));
+}
+
+static void SignGeneratedShellPayload(SignatureTest signature_test,
+                                      const string& payload_path) {
+  string private_key_path = kUnittestPrivateKeyPath;
+  if (signature_test == kSignatureGeneratedShellBadKey) {
+    ASSERT_TRUE(utils::MakeTempFile("key.XXXXXX",
+                                    &private_key_path,
+                                    nullptr));
+  } else {
+    ASSERT_TRUE(signature_test == kSignatureGeneratedShell ||
+                signature_test == kSignatureGeneratedShellRotateCl1 ||
+                signature_test == kSignatureGeneratedShellRotateCl2);
+  }
+  ScopedPathUnlinker key_unlinker(private_key_path);
+  key_unlinker.set_should_remove(signature_test ==
+                                 kSignatureGeneratedShellBadKey);
+  // Generates a new private key that will not match the public key.
+  if (signature_test == kSignatureGeneratedShellBadKey) {
+    LOG(INFO) << "Generating a mismatched private key.";
+    ASSERT_EQ(0, System(base::StringPrintf(
+        "openssl genrsa -out %s 2048", private_key_path.c_str())));
+  }
+  int signature_size = GetSignatureSize(private_key_path);
+  string hash_file;
+  ASSERT_TRUE(utils::MakeTempFile("hash.XXXXXX", &hash_file, nullptr));
+  ScopedPathUnlinker hash_unlinker(hash_file);
+  string signature_size_string;
+  if (signature_test == kSignatureGeneratedShellRotateCl1 ||
+      signature_test == kSignatureGeneratedShellRotateCl2)
+    signature_size_string = base::StringPrintf("%d:%d",
+                                               signature_size, signature_size);
+  else
+    signature_size_string = base::StringPrintf("%d", signature_size);
+  ASSERT_EQ(0,
+            System(base::StringPrintf(
+                "./delta_generator -in_file=%s -signature_size=%s "
+                "-out_hash_file=%s",
+                payload_path.c_str(),
+                signature_size_string.c_str(),
+                hash_file.c_str())));
+
+  // Pad the hash
+  brillo::Blob hash;
+  ASSERT_TRUE(utils::ReadFile(hash_file, &hash));
+  ASSERT_TRUE(PayloadVerifier::PadRSA2048SHA256Hash(&hash));
+  ASSERT_TRUE(test_utils::WriteFileVector(hash_file, hash));
+
+  string sig_file;
+  ASSERT_TRUE(utils::MakeTempFile("signature.XXXXXX", &sig_file, nullptr));
+  ScopedPathUnlinker sig_unlinker(sig_file);
+  ASSERT_EQ(0,
+            System(base::StringPrintf(
+                "openssl rsautl -raw -sign -inkey %s -in %s -out %s",
+                private_key_path.c_str(),
+                hash_file.c_str(),
+                sig_file.c_str())));
+  string sig_file2;
+  ASSERT_TRUE(utils::MakeTempFile("signature.XXXXXX", &sig_file2, nullptr));
+  ScopedPathUnlinker sig2_unlinker(sig_file2);
+  if (signature_test == kSignatureGeneratedShellRotateCl1 ||
+      signature_test == kSignatureGeneratedShellRotateCl2) {
+    ASSERT_EQ(0,
+              System(base::StringPrintf(
+                  "openssl rsautl -raw -sign -inkey %s -in %s -out %s",
+                  kUnittestPrivateKey2Path,
+                  hash_file.c_str(),
+                  sig_file2.c_str())));
+    // Append second sig file to first path
+    sig_file += ":" + sig_file2;
+  }
+
+  ASSERT_EQ(0,
+            System(base::StringPrintf(
+                "./delta_generator -in_file=%s -signature_file=%s "
+                "-out_file=%s",
+                payload_path.c_str(),
+                sig_file.c_str(),
+                payload_path.c_str())));
+  int verify_result =
+      System(base::StringPrintf(
+          "./delta_generator -in_file=%s -public_key=%s -public_key_version=%d",
+          payload_path.c_str(),
+          signature_test == kSignatureGeneratedShellRotateCl2 ?
+          kUnittestPublicKey2Path : kUnittestPublicKeyPath,
+          signature_test == kSignatureGeneratedShellRotateCl2 ? 2 : 1));
+  if (signature_test == kSignatureGeneratedShellBadKey) {
+    ASSERT_NE(0, verify_result);
+  } else {
+    ASSERT_EQ(0, verify_result);
+  }
+}
+
+static void GenerateDeltaFile(bool full_kernel,
+                              bool full_rootfs,
+                              bool noop,
+                              ssize_t chunk_size,
+                              SignatureTest signature_test,
+                              DeltaState *state,
+                              uint32_t minor_version) {
+  EXPECT_TRUE(utils::MakeTempFile("a_img.XXXXXX", &state->a_img, nullptr));
+  EXPECT_TRUE(utils::MakeTempFile("b_img.XXXXXX", &state->b_img, nullptr));
+
+  // result_img is used in minor version 2. Instead of applying the update
+  // in-place on A, we apply it to a new image, result_img.
+  EXPECT_TRUE(
+      utils::MakeTempFile("result_img.XXXXXX", &state->result_img, nullptr));
+  test_utils::CreateExtImageAtPath(state->a_img, nullptr);
+
+  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, &a_mnt, 0);
+
+    brillo::Blob hardtocompress;
+    while (hardtocompress.size() < 3 * kBlockSize) {
+      hardtocompress.insert(hardtocompress.end(),
+                            std::begin(kRandomString), std::end(kRandomString));
+    }
+    EXPECT_TRUE(utils::WriteFile(base::StringPrintf("%s/hardtocompress",
+                                                    a_mnt.c_str()).c_str(),
+                                 hardtocompress.data(),
+                                 hardtocompress.size()));
+
+    brillo::Blob zeros(16 * 1024, 0);
+    EXPECT_EQ(zeros.size(),
+              base::WriteFile(base::FilePath(base::StringPrintf(
+                                  "%s/move-to-sparse", a_mnt.c_str())),
+                              reinterpret_cast<const char*>(zeros.data()),
+                              zeros.size()));
+
+    EXPECT_TRUE(
+        WriteSparseFile(base::StringPrintf("%s/move-from-sparse",
+                                           a_mnt.c_str()), 16 * 1024));
+
+    EXPECT_EQ(0,
+              System(base::StringPrintf("dd if=/dev/zero of=%s/move-semi-sparse"
+                                        " bs=1 seek=4096 count=1 status=none",
+                                        a_mnt.c_str()).c_str()));
+
+    // Write 1 MiB of 0xff to try to catch the case where writing a bsdiff
+    // patch fails to zero out the final block.
+    brillo::Blob ones(1024 * 1024, 0xff);
+    EXPECT_TRUE(utils::WriteFile(base::StringPrintf("%s/ones",
+                                                    a_mnt.c_str()).c_str(),
+                                 ones.data(),
+                                 ones.size()));
+  }
+
+  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));
+    }
+
+    test_utils::CreateExtImageAtPath(state->b_img, nullptr);
+
+    // Make some changes to the B image.
+    string b_mnt;
+    ScopedLoopMounter b_mounter(state->b_img, &b_mnt, 0);
+
+    EXPECT_EQ(0, System(base::StringPrintf("cp %s/hello %s/hello2",
+                                           b_mnt.c_str(),
+                                           b_mnt.c_str()).c_str()));
+    EXPECT_EQ(0, System(base::StringPrintf("rm %s/hello",
+                                           b_mnt.c_str()).c_str()));
+    EXPECT_EQ(0, System(base::StringPrintf("mv %s/hello2 %s/hello",
+                                           b_mnt.c_str(),
+                                           b_mnt.c_str()).c_str()));
+    EXPECT_EQ(0, System(base::StringPrintf("echo foo > %s/foo",
+                                           b_mnt.c_str()).c_str()));
+    EXPECT_EQ(0, System(base::StringPrintf("touch %s/emptyfile",
+                                           b_mnt.c_str()).c_str()));
+    EXPECT_TRUE(WriteSparseFile(base::StringPrintf("%s/fullsparse",
+                                                   b_mnt.c_str()),
+                                                   1024 * 1024));
+
+    EXPECT_TRUE(
+        WriteSparseFile(base::StringPrintf("%s/move-to-sparse", b_mnt.c_str()),
+                        16 * 1024));
+
+    brillo::Blob zeros(16 * 1024, 0);
+    EXPECT_EQ(zeros.size(),
+              base::WriteFile(base::FilePath(base::StringPrintf(
+                                  "%s/move-from-sparse", b_mnt.c_str())),
+                              reinterpret_cast<const char*>(zeros.data()),
+                              zeros.size()));
+
+    EXPECT_EQ(0, System(base::StringPrintf("dd if=/dev/zero "
+                                           "of=%s/move-semi-sparse "
+                                           "bs=1 seek=4096 count=1 status=none",
+                                           b_mnt.c_str()).c_str()));
+
+    EXPECT_EQ(0, System(base::StringPrintf("dd if=/dev/zero "
+                                           "of=%s/partsparse bs=1 "
+                                           "seek=4096 count=1 status=none",
+                                           b_mnt.c_str()).c_str()));
+    EXPECT_EQ(0, System(base::StringPrintf("cp %s/srchardlink0 %s/tmp && "
+                                           "mv %s/tmp %s/srchardlink1",
+                                           b_mnt.c_str(),
+                                           b_mnt.c_str(),
+                                           b_mnt.c_str(),
+                                           b_mnt.c_str()).c_str()));
+    EXPECT_EQ(0, System(
+        base::StringPrintf("rm %s/boguslink && echo foobar > %s/boguslink",
+                           b_mnt.c_str(), b_mnt.c_str()).c_str()));
+
+    brillo::Blob hardtocompress;
+    while (hardtocompress.size() < 3 * kBlockSize) {
+      hardtocompress.insert(hardtocompress.end(),
+                            std::begin(kRandomString), std::end(kRandomString));
+    }
+    EXPECT_TRUE(utils::WriteFile(base::StringPrintf("%s/hardtocompress",
+                                              b_mnt.c_str()).c_str(),
+                                 hardtocompress.data(),
+                                 hardtocompress.size()));
+  }
+
+  string old_kernel;
+  EXPECT_TRUE(utils::MakeTempFile("old_kernel.XXXXXX",
+                                  &state->old_kernel,
+                                  nullptr));
+
+  string new_kernel;
+  EXPECT_TRUE(utils::MakeTempFile("new_kernel.XXXXXX",
+                                  &state->new_kernel,
+                                  nullptr));
+
+  string result_kernel;
+  EXPECT_TRUE(utils::MakeTempFile("result_kernel.XXXXXX",
+                                  &state->result_kernel,
+                                  nullptr));
+
+  state->kernel_size = kDefaultKernelSize;
+  state->old_kernel_data.resize(kDefaultKernelSize);
+  state->new_kernel_data.resize(state->old_kernel_data.size());
+  state->result_kernel_data.resize(state->old_kernel_data.size());
+  test_utils::FillWithData(&state->old_kernel_data);
+  test_utils::FillWithData(&state->new_kernel_data);
+  test_utils::FillWithData(&state->result_kernel_data);
+
+  // change the new kernel data
+  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.c_str(),
+                               state->old_kernel_data.data(),
+                               state->old_kernel_data.size()));
+  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.c_str(),
+                               state->result_kernel_data.data(),
+                               state->result_kernel_data.size()));
+
+  EXPECT_TRUE(utils::MakeTempFile("delta.XXXXXX",
+                                  &state->delta_path,
+                                  nullptr));
+  LOG(INFO) << "delta path: " << state->delta_path;
+  {
+    const string private_key =
+        signature_test == kSignatureGenerator ? kUnittestPrivateKeyPath : "";
+
+    PayloadGenerationConfig payload_config;
+    payload_config.is_delta = !full_rootfs;
+    payload_config.hard_chunk_size = chunk_size;
+    payload_config.rootfs_partition_size = kRootFSPartitionSize;
+    payload_config.major_version = kChromeOSMajorPayloadVersion;
+    payload_config.minor_version = minor_version;
+    if (!full_rootfs) {
+      payload_config.source.partitions.emplace_back(kLegacyPartitionNameRoot);
+      payload_config.source.partitions.emplace_back(kLegacyPartitionNameKernel);
+      payload_config.source.partitions.front().path = state->a_img;
+      if (!full_kernel)
+        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());
+    } else {
+      if (payload_config.hard_chunk_size == -1)
+        // Use 1 MiB chunk size for the full unittests.
+        payload_config.hard_chunk_size = 1024 * 1024;
+    }
+    payload_config.target.partitions.emplace_back(kLegacyPartitionNameRoot);
+    payload_config.target.partitions.back().path = state->b_img;
+    payload_config.target.partitions.emplace_back(kLegacyPartitionNameKernel);
+    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_path,
+            private_key,
+            &state->metadata_size));
+  }
+  // Extend the "partitions" holding the file system a bit.
+  EXPECT_EQ(0, HANDLE_EINTR(truncate(state->a_img.c_str(),
+                                     state->image_size + 1024 * 1024)));
+  EXPECT_EQ(state->image_size + 1024 * 1024, utils::FileSize(state->a_img));
+  EXPECT_EQ(0, HANDLE_EINTR(truncate(state->b_img.c_str(),
+                                     state->image_size + 1024 * 1024)));
+  EXPECT_EQ(state->image_size + 1024 * 1024, utils::FileSize(state->b_img));
+
+  if (signature_test == kSignatureGeneratedPlaceholder ||
+      signature_test == kSignatureGeneratedPlaceholderMismatch) {
+    int signature_size = GetSignatureSize(kUnittestPrivateKeyPath);
+    LOG(INFO) << "Inserting placeholder signature.";
+    ASSERT_TRUE(InsertSignaturePlaceholder(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_path,
+                                              &state->metadata_size));
+      return;
+    }
+  }
+
+  if (signature_test == kSignatureGenerated ||
+      signature_test == kSignatureGeneratedPlaceholder ||
+      signature_test == kSignatureGeneratedPlaceholderMismatch) {
+    // Generate the signed payload and update the metadata size in state to
+    // reflect the new size after adding the signature operation to the
+    // manifest.
+    LOG(INFO) << "Signing payload.";
+    SignGeneratedPayload(state->delta_path, &state->metadata_size);
+  } else if (signature_test == kSignatureGeneratedShell ||
+             signature_test == kSignatureGeneratedShellBadKey ||
+             signature_test == kSignatureGeneratedShellRotateCl1 ||
+             signature_test == kSignatureGeneratedShellRotateCl2) {
+    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,
+                           OperationHashTest op_hash_test,
+                           DeltaPerformer** performer,
+                           uint32_t minor_version) {
+  // Check the metadata.
+  {
+    DeltaArchiveManifest manifest;
+    EXPECT_TRUE(PayloadSigner::LoadPayload(state->delta_path,
+                                           &state->delta,
+                                           &manifest,
+                                           nullptr,
+                                           &state->metadata_size,
+                                           nullptr));
+    LOG(INFO) << "Metadata size: " << state->metadata_size;
+
+    if (signature_test == kSignatureNone) {
+      EXPECT_FALSE(manifest.has_signatures_offset());
+      EXPECT_FALSE(manifest.has_signatures_size());
+    } else {
+      EXPECT_TRUE(manifest.has_signatures_offset());
+      EXPECT_TRUE(manifest.has_signatures_size());
+      Signatures sigs_message;
+      EXPECT_TRUE(sigs_message.ParseFromArray(
+          &state->delta[state->metadata_size + manifest.signatures_offset()],
+          manifest.signatures_size()));
+      if (signature_test == kSignatureGeneratedShellRotateCl1 ||
+          signature_test == kSignatureGeneratedShellRotateCl2)
+        EXPECT_EQ(2, sigs_message.signatures_size());
+      else
+        EXPECT_EQ(1, sigs_message.signatures_size());
+      const Signatures_Signature& signature = sigs_message.signatures(0);
+      EXPECT_EQ(1, signature.version());
+
+      uint64_t expected_sig_data_length = 0;
+      vector<string> key_paths{kUnittestPrivateKeyPath};
+      if (signature_test == kSignatureGeneratedShellRotateCl1 ||
+          signature_test == kSignatureGeneratedShellRotateCl2) {
+        key_paths.push_back(kUnittestPrivateKey2Path);
+      }
+      EXPECT_TRUE(PayloadSigner::SignatureBlobLength(
+          key_paths,
+          &expected_sig_data_length));
+      EXPECT_EQ(expected_sig_data_length, manifest.signatures_size());
+      EXPECT_FALSE(signature.data().empty());
+    }
+
+    if (noop) {
+      EXPECT_EQ(0, manifest.install_operations_size());
+      EXPECT_EQ(1, manifest.kernel_install_operations_size());
+    }
+
+    if (full_kernel) {
+      EXPECT_FALSE(manifest.has_old_kernel_info());
+    } else {
+      EXPECT_EQ(state->old_kernel_data.size(),
+                manifest.old_kernel_info().size());
+      EXPECT_FALSE(manifest.old_kernel_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");
+      }
+    }
+
+
+    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());
+  }
+
+  MockPrefs prefs;
+  EXPECT_CALL(prefs, SetInt64(kPrefsManifestMetadataSize,
+                              state->metadata_size)).WillOnce(Return(true));
+  EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextOperation, _))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(prefs, GetInt64(kPrefsUpdateStateNextOperation, _))
+      .WillOnce(Return(false));
+  EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextDataOffset, _))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextDataLength, _))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSHA256Context, _))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignedSHA256Context, _))
+      .WillRepeatedly(Return(true));
+  if (op_hash_test == kValidOperationData && signature_test != kSignatureNone) {
+    EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignatureBlob, _))
+        .WillOnce(Return(true));
+  }
+
+  EXPECT_CALL(state->mock_delegate_, ShouldCancel(_))
+      .WillRepeatedly(Return(false));
+
+  // Update the A image in place.
+  InstallPlan* install_plan = &state->install_plan;
+  install_plan->hash_checks_mandatory = hash_checks_mandatory;
+  install_plan->metadata_size = state->metadata_size;
+  install_plan->is_full_update = full_kernel && full_rootfs;
+  install_plan->source_slot = 0;
+  install_plan->target_slot = 1;
+
+  InstallPlan::Partition root_part;
+  root_part.name = kLegacyPartitionNameRoot;
+
+  InstallPlan::Partition kernel_part;
+  kernel_part.name = kLegacyPartitionNameKernel;
+
+  LOG(INFO) << "Setting payload metadata size in Omaha  = "
+            << state->metadata_size;
+  ASSERT_TRUE(PayloadSigner::GetMetadataSignature(
+      state->delta.data(),
+      state->metadata_size,
+      kUnittestPrivateKeyPath,
+      &install_plan->metadata_signature));
+  EXPECT_FALSE(install_plan->metadata_signature.empty());
+
+  *performer = new DeltaPerformer(&prefs,
+                                  &state->fake_boot_control_,
+                                  &state->fake_hardware_,
+                                  &state->mock_delegate_,
+                                  install_plan);
+  EXPECT_TRUE(utils::FileExists(kUnittestPublicKeyPath));
+  (*performer)->set_public_key_path(kUnittestPublicKeyPath);
+  DeltaPerformerIntegrationTest::SetSupportedVersion(*performer, minor_version);
+
+  EXPECT_EQ(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));
+
+  // This partitions are normally filed by the FilesystemVerifierAction with
+  // the source hashes used for deltas.
+  install_plan->partitions = {root_part, kernel_part};
+
+  // 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(
+      kLegacyPartitionNameRoot, install_plan->source_slot, state->a_img);
+  state->fake_boot_control_.SetPartitionDevice(
+      kLegacyPartitionNameKernel, install_plan->source_slot, state->old_kernel);
+  state->fake_boot_control_.SetPartitionDevice(
+      kLegacyPartitionNameRoot, install_plan->target_slot, target_root);
+  state->fake_boot_control_.SetPartitionDevice(
+      kLegacyPartitionNameKernel, install_plan->target_slot, target_kernel);
+
+  ErrorCode expected_error, actual_error;
+  bool continue_writing;
+  switch (op_hash_test) {
+    case kInvalidOperationData: {
+      // Muck with some random offset post the metadata size so that
+      // some operation hash will result in a mismatch.
+      int some_offset = state->metadata_size + 300;
+      LOG(INFO) << "Tampered value at offset: " << some_offset;
+      state->delta[some_offset]++;
+      expected_error = ErrorCode::kDownloadOperationHashMismatch;
+      continue_writing = false;
+      break;
+    }
+
+    case kValidOperationData:
+    default:
+      // no change.
+      expected_error = ErrorCode::kSuccess;
+      continue_writing = true;
+      break;
+  }
+
+  // Write at some number of bytes per operation. Arbitrarily chose 5.
+  const size_t kBytesPerWrite = 5;
+  for (size_t i = 0; i < state->delta.size(); i += kBytesPerWrite) {
+    size_t count = std::min(state->delta.size() - i, kBytesPerWrite);
+    bool write_succeeded = ((*performer)->Write(&state->delta[i],
+                                                count,
+                                                &actual_error));
+    // Normally write_succeeded should be true every time and
+    // actual_error should be ErrorCode::kSuccess. If so, continue the loop.
+    // But if we seeded an operation hash error above, then write_succeeded
+    // will be false. The failure may happen at any operation n. So, all
+    // Writes until n-1 should succeed and the nth operation will fail with
+    // actual_error. In this case, we should bail out of the loop because
+    // we cannot proceed applying the delta.
+    if (!write_succeeded) {
+      LOG(INFO) << "Write failed. Checking if it failed with expected error";
+      EXPECT_EQ(expected_error, actual_error);
+      if (!continue_writing) {
+        LOG(INFO) << "Cannot continue writing. Bailing out.";
+        break;
+      }
+    }
+
+    EXPECT_EQ(ErrorCode::kSuccess, actual_error);
+  }
+
+  // If we had continued all the way through, Close should succeed.
+  // Otherwise, it should fail. Check appropriately.
+  bool close_result = (*performer)->Close();
+  if (continue_writing)
+    EXPECT_EQ(0, close_result);
+  else
+    EXPECT_LE(0, close_result);
+}
+
+void VerifyPayloadResult(DeltaPerformer* performer,
+                         DeltaState* state,
+                         ErrorCode expected_result,
+                         uint32_t minor_version) {
+  if (!performer) {
+    EXPECT_TRUE(!"Skipping payload verification since performer is null.");
+    return;
+  }
+
+  int expected_times = (expected_result == ErrorCode::kSuccess) ? 1 : 0;
+  EXPECT_CALL(state->mock_delegate_, DownloadComplete()).Times(expected_times);
+
+  LOG(INFO) << "Verifying payload for expected result "
+            << expected_result;
+  EXPECT_EQ(expected_result, performer->VerifyPayload(
+      HashCalculator::HashOfData(state->delta),
+      state->delta.size()));
+  LOG(INFO) << "Verified payload.";
+
+  if (expected_result != ErrorCode::kSuccess) {
+    // no need to verify new partition if VerifyPayload failed.
+    return;
+  }
+
+  brillo::Blob updated_kernel_partition;
+  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()));
+
+  const auto& partitions = state->install_plan.partitions;
+  EXPECT_EQ(2, partitions.size());
+  EXPECT_EQ(kLegacyPartitionNameRoot, partitions[0].name);
+  EXPECT_EQ(kLegacyPartitionNameKernel, partitions[1].name);
+
+  EXPECT_EQ(kDefaultKernelSize, partitions[1].target_size);
+  brillo::Blob expected_new_kernel_hash;
+  EXPECT_TRUE(HashCalculator::RawHashOfData(state->new_kernel_data,
+                                            &expected_new_kernel_hash));
+  EXPECT_EQ(expected_new_kernel_hash, partitions[1].target_hash);
+
+  EXPECT_EQ(state->image_size, partitions[0].target_size);
+  brillo::Blob expected_new_rootfs_hash;
+  EXPECT_EQ(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);
+}
+
+void VerifyPayload(DeltaPerformer* performer,
+                   DeltaState* state,
+                   SignatureTest signature_test,
+                   uint32_t minor_version) {
+  ErrorCode expected_result = ErrorCode::kSuccess;
+  switch (signature_test) {
+    case kSignatureNone:
+      expected_result = ErrorCode::kSignedDeltaPayloadExpectedError;
+      break;
+    case kSignatureGeneratedShellBadKey:
+      expected_result = ErrorCode::kDownloadPayloadPubKeyVerificationError;
+      break;
+    default: break;  // appease gcc
+  }
+
+  VerifyPayloadResult(performer, state, expected_result, minor_version);
+}
+
+void DoSmallImageTest(bool full_kernel, bool full_rootfs, bool noop,
+                      ssize_t chunk_size,
+                      SignatureTest signature_test,
+                      bool hash_checks_mandatory, uint32_t minor_version) {
+  DeltaState state;
+  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, kValidOperationData,
+                 &performer, minor_version);
+  VerifyPayload(performer, &state, signature_test, minor_version);
+  delete performer;
+}
+
+void DoOperationHashMismatchTest(OperationHashTest op_hash_test,
+                                 bool hash_checks_mandatory) {
+  DeltaState state;
+  uint64_t minor_version = kFullPayloadMinorVersion;
+  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, op_hash_test, &performer,
+                 minor_version);
+  delete performer;
+}
+
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageTest) {
+  DoSmallImageTest(false, false, false, -1, kSignatureGenerator,
+                   false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignaturePlaceholderTest) {
+  DoSmallImageTest(false, false, false, -1, kSignatureGeneratedPlaceholder,
+                   false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignaturePlaceholderMismatchTest) {
+  DeltaState state;
+  GenerateDeltaFile(false, false, false, -1,
+                    kSignatureGeneratedPlaceholderMismatch, &state,
+                    kInPlaceMinorPayloadVersion);
+}
+
+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, 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(DeltaPerformerIntegrationTest, RunAsRootMandatoryOperationHashMismatchTest) {
+  DoOperationHashMismatchTest(kInvalidOperationData, true);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/delta_performer_unittest.cc b/payload_consumer/delta_performer_unittest.cc
new file mode 100644
index 0000000..8192632
--- /dev/null
+++ b/payload_consumer/delta_performer_unittest.cc
@@ -0,0 +1,788 @@
+//
+// 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_consumer/delta_performer.h"
+
+#include <inttypes.h>
+
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.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/fake_boot_control.h"
+#include "update_engine/common/fake_hardware.h"
+#include "update_engine/common/fake_prefs.h"
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/mock_download_action.h"
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_generator/bzip.h"
+#include "update_engine/payload_generator/extent_ranges.h"
+#include "update_engine/payload_generator/payload_file.h"
+#include "update_engine/payload_generator/payload_signer.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+using std::string;
+using std::vector;
+using test_utils::System;
+using test_utils::kRandomString;
+using testing::_;
+
+extern const char* kUnittestPrivateKeyPath;
+extern const char* kUnittestPublicKeyPath;
+
+static const char* kBogusMetadataSignature1 =
+    "awSFIUdUZz2VWFiR+ku0Pj00V7bPQPQFYQSXjEXr3vaw3TE4xHV5CraY3/YrZpBv"
+    "J5z4dSBskoeuaO1TNC/S6E05t+yt36tE4Fh79tMnJ/z9fogBDXWgXLEUyG78IEQr"
+    "YH6/eBsQGT2RJtBgXIXbZ9W+5G9KmGDoPOoiaeNsDuqHiBc/58OFsrxskH8E6vMS"
+    "BmMGGk82mvgzic7ApcoURbCGey1b3Mwne/hPZ/bb9CIyky8Og9IfFMdL2uAweOIR"
+    "fjoTeLYZpt+WN65Vu7jJ0cQN8e1y+2yka5112wpRf/LLtPgiAjEZnsoYpLUd7CoV"
+    "pLRtClp97kN2+tXGNBQqkA==";
+
+namespace {
+// Different options that determine what we should fill into the
+// install_plan.metadata_signature to simulate the contents received in the
+// Omaha response.
+enum MetadataSignatureTest {
+  kEmptyMetadataSignature,
+  kInvalidMetadataSignature,
+  kValidMetadataSignature,
+};
+
+// Compressed data without checksum, generated with:
+// echo -n a | xz -9 --check=none | hexdump -v -e '"    " 12/1 "0x%02x, " "\n"'
+const uint8_t kXzCompressedData[] = {
+    0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00, 0x00, 0xff, 0x12, 0xd9, 0x41,
+    0x02, 0x00, 0x21, 0x01, 0x1c, 0x00, 0x00, 0x00, 0x10, 0xcf, 0x58, 0xcc,
+    0x01, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x01,
+    0xad, 0xa6, 0x58, 0x04, 0x06, 0x72, 0x9e, 0x7a, 0x01, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x59, 0x5a,
+};
+
+}  // namespace
+
+class DeltaPerformerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    install_plan_.source_slot = 0;
+    install_plan_.target_slot = 1;
+    EXPECT_CALL(mock_delegate_, ShouldCancel(_))
+        .WillRepeatedly(testing::Return(false));
+  }
+
+  // Test helper placed where it can easily be friended from DeltaPerformer.
+  void RunManifestValidation(const DeltaArchiveManifest& manifest,
+                             uint64_t major_version,
+                             bool full_payload,
+                             ErrorCode expected) {
+    // The install plan is for Full or Delta.
+    install_plan_.is_full_update = full_payload;
+
+    // The Manifest we are validating.
+    performer_.manifest_.CopyFrom(manifest);
+    performer_.major_payload_version_ = major_version;
+
+    EXPECT_EQ(expected, performer_.ValidateManifest());
+  }
+
+  brillo::Blob GeneratePayload(const brillo::Blob& blob_data,
+                               const vector<AnnotatedOperation>& aops,
+                               bool sign_payload) {
+    return GeneratePayload(blob_data, aops, sign_payload,
+                           DeltaPerformer::kSupportedMajorPayloadVersion,
+                           DeltaPerformer::kSupportedMinorPayloadVersion);
+  }
+
+  brillo::Blob GeneratePayload(const brillo::Blob& blob_data,
+                               const vector<AnnotatedOperation>& aops,
+                               bool sign_payload,
+                               uint64_t major_version,
+                               uint32_t minor_version) {
+    string blob_path;
+    EXPECT_TRUE(utils::MakeTempFile("Blob-XXXXXX", &blob_path, nullptr));
+    ScopedPathUnlinker blob_unlinker(blob_path);
+    EXPECT_TRUE(utils::WriteFile(blob_path.c_str(),
+                                 blob_data.data(),
+                                 blob_data.size()));
+
+    PayloadGenerationConfig config;
+    config.major_version = major_version;
+    config.minor_version = minor_version;
+
+    PayloadFile payload;
+    EXPECT_TRUE(payload.Init(config));
+
+    PartitionConfig old_part(kLegacyPartitionNameRoot);
+    PartitionConfig new_part(kLegacyPartitionNameRoot);
+    new_part.path = "/dev/zero";
+    new_part.size = 1234;
+
+    payload.AddPartition(old_part, new_part, aops);
+
+    // We include a kernel partition without operations.
+    old_part.name = kLegacyPartitionNameKernel;
+    new_part.name = kLegacyPartitionNameKernel;
+    new_part.size = 0;
+    payload.AddPartition(old_part, new_part, {});
+
+    string payload_path;
+    EXPECT_TRUE(utils::MakeTempFile("Payload-XXXXXX", &payload_path, nullptr));
+    ScopedPathUnlinker payload_unlinker(payload_path);
+    EXPECT_TRUE(payload.WritePayload(payload_path, blob_path,
+        sign_payload ? kUnittestPrivateKeyPath : "",
+        &install_plan_.metadata_size));
+
+    brillo::Blob payload_data;
+    EXPECT_TRUE(utils::ReadFile(payload_path, &payload_data));
+    return payload_data;
+  }
+
+  // Apply |payload_data| on partition specified in |source_path|.
+  // Expect result of performer_.Write() to be |expect_success|.
+  // Returns the result of the payload application.
+  brillo::Blob ApplyPayload(const brillo::Blob& payload_data,
+                            const string& source_path,
+                            bool expect_success) {
+    return ApplyPayloadToData(payload_data, source_path, brillo::Blob(),
+                              expect_success);
+  }
+
+  // Apply the payload provided in |payload_data| reading from the |source_path|
+  // file and writing the contents to a new partition. The existing data in the
+  // 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(const brillo::Blob& payload_data,
+                                  const string& source_path,
+                                  const brillo::Blob& target_data,
+                                  bool expect_success) {
+    string new_part;
+    EXPECT_TRUE(utils::MakeTempFile("Partition-XXXXXX", &new_part, nullptr));
+    ScopedPathUnlinker partition_unlinker(new_part);
+    EXPECT_TRUE(utils::WriteFile(new_part.c_str(), target_data.data(),
+                                 target_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(
+        kLegacyPartitionNameRoot, install_plan_.target_slot, new_part);
+    fake_boot_control_.SetPartitionDevice(
+        kLegacyPartitionNameRoot, install_plan_.source_slot, source_path);
+    fake_boot_control_.SetPartitionDevice(
+        kLegacyPartitionNameKernel, install_plan_.target_slot, "/dev/null");
+    fake_boot_control_.SetPartitionDevice(
+        kLegacyPartitionNameKernel, install_plan_.source_slot, "/dev/null");
+
+    EXPECT_EQ(expect_success,
+              performer_.Write(payload_data.data(), payload_data.size()));
+    EXPECT_EQ(0, performer_.Close());
+
+    brillo::Blob partition_data;
+    EXPECT_TRUE(utils::ReadFile(new_part, &partition_data));
+    return partition_data;
+  }
+
+  // Calls delta performer's Write method by pretending to pass in bytes from a
+  // delta file whose metadata size is actual_metadata_size and tests if all
+  // checks are correctly performed if the install plan contains
+  // expected_metadata_size and that the result of the parsing are as per
+  // hash_checks_mandatory flag.
+  void DoMetadataSizeTest(uint64_t expected_metadata_size,
+                          uint64_t actual_metadata_size,
+                          bool hash_checks_mandatory) {
+    install_plan_.hash_checks_mandatory = hash_checks_mandatory;
+
+    // Set a valid magic string and version number 1.
+    EXPECT_TRUE(performer_.Write("CrAU", 4));
+    uint64_t version = htobe64(kChromeOSMajorPayloadVersion);
+    EXPECT_TRUE(performer_.Write(&version, 8));
+
+    install_plan_.metadata_size = expected_metadata_size;
+    ErrorCode 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);
+    } else {
+      EXPECT_FALSE(result);
+      EXPECT_EQ(ErrorCode::kDownloadInvalidMetadataSize, error_code);
+    }
+
+    EXPECT_LT(performer_.Close(), 0);
+  }
+
+  // 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,
+                               bool sign_payload,
+                               bool hash_checks_mandatory) {
+
+    // Loads the payload and parses the manifest.
+    brillo::Blob payload = GeneratePayload(brillo::Blob(),
+        vector<AnnotatedOperation>(), sign_payload,
+        kChromeOSMajorPayloadVersion, kFullPayloadMinorVersion);
+
+    LOG(INFO) << "Payload size: " << payload.size();
+
+    install_plan_.hash_checks_mandatory = hash_checks_mandatory;
+
+    DeltaPerformer::MetadataParseResult expected_result, actual_result;
+    ErrorCode expected_error, actual_error;
+
+    // Fill up the metadata signature in install plan according to the test.
+    switch (metadata_signature_test) {
+      case kEmptyMetadataSignature:
+        install_plan_.metadata_signature.clear();
+        expected_result = DeltaPerformer::kMetadataParseError;
+        expected_error = ErrorCode::kDownloadMetadataSignatureMissingError;
+        break;
+
+      case kInvalidMetadataSignature:
+        install_plan_.metadata_signature = kBogusMetadataSignature1;
+        expected_result = DeltaPerformer::kMetadataParseError;
+        expected_error = ErrorCode::kDownloadMetadataSignatureMismatch;
+        break;
+
+      case kValidMetadataSignature:
+      default:
+        // Set the install plan's metadata size to be the same as the one
+        // in the manifest so that we pass the metadata size checks. Only
+        // then we can get to manifest signature checks.
+        ASSERT_TRUE(PayloadSigner::GetMetadataSignature(
+            payload.data(),
+            install_plan_.metadata_size,
+            kUnittestPrivateKeyPath,
+            &install_plan_.metadata_signature));
+        EXPECT_FALSE(install_plan_.metadata_signature.empty());
+        expected_result = DeltaPerformer::kMetadataParseSuccess;
+        expected_error = ErrorCode::kSuccess;
+        break;
+    }
+
+    // Ignore the expected result/error if hash checks are not mandatory.
+    if (!hash_checks_mandatory) {
+      expected_result = DeltaPerformer::kMetadataParseSuccess;
+      expected_error = ErrorCode::kSuccess;
+    }
+
+    // Use the public key corresponding to the private key used above to
+    // sign the metadata.
+    EXPECT_TRUE(utils::FileExists(kUnittestPublicKeyPath));
+    performer_.set_public_key_path(kUnittestPublicKeyPath);
+
+    // Init actual_error with an invalid value so that we make sure
+    // ParsePayloadMetadata properly populates it in all cases.
+    actual_error = ErrorCode::kUmaReportedMax;
+    actual_result = performer_.ParsePayloadMetadata(payload, &actual_error);
+
+    EXPECT_EQ(expected_result, actual_result);
+    EXPECT_EQ(expected_error, actual_error);
+
+    // Check that the parsed metadata size is what's expected. This test
+    // implicitly confirms that the metadata signature is valid, if required.
+    EXPECT_EQ(install_plan_.metadata_size, performer_.GetMetadataSize());
+  }
+
+  void SetSupportedMajorVersion(uint64_t major_version) {
+    performer_.supported_major_version_ = major_version;
+  }
+  FakePrefs prefs_;
+  InstallPlan install_plan_;
+  FakeBootControl fake_boot_control_;
+  FakeHardware fake_hardware_;
+  MockDownloadActionDelegate mock_delegate_;
+  DeltaPerformer performer_{
+      &prefs_, &fake_boot_control_, &fake_hardware_, &mock_delegate_, &install_plan_};
+};
+
+TEST_F(DeltaPerformerTest, FullPayloadWriteTest) {
+  install_plan_.is_full_update = true;
+  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,
+      kChromeOSMajorPayloadVersion, kFullPayloadMinorVersion);
+
+  EXPECT_EQ(expected_data, ApplyPayload(payload_data, "/dev/null", true));
+}
+
+TEST_F(DeltaPerformerTest, ShouldCancelTest) {
+  install_plan_.is_full_update = true;
+  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,
+      kChromeOSMajorPayloadVersion, kFullPayloadMinorVersion);
+
+  testing::Mock::VerifyAndClearExpectations(&mock_delegate_);
+  EXPECT_CALL(mock_delegate_, ShouldCancel(_))
+      .WillOnce(
+          testing::DoAll(testing::SetArgumentPointee<0>(ErrorCode::kError),
+                         testing::Return(true)));
+
+  ApplyPayload(payload_data, "/dev/null", false);
+}
+
+TEST_F(DeltaPerformerTest, ReplaceOperationTest) {
+  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);
+
+  EXPECT_EQ(expected_data, ApplyPayload(payload_data, "/dev/null", true));
+}
+
+TEST_F(DeltaPerformerTest, ReplaceBzOperationTest) {
+  brillo::Blob expected_data = brillo::Blob(std::begin(kRandomString),
+                                            std::end(kRandomString));
+  expected_data.resize(4096);  // block size
+  brillo::Blob bz_data;
+  EXPECT_TRUE(BzipCompress(expected_data, &bz_data));
+
+  vector<AnnotatedOperation> aops;
+  AnnotatedOperation aop;
+  *(aop.op.add_dst_extents()) = ExtentForRange(0, 1);
+  aop.op.set_data_offset(0);
+  aop.op.set_data_length(bz_data.size());
+  aop.op.set_type(InstallOperation::REPLACE_BZ);
+  aops.push_back(aop);
+
+  brillo::Blob payload_data = GeneratePayload(bz_data, aops, false);
+
+  EXPECT_EQ(expected_data, ApplyPayload(payload_data, "/dev/null", true));
+}
+
+TEST_F(DeltaPerformerTest, ReplaceXzOperationTest) {
+  brillo::Blob xz_data(std::begin(kXzCompressedData),
+                         std::end(kXzCompressedData));
+  // The compressed xz data contains only a single "a", but the operation should
+  // pad the rest of the two blocks with zeros.
+  brillo::Blob expected_data = brillo::Blob(4096, 0);
+  expected_data[0] = 'a';
+
+  AnnotatedOperation aop;
+  *(aop.op.add_dst_extents()) = ExtentForRange(0, 1);
+  aop.op.set_data_offset(0);
+  aop.op.set_data_length(xz_data.size());
+  aop.op.set_type(InstallOperation::REPLACE_XZ);
+  vector<AnnotatedOperation> aops = {aop};
+
+  brillo::Blob payload_data = GeneratePayload(xz_data, aops, false);
+
+  EXPECT_EQ(expected_data, ApplyPayload(payload_data, "/dev/null", true));
+}
+
+TEST_F(DeltaPerformerTest, ZeroOperationTest) {
+  brillo::Blob existing_data = brillo::Blob(4096 * 10, 'a');
+  brillo::Blob expected_data = existing_data;
+  // Blocks 4, 5 and 7 should have zeros instead of 'a' after the operation is
+  // applied.
+  std::fill(expected_data.data() + 4096 * 4, expected_data.data() + 4096 * 6,
+            0);
+  std::fill(expected_data.data() + 4096 * 7, expected_data.data() + 4096 * 8,
+            0);
+
+  AnnotatedOperation aop;
+  *(aop.op.add_dst_extents()) = ExtentForRange(4, 2);
+  *(aop.op.add_dst_extents()) = ExtentForRange(7, 1);
+  aop.op.set_type(InstallOperation::ZERO);
+  vector<AnnotatedOperation> aops = {aop};
+
+  brillo::Blob payload_data = GeneratePayload(brillo::Blob(), aops, false);
+
+  EXPECT_EQ(expected_data,
+            ApplyPayloadToData(payload_data, "/dev/null", existing_data, true));
+}
+
+TEST_F(DeltaPerformerTest, SourceCopyOperationTest) {
+  brillo::Blob expected_data(std::begin(kRandomString),
+                             std::end(kRandomString));
+  expected_data.resize(4096);  // block size
+  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;
+  EXPECT_TRUE(HashCalculator::RawHashOfData(expected_data, &src_hash));
+  aop.op.set_src_sha256_hash(src_hash.data(), src_hash.size());
+
+  brillo::Blob payload_data = GeneratePayload(brillo::Blob(), {aop}, false);
+
+  string source_path;
+  EXPECT_TRUE(utils::MakeTempFile("Source-XXXXXX",
+                                  &source_path, nullptr));
+  ScopedPathUnlinker path_unlinker(source_path);
+  EXPECT_TRUE(utils::WriteFile(source_path.c_str(),
+                               expected_data.data(),
+                               expected_data.size()));
+
+  EXPECT_EQ(expected_data, ApplyPayload(payload_data, source_path, true));
+}
+
+TEST_F(DeltaPerformerTest, SourceHashMismatchTest) {
+  brillo::Blob expected_data = {'f', 'o', 'o'};
+  brillo::Blob actual_data = {'b', 'a', 'r'};
+  expected_data.resize(4096);  // block size
+  actual_data.resize(4096);    // block size
+
+  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;
+  EXPECT_TRUE(HashCalculator::RawHashOfData(expected_data, &src_hash));
+  aop.op.set_src_sha256_hash(src_hash.data(), src_hash.size());
+
+  brillo::Blob payload_data = GeneratePayload(brillo::Blob(), {aop}, false);
+
+  string source_path;
+  EXPECT_TRUE(utils::MakeTempFile("Source-XXXXXX", &source_path, nullptr));
+  ScopedPathUnlinker path_unlinker(source_path);
+  EXPECT_TRUE(utils::WriteFile(source_path.c_str(), actual_data.data(),
+                               actual_data.size()));
+
+  EXPECT_EQ(actual_data, ApplyPayload(payload_data, source_path, false));
+}
+
+TEST_F(DeltaPerformerTest, ExtentsToByteStringTest) {
+  uint64_t test[] = {1, 1, 4, 2, 0, 1};
+  COMPILE_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 < arraysize(test); i += 2) {
+    *(extents.Add()) = ExtentForRange(test[i], test[i + 1]);
+  }
+
+  string expected_output = "4096:4096,16384:8192,0:4083";
+  string actual_output;
+  EXPECT_TRUE(DeltaPerformer::ExtentsToBsdiffPositionsString(extents,
+                                                             block_size,
+                                                             file_length,
+                                                             &actual_output));
+  EXPECT_EQ(expected_output, actual_output);
+}
+
+TEST_F(DeltaPerformerTest, ValidateManifestFullGoodTest) {
+  // The Manifest we are validating.
+  DeltaArchiveManifest manifest;
+  manifest.mutable_new_kernel_info();
+  manifest.mutable_new_rootfs_info();
+  manifest.set_minor_version(kFullPayloadMinorVersion);
+
+  RunManifestValidation(manifest, kChromeOSMajorPayloadVersion, true,
+                        ErrorCode::kSuccess);
+}
+
+TEST_F(DeltaPerformerTest, ValidateManifestDeltaGoodTest) {
+  // The Manifest we are validating.
+  DeltaArchiveManifest manifest;
+  manifest.mutable_old_kernel_info();
+  manifest.mutable_old_rootfs_info();
+  manifest.mutable_new_kernel_info();
+  manifest.mutable_new_rootfs_info();
+  manifest.set_minor_version(DeltaPerformer::kSupportedMinorPayloadVersion);
+
+  RunManifestValidation(manifest, kChromeOSMajorPayloadVersion, false,
+                        ErrorCode::kSuccess);
+}
+
+TEST_F(DeltaPerformerTest, ValidateManifestFullUnsetMinorVersion) {
+  // The Manifest we are validating.
+  DeltaArchiveManifest manifest;
+
+  RunManifestValidation(manifest, DeltaPerformer::kSupportedMajorPayloadVersion,
+                        true, ErrorCode::kSuccess);
+}
+
+TEST_F(DeltaPerformerTest, ValidateManifestDeltaUnsetMinorVersion) {
+  // The Manifest we are validating.
+  DeltaArchiveManifest manifest;
+
+  RunManifestValidation(manifest, DeltaPerformer::kSupportedMajorPayloadVersion,
+                        false, ErrorCode::kUnsupportedMinorPayloadVersion);
+}
+
+TEST_F(DeltaPerformerTest, ValidateManifestFullOldKernelTest) {
+  // The Manifest we are validating.
+  DeltaArchiveManifest manifest;
+  manifest.mutable_old_kernel_info();
+  manifest.mutable_new_kernel_info();
+  manifest.mutable_new_rootfs_info();
+  manifest.set_minor_version(DeltaPerformer::kSupportedMinorPayloadVersion);
+
+  RunManifestValidation(manifest, kChromeOSMajorPayloadVersion, true,
+                        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(DeltaPerformer::kSupportedMinorPayloadVersion);
+
+  RunManifestValidation(manifest, kChromeOSMajorPayloadVersion, true,
+                        ErrorCode::kPayloadMismatchedType);
+}
+
+TEST_F(DeltaPerformerTest, ValidateManifestFullPartitionUpdateTest) {
+  // The Manifest we are validating.
+  DeltaArchiveManifest manifest;
+  PartitionUpdate* partition = manifest.add_partitions();
+  partition->mutable_old_partition_info();
+  partition->mutable_new_partition_info();
+  manifest.set_minor_version(DeltaPerformer::kSupportedMinorPayloadVersion);
+
+  RunManifestValidation(manifest, kBrilloMajorPayloadVersion, true,
+                        ErrorCode::kPayloadMismatchedType);
+}
+
+TEST_F(DeltaPerformerTest, ValidateManifestBadMinorVersion) {
+  // The Manifest we are validating.
+  DeltaArchiveManifest manifest;
+
+  // Generate a bad version number.
+  manifest.set_minor_version(DeltaPerformer::kSupportedMinorPayloadVersion +
+                             10000);
+
+  RunManifestValidation(manifest, DeltaPerformer::kSupportedMajorPayloadVersion,
+                        false, ErrorCode::kUnsupportedMinorPayloadVersion);
+}
+
+TEST_F(DeltaPerformerTest, BrilloMetadataSignatureSizeTest) {
+  EXPECT_TRUE(performer_.Write(kDeltaMagic, sizeof(kDeltaMagic)));
+
+  uint64_t major_version = htobe64(kBrilloMajorPayloadVersion);
+  EXPECT_TRUE(performer_.Write(&major_version, 8));
+
+  uint64_t manifest_size = rand() % 256;
+  uint64_t manifest_size_be = htobe64(manifest_size);
+  EXPECT_TRUE(performer_.Write(&manifest_size_be, 8));
+
+  uint32_t metadata_signature_size = rand() % 256;
+  uint32_t metadata_signature_size_be = htobe32(metadata_signature_size);
+  EXPECT_TRUE(performer_.Write(&metadata_signature_size_be, 4));
+
+  EXPECT_LT(performer_.Close(), 0);
+
+  EXPECT_TRUE(performer_.IsHeaderParsed());
+  EXPECT_EQ(kBrilloMajorPayloadVersion, performer_.GetMajorVersion());
+  uint64_t manifest_offset;
+  EXPECT_TRUE(performer_.GetManifestOffset(&manifest_offset));
+  EXPECT_EQ(24, manifest_offset);  // 4 + 8 + 8 + 4
+  EXPECT_EQ(manifest_offset + manifest_size, performer_.GetMetadataSize());
+  EXPECT_EQ(metadata_signature_size, performer_.metadata_signature_size_);
+}
+
+TEST_F(DeltaPerformerTest, BrilloVerifyMetadataSignatureTest) {
+  brillo::Blob payload_data = GeneratePayload({}, {}, true,
+                                              kBrilloMajorPayloadVersion,
+                                              kSourceMinorPayloadVersion);
+  install_plan_.hash_checks_mandatory = true;
+  // Just set these value so that we can use ValidateMetadataSignature directly.
+  performer_.major_payload_version_ = kBrilloMajorPayloadVersion;
+  performer_.metadata_size_ = install_plan_.metadata_size;
+  uint64_t signature_length;
+  EXPECT_TRUE(PayloadSigner::SignatureBlobLength({kUnittestPrivateKeyPath},
+                                                 &signature_length));
+  performer_.metadata_signature_size_ = signature_length;
+  performer_.set_public_key_path(kUnittestPublicKeyPath);
+  EXPECT_EQ(ErrorCode::kSuccess,
+            performer_.ValidateMetadataSignature(payload_data));
+}
+
+TEST_F(DeltaPerformerTest, BadDeltaMagicTest) {
+  EXPECT_TRUE(performer_.Write("junk", 4));
+  EXPECT_FALSE(performer_.Write("morejunk", 8));
+  EXPECT_LT(performer_.Close(), 0);
+}
+
+TEST_F(DeltaPerformerTest, MissingMandatoryMetadataSizeTest) {
+  DoMetadataSizeTest(0, 75456, true);
+}
+
+TEST_F(DeltaPerformerTest, MissingNonMandatoryMetadataSizeTest) {
+  DoMetadataSizeTest(0, 123456, false);
+}
+
+TEST_F(DeltaPerformerTest, InvalidMandatoryMetadataSizeTest) {
+  DoMetadataSizeTest(13000, 140000, true);
+}
+
+TEST_F(DeltaPerformerTest, InvalidNonMandatoryMetadataSizeTest) {
+  DoMetadataSizeTest(40000, 50000, false);
+}
+
+TEST_F(DeltaPerformerTest, ValidMandatoryMetadataSizeTest) {
+  DoMetadataSizeTest(85376, 85376, true);
+}
+
+TEST_F(DeltaPerformerTest, MandatoryEmptyMetadataSignatureTest) {
+  DoMetadataSignatureTest(kEmptyMetadataSignature, true, true);
+}
+
+TEST_F(DeltaPerformerTest, NonMandatoryEmptyMetadataSignatureTest) {
+  DoMetadataSignatureTest(kEmptyMetadataSignature, true, false);
+}
+
+TEST_F(DeltaPerformerTest, MandatoryInvalidMetadataSignatureTest) {
+  DoMetadataSignatureTest(kInvalidMetadataSignature, true, true);
+}
+
+TEST_F(DeltaPerformerTest, NonMandatoryInvalidMetadataSignatureTest) {
+  DoMetadataSignatureTest(kInvalidMetadataSignature, true, false);
+}
+
+TEST_F(DeltaPerformerTest, MandatoryValidMetadataSignature1Test) {
+  DoMetadataSignatureTest(kValidMetadataSignature, false, true);
+}
+
+TEST_F(DeltaPerformerTest, MandatoryValidMetadataSignature2Test) {
+  DoMetadataSignatureTest(kValidMetadataSignature, true, true);
+}
+
+TEST_F(DeltaPerformerTest, NonMandatoryValidMetadataSignatureTest) {
+  DoMetadataSignatureTest(kValidMetadataSignature, true, false);
+}
+
+TEST_F(DeltaPerformerTest, UsePublicKeyFromResponse) {
+  base::FilePath key_path;
+
+  // The result of the GetPublicKeyResponse() method is based on three things
+  //
+  //  1. Whether it's an official build; and
+  //  2. Whether the Public RSA key to be used is in the root filesystem; and
+  //  3. Whether the response has a public key
+  //
+  // We test all eight combinations to ensure that we only use the
+  // public key in the response if
+  //
+  //  a. it's not an official build; and
+  //  b. there is no key in the root filesystem.
+
+  string temp_dir;
+  EXPECT_TRUE(utils::MakeTempDirectory("PublicKeyFromResponseTests.XXXXXX",
+                                       &temp_dir));
+  string non_existing_file = temp_dir + "/non-existing";
+  string existing_file = temp_dir + "/existing";
+  EXPECT_EQ(0, System(base::StringPrintf("touch %s", existing_file.c_str())));
+
+  // Non-official build, non-existing public-key, key in response -> true
+  fake_hardware_.SetIsOfficialBuild(false);
+  performer_.public_key_path_ = non_existing_file;
+  install_plan_.public_key_rsa = "VGVzdAo="; // result of 'echo "Test" | base64'
+  EXPECT_TRUE(performer_.GetPublicKeyFromResponse(&key_path));
+  EXPECT_FALSE(key_path.empty());
+  EXPECT_EQ(unlink(key_path.value().c_str()), 0);
+  // Same with official build -> false
+  fake_hardware_.SetIsOfficialBuild(true);
+  EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
+
+  // Non-official build, existing public-key, key in response -> false
+  fake_hardware_.SetIsOfficialBuild(false);
+  performer_.public_key_path_ = existing_file;
+  install_plan_.public_key_rsa = "VGVzdAo="; // result of 'echo "Test" | base64'
+  EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
+  // Same with official build -> false
+  fake_hardware_.SetIsOfficialBuild(true);
+  EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
+
+  // Non-official build, non-existing public-key, no key in response -> false
+  fake_hardware_.SetIsOfficialBuild(false);
+  performer_.public_key_path_ = non_existing_file;
+  install_plan_.public_key_rsa = "";
+  EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
+  // Same with official build -> false
+  fake_hardware_.SetIsOfficialBuild(true);
+  EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
+
+  // Non-official build, existing public-key, no key in response -> false
+  fake_hardware_.SetIsOfficialBuild(false);
+  performer_.public_key_path_ = existing_file;
+  install_plan_.public_key_rsa = "";
+  EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
+  // Same with official build -> false
+  fake_hardware_.SetIsOfficialBuild(true);
+  EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
+
+  // Non-official build, non-existing public-key, key in response
+  // but invalid base64 -> false
+  fake_hardware_.SetIsOfficialBuild(false);
+  performer_.public_key_path_ = non_existing_file;
+  install_plan_.public_key_rsa = "not-valid-base64";
+  EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
+
+  EXPECT_TRUE(base::DeleteFile(base::FilePath(temp_dir), true));
+}
+
+TEST_F(DeltaPerformerTest, ConfVersionsMatch) {
+  // Test that the versions in update_engine.conf that is installed to the
+  // image match the supported delta versions in the update engine.
+  uint32_t minor_version;
+  brillo::KeyValueStore store;
+  EXPECT_TRUE(store.Load(base::FilePath("update_engine.conf")));
+  EXPECT_TRUE(utils::GetMinorVersion(store, &minor_version));
+  EXPECT_EQ(DeltaPerformer::kSupportedMinorPayloadVersion, minor_version);
+
+  string major_version_str;
+  uint64_t major_version;
+  EXPECT_TRUE(store.GetString("PAYLOAD_MAJOR_VERSION", &major_version_str));
+  EXPECT_TRUE(base::StringToUint64(major_version_str, &major_version));
+  EXPECT_EQ(DeltaPerformer::kSupportedMajorPayloadVersion, major_version);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/download_action.cc b/payload_consumer/download_action.cc
new file mode 100644
index 0000000..cb404b8
--- /dev/null
+++ b/payload_consumer/download_action.cc
@@ -0,0 +1,318 @@
+//
+// 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/payload_consumer/download_action.h"
+
+#include <errno.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.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/utils.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;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+DownloadAction::DownloadAction(PrefsInterface* prefs,
+                               SystemState* system_state,
+                               HttpFetcher* http_fetcher)
+    : prefs_(prefs),
+      system_state_(system_state),
+      http_fetcher_(http_fetcher),
+      writer_(nullptr),
+      code_(ErrorCode::kSuccess),
+      delegate_(nullptr),
+      bytes_received_(0),
+      p2p_sharing_fd_(-1),
+      p2p_visible_(true) {}
+
+DownloadAction::~DownloadAction() {}
+
+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";
+    }
+    p2p_sharing_fd_ = -1;
+  }
+
+  if (delete_p2p_file) {
+    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 {
+      LOG(INFO) << "Deleted p2p file " << path.value();
+    }
+  }
+
+  // Don't use p2p from this point onwards.
+  p2p_file_id_.clear();
+}
+
+bool DownloadAction::SetupP2PSharingFd() {
+  P2PManager *p2p_manager = system_state_->p2p_manager();
+
+  if (!p2p_manager->FileShare(p2p_file_id_, install_plan_.payload_size)) {
+    LOG(ERROR) << "Unable to share file via p2p";
+    CloseP2PSharingFd(true);  // delete p2p file
+    return false;
+  }
+
+  // File has already been created (and allocated, xattrs been
+  // populated etc.) by FileShare() so just open it for writing.
+  FilePath path = p2p_manager->FileGetPath(p2p_file_id_);
+  p2p_sharing_fd_ = open(path.value().c_str(), O_WRONLY);
+  if (p2p_sharing_fd_ == -1) {
+    PLOG(ERROR) << "Error opening file " << path.value();
+    CloseP2PSharingFd(true);  // Delete p2p file.
+    return false;
+  }
+
+  // Ensure file to share is world-readable, otherwise
+  // p2p-server and p2p-http-server can't access it.
+  //
+  // (Q: Why doesn't the file have mode 0644 already? A: Because
+  // the process-wide umask is set to 0700 in main.cc.)
+  if (fchmod(p2p_sharing_fd_, 0644) != 0) {
+    PLOG(ERROR) << "Error setting mode 0644 on " << path.value();
+    CloseP2PSharingFd(true);  // Delete p2p file.
+    return false;
+  }
+
+  // All good.
+  LOG(INFO) << "Writing payload contents to " << path.value();
+  p2p_manager->FileGetVisible(p2p_file_id_, &p2p_visible_);
+  return true;
+}
+
+void DownloadAction::WriteToP2PFile(const void* data,
+                                    size_t length,
+                                    off_t file_offset) {
+  if (p2p_sharing_fd_ == -1) {
+    if (!SetupP2PSharingFd())
+      return;
+  }
+
+  // Check that the file is at least |file_offset| bytes long - if
+  // it's not something is wrong and we must immediately delete the
+  // file to avoid propagating this problem to other peers.
+  //
+  // How can this happen? It could be that we're resuming an update
+  // after a system crash... in this case, it could be that
+  //
+  //  1. the p2p file didn't get properly synced to stable storage; or
+  //  2. the file was deleted at bootup (it's in /var/cache after all); or
+  //  3. other reasons
+  off_t p2p_size = utils::FileSize(p2p_sharing_fd_);
+  if (p2p_size < 0) {
+    PLOG(ERROR) << "Error getting file status for p2p file";
+    CloseP2PSharingFd(true);  // Delete p2p file.
+    return;
+  }
+  if (p2p_size < file_offset) {
+    LOG(ERROR) << "Wanting to write to file offset " << file_offset
+               << " but existing p2p file is only " << p2p_size
+               << " bytes.";
+    CloseP2PSharingFd(true);  // Delete p2p file.
+    return;
+  }
+
+  off_t cur_file_offset = lseek(p2p_sharing_fd_, file_offset, SEEK_SET);
+  if (cur_file_offset != static_cast<off_t>(file_offset)) {
+    PLOG(ERROR) << "Error seeking to position "
+                << file_offset << " in p2p file";
+    CloseP2PSharingFd(true);  // Delete p2p file.
+  } else {
+    // OK, seeking worked, now write the data
+    ssize_t bytes_written = write(p2p_sharing_fd_, data, length);
+    if (bytes_written != static_cast<ssize_t>(length)) {
+      PLOG(ERROR) << "Error writing "
+                  << length << " bytes at file offset "
+                  << file_offset << " in p2p file";
+      CloseP2PSharingFd(true);  // Delete p2p file.
+    }
+  }
+}
+
+void DownloadAction::PerformAction() {
+  http_fetcher_->set_delegate(this);
+
+  // Get the InstallPlan and read it
+  CHECK(HasInputObject());
+  install_plan_ = GetInputObject();
+  bytes_received_ = 0;
+
+  install_plan_.Dump();
+
+  LOG(INFO) << "Marking new slot as unbootable";
+  if (!system_state_->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.";
+  }
+
+  if (writer_) {
+    LOG(INFO) << "Using writer for test.";
+  } else {
+    delta_performer_.reset(new DeltaPerformer(prefs_,
+                                              system_state_->boot_control(),
+                                              system_state_->hardware(),
+                                              delegate_,
+                                              &install_plan_));
+    writer_ = delta_performer_.get();
+  }
+  download_active_= true;
+
+  if (system_state_ != nullptr) {
+    const PayloadStateInterface* payload_state = system_state_->payload_state();
+    string file_id = utils::CalculateP2PFileId(install_plan_.payload_hash,
+                                               install_plan_.payload_size);
+    if (payload_state->GetUsingP2PForSharing()) {
+      // If we're sharing the update, store the file_id to convey
+      // that we should write to the file.
+      p2p_file_id_ = file_id;
+      LOG(INFO) << "p2p file id: " << p2p_file_id_;
+    } else {
+      // Even if we're not sharing the update, it could be that
+      // there's a partial file from a previous attempt with the same
+      // 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 = 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();
+        } else {
+          LOG(INFO) << "Deleting partial p2p file " << path.value()
+                    << " since we're not using p2p to share.";
+        }
+      }
+    }
+
+    // Tweak timeouts on the HTTP fetcher if we're downloading from a
+    // local peer.
+    if (payload_state->GetUsingP2PForDownloading() &&
+        payload_state->GetP2PUrl() == install_plan_.download_url) {
+      LOG(INFO) << "Tweaking HTTP fetcher since we're downloading via p2p";
+      http_fetcher_->set_low_speed_limit(kDownloadP2PLowSpeedLimitBps,
+                                         kDownloadP2PLowSpeedTimeSeconds);
+      http_fetcher_->set_max_retry_count(kDownloadP2PMaxRetryCount);
+      http_fetcher_->set_connect_timeout(kDownloadP2PConnectTimeoutSeconds);
+    }
+  }
+
+  http_fetcher_->BeginTransfer(install_plan_.download_url);
+}
+
+void DownloadAction::TerminateProcessing() {
+  if (writer_) {
+    writer_->Close();
+    writer_ = nullptr;
+  }
+  download_active_= false;
+  CloseP2PSharingFd(false);  // Keep p2p file.
+  // 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;
+}
+
+void 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_);
+  }
+
+  bytes_received_ += length;
+  if (delegate_ && download_active_) {
+    delegate_->BytesReceived(
+        length, bytes_received_, install_plan_.payload_size);
+  }
+  if (writer_ && !writer_->Write(bytes, length, &code_)) {
+    LOG(ERROR) << "Error " << code_ << " in DeltaPerformer's Write method when "
+               << "processing the received payload -- Terminating processing";
+    // Delete p2p file, if applicable.
+    if (!p2p_file_id_.empty())
+      CloseP2PSharingFd(true);
+    // 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;
+  }
+
+  // Call p2p_manager_->FileMakeVisible() when we've successfully
+  // verified the manifest!
+  if (!p2p_visible_ &&
+      delta_performer_.get() && delta_performer_->IsManifestValid()) {
+    LOG(INFO) << "Manifest has been validated. Making p2p file visible.";
+    system_state_->p2p_manager()->FileMakeVisible(p2p_file_id_);
+    p2p_visible_ = true;
+  }
+}
+
+void DownloadAction::TransferComplete(HttpFetcher* fetcher, bool successful) {
+  if (writer_) {
+    LOG_IF(WARNING, writer_->Close() != 0) << "Error closing the writer.";
+    writer_ = nullptr;
+  }
+  download_active_= false;
+  ErrorCode code =
+      successful ? ErrorCode::kSuccess : ErrorCode::kDownloadTransferError;
+  if (code == ErrorCode::kSuccess && delta_performer_.get()) {
+    code = delta_performer_->VerifyPayload(install_plan_.payload_hash,
+                                           install_plan_.payload_size);
+    if (code != ErrorCode::kSuccess) {
+      LOG(ERROR) << "Download of " << install_plan_.download_url
+                 << " failed due to payload verification error.";
+      // Delete p2p file, if applicable.
+      if (!p2p_file_id_.empty())
+        CloseP2PSharingFd(true);
+    }
+  }
+
+  // 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_);
+  }
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/download_action.h b/payload_consumer/download_action.h
new file mode 100644
index 0000000..300d97e
--- /dev/null
+++ b/payload_consumer/download_action.h
@@ -0,0 +1,175 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_PAYLOAD_CONSUMER_DOWNLOAD_ACTION_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_DOWNLOAD_ACTION_H_
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+
+#include <curl/curl.h>
+
+#include "update_engine/common/action.h"
+#include "update_engine/common/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
+// DeltaPerformer that will apply the delta to the disk.
+
+namespace chromeos_update_engine {
+
+class DownloadActionDelegate {
+ public:
+  virtual ~DownloadActionDelegate() = default;
+
+  // Called periodically after bytes are received. This method will be invoked
+  // only if the DownloadAction is running. |bytes_progressed| is the number of
+  // bytes downloaded since the last call of this method, |bytes_received|
+  // the number of bytes downloaded thus far and |total| is the number of bytes
+  // expected.
+  virtual void BytesReceived(uint64_t bytes_progressed,
+                             uint64_t bytes_received,
+                             uint64_t total) = 0;
+
+  // Returns whether the download should be canceled, in which case the
+  // |cancel_reason| error should be set to the reason why the download was
+  // canceled.
+  virtual bool ShouldCancel(ErrorCode* cancel_reason) = 0;
+
+  // Called once the complete payload has been downloaded. Note that any errors
+  // while applying or downloading the partial payload will result in this
+  // method not being called.
+  virtual void DownloadComplete() = 0;
+};
+
+class PrefsInterface;
+
+class DownloadAction : public InstallPlanAction,
+                       public HttpFetcherDelegate {
+ public:
+  // Takes ownership of the passed in HttpFetcher. Useful for testing.
+  // A good calling pattern is:
+  // DownloadAction(prefs, system_state, new WhateverHttpFetcher);
+  DownloadAction(PrefsInterface* prefs,
+                 SystemState* system_state,
+                 HttpFetcher* http_fetcher);
+  ~DownloadAction() override;
+  void PerformAction() override;
+  void TerminateProcessing() override;
+
+  // Testing
+  void SetTestFileWriter(FileWriter* writer) {
+    writer_ = writer;
+  }
+
+  int GetHTTPResponseCode() { return http_fetcher_->http_response_code(); }
+
+  // Debugging/logging
+  static std::string StaticType() { return "DownloadAction"; }
+  std::string Type() const override { return StaticType(); }
+
+  // HttpFetcherDelegate methods (see http_fetcher.h)
+  void 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;
+  }
+
+  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 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);
+
+  // The InstallPlan passed in
+  InstallPlan install_plan_;
+
+  // Update Engine preference store.
+  PrefsInterface* prefs_;
+
+  // Global context for the system.
+  SystemState* system_state_;
+
+  // Pointer to the HttpFetcher that does the http work.
+  std::unique_ptr<HttpFetcher> http_fetcher_;
+
+  // 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_;
+  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_;
+
+  DISALLOW_COPY_AND_ASSIGN(DownloadAction);
+};
+
+// We want to be sure that we're compiled with large file support on linux,
+// just in case we find ourselves downloading large images.
+COMPILE_ASSERT(8 == sizeof(off_t), off_t_not_64_bit);
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_DOWNLOAD_ACTION_H_
diff --git a/payload_consumer/download_action_unittest.cc b/payload_consumer/download_action_unittest.cc
new file mode 100644
index 0000000..08053bf
--- /dev/null
+++ b/payload_consumer/download_action_unittest.cc
@@ -0,0 +1,626 @@
+//
+// 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/payload_consumer/download_action.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.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/location.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/bind_lambda.h>
+#include <brillo/message_loops/fake_message_loop.h>
+#include <brillo/message_loops/message_loop.h>
+
+#include "update_engine/common/action_pipe.h"
+#include "update_engine/common/hash_calculator.h"
+#include "update_engine/common/mock_http_fetcher.h"
+#include "update_engine/common/mock_prefs.h"
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/fake_p2p_manager_configuration.h"
+#include "update_engine/fake_system_state.h"
+#include "update_engine/payload_consumer/mock_download_action.h"
+#include "update_engine/update_manager/fake_update_manager.h"
+
+namespace chromeos_update_engine {
+
+using base::FilePath;
+using base::ReadFileToString;
+using base::WriteFile;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+using test_utils::ScopedTempFile;
+using testing::AtLeast;
+using testing::InSequence;
+using testing::Return;
+using testing::_;
+
+class DownloadActionTest : public ::testing::Test { };
+
+namespace {
+
+class DownloadActionTestProcessorDelegate : public ActionProcessorDelegate {
+ public:
+  explicit DownloadActionTestProcessorDelegate(ErrorCode expected_code)
+      : processing_done_called_(false),
+        expected_code_(expected_code) {}
+  ~DownloadActionTestProcessorDelegate() override {
+    EXPECT_TRUE(processing_done_called_);
+  }
+  void ProcessingDone(const ActionProcessor* processor,
+                      ErrorCode code) override {
+    brillo::MessageLoop::current()->BreakLoop();
+    brillo::Blob found_data;
+    ASSERT_TRUE(utils::ReadFile(path_, &found_data));
+    if (expected_code_ != ErrorCode::kDownloadWriteError) {
+      ASSERT_EQ(expected_data_.size(), found_data.size());
+      for (unsigned i = 0; i < expected_data_.size(); i++) {
+        EXPECT_EQ(expected_data_[i], found_data[i]);
+      }
+    }
+    processing_done_called_ = true;
+  }
+
+  void ActionCompleted(ActionProcessor* processor,
+                       AbstractAction* action,
+                       ErrorCode code) override {
+    const string type = action->Type();
+    if (type == DownloadAction::StaticType()) {
+      EXPECT_EQ(expected_code_, code);
+    } else {
+      EXPECT_EQ(ErrorCode::kSuccess, code);
+    }
+  }
+
+  string path_;
+  brillo::Blob expected_data_;
+  bool processing_done_called_;
+  ErrorCode expected_code_;
+};
+
+class TestDirectFileWriter : public DirectFileWriter {
+ public:
+  TestDirectFileWriter() : fail_write_(0), current_write_(0) {}
+  void set_fail_write(int fail_write) { fail_write_ = fail_write; }
+
+  virtual bool Write(const void* bytes, size_t count) {
+    if (++current_write_ == fail_write_) {
+      return false;
+    }
+    return DirectFileWriter::Write(bytes, count);
+  }
+
+ private:
+  // If positive, fail on the |fail_write_| call to Write.
+  int fail_write_;
+  int current_write_;
+};
+
+void StartProcessorInRunLoop(ActionProcessor* processor,
+                             MockHttpFetcher* http_fetcher) {
+  processor->StartProcessing();
+  http_fetcher->SetOffset(1);
+}
+
+void TestWithData(const brillo::Blob& data,
+                  int fail_write,
+                  bool use_download_delegate) {
+  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(0, writer.Open(output_temp_file.GetPath().c_str(),
+                           O_WRONLY | O_CREAT,
+                           0));
+  writer.set_fail_write(fail_write);
+
+  // We pull off the first byte from data and seek past it.
+  string hash = HashCalculator::HashOfBytes(&data[1], data.size() - 1);
+  uint64_t size = data.size();
+  InstallPlan install_plan(false,
+                           false,
+                           "",
+                           size,
+                           hash,
+                           0,
+                           "",
+                           "");
+  install_plan.source_slot = 0;
+  install_plan.target_slot = 1;
+  // We mark both slots as bootable. Only the target slot should be unbootable
+  // after the download starts.
+  fake_system_state.fake_boot_control()->SetSlotBootable(
+      install_plan.source_slot, true);
+  fake_system_state.fake_boot_control()->SetSlotBootable(
+      install_plan.target_slot, true);
+  ObjectFeederAction<InstallPlan> feeder_action;
+  feeder_action.set_obj(install_plan);
+  MockPrefs prefs;
+  MockHttpFetcher* http_fetcher = new MockHttpFetcher(data.data(),
+                                                      data.size(),
+                                                      nullptr);
+  // takes ownership of passed in HttpFetcher
+  DownloadAction download_action(&prefs, &fake_system_state, http_fetcher);
+  download_action.SetTestFileWriter(&writer);
+  BondActions(&feeder_action, &download_action);
+  MockDownloadActionDelegate download_delegate;
+  if (use_download_delegate) {
+    InSequence s;
+    download_action.set_delegate(&download_delegate);
+    if (data.size() > kMockHttpFetcherChunkSize)
+      EXPECT_CALL(download_delegate,
+                  BytesReceived(_, 1 + kMockHttpFetcherChunkSize, _));
+    EXPECT_CALL(download_delegate, BytesReceived(_, _, _)).Times(AtLeast(1));
+  }
+  ErrorCode expected_code = ErrorCode::kSuccess;
+  if (fail_write > 0)
+    expected_code = ErrorCode::kDownloadWriteError;
+  DownloadActionTestProcessorDelegate delegate(expected_code);
+  delegate.expected_data_ = brillo::Blob(data.begin() + 1, data.end());
+  delegate.path_ = output_temp_file.GetPath();
+  ActionProcessor processor;
+  processor.set_delegate(&delegate);
+  processor.EnqueueAction(&feeder_action);
+  processor.EnqueueAction(&download_action);
+
+  loop.PostTask(FROM_HERE,
+                base::Bind(&StartProcessorInRunLoop, &processor, http_fetcher));
+  loop.Run();
+  EXPECT_FALSE(loop.PendingTasks());
+
+  EXPECT_TRUE(fake_system_state.fake_boot_control()->IsSlotBootable(
+      install_plan.source_slot));
+  EXPECT_FALSE(fake_system_state.fake_boot_control()->IsSlotBootable(
+      install_plan.target_slot));
+}
+}  // namespace
+
+TEST(DownloadActionTest, SimpleTest) {
+  brillo::Blob small;
+  const char* foo = "foo";
+  small.insert(small.end(), foo, foo + strlen(foo));
+  TestWithData(small,
+               0,  // fail_write
+               true);  // use_download_delegate
+}
+
+TEST(DownloadActionTest, LargeTest) {
+  brillo::Blob big(5 * kMockHttpFetcherChunkSize);
+  char c = '0';
+  for (unsigned int i = 0; i < big.size(); i++) {
+    big[i] = c;
+    c = ('9' == c) ? '0' : c + 1;
+  }
+  TestWithData(big,
+               0,  // fail_write
+               true);  // use_download_delegate
+}
+
+TEST(DownloadActionTest, FailWriteTest) {
+  brillo::Blob big(5 * kMockHttpFetcherChunkSize);
+  char c = '0';
+  for (unsigned int i = 0; i < big.size(); i++) {
+    big[i] = c;
+    c = ('9' == c) ? '0' : c + 1;
+  }
+  TestWithData(big,
+               2,  // fail_write
+               true);  // use_download_delegate
+}
+
+TEST(DownloadActionTest, NoDownloadDelegateTest) {
+  brillo::Blob small;
+  const char* foo = "foofoo";
+  small.insert(small.end(), foo, foo + strlen(foo));
+  TestWithData(small,
+               0,  // fail_write
+               false);  // use_download_delegate
+}
+
+namespace {
+class TerminateEarlyTestProcessorDelegate : public ActionProcessorDelegate {
+ public:
+  void ProcessingStopped(const ActionProcessor* processor) {
+    brillo::MessageLoop::current()->BreakLoop();
+  }
+};
+
+void TerminateEarlyTestStarter(ActionProcessor* processor) {
+  processor->StartProcessing();
+  CHECK(processor->IsRunning());
+  processor->StopProcessing();
+}
+
+void TestTerminateEarly(bool use_download_delegate) {
+  brillo::FakeMessageLoop loop(nullptr);
+  loop.SetAsCurrent();
+
+  brillo::Blob data(kMockHttpFetcherChunkSize +
+                      kMockHttpFetcherChunkSize / 2);
+  memset(data.data(), 0, data.size());
+
+  ScopedTempFile temp_file;
+  {
+    DirectFileWriter writer;
+    EXPECT_EQ(0, writer.Open(temp_file.GetPath().c_str(),
+                             O_WRONLY | O_CREAT,
+                             0));
+
+    // takes ownership of passed in HttpFetcher
+    ObjectFeederAction<InstallPlan> feeder_action;
+    InstallPlan install_plan(false, false, "", 0, "", 0, "", "");
+    feeder_action.set_obj(install_plan);
+    FakeSystemState fake_system_state_;
+    MockPrefs prefs;
+    DownloadAction download_action(&prefs, &fake_system_state_,
+                                   new MockHttpFetcher(data.data(),
+                                                       data.size(),
+                                                       nullptr));
+    download_action.SetTestFileWriter(&writer);
+    MockDownloadActionDelegate download_delegate;
+    if (use_download_delegate) {
+      download_action.set_delegate(&download_delegate);
+      EXPECT_CALL(download_delegate, BytesReceived(_, _, _)).Times(0);
+    }
+    TerminateEarlyTestProcessorDelegate delegate;
+    ActionProcessor processor;
+    processor.set_delegate(&delegate);
+    processor.EnqueueAction(&feeder_action);
+    processor.EnqueueAction(&download_action);
+    BondActions(&feeder_action, &download_action);
+
+    loop.PostTask(FROM_HERE,
+                  base::Bind(&TerminateEarlyTestStarter, &processor));
+    loop.Run();
+    EXPECT_FALSE(loop.PendingTasks());
+  }
+
+  // 1 or 0 chunks should have come through
+  const off_t resulting_file_size(utils::FileSize(temp_file.GetPath()));
+  EXPECT_GE(resulting_file_size, 0);
+  if (resulting_file_size != 0)
+    EXPECT_EQ(kMockHttpFetcherChunkSize, resulting_file_size);
+}
+
+}  // namespace
+
+TEST(DownloadActionTest, TerminateEarlyTest) {
+  TestTerminateEarly(true);
+}
+
+TEST(DownloadActionTest, TerminateEarlyNoDownloadDelegateTest) {
+  TestTerminateEarly(false);
+}
+
+class DownloadActionTestAction;
+
+template<>
+class ActionTraits<DownloadActionTestAction> {
+ public:
+  typedef InstallPlan OutputObjectType;
+  typedef InstallPlan InputObjectType;
+};
+
+// This is a simple Action class for testing.
+class DownloadActionTestAction : public Action<DownloadActionTestAction> {
+ public:
+  DownloadActionTestAction() : did_run_(false) {}
+  typedef InstallPlan InputObjectType;
+  typedef InstallPlan OutputObjectType;
+  ActionPipe<InstallPlan>* in_pipe() { return in_pipe_.get(); }
+  ActionPipe<InstallPlan>* out_pipe() { return out_pipe_.get(); }
+  ActionProcessor* processor() { return processor_; }
+  void PerformAction() {
+    did_run_ = true;
+    ASSERT_TRUE(HasInputObject());
+    EXPECT_TRUE(expected_input_object_ == GetInputObject());
+    ASSERT_TRUE(processor());
+    processor()->ActionComplete(this, ErrorCode::kSuccess);
+  }
+  string Type() const { return "DownloadActionTestAction"; }
+  InstallPlan expected_input_object_;
+  bool did_run_;
+};
+
+namespace {
+// This class is an ActionProcessorDelegate that simply terminates the
+// run loop when the ActionProcessor has completed processing. It's used
+// only by the test PassObjectOutTest.
+class PassObjectOutTestProcessorDelegate : public ActionProcessorDelegate {
+ public:
+  void ProcessingDone(const ActionProcessor* processor, ErrorCode code) {
+    brillo::MessageLoop::current()->BreakLoop();
+  }
+};
+
+}  // namespace
+
+TEST(DownloadActionTest, PassObjectOutTest) {
+  brillo::FakeMessageLoop loop(nullptr);
+  loop.SetAsCurrent();
+
+  DirectFileWriter writer;
+  EXPECT_EQ(0, writer.Open("/dev/null", O_WRONLY | O_CREAT, 0));
+
+  // takes ownership of passed in HttpFetcher
+  InstallPlan install_plan(false,
+                           false,
+                           "",
+                           1,
+                           HashCalculator::HashOfString("x"),
+                           0,
+                           "",
+                           "");
+  ObjectFeederAction<InstallPlan> feeder_action;
+  feeder_action.set_obj(install_plan);
+  MockPrefs prefs;
+  FakeSystemState fake_system_state_;
+  DownloadAction download_action(&prefs, &fake_system_state_,
+                                 new MockHttpFetcher("x", 1, nullptr));
+  download_action.SetTestFileWriter(&writer);
+
+  DownloadActionTestAction test_action;
+  test_action.expected_input_object_ = install_plan;
+  BondActions(&feeder_action, &download_action);
+  BondActions(&download_action, &test_action);
+
+  ActionProcessor processor;
+  PassObjectOutTestProcessorDelegate delegate;
+  processor.set_delegate(&delegate);
+  processor.EnqueueAction(&feeder_action);
+  processor.EnqueueAction(&download_action);
+  processor.EnqueueAction(&test_action);
+
+  loop.PostTask(FROM_HERE,
+                base::Bind([&processor] { processor.StartProcessing(); }));
+  loop.Run();
+  EXPECT_FALSE(loop.PendingTasks());
+
+  EXPECT_EQ(true, test_action.did_run_);
+}
+
+// Test fixture for P2P tests.
+class P2PDownloadActionTest : public testing::Test {
+ protected:
+  P2PDownloadActionTest()
+    : start_at_offset_(0),
+      fake_um_(fake_system_state_.fake_clock()) {}
+
+  ~P2PDownloadActionTest() override {}
+
+  // Derived from testing::Test.
+  void SetUp() override {
+    loop_.SetAsCurrent();
+  }
+
+  // Derived from testing::Test.
+  void TearDown() override {
+    EXPECT_FALSE(loop_.PendingTasks());
+  }
+
+  // To be called by tests to setup the download. The
+  // |starting_offset| parameter is for where to resume.
+  void SetupDownload(off_t starting_offset) {
+    start_at_offset_ = starting_offset;
+    // Prepare data 10 kB of data.
+    data_.clear();
+    for (unsigned int i = 0; i < 10 * 1000; i++)
+      data_ += 'a' + (i % 25);
+
+    // Setup p2p.
+    FakeP2PManagerConfiguration *test_conf = new FakeP2PManagerConfiguration();
+    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(*fake_system_state_.mock_payload_state(),
+                GetUsingP2PForSharing())
+        .WillRepeatedly(Return(use_p2p_to_share));
+
+    ScopedTempFile output_temp_file;
+    TestDirectFileWriter writer;
+    EXPECT_EQ(0, writer.Open(output_temp_file.GetPath().c_str(),
+                             O_WRONLY | O_CREAT,
+                             0));
+    InstallPlan install_plan(false,
+                             false,
+                             "",
+                             data_.length(),
+                             "1234hash",
+                             0,
+                             "",
+                             "");
+    ObjectFeederAction<InstallPlan> feeder_action;
+    feeder_action.set_obj(install_plan);
+    MockPrefs prefs;
+    http_fetcher_ = new MockHttpFetcher(data_.c_str(),
+                                        data_.length(),
+                                        nullptr);
+    // Note that DownloadAction takes ownership of the passed in HttpFetcher.
+    download_action_.reset(new DownloadAction(&prefs, &fake_system_state_,
+                                              http_fetcher_));
+    download_action_->SetTestFileWriter(&writer);
+    BondActions(&feeder_action, download_action_.get());
+    DownloadActionTestProcessorDelegate delegate(ErrorCode::kSuccess);
+    delegate.expected_data_ = brillo::Blob(data_.begin() + start_at_offset_,
+                                           data_.end());
+    delegate.path_ = output_temp_file.GetPath();
+    processor_.set_delegate(&delegate);
+    processor_.EnqueueAction(&feeder_action);
+    processor_.EnqueueAction(download_action_.get());
+
+    loop_.PostTask(FROM_HERE, base::Bind(
+        &P2PDownloadActionTest::StartProcessorInRunLoopForP2P,
+        base::Unretained(this)));
+    loop_.Run();
+  }
+
+  // Mainloop used to make StartDownload() synchronous.
+  brillo::FakeMessageLoop loop_{nullptr};
+
+  // The DownloadAction instance under test.
+  unique_ptr<DownloadAction> download_action_;
+
+  // The HttpFetcher used in the test.
+  MockHttpFetcher* http_fetcher_;
+
+  // The P2PManager used in the test.
+  unique_ptr<P2PManager> p2p_manager_;
+
+  // The ActionProcessor used for running the actions.
+  ActionProcessor processor_;
+
+  // A fake system state.
+  FakeSystemState fake_system_state_;
+
+  // The data being downloaded.
+  string data_;
+
+ private:
+  // Callback used in StartDownload() method.
+  void StartProcessorInRunLoopForP2P() {
+    processor_.StartProcessing();
+    http_fetcher_->SetOffset(start_at_offset_);
+  }
+
+  // The requested starting offset passed to SetupDownload().
+  off_t start_at_offset_;
+
+  chromeos_update_manager::FakeUpdateManager fake_um_;
+};
+
+TEST_F(P2PDownloadActionTest, IsWrittenTo) {
+  if (!test_utils::IsXAttrSupported(FilePath("/tmp"))) {
+    LOG(WARNING) << "Skipping test because /tmp does not support xattr. "
+                 << "Please update your system to support this feature.";
+    return;
+  }
+
+  SetupDownload(0);     // starting_offset
+  StartDownload(true);  // use_p2p_to_share
+
+  // Check the p2p file and its content matches what was sent.
+  string file_id = download_action_->p2p_file_id();
+  EXPECT_NE("", file_id);
+  EXPECT_EQ(data_.length(), p2p_manager_->FileGetSize(file_id));
+  EXPECT_EQ(data_.length(), p2p_manager_->FileGetExpectedSize(file_id));
+  string p2p_file_contents;
+  EXPECT_TRUE(ReadFileToString(p2p_manager_->FileGetPath(file_id),
+                               &p2p_file_contents));
+  EXPECT_EQ(data_, p2p_file_contents);
+}
+
+TEST_F(P2PDownloadActionTest, DeleteIfHoleExists) {
+  if (!test_utils::IsXAttrSupported(FilePath("/tmp"))) {
+    LOG(WARNING) << "Skipping test because /tmp does not support xattr. "
+                 << "Please update your system to support this feature.";
+    return;
+  }
+
+  SetupDownload(1000);  // starting_offset
+  StartDownload(true);  // use_p2p_to_share
+
+  // DownloadAction should convey that the file is not being shared.
+  // and that we don't have any p2p files.
+  EXPECT_EQ(download_action_->p2p_file_id(), "");
+  EXPECT_EQ(p2p_manager_->CountSharedFiles(), 0);
+}
+
+TEST_F(P2PDownloadActionTest, CanAppend) {
+  if (!test_utils::IsXAttrSupported(FilePath("/tmp"))) {
+    LOG(WARNING) << "Skipping test because /tmp does not support xattr. "
+                 << "Please update your system to support this feature.";
+    return;
+  }
+
+  SetupDownload(1000);  // starting_offset
+
+  // Prepare the file with existing data before starting to write to
+  // it via DownloadAction.
+  string file_id = utils::CalculateP2PFileId("1234hash", data_.length());
+  ASSERT_TRUE(p2p_manager_->FileShare(file_id, data_.length()));
+  string existing_data;
+  for (unsigned int i = 0; i < 1000; i++)
+    existing_data += '0' + (i % 10);
+  ASSERT_EQ(WriteFile(p2p_manager_->FileGetPath(file_id), existing_data.c_str(),
+                      1000), 1000);
+
+  StartDownload(true);  // use_p2p_to_share
+
+  // DownloadAction should convey the same file_id and the file should
+  // have the expected size.
+  EXPECT_EQ(download_action_->p2p_file_id(), file_id);
+  EXPECT_EQ(p2p_manager_->FileGetSize(file_id), data_.length());
+  EXPECT_EQ(p2p_manager_->FileGetExpectedSize(file_id), data_.length());
+  string p2p_file_contents;
+  // Check that the first 1000 bytes wasn't touched and that we
+  // appended the remaining as appropriate.
+  EXPECT_TRUE(ReadFileToString(p2p_manager_->FileGetPath(file_id),
+                               &p2p_file_contents));
+  EXPECT_EQ(existing_data, p2p_file_contents.substr(0, 1000));
+  EXPECT_EQ(data_.substr(1000), p2p_file_contents.substr(1000));
+}
+
+TEST_F(P2PDownloadActionTest, DeletePartialP2PFileIfResumingWithoutP2P) {
+  if (!test_utils::IsXAttrSupported(FilePath("/tmp"))) {
+    LOG(WARNING) << "Skipping test because /tmp does not support xattr. "
+                 << "Please update your system to support this feature.";
+    return;
+  }
+
+  SetupDownload(1000);  // starting_offset
+
+  // Prepare the file with all existing data before starting to write
+  // to it via DownloadAction.
+  string file_id = utils::CalculateP2PFileId("1234hash", data_.length());
+  ASSERT_TRUE(p2p_manager_->FileShare(file_id, data_.length()));
+  string existing_data;
+  for (unsigned int i = 0; i < 1000; i++)
+    existing_data += '0' + (i % 10);
+  ASSERT_EQ(WriteFile(p2p_manager_->FileGetPath(file_id), existing_data.c_str(),
+                      1000), 1000);
+
+  // Check that the file is there.
+  EXPECT_EQ(p2p_manager_->FileGetSize(file_id), 1000);
+  EXPECT_EQ(p2p_manager_->CountSharedFiles(), 1);
+
+  StartDownload(false);  // use_p2p_to_share
+
+  // DownloadAction should have deleted the p2p file. Check that it's gone.
+  EXPECT_EQ(p2p_manager_->FileGetSize(file_id), -1);
+  EXPECT_EQ(p2p_manager_->CountSharedFiles(), 0);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/extent_writer.cc b/payload_consumer/extent_writer.cc
new file mode 100644
index 0000000..5501e22
--- /dev/null
+++ b/payload_consumer/extent_writer.cc
@@ -0,0 +1,71 @@
+//
+// 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_consumer/extent_writer.h"
+
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/payload_constants.h"
+
+using std::min;
+
+namespace chromeos_update_engine {
+
+bool DirectExtentWriter::Write(const void* bytes, size_t count) {
+  if (count == 0)
+    return true;
+  const char* c_bytes = reinterpret_cast<const char*>(bytes);
+  size_t bytes_written = 0;
+  while (count - bytes_written > 0) {
+    TEST_AND_RETURN_FALSE(next_extent_index_ < extents_.size());
+    uint64_t bytes_remaining_next_extent =
+        extents_[next_extent_index_].num_blocks() * block_size_ -
+        extent_bytes_written_;
+    CHECK_NE(bytes_remaining_next_extent, static_cast<uint64_t>(0));
+    size_t bytes_to_write =
+        static_cast<size_t>(min(static_cast<uint64_t>(count - bytes_written),
+                                bytes_remaining_next_extent));
+    TEST_AND_RETURN_FALSE(bytes_to_write > 0);
+
+    if (extents_[next_extent_index_].start_block() != kSparseHole) {
+      const off64_t offset =
+          extents_[next_extent_index_].start_block() * block_size_ +
+          extent_bytes_written_;
+      TEST_AND_RETURN_FALSE_ERRNO(fd_->Seek(offset, SEEK_SET) !=
+                                  static_cast<off64_t>(-1));
+      TEST_AND_RETURN_FALSE(
+          utils::WriteAll(fd_, c_bytes + bytes_written, bytes_to_write));
+    }
+    bytes_written += bytes_to_write;
+    extent_bytes_written_ += bytes_to_write;
+    if (bytes_remaining_next_extent == bytes_to_write) {
+      // We filled this extent
+      CHECK_EQ(extent_bytes_written_,
+               extents_[next_extent_index_].num_blocks() * block_size_);
+      // move to next extent
+      extent_bytes_written_ = 0;
+      next_extent_index_++;
+    }
+  }
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/extent_writer.h b/payload_consumer/extent_writer.h
new file mode 100644
index 0000000..6484ebf
--- /dev/null
+++ b/payload_consumer/extent_writer.h
@@ -0,0 +1,134 @@
+//
+// 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_CONSUMER_EXTENT_WRITER_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_EXTENT_WRITER_H_
+
+#include <vector>
+
+#include <base/logging.h>
+#include <brillo/secure_blob.h>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/file_descriptor.h"
+#include "update_engine/update_metadata.pb.h"
+
+// ExtentWriter is an abstract class which synchronously writes to a given
+// file descriptor at the extents given.
+
+namespace chromeos_update_engine {
+
+class ExtentWriter {
+ public:
+  ExtentWriter() = default;
+  virtual ~ExtentWriter() {
+    LOG_IF(ERROR, !end_called_) << "End() not called on ExtentWriter.";
+  }
+
+  // Returns true on success.
+  virtual bool Init(FileDescriptorPtr fd,
+                    const std::vector<Extent>& extents,
+                    uint32_t block_size) = 0;
+
+  // Returns true on success.
+  virtual bool Write(const void* bytes, size_t count) = 0;
+
+  // Should be called when all writing is complete. Returns true on success.
+  // The fd is not closed. Caller is responsible for closing it.
+  bool End() {
+    end_called_ = true;
+    return EndImpl();
+  }
+  virtual bool EndImpl() = 0;
+ private:
+  bool end_called_{false};
+};
+
+// DirectExtentWriter is probably the simplest ExtentWriter implementation.
+// It writes the data directly into the extents.
+
+class DirectExtentWriter : public ExtentWriter {
+ public:
+  DirectExtentWriter() = default;
+  ~DirectExtentWriter() override = default;
+
+  bool Init(FileDescriptorPtr fd,
+            const std::vector<Extent>& extents,
+            uint32_t block_size) override {
+    fd_ = fd;
+    block_size_ = block_size;
+    extents_ = extents;
+    return true;
+  }
+  bool Write(const void* bytes, size_t count) override;
+  bool EndImpl() override { return true; }
+
+ private:
+  FileDescriptorPtr fd_{nullptr};
+
+  size_t block_size_{0};
+  // Bytes written into next_extent_index_ thus far
+  uint64_t extent_bytes_written_{0};
+  std::vector<Extent> extents_;
+  // The next call to write should correspond to extents_[next_extent_index_]
+  std::vector<Extent>::size_type next_extent_index_{0};
+};
+
+// Takes an underlying ExtentWriter to which all operations are delegated.
+// When End() is called, ZeroPadExtentWriter ensures that the total number
+// of bytes written is a multiple of block_size_. If not, it writes zeros
+// to pad as needed.
+
+class ZeroPadExtentWriter : public ExtentWriter {
+ public:
+  explicit ZeroPadExtentWriter(
+      std::unique_ptr<ExtentWriter> underlying_extent_writer)
+      : underlying_extent_writer_(std::move(underlying_extent_writer)) {}
+  ~ZeroPadExtentWriter() override = default;
+
+  bool Init(FileDescriptorPtr fd,
+            const std::vector<Extent>& extents,
+            uint32_t block_size) override {
+    block_size_ = block_size;
+    return underlying_extent_writer_->Init(fd, extents, block_size);
+  }
+  bool Write(const void* bytes, size_t count) override {
+    if (underlying_extent_writer_->Write(bytes, count)) {
+      bytes_written_mod_block_size_ += count;
+      bytes_written_mod_block_size_ %= block_size_;
+      return true;
+    }
+    return false;
+  }
+  bool EndImpl() override {
+    if (bytes_written_mod_block_size_) {
+      const size_t write_size = block_size_ - bytes_written_mod_block_size_;
+      brillo::Blob zeros(write_size, 0);
+      TEST_AND_RETURN_FALSE(underlying_extent_writer_->Write(zeros.data(),
+                                                             write_size));
+    }
+    return underlying_extent_writer_->End();
+  }
+
+ private:
+  std::unique_ptr<ExtentWriter> underlying_extent_writer_;
+  size_t block_size_{0};
+  size_t bytes_written_mod_block_size_{0};
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_EXTENT_WRITER_H_
diff --git a/payload_consumer/extent_writer_unittest.cc b/payload_consumer/extent_writer_unittest.cc
new file mode 100644
index 0000000..24ea5bf
--- /dev/null
+++ b/payload_consumer/extent_writer_unittest.cc
@@ -0,0 +1,270 @@
+//
+// 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_consumer/extent_writer.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <brillo/make_unique_ptr.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/payload_constants.h"
+
+using chromeos_update_engine::test_utils::ExpectVectorsEq;
+using std::min;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+COMPILE_ASSERT(sizeof(off_t) == 8, off_t_not_64_bit);
+
+namespace {
+const char kPathTemplate[] = "./ExtentWriterTest-file.XXXXXX";
+const size_t kBlockSize = 4096;
+}
+
+class ExtentWriterTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memcpy(path_, kPathTemplate, sizeof(kPathTemplate));
+    fd_.reset(new EintrSafeFileDescriptor);
+    int fd = mkstemp(path_);
+    ASSERT_TRUE(fd_->Open(path_, O_RDWR, 0600));
+    close(fd);
+  }
+  void TearDown() override {
+    fd_->Close();
+    unlink(path_);
+  }
+
+  // Writes data to an extent writer in 'chunk_size' chunks with
+  // the first chunk of size first_chunk_size. It calculates what the
+  // resultant file should look like and ensure that the extent writer
+  // wrote the file correctly.
+  void WriteAlignedExtents(size_t chunk_size, size_t first_chunk_size);
+  void TestZeroPad(bool aligned_size);
+
+  FileDescriptorPtr fd_;
+  char path_[sizeof(kPathTemplate)];
+};
+
+TEST_F(ExtentWriterTest, SimpleTest) {
+  vector<Extent> extents;
+  Extent extent;
+  extent.set_start_block(1);
+  extent.set_num_blocks(1);
+  extents.push_back(extent);
+
+  const string bytes = "1234";
+
+  DirectExtentWriter direct_writer;
+  EXPECT_TRUE(direct_writer.Init(fd_, extents, kBlockSize));
+  EXPECT_TRUE(direct_writer.Write(bytes.data(), bytes.size()));
+  EXPECT_TRUE(direct_writer.End());
+
+  EXPECT_EQ(kBlockSize + bytes.size(), utils::FileSize(path_));
+
+  brillo::Blob result_file;
+  EXPECT_TRUE(utils::ReadFile(path_, &result_file));
+
+  brillo::Blob expected_file(kBlockSize);
+  expected_file.insert(expected_file.end(),
+                       bytes.data(), bytes.data() + bytes.size());
+  ExpectVectorsEq(expected_file, result_file);
+}
+
+TEST_F(ExtentWriterTest, ZeroLengthTest) {
+  vector<Extent> extents;
+  Extent extent;
+  extent.set_start_block(1);
+  extent.set_num_blocks(1);
+  extents.push_back(extent);
+
+  DirectExtentWriter direct_writer;
+  EXPECT_TRUE(direct_writer.Init(fd_, extents, kBlockSize));
+  EXPECT_TRUE(direct_writer.Write(nullptr, 0));
+  EXPECT_TRUE(direct_writer.End());
+}
+
+TEST_F(ExtentWriterTest, OverflowExtentTest) {
+  WriteAlignedExtents(kBlockSize * 3, kBlockSize * 3);
+}
+
+TEST_F(ExtentWriterTest, UnalignedWriteTest) {
+  WriteAlignedExtents(7, 7);
+}
+
+TEST_F(ExtentWriterTest, LargeUnalignedWriteTest) {
+  WriteAlignedExtents(kBlockSize * 2, kBlockSize / 2);
+}
+
+void ExtentWriterTest::WriteAlignedExtents(size_t chunk_size,
+                                           size_t first_chunk_size) {
+  vector<Extent> extents;
+  Extent extent;
+  extent.set_start_block(1);
+  extent.set_num_blocks(1);
+  extents.push_back(extent);
+  extent.set_start_block(0);
+  extent.set_num_blocks(1);
+  extents.push_back(extent);
+  extent.set_start_block(2);
+  extent.set_num_blocks(1);
+  extents.push_back(extent);
+
+  brillo::Blob data(kBlockSize * 3);
+  test_utils::FillWithData(&data);
+
+  DirectExtentWriter direct_writer;
+  EXPECT_TRUE(direct_writer.Init(fd_, extents, kBlockSize));
+
+  size_t bytes_written = 0;
+  while (bytes_written < data.size()) {
+    size_t bytes_to_write = min(data.size() - bytes_written, chunk_size);
+    if (bytes_written == 0) {
+      bytes_to_write = min(data.size() - bytes_written, first_chunk_size);
+    }
+    EXPECT_TRUE(direct_writer.Write(&data[bytes_written], bytes_to_write));
+    bytes_written += bytes_to_write;
+  }
+  EXPECT_TRUE(direct_writer.End());
+
+  EXPECT_EQ(data.size(), utils::FileSize(path_));
+
+  brillo::Blob result_file;
+  EXPECT_TRUE(utils::ReadFile(path_, &result_file));
+
+  brillo::Blob expected_file;
+  expected_file.insert(expected_file.end(),
+                       data.begin() + kBlockSize,
+                       data.begin() + kBlockSize * 2);
+  expected_file.insert(expected_file.end(),
+                       data.begin(), data.begin() + kBlockSize);
+  expected_file.insert(expected_file.end(),
+                       data.begin() + kBlockSize * 2, data.end());
+  ExpectVectorsEq(expected_file, result_file);
+}
+
+TEST_F(ExtentWriterTest, ZeroPadNullTest) {
+  TestZeroPad(true);
+}
+
+TEST_F(ExtentWriterTest, ZeroPadFillTest) {
+  TestZeroPad(false);
+}
+
+void ExtentWriterTest::TestZeroPad(bool aligned_size) {
+  vector<Extent> extents;
+  Extent extent;
+  extent.set_start_block(1);
+  extent.set_num_blocks(1);
+  extents.push_back(extent);
+  extent.set_start_block(0);
+  extent.set_num_blocks(1);
+  extents.push_back(extent);
+
+  brillo::Blob data(kBlockSize * 2);
+  test_utils::FillWithData(&data);
+
+  ZeroPadExtentWriter zero_pad_writer(
+      brillo::make_unique_ptr(new DirectExtentWriter()));
+
+  EXPECT_TRUE(zero_pad_writer.Init(fd_, extents, kBlockSize));
+  size_t bytes_to_write = data.size();
+  const size_t missing_bytes = (aligned_size ? 0 : 9);
+  bytes_to_write -= missing_bytes;
+  fd_->Seek(kBlockSize - missing_bytes, SEEK_SET);
+  EXPECT_EQ(3, fd_->Write("xxx", 3));
+  ASSERT_TRUE(zero_pad_writer.Write(data.data(), bytes_to_write));
+  EXPECT_TRUE(zero_pad_writer.End());
+
+  EXPECT_EQ(data.size(), utils::FileSize(path_));
+
+  brillo::Blob result_file;
+  EXPECT_TRUE(utils::ReadFile(path_, &result_file));
+
+  brillo::Blob expected_file;
+  expected_file.insert(expected_file.end(),
+                       data.begin() + kBlockSize,
+                       data.begin() + kBlockSize * 2);
+  expected_file.insert(expected_file.end(),
+                       data.begin(), data.begin() + kBlockSize);
+  if (missing_bytes) {
+    memset(&expected_file[kBlockSize - missing_bytes], 0, missing_bytes);
+  }
+
+  ExpectVectorsEq(expected_file, result_file);
+}
+
+TEST_F(ExtentWriterTest, SparseFileTest) {
+  vector<Extent> extents;
+  Extent extent;
+  extent.set_start_block(1);
+  extent.set_num_blocks(1);
+  extents.push_back(extent);
+  extent.set_start_block(kSparseHole);
+  extent.set_num_blocks(2);
+  extents.push_back(extent);
+  extent.set_start_block(0);
+  extent.set_num_blocks(1);
+  extents.push_back(extent);
+  const int block_count = 4;
+  const int on_disk_count = 2;
+
+  brillo::Blob data(17);
+  test_utils::FillWithData(&data);
+
+  DirectExtentWriter direct_writer;
+  EXPECT_TRUE(direct_writer.Init(fd_, extents, kBlockSize));
+
+  size_t bytes_written = 0;
+  while (bytes_written < (block_count * kBlockSize)) {
+    size_t bytes_to_write = min(block_count * kBlockSize - bytes_written,
+                                data.size());
+    EXPECT_TRUE(direct_writer.Write(data.data(), bytes_to_write));
+    bytes_written += bytes_to_write;
+  }
+  EXPECT_TRUE(direct_writer.End());
+
+  // check file size, then data inside
+  ASSERT_EQ(2 * kBlockSize, utils::FileSize(path_));
+
+  brillo::Blob resultant_data;
+  EXPECT_TRUE(utils::ReadFile(path_, &resultant_data));
+
+  // Create expected data
+  brillo::Blob expected_data(on_disk_count * kBlockSize);
+  brillo::Blob big(block_count * kBlockSize);
+  for (brillo::Blob::size_type i = 0; i < big.size(); i++) {
+    big[i] = data[i % data.size()];
+  }
+  memcpy(&expected_data[kBlockSize], &big[0], kBlockSize);
+  memcpy(&expected_data[0], &big[3 * kBlockSize], kBlockSize);
+  ExpectVectorsEq(expected_data, resultant_data);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/fake_extent_writer.h b/payload_consumer/fake_extent_writer.h
new file mode 100644
index 0000000..762c6d5
--- /dev/null
+++ b/payload_consumer/fake_extent_writer.h
@@ -0,0 +1,71 @@
+//
+// 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_CONSUMER_FAKE_EXTENT_WRITER_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_FAKE_EXTENT_WRITER_H_
+
+#include <memory>
+#include <vector>
+
+#include <brillo/secure_blob.h>
+
+#include "update_engine/payload_consumer/extent_writer.h"
+
+namespace chromeos_update_engine {
+
+// FakeExtentWriter is a concrete ExtentWriter subclass that keeps track of all
+// the written data, useful for testing.
+class FakeExtentWriter : public ExtentWriter {
+ public:
+  FakeExtentWriter() = default;
+  ~FakeExtentWriter() override = default;
+
+  // ExtentWriter overrides.
+  bool Init(FileDescriptorPtr /* fd */,
+            const std::vector<Extent>& /* extents */,
+            uint32_t /* block_size */) override {
+    init_called_ = true;
+    return true;
+  };
+  bool Write(const void* bytes, size_t count) override {
+    if (!init_called_ || end_called_)
+      return false;
+    written_data_.insert(written_data_.end(),
+                         reinterpret_cast<const uint8_t*>(bytes),
+                         reinterpret_cast<const uint8_t*>(bytes) + count);
+    return true;
+  }
+  bool EndImpl() override {
+    end_called_ = true;
+    return true;
+  }
+
+  // Fake methods.
+  bool InitCalled() { return init_called_; }
+  bool EndCalled() { return end_called_; }
+  brillo::Blob WrittenData() { return written_data_; }
+
+ private:
+  bool init_called_{false};
+  bool end_called_{false};
+  brillo::Blob written_data_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeExtentWriter);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_FAKE_EXTENT_WRITER_H_
diff --git a/payload_consumer/file_descriptor.cc b/payload_consumer/file_descriptor.cc
new file mode 100644
index 0000000..f26be28
--- /dev/null
+++ b/payload_consumer/file_descriptor.cc
@@ -0,0 +1,116 @@
+//
+// 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_consumer/file_descriptor.h"
+
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <base/posix/eintr_wrapper.h>
+
+namespace chromeos_update_engine {
+
+bool EintrSafeFileDescriptor::Open(const char* path, int flags, mode_t mode) {
+  CHECK_EQ(fd_, -1);
+  return ((fd_ = HANDLE_EINTR(open(path, flags, mode))) >= 0);
+}
+
+bool EintrSafeFileDescriptor::Open(const char* path, int flags) {
+  CHECK_EQ(fd_, -1);
+  return ((fd_ = HANDLE_EINTR(open(path, flags))) >= 0);
+}
+
+ssize_t EintrSafeFileDescriptor::Read(void* buf, size_t count) {
+  CHECK_GE(fd_, 0);
+  return HANDLE_EINTR(read(fd_, buf, count));
+}
+
+ssize_t EintrSafeFileDescriptor::Write(const void* buf, size_t count) {
+  CHECK_GE(fd_, 0);
+
+  // Attempt repeated writes, as long as some progress is being made.
+  char* char_buf = const_cast<char*>(reinterpret_cast<const char*>(buf));
+  ssize_t written = 0;
+  while (count > 0) {
+    ssize_t ret = HANDLE_EINTR(write(fd_, char_buf, count));
+
+    // Fail on either an error or no progress.
+    if (ret <= 0)
+      return (written ? written : ret);
+    written += ret;
+    count -= ret;
+    char_buf += ret;
+  }
+  return written;
+}
+
+off64_t EintrSafeFileDescriptor::Seek(off64_t offset, int whence) {
+  CHECK_GE(fd_, 0);
+  return lseek64(fd_, offset, whence);
+}
+
+bool EintrSafeFileDescriptor::BlkIoctl(int request,
+                                       uint64_t start,
+                                       uint64_t length,
+                                       int* result) {
+  DCHECK(request == BLKDISCARD || request == BLKZEROOUT ||
+         request == BLKSECDISCARD);
+  // On some devices, the BLKDISCARD will actually read back as zeros, instead
+  // of "undefined" data. The BLKDISCARDZEROES ioctl tells whether that's the
+  // case, so we issue a BLKDISCARD in those cases to speed up the writes.
+  unsigned int arg;
+  if (request == BLKZEROOUT && ioctl(fd_, BLKDISCARDZEROES, &arg) == 0 && arg)
+    request = BLKDISCARD;
+
+  // Ensure the |fd_| is in O_DIRECT mode during this operation, so the write
+  // cache for this region is invalidated. This is required since otherwise
+  // reading back this region could consume stale data from the cache.
+  int flags = fcntl(fd_, F_GETFL, 0);
+  if (flags == -1) {
+    PLOG(WARNING) << "Couldn't get flags on fd " << fd_;
+    return false;
+  }
+  if ((flags & O_DIRECT) == 0 && fcntl(fd_, F_SETFL, flags | O_DIRECT) == -1) {
+    PLOG(WARNING) << "Couldn't set O_DIRECT on fd " << fd_;
+    return false;
+  }
+
+  uint64_t range[2] = {start, length};
+  *result = ioctl(fd_, request, range);
+
+  if ((flags & O_DIRECT) == 0 && fcntl(fd_, F_SETFL, flags) == -1) {
+    PLOG(WARNING) << "Couldn't remove O_DIRECT on fd " << fd_;
+    return false;
+  }
+  return true;
+}
+
+bool EintrSafeFileDescriptor::Close() {
+  CHECK_GE(fd_, 0);
+  if (IGNORE_EINTR(close(fd_)))
+    return false;
+  Reset();
+  return true;
+}
+
+void EintrSafeFileDescriptor::Reset() {
+  fd_ = -1;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/file_descriptor.h b/payload_consumer/file_descriptor.h
new file mode 100644
index 0000000..3c15415
--- /dev/null
+++ b/payload_consumer/file_descriptor.h
@@ -0,0 +1,141 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_PAYLOAD_CONSUMER_FILE_DESCRIPTOR_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_FILE_DESCRIPTOR_H_
+
+#include <errno.h>
+#include <memory>
+#include <sys/types.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
+// based on POSIX system calls.
+//
+// TODO(garnold) this class is modeled after (and augments the functionality of)
+// the FileWriter class; ultimately, the latter should be replaced by the former
+// throughout the codebase.  A few deviations from the original FileWriter:
+//
+// * Providing two flavors of Open()
+//
+// * A FileDescriptor is reusable and can be used to read/write multiple files
+//   as long as open/close preconditions are respected.
+//
+// * Write() returns the number of bytes written: this appears to be more useful
+//   for clients, who may wish to retry or otherwise do something useful with
+//   the remaining data that was not written.
+//
+// * Provides a Reset() method, which will force to abandon a currently open
+//   file descriptor and allow opening another file, without necessarily
+//   properly closing the old one. This may be useful in cases where a "closer"
+//   class does not care whether Close() was successful, but may need to reuse
+//   the same file descriptor again.
+
+namespace chromeos_update_engine {
+
+class FileDescriptor;
+using FileDescriptorPtr = std::shared_ptr<FileDescriptor>;
+
+// An abstract class defining the file descriptor API.
+class FileDescriptor {
+ public:
+  FileDescriptor() {}
+  virtual ~FileDescriptor() {}
+
+  // Opens a file descriptor. The descriptor must be in the closed state prior
+  // to this call. Returns true on success, false otherwise. Specific
+  // implementations may set errno accordingly.
+  virtual bool Open(const char* path, int flags, mode_t mode) = 0;
+  virtual bool Open(const char* path, int flags) = 0;
+
+  // Reads from a file descriptor up to a given count. The descriptor must be
+  // open prior to this call. Returns the number of bytes read, or -1 on error.
+  // Specific implementations may set errno accordingly.
+  virtual ssize_t Read(void* buf, size_t count) = 0;
+
+  // Writes to a file descriptor. The descriptor must be open prior to this
+  // call. Returns the number of bytes written, or -1 if an error occurred and
+  // no bytes were written. Specific implementations may set errno accordingly.
+  virtual ssize_t Write(const void* buf, size_t count) = 0;
+
+  // Seeks to an offset. Returns the resulting offset location as measured in
+  // bytes from the beginning. On error, return -1. Specific implementations
+  // may set errno accordingly.
+  virtual off64_t Seek(off64_t offset, int whence) = 0;
+
+  // Runs a ioctl() on the file descriptor if supported. Returns whether
+  // the operation is supported. The |request| can be one of BLKDISCARD,
+  // BLKZEROOUT and BLKSECDISCARD to discard, write zeros or securely discard
+  // the blocks. These ioctls accept a range of bytes (|start| and |length|)
+  // over which they perform the operation. The return value from the ioctl is
+  // stored in |result|.
+  virtual bool BlkIoctl(int request,
+                        uint64_t start,
+                        uint64_t length,
+                        int* result) = 0;
+
+  // Closes a file descriptor. The descriptor must be open prior to this call.
+  // Returns true on success, false otherwise. Specific implementations may set
+  // errno accordingly.
+  virtual bool Close() = 0;
+
+  // Resets the file descriptor, abandoning a currently open file and returning
+  // the descriptor to the closed state.
+  virtual void Reset() = 0;
+
+  // Indicates whether or not an implementation sets meaningful errno.
+  virtual bool IsSettingErrno() = 0;
+
+  // Indicates whether the descriptor is currently open.
+  virtual bool IsOpen() = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FileDescriptor);
+};
+
+// A simple EINTR-immune wrapper implementation around standard system calls.
+class EintrSafeFileDescriptor : public FileDescriptor {
+ public:
+  EintrSafeFileDescriptor() : fd_(-1) {}
+
+  // Interface methods.
+  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;
+  bool BlkIoctl(int request,
+                uint64_t start,
+                uint64_t length,
+                int* result) override;
+  bool Close() override;
+  void Reset() override;
+  bool IsSettingErrno() override {
+    return true;
+  }
+  bool IsOpen() override {
+    return (fd_ >= 0);
+  }
+
+ protected:
+  int fd_;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_FILE_DESCRIPTOR_H_
diff --git a/payload_consumer/file_writer.cc b/payload_consumer/file_writer.cc
new file mode 100644
index 0000000..d280ddb
--- /dev/null
+++ b/payload_consumer/file_writer.cc
@@ -0,0 +1,60 @@
+//
+// 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_consumer/file_writer.h"
+
+#include <errno.h>
+
+namespace chromeos_update_engine {
+
+int DirectFileWriter::Open(const char* path, int flags, mode_t mode) {
+  CHECK_EQ(fd_, -1);
+  fd_ = open(path, flags, mode);
+  if (fd_ < 0)
+    return -errno;
+  return 0;
+}
+
+bool DirectFileWriter::Write(const void* bytes, size_t count) {
+  CHECK_GE(fd_, 0);
+  const char* char_bytes = reinterpret_cast<const char*>(bytes);
+
+  size_t bytes_written = 0;
+  while (bytes_written < count) {
+    ssize_t rc = write(fd_, char_bytes + bytes_written,
+                       count - bytes_written);
+    if (rc < 0)
+      return false;
+    bytes_written += rc;
+  }
+  CHECK_EQ(bytes_written, count);
+  return bytes_written == count;
+}
+
+int DirectFileWriter::Close() {
+  CHECK_GE(fd_, 0);
+  int rc = close(fd_);
+
+  // This can be any negative number that's not -1. This way, this FileWriter
+  // won't be used again for another file.
+  fd_ = -2;
+
+  if (rc < 0)
+    return -errno;
+  return rc;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/file_writer.h b/payload_consumer/file_writer.h
new file mode 100644
index 0000000..96ebde6
--- /dev/null
+++ b/payload_consumer/file_writer.h
@@ -0,0 +1,103 @@
+//
+// 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_CONSUMER_FILE_WRITER_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_FILE_WRITER_H_
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <base/logging.h>
+
+#include "update_engine/common/error_code.h"
+#include "update_engine/common/utils.h"
+
+// FileWriter is a class that is used to (synchronously, for now) write to
+// a file. This file is a thin wrapper around open/write/close system calls,
+// but provides and interface that can be customized by subclasses that wish
+// to filter the data.
+
+namespace chromeos_update_engine {
+
+class FileWriter {
+ public:
+  FileWriter() {}
+  virtual ~FileWriter() {}
+
+  // Wrapper around write. Returns true if all requested bytes
+  // were written, or false on any error, regardless of progress.
+  virtual bool Write(const void* bytes, size_t count) = 0;
+
+  // Same as the Write method above but returns a detailed |error| code
+  // in addition if the returned value is false. By default this method
+  // returns kActionExitDownloadWriteError as the error code, but subclasses
+  // can override if they wish to return more specific error codes.
+  virtual bool Write(const void* bytes,
+                     size_t count,
+                     ErrorCode* error) {
+     *error = ErrorCode::kDownloadWriteError;
+     return Write(bytes, count);
+  }
+
+  // Wrapper around close. Returns 0 on success or -errno on error.
+  virtual int Close() = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FileWriter);
+};
+
+// Direct file writer is probably the simplest FileWriter implementation.
+// It calls the system calls directly.
+
+class DirectFileWriter : public FileWriter {
+ public:
+  DirectFileWriter() = default;
+
+  // FileWriter overrides.
+  bool Write(const void* bytes, size_t count) override;
+  int Close() override;
+
+  // Wrapper around open. Returns 0 on success or -errno on error.
+  int Open(const char* path, int flags, mode_t mode);
+
+  int fd() const { return fd_; }
+
+ private:
+  int fd_{-1};
+
+  DISALLOW_COPY_AND_ASSIGN(DirectFileWriter);
+};
+
+class ScopedFileWriterCloser {
+ public:
+  explicit ScopedFileWriterCloser(FileWriter* writer) : writer_(writer) {}
+  ~ScopedFileWriterCloser() {
+    int err = writer_->Close();
+    if (err)
+      LOG(ERROR) << "FileWriter::Close failed: "
+                 << utils::ErrnoNumberAsString(-err);
+  }
+ private:
+  FileWriter* writer_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedFileWriterCloser);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_FILE_WRITER_H_
diff --git a/payload_consumer/file_writer_unittest.cc b/payload_consumer/file_writer_unittest.cc
new file mode 100644
index 0000000..debb4c3
--- /dev/null
+++ b/payload_consumer/file_writer_unittest.cc
@@ -0,0 +1,79 @@
+//
+// 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_consumer/file_writer.h"
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <brillo/secure_blob.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+class FileWriterTest : public ::testing::Test { };
+
+TEST(FileWriterTest, SimpleTest) {
+  // Create a uniquely named file for testing.
+  string path;
+  ASSERT_TRUE(utils::MakeTempFile("FileWriterTest-XXXXXX", &path, nullptr));
+  ScopedPathUnlinker path_unlinker(path);
+
+  DirectFileWriter file_writer;
+  EXPECT_EQ(0, file_writer.Open(path.c_str(),
+                                O_CREAT | O_LARGEFILE | O_TRUNC | O_WRONLY,
+                                0644));
+  EXPECT_TRUE(file_writer.Write("test", 4));
+  brillo::Blob actual_data;
+  EXPECT_TRUE(utils::ReadFile(path, &actual_data));
+
+  EXPECT_FALSE(memcmp("test", actual_data.data(), actual_data.size()));
+  EXPECT_EQ(0, file_writer.Close());
+}
+
+TEST(FileWriterTest, ErrorTest) {
+  DirectFileWriter file_writer;
+  const string path("/tmp/ENOENT/FileWriterTest");
+  EXPECT_EQ(-ENOENT, file_writer.Open(path.c_str(),
+                                      O_CREAT | O_LARGEFILE | O_TRUNC, 0644));
+}
+
+TEST(FileWriterTest, WriteErrorTest) {
+  // Create a uniquely named file for testing.
+  string path;
+  ASSERT_TRUE(utils::MakeTempFile("FileWriterTest-XXXXXX", &path, nullptr));
+  ScopedPathUnlinker path_unlinker(path);
+
+  DirectFileWriter file_writer;
+  EXPECT_EQ(0, file_writer.Open(path.c_str(),
+                                O_CREAT | O_LARGEFILE | O_TRUNC | O_RDONLY,
+                                0644));
+  EXPECT_FALSE(file_writer.Write("x", 1));
+  EXPECT_EQ(0, file_writer.Close());
+}
+
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/filesystem_verifier_action.cc b/payload_consumer/filesystem_verifier_action.cc
new file mode 100644
index 0000000..8530d37
--- /dev/null
+++ b/payload_consumer/filesystem_verifier_action.cc
@@ -0,0 +1,287 @@
+//
+// 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_consumer/filesystem_verifier_action.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <algorithm>
+#include <cstdlib>
+#include <string>
+
+#include <base/bind.h>
+#include <brillo/streams/file_stream.h>
+
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/delta_performer.h"
+#include "update_engine/payload_consumer/payload_constants.h"
+
+using std::string;
+
+namespace chromeos_update_engine {
+
+namespace {
+const off_t kReadFileBufferSize = 128 * 1024;
+}  // namespace
+
+FilesystemVerifierAction::FilesystemVerifierAction(
+    const BootControlInterface* boot_control,
+    VerifierMode verifier_mode)
+    : verifier_mode_(verifier_mode),
+      boot_control_(boot_control) {}
+
+void FilesystemVerifierAction::PerformAction() {
+  // Will tell the ActionProcessor we've failed if we return.
+  ScopedActionCompleter abort_action_completer(processor_, this);
+
+  if (!HasInputObject()) {
+    LOG(ERROR) << "FilesystemVerifierAction missing input object.";
+    return;
+  }
+  install_plan_ = GetInputObject();
+
+  // For delta updates (major version 1) we need to populate the source
+  // partition hash if not pre-populated.
+  if (!install_plan_.is_full_update && install_plan_.partitions.empty() &&
+      verifier_mode_ == VerifierMode::kComputeSourceHash &&
+      DeltaPerformer::kSupportedMinorPayloadVersion <
+          kOpSrcHashMinorPayloadVersion) {
+    LOG(INFO) << "Using legacy partition names.";
+    InstallPlan::Partition part;
+    string part_path;
+
+    part.name = kLegacyPartitionNameRoot;
+    if (!boot_control_->GetPartitionDevice(
+        part.name, install_plan_.source_slot, &part_path))
+      return;
+    int block_count = 0, block_size = 0;
+    if (utils::GetFilesystemSize(part_path, &block_count, &block_size)) {
+      part.source_size = static_cast<int64_t>(block_count) * block_size;
+      LOG(INFO) << "Partition " << part.name << " size: " << part.source_size
+                << " bytes (" << block_count << "x" << block_size << ").";
+    }
+    install_plan_.partitions.push_back(part);
+
+    part.name = kLegacyPartitionNameKernel;
+    if (!boot_control_->GetPartitionDevice(
+        part.name, install_plan_.source_slot, &part_path))
+      return;
+    off_t kernel_part_size = utils::FileSize(part_path);
+    if (kernel_part_size < 0)
+      return;
+    LOG(INFO) << "Partition " << part.name << " size: " << kernel_part_size
+              << " bytes.";
+    part.source_size = kernel_part_size;
+    install_plan_.partitions.push_back(part);
+  }
+
+  if (install_plan_.partitions.empty()) {
+    LOG(INFO) << "No partitions to verify.";
+    if (HasOutputPipe())
+      SetOutputObject(install_plan_);
+    abort_action_completer.set_code(ErrorCode::kSuccess);
+    return;
+  }
+
+  StartPartitionHashing();
+  abort_action_completer.set_should_complete(false);
+}
+
+void FilesystemVerifierAction::TerminateProcessing() {
+  cancelled_ = true;
+  Cleanup(ErrorCode::kSuccess);  // error code is ignored if canceled_ is true.
+}
+
+bool FilesystemVerifierAction::IsCleanupPending() const {
+  return src_stream_ != nullptr;
+}
+
+void FilesystemVerifierAction::Cleanup(ErrorCode code) {
+  src_stream_.reset();
+  // This memory is not used anymore.
+  buffer_.clear();
+
+  if (cancelled_)
+    return;
+  if (code == ErrorCode::kSuccess && HasOutputPipe())
+    SetOutputObject(install_plan_);
+  processor_->ActionComplete(this, code);
+}
+
+void FilesystemVerifierAction::StartPartitionHashing() {
+  if (partition_index_ == install_plan_.partitions.size()) {
+    // We never called this action with kVerifySourceHash directly, if we are in
+    // this mode, it means the target partition verification has failed, so we
+    // should set the error code to reflect the error in target.
+    if (verifier_mode_ == VerifierMode::kVerifySourceHash)
+      Cleanup(ErrorCode::kNewRootfsVerificationError);
+    else
+      Cleanup(ErrorCode::kSuccess);
+    return;
+  }
+  InstallPlan::Partition& partition =
+      install_plan_.partitions[partition_index_];
+
+  string part_path;
+  switch (verifier_mode_) {
+    case VerifierMode::kComputeSourceHash:
+    case VerifierMode::kVerifySourceHash:
+      boot_control_->GetPartitionDevice(
+          partition.name, install_plan_.source_slot, &part_path);
+      remaining_size_ = partition.source_size;
+      break;
+    case VerifierMode::kVerifyTargetHash:
+      boot_control_->GetPartitionDevice(
+          partition.name, install_plan_.target_slot, &part_path);
+      remaining_size_ = partition.target_size;
+      break;
+  }
+  LOG(INFO) << "Hashing partition " << partition_index_ << " ("
+            << partition.name << ") on device " << part_path;
+  if (part_path.empty())
+    return Cleanup(ErrorCode::kFilesystemVerifierError);
+
+  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";
+    return Cleanup(ErrorCode::kFilesystemVerifierError);
+  }
+
+  buffer_.resize(kReadFileBufferSize);
+  read_done_ = false;
+  hasher_.reset(new HashCalculator());
+
+  // Start the first read.
+  ScheduleRead();
+}
+
+void FilesystemVerifierAction::ScheduleRead() {
+  size_t bytes_to_read = std::min(static_cast<int64_t>(buffer_.size()),
+                                  remaining_size_);
+  if (!bytes_to_read) {
+    OnReadDoneCallback(0);
+    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);
+  }
+}
+
+void FilesystemVerifierAction::OnReadDoneCallback(size_t bytes_read) {
+  if (bytes_read == 0) {
+    read_done_ = true;
+  } else {
+    remaining_size_ -= bytes_read;
+    CHECK(!read_done_);
+    if (!hasher_->Update(buffer_.data(), bytes_read)) {
+      LOG(ERROR) << "Unable to update the hash.";
+      Cleanup(ErrorCode::kError);
+      return;
+    }
+  }
+
+  // We either terminate the current partition or have more data to read.
+  if (cancelled_)
+    return Cleanup(ErrorCode::kError);
+
+  if (read_done_ || remaining_size_ == 0) {
+    if (remaining_size_ != 0) {
+      LOG(ERROR) << "Failed to read the remaining " << remaining_size_
+                 << " bytes from partition "
+                 << install_plan_.partitions[partition_index_].name;
+      return Cleanup(ErrorCode::kFilesystemVerifierError);
+    }
+    return FinishPartitionHashing();
+  }
+  ScheduleRead();
+}
+
+void FilesystemVerifierAction::OnReadErrorCallback(
+      const brillo::Error* error) {
+  // TODO(deymo): Transform the read-error into an specific ErrorCode.
+  LOG(ERROR) << "Asynchronous read failed.";
+  Cleanup(ErrorCode::kError);
+}
+
+void FilesystemVerifierAction::FinishPartitionHashing() {
+  if (!hasher_->Finalize()) {
+    LOG(ERROR) << "Unable to finalize the hash.";
+    return Cleanup(ErrorCode::kError);
+  }
+  InstallPlan::Partition& partition =
+      install_plan_.partitions[partition_index_];
+  LOG(INFO) << "Hash of " << partition.name << ": " << hasher_->hash();
+
+  switch (verifier_mode_) {
+    case VerifierMode::kComputeSourceHash:
+      partition.source_hash = hasher_->raw_hash();
+      partition_index_++;
+      break;
+    case VerifierMode::kVerifyTargetHash:
+      if (partition.target_hash != hasher_->raw_hash()) {
+        LOG(ERROR) << "New '" << partition.name
+                   << "' partition verification failed.";
+        if (DeltaPerformer::kSupportedMinorPayloadVersion <
+            kOpSrcHashMinorPayloadVersion)
+          return Cleanup(ErrorCode::kNewRootfsVerificationError);
+        // If we support per-operation source hash, then we skipped source
+        // filesystem verification, now that the target partition does not
+        // match, we need to switch to kVerifySourceHash mode to check if it's
+        // because the source partition does not match either.
+        verifier_mode_ = VerifierMode::kVerifySourceHash;
+        partition_index_ = 0;
+      } else {
+        partition_index_++;
+      }
+      break;
+    case VerifierMode::kVerifySourceHash:
+      if (partition.source_hash != hasher_->raw_hash()) {
+        LOG(ERROR) << "Old '" << partition.name
+                   << "' partition verification failed.";
+        return Cleanup(ErrorCode::kDownloadStateInitializationError);
+      }
+      partition_index_++;
+      break;
+  }
+  // Start hashing the next partition, if any.
+  hasher_.reset();
+  buffer_.clear();
+  src_stream_->CloseBlocking(nullptr);
+  StartPartitionHashing();
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/filesystem_verifier_action.h b/payload_consumer/filesystem_verifier_action.h
new file mode 100644
index 0000000..94f1b4e
--- /dev/null
+++ b/payload_consumer/filesystem_verifier_action.h
@@ -0,0 +1,130 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_PAYLOAD_CONSUMER_FILESYSTEM_VERIFIER_ACTION_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_FILESYSTEM_VERIFIER_ACTION_H_
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include <brillo/streams/stream.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "update_engine/common/action.h"
+#include "update_engine/common/hash_calculator.h"
+#include "update_engine/payload_consumer/install_plan.h"
+
+// This action will hash all the partitions of a single slot involved in the
+// update (either source or target slot). The hashes are then either stored in
+// the InstallPlan (for source partitions) or verified against it (for target
+// partitions).
+
+namespace chromeos_update_engine {
+
+// The mode we are running the FilesystemVerifier on. On kComputeSourceHash mode
+// it computes the source_hash of all the partitions in the InstallPlan, based
+// on the already populated source_size values. On kVerifyTargetHash it computes
+// the hash on the target partitions based on the already populated size and
+// verifies it matches the one in the target_hash in the InstallPlan.
+enum class VerifierMode {
+  kComputeSourceHash,
+  kVerifyTargetHash,
+  kVerifySourceHash,
+};
+
+class FilesystemVerifierAction : public InstallPlanAction {
+ public:
+  FilesystemVerifierAction(const BootControlInterface* boot_control,
+                           VerifierMode verifier_mode);
+
+  void PerformAction() override;
+  void TerminateProcessing() override;
+
+  // Used for testing. Return true if Cleanup() has not yet been called due
+  // to a callback upon the completion or cancellation of the verifier action.
+  // A test should wait until IsCleanupPending() returns false before
+  // terminating the main loop.
+  bool IsCleanupPending() const;
+
+  // Debugging/logging
+  static std::string StaticType() { return "FilesystemVerifierAction"; }
+  std::string Type() const override { return StaticType(); }
+
+ private:
+  friend class FilesystemVerifierActionTest;
+  FRIEND_TEST(FilesystemVerifierActionTest,
+              RunAsRootDetermineFilesystemSizeTest);
+
+  // Starts the hashing of the current partition. If there aren't any partitions
+  // remaining to be hashed, if finishes the action.
+  void StartPartitionHashing();
+
+  // Schedules the asynchronous read of the filesystem.
+  void ScheduleRead();
+
+  // 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.
+  void FinishPartitionHashing();
+
+  // Cleans up all the variables we use for async operations and tells the
+  // ActionProcessor we're done w/ |code| as passed in. |cancelled_| should be
+  // true if TerminateProcessing() was called.
+  void Cleanup(ErrorCode code);
+
+  // The type of the partition that we are verifying.
+  VerifierMode verifier_mode_;
+
+  // The BootControlInterface used to get the partitions based on the slots.
+  const BootControlInterface* const boot_control_;
+
+  // The index in the install_plan_.partitions vector of the partition currently
+  // being hashed.
+  size_t partition_index_{0};
+
+  // 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 read_done_{false};  // true if reached EOF on the input stream.
+  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_;
+
+  // Reads and hashes this many bytes from the head of the input stream. This
+  // field is initialized from the corresponding InstallPlan::Partition size,
+  // when the partition starts to be hashed.
+  int64_t remaining_size_{0};
+
+  DISALLOW_COPY_AND_ASSIGN(FilesystemVerifierAction);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_FILESYSTEM_VERIFIER_ACTION_H_
diff --git a/payload_consumer/filesystem_verifier_action_unittest.cc b/payload_consumer/filesystem_verifier_action_unittest.cc
new file mode 100644
index 0000000..0b6232b
--- /dev/null
+++ b/payload_consumer/filesystem_verifier_action_unittest.cc
@@ -0,0 +1,386 @@
+//
+// 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_consumer/filesystem_verifier_action.h"
+
+#include <fcntl.h>
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/posix/eintr_wrapper.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/bind_lambda.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/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"
+#include "update_engine/payload_consumer/payload_constants.h"
+
+using brillo::MessageLoop;
+using std::set;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+class FilesystemVerifierActionTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    loop_.SetAsCurrent();
+  }
+
+  void TearDown() override {
+    EXPECT_EQ(0, brillo::MessageLoopRunMaxIterations(&loop_, 1));
+  }
+
+  // Returns true iff test has completed successfully.
+  bool DoTest(bool terminate_early,
+              bool hash_fail,
+              VerifierMode verifier_mode);
+
+  brillo::FakeMessageLoop loop_{nullptr};
+  FakeBootControl fake_boot_control_;
+};
+
+class FilesystemVerifierActionTestDelegate : public ActionProcessorDelegate {
+ public:
+  explicit FilesystemVerifierActionTestDelegate(
+      FilesystemVerifierAction* action)
+      : action_(action), ran_(false), code_(ErrorCode::kError) {}
+  void ExitMainLoop() {
+    // We need to wait for the Action to call Cleanup.
+    if (action_->IsCleanupPending()) {
+      LOG(INFO) << "Waiting for Cleanup() to be called.";
+      MessageLoop::current()->PostDelayedTask(
+          FROM_HERE,
+          base::Bind(&FilesystemVerifierActionTestDelegate::ExitMainLoop,
+                     base::Unretained(this)),
+          base::TimeDelta::FromMilliseconds(100));
+    } else {
+      MessageLoop::current()->BreakLoop();
+    }
+  }
+  void ProcessingDone(const ActionProcessor* processor, ErrorCode code) {
+    ExitMainLoop();
+  }
+  void ProcessingStopped(const ActionProcessor* processor) {
+    ExitMainLoop();
+  }
+  void ActionCompleted(ActionProcessor* processor,
+                       AbstractAction* action,
+                       ErrorCode code) {
+    if (action->Type() == FilesystemVerifierAction::StaticType()) {
+      ran_ = true;
+      code_ = code;
+    }
+  }
+  bool ran() const { return ran_; }
+  ErrorCode code() const { return code_; }
+
+ private:
+  FilesystemVerifierAction* action_;
+  bool ran_;
+  ErrorCode code_;
+};
+
+void StartProcessorInRunLoop(ActionProcessor* processor,
+                             FilesystemVerifierAction* filesystem_copier_action,
+                             bool terminate_early) {
+  processor->StartProcessing();
+  if (terminate_early) {
+    EXPECT_NE(nullptr, filesystem_copier_action);
+    processor->StopProcessing();
+  }
+}
+
+// TODO(garnold) Temporarily disabling this test, see chromium-os:31082 for
+// details; still trying to track down the root cause for these rare write
+// failures and whether or not they are due to the test setup or an inherent
+// issue with the chroot environment, library versions we use, etc.
+TEST_F(FilesystemVerifierActionTest, DISABLED_RunAsRootSimpleTest) {
+  ASSERT_EQ(0, getuid());
+  bool test = DoTest(false, false, VerifierMode::kComputeSourceHash);
+  EXPECT_TRUE(test);
+  if (!test)
+    return;
+  test = DoTest(false, false, VerifierMode::kVerifyTargetHash);
+  EXPECT_TRUE(test);
+}
+
+bool FilesystemVerifierActionTest::DoTest(bool terminate_early,
+                                          bool hash_fail,
+                                          VerifierMode verifier_mode) {
+  string a_loop_file;
+
+  if (!(utils::MakeTempFile("a_loop_file.XXXXXX", &a_loop_file, nullptr))) {
+    ADD_FAILURE();
+    return false;
+  }
+  ScopedPathUnlinker a_loop_file_unlinker(a_loop_file);
+
+  // Make random data for a.
+  const size_t kLoopFileSize = 10 * 1024 * 1024 + 512;
+  brillo::Blob a_loop_data(kLoopFileSize);
+  test_utils::FillWithData(&a_loop_data);
+
+  // Write data to disk
+  if (!(test_utils::WriteFileVector(a_loop_file, a_loop_data))) {
+    ADD_FAILURE();
+    return false;
+  }
+
+  // Attach loop devices to the files
+  string a_dev;
+  test_utils::ScopedLoopbackDeviceBinder a_dev_releaser(a_loop_file, &a_dev);
+  if (!(a_dev_releaser.is_bound())) {
+    ADD_FAILURE();
+    return false;
+  }
+
+  LOG(INFO) << "verifying: "  << a_loop_file << " (" << a_dev << ")";
+
+  bool success = true;
+
+  // Set up the action objects
+  InstallPlan install_plan;
+  install_plan.source_slot = 0;
+  install_plan.target_slot = 1;
+  InstallPlan::Partition part;
+  part.name = "part";
+  if (verifier_mode == VerifierMode::kVerifyTargetHash) {
+    part.target_size = kLoopFileSize - (hash_fail ? 1 : 0);
+    part.target_path = a_dev;
+    fake_boot_control_.SetPartitionDevice(
+        part.name, install_plan.target_slot, a_dev);
+    if (!HashCalculator::RawHashOfData(a_loop_data, &part.target_hash)) {
+      ADD_FAILURE();
+      success = false;
+    }
+  }
+  part.source_size = kLoopFileSize;
+  part.source_path = a_dev;
+  fake_boot_control_.SetPartitionDevice(
+      part.name, install_plan.source_slot, a_dev);
+  if (!HashCalculator::RawHashOfData(a_loop_data, &part.source_hash)) {
+    ADD_FAILURE();
+    success = false;
+  }
+  install_plan.partitions = {part};
+
+  ActionProcessor processor;
+
+  ObjectFeederAction<InstallPlan> feeder_action;
+  FilesystemVerifierAction copier_action(&fake_boot_control_, verifier_mode);
+  ObjectCollectorAction<InstallPlan> collector_action;
+
+  BondActions(&feeder_action, &copier_action);
+  BondActions(&copier_action, &collector_action);
+
+  FilesystemVerifierActionTestDelegate delegate(&copier_action);
+  processor.set_delegate(&delegate);
+  processor.EnqueueAction(&feeder_action);
+  processor.EnqueueAction(&copier_action);
+  processor.EnqueueAction(&collector_action);
+
+  feeder_action.set_obj(install_plan);
+
+  loop_.PostTask(FROM_HERE, base::Bind(&StartProcessorInRunLoop,
+                                       &processor,
+                                       &copier_action,
+                                       terminate_early));
+  loop_.Run();
+
+  if (!terminate_early) {
+    bool is_delegate_ran = delegate.ran();
+    EXPECT_TRUE(is_delegate_ran);
+    success = success && is_delegate_ran;
+  } else {
+    EXPECT_EQ(ErrorCode::kError, delegate.code());
+    return (ErrorCode::kError == delegate.code());
+  }
+  if (hash_fail) {
+    ErrorCode expected_exit_code = ErrorCode::kNewRootfsVerificationError;
+    EXPECT_EQ(expected_exit_code, delegate.code());
+    return (expected_exit_code == delegate.code());
+  }
+  EXPECT_EQ(ErrorCode::kSuccess, delegate.code());
+
+  // Make sure everything in the out_image is there
+  brillo::Blob a_out;
+  if (!utils::ReadFile(a_dev, &a_out)) {
+    ADD_FAILURE();
+    return false;
+  }
+  const bool is_a_file_reading_eq =
+      test_utils::ExpectVectorsEq(a_loop_data, a_out);
+  EXPECT_TRUE(is_a_file_reading_eq);
+  success = success && is_a_file_reading_eq;
+
+  bool is_install_plan_eq = (collector_action.object() == install_plan);
+  EXPECT_TRUE(is_install_plan_eq);
+  success = success && is_install_plan_eq;
+  return success;
+}
+
+class FilesystemVerifierActionTest2Delegate : public ActionProcessorDelegate {
+ public:
+  void ActionCompleted(ActionProcessor* processor,
+                       AbstractAction* action,
+                       ErrorCode code) {
+    if (action->Type() == FilesystemVerifierAction::StaticType()) {
+      ran_ = true;
+      code_ = code;
+    }
+  }
+  bool ran_;
+  ErrorCode code_;
+};
+
+TEST_F(FilesystemVerifierActionTest, MissingInputObjectTest) {
+  ActionProcessor processor;
+  FilesystemVerifierActionTest2Delegate delegate;
+
+  processor.set_delegate(&delegate);
+
+  FilesystemVerifierAction copier_action(&fake_boot_control_,
+                                         VerifierMode::kVerifyTargetHash);
+  ObjectCollectorAction<InstallPlan> collector_action;
+
+  BondActions(&copier_action, &collector_action);
+
+  processor.EnqueueAction(&copier_action);
+  processor.EnqueueAction(&collector_action);
+  processor.StartProcessing();
+  EXPECT_FALSE(processor.IsRunning());
+  EXPECT_TRUE(delegate.ran_);
+  EXPECT_EQ(ErrorCode::kError, delegate.code_);
+}
+
+TEST_F(FilesystemVerifierActionTest, NonExistentDriveTest) {
+  ActionProcessor processor;
+  FilesystemVerifierActionTest2Delegate delegate;
+
+  processor.set_delegate(&delegate);
+
+  ObjectFeederAction<InstallPlan> feeder_action;
+  InstallPlan install_plan(false,
+                           false,
+                           "",
+                           0,
+                           "",
+                           0,
+                           "",
+                           "");
+  InstallPlan::Partition part;
+  part.name = "nope";
+  part.source_path = "/no/such/file";
+  part.target_path = "/no/such/file";
+  install_plan.partitions = {part};
+
+  feeder_action.set_obj(install_plan);
+  FilesystemVerifierAction verifier_action(&fake_boot_control_,
+                                           VerifierMode::kVerifyTargetHash);
+  ObjectCollectorAction<InstallPlan> collector_action;
+
+  BondActions(&verifier_action, &collector_action);
+
+  processor.EnqueueAction(&feeder_action);
+  processor.EnqueueAction(&verifier_action);
+  processor.EnqueueAction(&collector_action);
+  processor.StartProcessing();
+  EXPECT_FALSE(processor.IsRunning());
+  EXPECT_TRUE(delegate.ran_);
+  EXPECT_EQ(ErrorCode::kError, delegate.code_);
+}
+
+TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashTest) {
+  ASSERT_EQ(0, getuid());
+  EXPECT_TRUE(DoTest(false, false, VerifierMode::kVerifyTargetHash));
+  EXPECT_TRUE(DoTest(false, false, VerifierMode::kComputeSourceHash));
+}
+
+TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashFailTest) {
+  ASSERT_EQ(0, getuid());
+  EXPECT_TRUE(DoTest(false, true, VerifierMode::kVerifyTargetHash));
+}
+
+TEST_F(FilesystemVerifierActionTest, RunAsRootTerminateEarlyTest) {
+  ASSERT_EQ(0, getuid());
+  EXPECT_TRUE(DoTest(true, false, VerifierMode::kVerifyTargetHash));
+  // TerminateEarlyTest may leak some null callbacks from the Stream class.
+  while (loop_.RunOnce(false)) {}
+}
+
+// Disabled as we switched to minor version 3, so this test is obsolete, will be
+// deleted when we delete the corresponding code in PerformAction().
+// Test that the rootfs and kernel size used for hashing in delta payloads for
+// major version 1 is properly read.
+TEST_F(FilesystemVerifierActionTest,
+       DISABLED_RunAsRootDetermineLegacySizeTest) {
+  string img;
+  EXPECT_TRUE(utils::MakeTempFile("img.XXXXXX", &img, nullptr));
+  ScopedPathUnlinker img_unlinker(img);
+  test_utils::CreateExtImageAtPath(img, nullptr);
+  // Extend the "partition" holding the file system from 10MiB to 20MiB.
+  EXPECT_EQ(0, truncate(img.c_str(), 20 * 1024 * 1024));
+
+  InstallPlan install_plan;
+  install_plan.source_slot = 1;
+
+  fake_boot_control_.SetPartitionDevice(
+      kLegacyPartitionNameRoot, install_plan.source_slot, img);
+  fake_boot_control_.SetPartitionDevice(
+      kLegacyPartitionNameKernel, install_plan.source_slot, img);
+  FilesystemVerifierAction action(&fake_boot_control_,
+                                  VerifierMode::kComputeSourceHash);
+
+  ObjectFeederAction<InstallPlan> feeder_action;
+  feeder_action.set_obj(install_plan);
+
+  ObjectCollectorAction<InstallPlan> collector_action;
+
+  BondActions(&feeder_action, &action);
+  BondActions(&action, &collector_action);
+  ActionProcessor processor;
+  processor.EnqueueAction(&feeder_action);
+  processor.EnqueueAction(&action);
+  processor.EnqueueAction(&collector_action);
+
+  loop_.PostTask(FROM_HERE,
+                 base::Bind([&processor]{ processor.StartProcessing(); }));
+  loop_.Run();
+  install_plan = collector_action.object();
+
+  ASSERT_EQ(2, install_plan.partitions.size());
+  // When computing the size of the rootfs on legacy delta updates we use the
+  // size of the filesystem, but when updating the kernel we use the whole
+  // partition.
+  EXPECT_EQ(10 * 1024 * 1024, install_plan.partitions[0].source_size);
+  EXPECT_EQ(kLegacyPartitionNameRoot, install_plan.partitions[0].name);
+  EXPECT_EQ(20 * 1024 * 1024, install_plan.partitions[1].source_size);
+  EXPECT_EQ(kLegacyPartitionNameKernel, install_plan.partitions[1].name);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/install_plan.cc b/payload_consumer/install_plan.cc
new file mode 100644
index 0000000..a2a1b7b
--- /dev/null
+++ b/payload_consumer/install_plan.cc
@@ -0,0 +1,124 @@
+//
+// 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.
+//
+
+#include "update_engine/payload_consumer/install_plan.h"
+
+#include <base/format_macros.h>
+#include <base/logging.h>
+#include <base/strings/stringprintf.h>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/payload_constants.h"
+
+using std::string;
+
+namespace chromeos_update_engine {
+
+InstallPlan::InstallPlan(bool is_resume,
+                         bool is_full_update,
+                         const string& url,
+                         uint64_t payload_size,
+                         const string& payload_hash,
+                         uint64_t metadata_size,
+                         const string& metadata_signature,
+                         const string& public_key_rsa)
+    : is_resume(is_resume),
+      is_full_update(is_full_update),
+      download_url(url),
+      payload_size(payload_size),
+      payload_hash(payload_hash),
+      metadata_size(metadata_size),
+      metadata_signature(metadata_signature),
+      hash_checks_mandatory(false),
+      powerwash_required(false),
+      public_key_rsa(public_key_rsa) {}
+
+
+bool InstallPlan::operator==(const InstallPlan& that) const {
+  return ((is_resume == that.is_resume) &&
+          (is_full_update == that.is_full_update) &&
+          (download_url == that.download_url) &&
+          (payload_size == that.payload_size) &&
+          (payload_hash == that.payload_hash) &&
+          (metadata_size == that.metadata_size) &&
+          (metadata_signature == that.metadata_signature) &&
+          (source_slot == that.source_slot) &&
+          (target_slot == that.target_slot) &&
+          (partitions == that.partitions));
+}
+
+bool InstallPlan::operator!=(const InstallPlan& that) const {
+  return !((*this) == that);
+}
+
+void InstallPlan::Dump() const {
+  string partitions_str;
+  for (const auto& partition : partitions) {
+    partitions_str += base::StringPrintf(
+        ", part: %s (source_size: %" PRIu64 ", target_size %" PRIu64 ")",
+        partition.name.c_str(), partition.source_size, partition.target_size);
+  }
+
+  LOG(INFO) << "InstallPlan: "
+            << (is_resume ? "resume" : "new_update")
+            << ", payload type: " << (is_full_update ? "full" : "delta")
+            << ", source_slot: " << BootControlInterface::SlotName(source_slot)
+            << ", target_slot: " << BootControlInterface::SlotName(target_slot)
+            << ", url: " << download_url
+            << ", payload size: " << payload_size
+            << ", payload hash: " << payload_hash
+            << ", metadata size: " << metadata_size
+            << ", metadata signature: " << metadata_signature
+            << partitions_str
+            << ", hash_checks_mandatory: " << utils::ToString(
+                hash_checks_mandatory)
+            << ", powerwash_required: " << utils::ToString(
+                powerwash_required);
+}
+
+bool InstallPlan::LoadPartitionsFromSlots(BootControlInterface* boot_control) {
+  bool result = true;
+  for (Partition& partition : partitions) {
+    if (source_slot != BootControlInterface::kInvalidSlot) {
+      result = boot_control->GetPartitionDevice(
+          partition.name, source_slot, &partition.source_path) && result;
+    } else {
+      partition.source_path.clear();
+    }
+
+    if (target_slot != BootControlInterface::kInvalidSlot) {
+      result = boot_control->GetPartitionDevice(
+          partition.name, target_slot, &partition.target_path) && result;
+    } else {
+      partition.target_path.clear();
+    }
+  }
+  return result;
+}
+
+bool InstallPlan::Partition::operator==(
+    const InstallPlan::Partition& that) const {
+  return (name == that.name &&
+          source_path == that.source_path &&
+          source_size == that.source_size &&
+          source_hash == that.source_hash &&
+          target_path == that.target_path &&
+          target_size == that.target_size &&
+          target_hash == that.target_hash &&
+          run_postinstall == that.run_postinstall);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/install_plan.h b/payload_consumer/install_plan.h
new file mode 100644
index 0000000..e69462d
--- /dev/null
+++ b/payload_consumer/install_plan.h
@@ -0,0 +1,156 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_PAYLOAD_CONSUMER_INSTALL_PLAN_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_INSTALL_PLAN_H_
+
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <brillo/secure_blob.h>
+
+#include "update_engine/common/action.h"
+#include "update_engine/common/boot_control_interface.h"
+
+// InstallPlan is a simple struct that contains relevant info for many
+// parts of the update system about the install that should happen.
+namespace chromeos_update_engine {
+
+struct InstallPlan {
+  InstallPlan(bool is_resume,
+              bool is_full_update,
+              const std::string& url,
+              uint64_t payload_size,
+              const std::string& payload_hash,
+              uint64_t metadata_size,
+              const std::string& metadata_signature,
+              const std::string& public_key_rsa);
+
+  // Default constructor.
+  InstallPlan() = default;
+
+  bool operator==(const InstallPlan& that) const;
+  bool operator!=(const InstallPlan& that) const;
+
+  void Dump() const;
+
+  // Load the |source_path| and |target_path| of all |partitions| based on the
+  // |source_slot| and |target_slot| if available. Returns whether it succeeded
+  // to load all the partitions for the valid slots.
+  bool LoadPartitionsFromSlots(BootControlInterface* boot_control);
+
+  bool is_resume{false};
+  bool is_full_update{false};
+  std::string download_url;  // url to download from
+  std::string version;       // version we are installing.
+
+  uint64_t payload_size{0};              // size of the payload
+  std::string payload_hash;              // SHA256 hash of the payload
+  uint64_t metadata_size{0};             // size of the metadata
+  std::string metadata_signature;        // signature of the  metadata
+
+  // The partition slots used for the update.
+  BootControlInterface::Slot source_slot{BootControlInterface::kInvalidSlot};
+  BootControlInterface::Slot target_slot{BootControlInterface::kInvalidSlot};
+
+  // The vector below is used for partition verification. The flow is:
+  //
+  // 1. FilesystemVerifierAction computes and fills in the source partition
+  // hash based on the guessed source size for delta major version 1 updates.
+  //
+  // 2. DownloadAction verifies the source partition sizes and hashes against
+  // the expected values transmitted in the update manifest. It fills in the
+  // expected target partition sizes and hashes based on the manifest.
+  //
+  // 3. FilesystemVerifierAction computes and verifies the applied partition
+  // sizes and hashes against the expected values in target_partition_hashes.
+  struct Partition {
+    bool operator==(const Partition& that) const;
+
+    // The name of the partition.
+    std::string name;
+
+    std::string source_path;
+    uint64_t source_size{0};
+    brillo::Blob source_hash;
+
+    std::string target_path;
+    uint64_t target_size{0};
+    brillo::Blob target_hash;
+
+    // Whether we should run the postinstall script from this partition.
+    bool run_postinstall{false};
+  };
+  std::vector<Partition> partitions;
+
+  // True if payload hash checks are mandatory based on the system state and
+  // the Omaha response.
+  bool hash_checks_mandatory{false};
+
+  // True if Powerwash is required on reboot after applying the payload.
+  // False otherwise.
+  bool powerwash_required{false};
+
+  // If not blank, a base-64 encoded representation of the PEM-encoded
+  // public key in the response.
+  std::string public_key_rsa;
+};
+
+class InstallPlanAction;
+
+template<>
+class ActionTraits<InstallPlanAction> {
+ public:
+  // Takes the install plan as input
+  typedef InstallPlan InputObjectType;
+  // Passes the install plan as output
+  typedef InstallPlan OutputObjectType;
+};
+
+// Basic action that only receives and sends Install Plans.
+// Can be used to construct an Install Plan to send to any other Action that
+// accept an InstallPlan.
+class InstallPlanAction : public Action<InstallPlanAction> {
+ public:
+  InstallPlanAction() {}
+  explicit InstallPlanAction(const InstallPlan& install_plan):
+    install_plan_(install_plan) {}
+
+  void PerformAction() override {
+    if (HasOutputPipe()) {
+      SetOutputObject(install_plan_);
+    }
+    processor_->ActionComplete(this, ErrorCode::kSuccess);
+  }
+
+  InstallPlan* install_plan() { return &install_plan_; }
+
+  static std::string StaticType() { return "InstallPlanAction"; }
+  std::string Type() const override { return StaticType(); }
+
+  typedef ActionTraits<InstallPlanAction>::InputObjectType InputObjectType;
+  typedef ActionTraits<InstallPlanAction>::OutputObjectType OutputObjectType;
+
+ private:
+  InstallPlan install_plan_;
+
+  DISALLOW_COPY_AND_ASSIGN(InstallPlanAction);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_INSTALL_PLAN_H_
diff --git a/payload_consumer/mock_download_action.h b/payload_consumer/mock_download_action.h
new file mode 100644
index 0000000..3abb809
--- /dev/null
+++ b/payload_consumer/mock_download_action.h
@@ -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.
+//
+
+#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/error_code.h"
+#include "update_engine/payload_consumer/download_action.h"
+
+namespace chromeos_update_engine {
+
+class MockDownloadActionDelegate : public DownloadActionDelegate {
+ public:
+  MOCK_METHOD3(BytesReceived,
+               void(uint64_t bytes_progressed,
+                    uint64_t bytes_received,
+                    uint64_t total));
+  MOCK_METHOD1(ShouldCancel, bool(ErrorCode* cancel_reason));
+  MOCK_METHOD0(DownloadComplete, void());
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_MOCK_DOWNLOAD_ACTION_H_
diff --git a/payload_consumer/mtd_file_descriptor.cc b/payload_consumer/mtd_file_descriptor.cc
new file mode 100644
index 0000000..da8fd58
--- /dev/null
+++ b/payload_consumer/mtd_file_descriptor.cc
@@ -0,0 +1,265 @@
+//
+// 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 <string>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <vector>
+
+#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;
+using std::vector;
+
+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::StartsWithASCII(ubi_name.MaybeAsASCII(), "ubi", true));
+
+  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..9ac1ec1
--- /dev/null
+++ b/payload_consumer/mtd_file_descriptor.h
@@ -0,0 +1,102 @@
+//
+// 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 <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;
+  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;
+  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/payload_constants.cc b/payload_consumer/payload_constants.cc
new file mode 100644
index 0000000..72abf8c
--- /dev/null
+++ b/payload_consumer/payload_constants.cc
@@ -0,0 +1,59 @@
+//
+// 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/payload_constants.h"
+
+namespace chromeos_update_engine {
+
+const uint64_t kChromeOSMajorPayloadVersion = 1;
+const uint64_t kBrilloMajorPayloadVersion = 2;
+
+const uint32_t kFullPayloadMinorVersion = 0;
+const uint32_t kInPlaceMinorPayloadVersion = 1;
+const uint32_t kSourceMinorPayloadVersion = 2;
+const uint32_t kOpSrcHashMinorPayloadVersion = 3;
+
+const char kLegacyPartitionNameKernel[] = "boot";
+const char kLegacyPartitionNameRoot[] = "system";
+
+const char kDeltaMagic[4] = {'C', 'r', 'A', 'U'};
+const char kBspatchPath[] = "bspatch";
+
+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:
+      return "REPLACE_BZ";
+    case InstallOperation::SOURCE_COPY:
+      return "SOURCE_COPY";
+    case InstallOperation::SOURCE_BSDIFF:
+      return "SOURCE_BSDIFF";
+    case InstallOperation::ZERO:
+      return "ZERO";
+    case InstallOperation::DISCARD:
+      return "DISCARD";
+    case InstallOperation::REPLACE_XZ:
+      return "REPLACE_XZ";
+  }
+  return "<unknown_op>";
+}
+
+};  // namespace chromeos_update_engine
diff --git a/payload_consumer/payload_constants.h b/payload_consumer/payload_constants.h
new file mode 100644
index 0000000..c3cd363
--- /dev/null
+++ b/payload_consumer/payload_constants.h
@@ -0,0 +1,65 @@
+//
+// 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_PAYLOAD_CONSTANTS_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_PAYLOAD_CONSTANTS_H_
+
+#include <stdint.h>
+
+#include <limits>
+
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+// The major version used by Chrome OS.
+extern const uint64_t kChromeOSMajorPayloadVersion;
+
+// The major version used by Brillo.
+extern const uint64_t kBrilloMajorPayloadVersion;
+
+// The minor version used for all full payloads.
+extern const uint32_t kFullPayloadMinorVersion;
+
+// The minor version used by the in-place delta generator algorithm.
+extern const uint32_t kInPlaceMinorPayloadVersion;
+
+// The minor version used by the A to B delta generator algorithm.
+extern const uint32_t kSourceMinorPayloadVersion;
+
+// The minor version that allows per-operation source hash.
+extern const uint32_t kOpSrcHashMinorPayloadVersion;
+
+
+// The kernel and rootfs partition names used by the BootControlInterface when
+// handling update payloads with a major version 1. The names of the updated
+// partitions are include in the payload itself for major version 2.
+extern const char kLegacyPartitionNameKernel[];
+extern const char kLegacyPartitionNameRoot[];
+
+extern const char kBspatchPath[];
+extern const char kDeltaMagic[4];
+
+// A block number denoting a hole on a sparse file. Used on Extents to refer to
+// section of blocks not present on disk on a sparse file.
+const uint64_t kSparseHole = std::numeric_limits<uint64_t>::max();
+
+// Return the name of the operation type.
+const char* InstallOperationTypeName(InstallOperation_Type op_type);
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_PAYLOAD_CONSTANTS_H_
diff --git a/payload_consumer/payload_verifier.cc b/payload_consumer/payload_verifier.cc
new file mode 100644
index 0000000..ab5238c
--- /dev/null
+++ b/payload_consumer/payload_verifier.cc
@@ -0,0 +1,183 @@
+//
+// 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/payload_verifier.h"
+
+#include <base/logging.h>
+#include <openssl/pem.h>
+
+#include "update_engine/common/hash_calculator.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/update_metadata.pb.h"
+
+using std::string;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+// The following is a standard PKCS1-v1_5 padding for SHA256 signatures, as
+// defined in RFC3447. It is prepended to the actual signature (32 bytes) to
+// form a sequence of 256 bytes (2048 bits) that is amenable to RSA signing. The
+// padded hash will look as follows:
+//
+//    0x00 0x01 0xff ... 0xff 0x00  ASN1HEADER  SHA256HASH
+//   |--------------205-----------||----19----||----32----|
+//
+// where ASN1HEADER is the ASN.1 description of the signed data. The complete 51
+// bytes of actual data (i.e. the ASN.1 header complete with the hash) are
+// packed as follows:
+//
+//  SEQUENCE(2+49) {
+//   SEQUENCE(2+13) {
+//    OBJECT(2+9) id-sha256
+//    NULL(2+0)
+//   }
+//   OCTET STRING(2+32) <actual signature bytes...>
+//  }
+const uint8_t kRSA2048SHA256Padding[] = {
+  // PKCS1-v1_5 padding
+  0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0x00,
+  // ASN.1 header
+  0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+  0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
+  0x00, 0x04, 0x20,
+};
+
+}  // namespace
+
+bool PayloadVerifier::VerifySignature(const brillo::Blob& signature_blob,
+                                      const string& public_key_path,
+                                      const brillo::Blob& hash_data) {
+  TEST_AND_RETURN_FALSE(!public_key_path.empty());
+
+  Signatures signatures;
+  LOG(INFO) << "signature blob size = " <<  signature_blob.size();
+  TEST_AND_RETURN_FALSE(signatures.ParseFromArray(signature_blob.data(),
+                                                  signature_blob.size()));
+
+  if (!signatures.signatures_size()) {
+    LOG(ERROR) << "No signatures stored in the blob.";
+    return false;
+  }
+
+  std::vector<brillo::Blob> tested_hashes;
+  // Tries every signature in the signature blob.
+  for (int i = 0; i < signatures.signatures_size(); i++) {
+    const Signatures_Signature& signature = signatures.signatures(i);
+    brillo::Blob sig_data(signature.data().begin(), signature.data().end());
+    brillo::Blob sig_hash_data;
+    if (!GetRawHashFromSignature(sig_data, public_key_path, &sig_hash_data))
+      continue;
+
+    if (hash_data == sig_hash_data) {
+      LOG(INFO) << "Verified correct signature " << i + 1 << " out of "
+                << signatures.signatures_size() << " signatures.";
+      return true;
+    }
+    tested_hashes.push_back(sig_hash_data);
+  }
+  LOG(ERROR) << "None of the " << signatures.signatures_size()
+             << " signatures is correct. Expected:";
+  utils::HexDumpVector(hash_data);
+  LOG(ERROR) << "But found decrypted hashes:";
+  for (const auto& sig_hash_data : tested_hashes) {
+    utils::HexDumpVector(sig_hash_data);
+  }
+  return false;
+}
+
+
+bool PayloadVerifier::GetRawHashFromSignature(
+    const brillo::Blob& sig_data,
+    const string& public_key_path,
+    brillo::Blob* out_hash_data) {
+  TEST_AND_RETURN_FALSE(!public_key_path.empty());
+
+  // The code below executes the equivalent of:
+  //
+  // openssl rsautl -verify -pubin -inkey |public_key_path|
+  //   -in |sig_data| -out |out_hash_data|
+
+  // Loads the public key.
+  FILE* fpubkey = fopen(public_key_path.c_str(), "rb");
+  if (!fpubkey) {
+    LOG(ERROR) << "Unable to open public key file: " << public_key_path;
+    return false;
+  }
+
+  char dummy_password[] = { ' ', 0 };  // Ensure no password is read from stdin.
+  RSA* rsa = PEM_read_RSA_PUBKEY(fpubkey, nullptr, nullptr, dummy_password);
+  fclose(fpubkey);
+  TEST_AND_RETURN_FALSE(rsa != nullptr);
+  unsigned int keysize = RSA_size(rsa);
+  if (sig_data.size() > 2 * keysize) {
+    LOG(ERROR) << "Signature size is too big for public key size.";
+    RSA_free(rsa);
+    return false;
+  }
+
+  // Decrypts the signature.
+  brillo::Blob hash_data(keysize);
+  int decrypt_size = RSA_public_decrypt(sig_data.size(),
+                                        sig_data.data(),
+                                        hash_data.data(),
+                                        rsa,
+                                        RSA_NO_PADDING);
+  RSA_free(rsa);
+  TEST_AND_RETURN_FALSE(decrypt_size > 0 &&
+                        decrypt_size <= static_cast<int>(hash_data.size()));
+  hash_data.resize(decrypt_size);
+  out_hash_data->swap(hash_data);
+  return true;
+}
+
+bool PayloadVerifier::PadRSA2048SHA256Hash(brillo::Blob* hash) {
+  TEST_AND_RETURN_FALSE(hash->size() == 32);
+  hash->insert(hash->begin(),
+               reinterpret_cast<const char*>(kRSA2048SHA256Padding),
+               reinterpret_cast<const char*>(kRSA2048SHA256Padding +
+                                             sizeof(kRSA2048SHA256Padding)));
+  TEST_AND_RETURN_FALSE(hash->size() == 256);
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/payload_verifier.h b/payload_consumer/payload_verifier.h
new file mode 100644
index 0000000..22ced40
--- /dev/null
+++ b/payload_consumer/payload_verifier.h
@@ -0,0 +1,65 @@
+//
+// 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_PAYLOAD_VERIFIER_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_PAYLOAD_VERIFIER_H_
+
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <brillo/secure_blob.h>
+
+#include "update_engine/update_metadata.pb.h"
+
+// This class encapsulates methods used for payload signature verification.
+// See payload_generator/payload_signer.h for payload signing.
+
+namespace chromeos_update_engine {
+
+class PayloadVerifier {
+ public:
+  // Interprets |signature_blob| as a protocol buffer containing the Signatures
+  // message and decrypts each signature data using the |public_key_path|.
+  // Returns whether *any* of the decrypted hashes matches the |hash_data|.
+  // In case of any error parsing the signatures or the public key, returns
+  // false.
+  static bool VerifySignature(const brillo::Blob& signature_blob,
+                              const std::string& public_key_path,
+                              const brillo::Blob& hash_data);
+
+  // Decrypts sig_data with the given public_key_path and populates
+  // out_hash_data with the decoded raw hash. Returns true if successful,
+  // false otherwise.
+  static bool GetRawHashFromSignature(const brillo::Blob& sig_data,
+                                      const std::string& public_key_path,
+                                      brillo::Blob* out_hash_data);
+
+  // Pads a SHA256 hash so that it may be encrypted/signed with RSA2048
+  // using the PKCS#1 v1.5 scheme.
+  // hash should be a pointer to vector of exactly 256 bits. The vector
+  // will be modified in place and will result in having a length of
+  // 2048 bits. Returns true on success, false otherwise.
+  static bool PadRSA2048SHA256Hash(brillo::Blob* hash);
+
+ private:
+  // This should never be constructed
+  DISALLOW_IMPLICIT_CONSTRUCTORS(PayloadVerifier);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_PAYLOAD_VERIFIER_H_
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
new file mode 100644
index 0000000..84ca398
--- /dev/null
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -0,0 +1,187 @@
+//
+// 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/payload_consumer/postinstall_runner_action.h"
+
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+
+#include "update_engine/common/action_processor.h"
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/subprocess.h"
+#include "update_engine/common/utils.h"
+
+namespace chromeos_update_engine {
+
+using std::string;
+using std::vector;
+
+namespace {
+// The absolute path to the post install command.
+const char kPostinstallScript[] = "/postinst";
+
+// Path to the binary file used by kPostinstallScript. Used to get and log the
+// file format of the binary to debug issues when the ELF format on the update
+// doesn't match the one on the current system. This path is not executed.
+const char kDebugPostinstallBinaryPath[] = "/usr/bin/cros_installer";
+}
+
+void PostinstallRunnerAction::PerformAction() {
+  CHECK(HasInputObject());
+  install_plan_ = GetInputObject();
+
+  if (install_plan_.powerwash_required) {
+    if (utils::CreatePowerwashMarkerFile(powerwash_marker_file_)) {
+      powerwash_marker_created_ = true;
+    } else {
+      return CompletePostinstall(ErrorCode::kPostinstallPowerwashError);
+    }
+  }
+
+  PerformPartitionPostinstall();
+}
+
+void PostinstallRunnerAction::PerformPartitionPostinstall() {
+  // Skip all the partitions that don't have a post-install step.
+  while (current_partition_ < install_plan_.partitions.size() &&
+         !install_plan_.partitions[current_partition_].run_postinstall) {
+    VLOG(1) << "Skipping post-install on partition "
+            << install_plan_.partitions[current_partition_].name;
+    current_partition_++;
+  }
+  if (current_partition_ == install_plan_.partitions.size())
+    return CompletePostinstall(ErrorCode::kSuccess);
+
+  const InstallPlan::Partition& partition =
+      install_plan_.partitions[current_partition_];
+
+  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);
+  }
+
+  // Perform post-install for the current_partition_ partition. At this point we
+  // need to call CompletePartitionPostinstall to complete the operation and
+  // cleanup.
+  TEST_AND_RETURN(
+      utils::MakeTempDirectory("au_postint_mount.XXXXXX", &temp_rootfs_dir_));
+
+  if (!utils::MountFilesystem(mountable_device, temp_rootfs_dir_, MS_RDONLY)) {
+    return CompletePartitionPostinstall(
+        1, "Error mounting the device " + mountable_device);
+  }
+
+  LOG(INFO) << "Performing postinst (" << kPostinstallScript
+            << ") installed on device " << partition.target_path
+            << " and mountable device " << mountable_device;
+
+  // Logs the file format of the postinstall script we are about to run. This
+  // will help debug when the postinstall script doesn't match the architecture
+  // of our build.
+  LOG(INFO) << "Format file for new " <<  kPostinstallScript << " is: "
+            << utils::GetFileFormat(temp_rootfs_dir_ + kPostinstallScript);
+  LOG(INFO) << "Format file for new " <<  kDebugPostinstallBinaryPath << " is: "
+            << utils::GetFileFormat(
+                temp_rootfs_dir_ + kDebugPostinstallBinaryPath);
+
+  // Runs the postinstall script asynchronously to free up the main loop while
+  // it's running.
+  vector<string> command;
+  if (!install_plan_.download_url.empty()) {
+    command.push_back(temp_rootfs_dir_ + kPostinstallScript);
+  } else {
+    // TODO(sosa): crbug.com/366207.
+    // If we're doing a rollback, just run our own postinstall.
+    command.push_back(kPostinstallScript);
+  }
+  command.push_back(partition.target_path);
+  if (!Subprocess::Get().Exec(
+          command,
+          base::Bind(
+              &PostinstallRunnerAction::CompletePartitionPostinstall,
+              base::Unretained(this)))) {
+    CompletePartitionPostinstall(1, "Postinstall didn't launch");
+  }
+}
+
+void PostinstallRunnerAction::CompletePartitionPostinstall(
+    int return_code,
+    const string& output) {
+  utils::UnmountFilesystem(temp_rootfs_dir_);
+  if (!base::DeleteFile(base::FilePath(temp_rootfs_dir_), false)) {
+    PLOG(WARNING) << "Not removing mountpoint " << temp_rootfs_dir_;
+  }
+  temp_rootfs_dir_.clear();
+
+  if (return_code != 0) {
+    LOG(ERROR) << "Postinst command failed with code: " << return_code;
+    ErrorCode error_code = ErrorCode::kPostinstallRunnerError;
+
+    if (return_code == 3) {
+      // This special return code means that we tried to update firmware,
+      // but couldn't because we booted from FW B, and we need to reboot
+      // to get back to FW A.
+      error_code = ErrorCode::kPostinstallBootedFromFirmwareB;
+    }
+
+    if (return_code == 4) {
+      // This special return code means that we tried to update firmware,
+      // but couldn't because we booted from FW B, and we need to reboot
+      // to get back to FW A.
+      error_code = ErrorCode::kPostinstallFirmwareRONotUpdatable;
+    }
+    return CompletePostinstall(error_code);
+  }
+  current_partition_++;
+  PerformPartitionPostinstall();
+}
+
+void PostinstallRunnerAction::CompletePostinstall(ErrorCode error_code) {
+  // We only attempt to mark the new slot as active if all the postinstall
+  // steps succeeded.
+  if (error_code == ErrorCode::kSuccess &&
+      !boot_control_->SetActiveBootSlot(install_plan_.target_slot)) {
+    error_code = ErrorCode::kPostinstallRunnerError;
+  }
+
+  ScopedActionCompleter completer(processor_, this);
+
+  if (error_code != ErrorCode::kSuccess) {
+    LOG(ERROR) << "Postinstall action failed.";
+
+    // Undo any changes done to trigger Powerwash using clobber-state.
+    if (powerwash_marker_created_)
+      utils::DeletePowerwashMarkerFile(powerwash_marker_file_);
+
+    return;
+  }
+
+  LOG(INFO) << "All post-install commands succeeded";
+  if (HasOutputPipe()) {
+    SetOutputObject(install_plan_);
+  }
+
+  completer.set_code(ErrorCode::kSuccess);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/postinstall_runner_action.h b/payload_consumer/postinstall_runner_action.h
new file mode 100644
index 0000000..ab267b8
--- /dev/null
+++ b/payload_consumer/postinstall_runner_action.h
@@ -0,0 +1,88 @@
+//
+// 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_CONSUMER_POSTINSTALL_RUNNER_ACTION_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_POSTINSTALL_RUNNER_ACTION_H_
+
+#include <string>
+
+#include "update_engine/common/action.h"
+#include "update_engine/payload_consumer/install_plan.h"
+
+// The Postinstall Runner Action is responsible for running the postinstall
+// script of a successfully downloaded update.
+
+namespace chromeos_update_engine {
+
+class BootControlInterface;
+
+class PostinstallRunnerAction : public InstallPlanAction {
+ public:
+  explicit PostinstallRunnerAction(BootControlInterface* boot_control)
+      : PostinstallRunnerAction(boot_control, nullptr) {}
+
+  void PerformAction();
+
+  // Note that there's no support for terminating this action currently.
+  void TerminateProcessing() { CHECK(false); }
+
+  // Debugging/logging
+  static std::string StaticType() { return "PostinstallRunnerAction"; }
+  std::string Type() const { return StaticType(); }
+
+ private:
+  friend class PostinstallRunnerActionTest;
+
+  // Special constructor used for testing purposes.
+  PostinstallRunnerAction(BootControlInterface* boot_control,
+                          const char* powerwash_marker_file)
+      : boot_control_(boot_control),
+        powerwash_marker_file_(powerwash_marker_file) {}
+
+  void PerformPartitionPostinstall();
+
+  // Subprocess::Exec callback.
+  void CompletePartitionPostinstall(int return_code,
+                                    const std::string& output);
+
+  // Complete the Action with the passed |error_code| and mark the new slot as
+  // ready. Called when the post-install script was run for all the partitions.
+  void CompletePostinstall(ErrorCode error_code);
+
+  InstallPlan install_plan_;
+  std::string temp_rootfs_dir_;
+
+  // The partition being processed on the list of partitions specified in the
+  // InstallPlan.
+  size_t current_partition_{0};
+
+  // The BootControlInerface used to mark the new slot as ready.
+  BootControlInterface* boot_control_;
+
+  // True if Powerwash Marker was created before invoking post-install script.
+  // False otherwise. Used for cleaning up if post-install fails.
+  bool powerwash_marker_created_{false};
+
+  // Non-null value will cause post-install to override the default marker
+  // file name; used for testing.
+  const char* powerwash_marker_file_;
+
+  DISALLOW_COPY_AND_ASSIGN(PostinstallRunnerAction);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_POSTINSTALL_RUNNER_ACTION_H_
diff --git a/payload_consumer/postinstall_runner_action_unittest.cc b/payload_consumer/postinstall_runner_action_unittest.cc
new file mode 100644
index 0000000..beed4f1
--- /dev/null
+++ b/payload_consumer/postinstall_runner_action_unittest.cc
@@ -0,0 +1,260 @@
+//
+// 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_consumer/postinstall_runner_action.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/message_loop/message_loop.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/bind_lambda.h>
+#include <brillo/message_loops/base_message_loop.h>
+#include <brillo/message_loops/message_loop_utils.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/constants.h"
+#include "update_engine/common/fake_boot_control.h"
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+
+using brillo::MessageLoop;
+using chromeos_update_engine::test_utils::System;
+using chromeos_update_engine::test_utils::WriteFileString;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+class PostinstallRunnerActionTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    loop_.SetAsCurrent();
+    async_signal_handler_.Init();
+    subprocess_.Init(&async_signal_handler_);
+  }
+
+  // DoTest with various combinations of do_losetup, err_code and
+  // powerwash_required.
+  void DoTest(bool do_losetup, int err_code, bool powerwash_required);
+
+ protected:
+  static const char* kImageMountPointTemplate;
+
+  base::MessageLoopForIO base_loop_;
+  brillo::BaseMessageLoop loop_{&base_loop_};
+  brillo::AsynchronousSignalHandler async_signal_handler_;
+  Subprocess subprocess_;
+  FakeBootControl fake_boot_control_;
+};
+
+class PostinstActionProcessorDelegate : public ActionProcessorDelegate {
+ public:
+  PostinstActionProcessorDelegate()
+      : code_(ErrorCode::kError),
+        code_set_(false) {}
+  void ProcessingDone(const ActionProcessor* processor,
+                      ErrorCode code) {
+    MessageLoop::current()->BreakLoop();
+  }
+  void ActionCompleted(ActionProcessor* processor,
+                       AbstractAction* action,
+                       ErrorCode code) {
+    if (action->Type() == PostinstallRunnerAction::StaticType()) {
+      code_ = code;
+      code_set_ = true;
+    }
+  }
+  ErrorCode code_;
+  bool code_set_;
+};
+
+TEST_F(PostinstallRunnerActionTest, RunAsRootSimpleTest) {
+  DoTest(true, 0, false);
+}
+
+TEST_F(PostinstallRunnerActionTest, RunAsRootPowerwashRequiredTest) {
+  DoTest(true, 0, true);
+}
+
+TEST_F(PostinstallRunnerActionTest, RunAsRootCantMountTest) {
+  DoTest(false, 0, true);
+}
+
+TEST_F(PostinstallRunnerActionTest, RunAsRootErrScriptTest) {
+  DoTest(true, 1, false);
+}
+
+TEST_F(PostinstallRunnerActionTest, RunAsRootFirmwareBErrScriptTest) {
+  DoTest(true, 3, false);
+}
+
+TEST_F(PostinstallRunnerActionTest, RunAsRootFirmwareROErrScriptTest) {
+  DoTest(true, 4, false);
+}
+
+const char* PostinstallRunnerActionTest::kImageMountPointTemplate =
+    "au_destination-XXXXXX";
+
+void PostinstallRunnerActionTest::DoTest(
+    bool do_losetup,
+    int err_code,
+    bool powerwash_required) {
+  ASSERT_EQ(0, getuid()) << "Run me as root. Ideally don't run other tests "
+                         << "as root, tho.";
+  // True if the post-install action is expected to succeed.
+  bool should_succeed = do_losetup && !err_code;
+
+  string orig_cwd;
+  {
+    vector<char> buf(1000);
+    ASSERT_EQ(buf.data(), getcwd(buf.data(), buf.size()));
+    orig_cwd = string(buf.data(), strlen(buf.data()));
+  }
+
+  // Create a unique named working directory and chdir into it.
+  string cwd;
+  ASSERT_TRUE(utils::MakeTempDirectory(
+          "postinstall_runner_action_unittest-XXXXXX",
+          &cwd));
+  ASSERT_EQ(0, test_utils::Chdir(cwd));
+
+  // Create a 10MiB sparse file to be used as image; format it as ext2.
+  ASSERT_EQ(0, System(
+          "dd if=/dev/zero of=image.dat seek=10485759 bs=1 count=1 "
+          "status=none"));
+  ASSERT_EQ(0, System("mkfs.ext2 -F image.dat"));
+
+  // Create a uniquely named image mount point, mount the image.
+  ASSERT_EQ(0, System(string("mkdir -p ") + kStatefulPartition));
+  string mountpoint;
+  ASSERT_TRUE(utils::MakeTempDirectory(
+          string(kStatefulPartition) + "/" + kImageMountPointTemplate,
+          &mountpoint));
+  ASSERT_EQ(0, System(string("mount -o loop image.dat ") + mountpoint));
+
+  // Generate a fake postinst script inside the image.
+  string script = (err_code ?
+                   base::StringPrintf("#!/bin/bash\nexit %d", err_code) :
+                   base::StringPrintf(
+                       "#!/bin/bash\n"
+                       "mount | grep au_postint_mount | grep ext2\n"
+                       "if [ $? -eq 0 ]; then\n"
+                       "  touch %s/postinst_called\n"
+                       "fi\n",
+                       cwd.c_str()));
+  const string script_file_name = mountpoint + "/postinst";
+  ASSERT_TRUE(WriteFileString(script_file_name, script));
+  ASSERT_EQ(0, System(string("chmod a+x ") + script_file_name));
+
+  // Unmount image; do not remove the uniquely named directory as it will be
+  // reused during the test.
+  ASSERT_TRUE(utils::UnmountFilesystem(mountpoint));
+
+  // get a loop device we can use for the install device
+  string dev = "/dev/null";
+
+  unique_ptr<test_utils::ScopedLoopbackDeviceBinder> loop_releaser;
+  if (do_losetup) {
+    loop_releaser.reset(new test_utils::ScopedLoopbackDeviceBinder(
+            cwd + "/image.dat", &dev));
+  }
+
+  // We use a test-specific powerwash marker file, to avoid race conditions.
+  string powerwash_marker_file = mountpoint + "/factory_install_reset";
+  LOG(INFO) << ">>> powerwash_marker_file=" << powerwash_marker_file;
+
+  ActionProcessor processor;
+  ObjectFeederAction<InstallPlan> feeder_action;
+  InstallPlan::Partition part;
+  part.name = "part";
+  part.target_path = dev;
+  part.run_postinstall = true;
+  InstallPlan install_plan;
+  install_plan.partitions = {part};
+  install_plan.download_url = "http://devserver:8080/update";
+  install_plan.powerwash_required = powerwash_required;
+  feeder_action.set_obj(install_plan);
+  PostinstallRunnerAction runner_action(&fake_boot_control_,
+                                        powerwash_marker_file.c_str());
+  BondActions(&feeder_action, &runner_action);
+  ObjectCollectorAction<InstallPlan> collector_action;
+  BondActions(&runner_action, &collector_action);
+  PostinstActionProcessorDelegate delegate;
+  processor.EnqueueAction(&feeder_action);
+  processor.EnqueueAction(&runner_action);
+  processor.EnqueueAction(&collector_action);
+  processor.set_delegate(&delegate);
+
+  loop_.PostTask(FROM_HERE,
+                 base::Bind([&processor] { processor.StartProcessing(); }));
+  loop_.Run();
+  ASSERT_FALSE(processor.IsRunning());
+
+  EXPECT_TRUE(delegate.code_set_);
+  EXPECT_EQ(should_succeed, delegate.code_ == ErrorCode::kSuccess);
+  if (should_succeed)
+    EXPECT_TRUE(install_plan == collector_action.object());
+
+  const base::FilePath kPowerwashMarkerPath(powerwash_marker_file);
+  string actual_cmd;
+  if (should_succeed && powerwash_required) {
+    EXPECT_TRUE(base::ReadFileToString(kPowerwashMarkerPath, &actual_cmd));
+    EXPECT_EQ(kPowerwashCommand, actual_cmd);
+  } else {
+    EXPECT_FALSE(
+        base::ReadFileToString(kPowerwashMarkerPath, &actual_cmd));
+  }
+
+  if (err_code == 2)
+    EXPECT_EQ(ErrorCode::kPostinstallBootedFromFirmwareB, delegate.code_);
+
+  struct stat stbuf;
+  int rc = lstat((string(cwd) + "/postinst_called").c_str(), &stbuf);
+  if (should_succeed)
+    ASSERT_EQ(0, rc);
+  else
+    ASSERT_LT(rc, 0);
+
+  if (do_losetup) {
+    loop_releaser.reset(nullptr);
+  }
+
+  // Remove unique stateful directory.
+  ASSERT_EQ(0, System(string("rm -fr ") + mountpoint));
+
+  // Remove the temporary work directory.
+  ASSERT_EQ(0, test_utils::Chdir(orig_cwd));
+  ASSERT_EQ(0, System(string("rm -fr ") + cwd));
+}
+
+// Death tests don't seem to be working on Hardy
+TEST_F(PostinstallRunnerActionTest, DISABLED_RunAsRootDeathTest) {
+  ASSERT_EQ(0, getuid());
+  PostinstallRunnerAction runner_action(&fake_boot_control_);
+  ASSERT_DEATH({ runner_action.TerminateProcessing(); },
+               "postinstall_runner_action.h:.*] Check failed");
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/xz_extent_writer.cc b/payload_consumer/xz_extent_writer.cc
new file mode 100644
index 0000000..4bd893d
--- /dev/null
+++ b/payload_consumer/xz_extent_writer.cc
@@ -0,0 +1,118 @@
+//
+// 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_consumer/xz_extent_writer.h"
+
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+const brillo::Blob::size_type kOutputBufferLength = 16 * 1024;
+
+// xz uses a variable dictionary size which impacts on the compression ratio
+// and is required to be reconstructed in RAM during decompression. While we
+// control the required memory from the compressor side, the decompressor allows
+// to set a limit on this dictionary size, rejecting compressed streams that
+// require more than that. "xz -9" requires up to 64 MiB, so a 64 MiB limit
+// will allow compressed streams up to -9, the maximum compression setting.
+const uint32_t kXzMaxDictSize = 64 * 1024 * 1024;
+
+const char* XzErrorString(enum xz_ret error) {
+  #define __XZ_ERROR_STRING_CASE(code) case code: return #code;
+  switch (error) {
+    __XZ_ERROR_STRING_CASE(XZ_OK)
+    __XZ_ERROR_STRING_CASE(XZ_STREAM_END)
+    __XZ_ERROR_STRING_CASE(XZ_UNSUPPORTED_CHECK)
+    __XZ_ERROR_STRING_CASE(XZ_MEM_ERROR)
+    __XZ_ERROR_STRING_CASE(XZ_MEMLIMIT_ERROR)
+    __XZ_ERROR_STRING_CASE(XZ_FORMAT_ERROR)
+    __XZ_ERROR_STRING_CASE(XZ_OPTIONS_ERROR)
+    __XZ_ERROR_STRING_CASE(XZ_DATA_ERROR)
+    __XZ_ERROR_STRING_CASE(XZ_BUF_ERROR)
+    default:
+      return "<unknown xz error>";
+  }
+  #undef __XZ_ERROR_STRING_CASE
+};
+}  // namespace
+
+XzExtentWriter::~XzExtentWriter() {
+  xz_dec_end(stream_);
+}
+
+bool XzExtentWriter::Init(FileDescriptorPtr fd,
+                          const vector<Extent>& extents,
+                          uint32_t block_size) {
+  stream_ = xz_dec_init(XZ_DYNALLOC, kXzMaxDictSize);
+  TEST_AND_RETURN_FALSE(stream_ != nullptr);
+  return underlying_writer_->Init(fd, extents, block_size);
+}
+
+bool XzExtentWriter::Write(const void* bytes, size_t count) {
+  // Copy the input data into |input_buffer_| only if |input_buffer_| already
+  // contains unconsumed data. Otherwise, process the data directly from the
+  // source.
+  const uint8_t* input = reinterpret_cast<const uint8_t*>(bytes);
+  if (!input_buffer_.empty()) {
+    input_buffer_.insert(input_buffer_.end(), input, input + count);
+    input = input_buffer_.data();
+    count = input_buffer_.size();
+  }
+
+  xz_buf request;
+  request.in = input;
+  request.in_pos = 0;
+  request.in_size = count;
+
+  brillo::Blob output_buffer(kOutputBufferLength);
+  request.out = output_buffer.data();
+  request.out_size = output_buffer.size();
+  for (;;) {
+    request.out_pos = 0;
+
+    xz_ret ret = xz_dec_run(stream_, &request);
+    if (ret != XZ_OK && ret != XZ_STREAM_END) {
+      LOG(ERROR) << "xz_dec_run returned " << XzErrorString(ret);
+      return false;
+    }
+
+    if (request.out_pos == 0)
+      break;
+
+    TEST_AND_RETURN_FALSE(
+        underlying_writer_->Write(output_buffer.data(), request.out_pos));
+    if (ret == XZ_STREAM_END)
+      CHECK_EQ(request.in_size, request.in_pos);
+    if (request.in_size == request.in_pos)
+      break;  // No more input to process.
+  }
+  output_buffer.clear();
+
+  // Store unconsumed data (if any) in |input_buffer_|. Since |input| can point
+  // to the existing |input_buffer_| we create a new one before assigning it.
+  brillo::Blob new_input_buffer(request.in + request.in_pos,
+                                request.in + request.in_size);
+  input_buffer_ = std::move(new_input_buffer);
+  return true;
+}
+
+bool XzExtentWriter::EndImpl() {
+  TEST_AND_RETURN_FALSE(input_buffer_.empty());
+  return underlying_writer_->End();
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/xz_extent_writer.h b/payload_consumer/xz_extent_writer.h
new file mode 100644
index 0000000..a6b3257
--- /dev/null
+++ b/payload_consumer/xz_extent_writer.h
@@ -0,0 +1,60 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_PAYLOAD_CONSUMER_XZ_EXTENT_WRITER_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_XZ_EXTENT_WRITER_H_
+
+#include <xz.h>
+
+#include <memory>
+#include <vector>
+
+#include <brillo/secure_blob.h>
+
+#include "update_engine/payload_consumer/extent_writer.h"
+
+// XzExtentWriter is a concrete ExtentWriter subclass that xz-decompresses
+// what it's given in Write using xz-embedded. Note that xz-embedded only
+// supports files with either no CRC or CRC-32. It passes the decompressed data
+// to an underlying ExtentWriter.
+
+namespace chromeos_update_engine {
+
+class XzExtentWriter : public ExtentWriter {
+ public:
+  explicit XzExtentWriter(std::unique_ptr<ExtentWriter> underlying_writer)
+      : underlying_writer_(std::move(underlying_writer)) {}
+  ~XzExtentWriter() override;
+
+  bool Init(FileDescriptorPtr fd,
+            const std::vector<Extent>& extents,
+            uint32_t block_size) override;
+  bool Write(const void* bytes, size_t count) override;
+  bool EndImpl() override;
+
+ private:
+  // The underlying ExtentWriter.
+  std::unique_ptr<ExtentWriter> underlying_writer_;
+  // The opaque xz decompressor struct.
+  xz_dec* stream_{nullptr};
+  brillo::Blob input_buffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(XzExtentWriter);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_XZ_EXTENT_WRITER_H_
diff --git a/payload_consumer/xz_extent_writer_unittest.cc b/payload_consumer/xz_extent_writer_unittest.cc
new file mode 100644
index 0000000..fb8bb40
--- /dev/null
+++ b/payload_consumer/xz_extent_writer_unittest.cc
@@ -0,0 +1,165 @@
+//
+// 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_consumer/xz_extent_writer.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <brillo/make_unique_ptr.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/fake_extent_writer.h"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+const char kSampleData[] = "Redundaaaaaaaaaaaaaant\n";
+
+// Compressed data with CRC-32 check, generated with:
+// echo "Redundaaaaaaaaaaaaaant" | xz -9 --check=crc32 |
+// hexdump -v -e '"    " 12/1 "0x%02x, " "\n"'
+const uint8_t kCompressedDataCRC32[] = {
+    0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00, 0x01, 0x69, 0x22, 0xde, 0x36,
+    0x02, 0x00, 0x21, 0x01, 0x1c, 0x00, 0x00, 0x00, 0x10, 0xcf, 0x58, 0xcc,
+    0xe0, 0x00, 0x16, 0x00, 0x10, 0x5d, 0x00, 0x29, 0x19, 0x48, 0x87, 0x88,
+    0xec, 0x49, 0x88, 0x73, 0x8b, 0x5d, 0xa6, 0x46, 0xb4, 0x00, 0x00, 0x00,
+    0x68, 0xfc, 0x7b, 0x25, 0x00, 0x01, 0x28, 0x17, 0x46, 0x9e, 0x08, 0xfe,
+    0x90, 0x42, 0x99, 0x0d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x59, 0x5a,
+};
+
+// Compressed data without checksum, generated with:
+// echo "Redundaaaaaaaaaaaaaant" | xz -9 --check=none |
+// hexdump -v -e '"    " 12/1 "0x%02x, " "\n"'
+const uint8_t kCompressedDataNoCheck[] = {
+    0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00, 0x00, 0xff, 0x12, 0xd9, 0x41,
+    0x02, 0x00, 0x21, 0x01, 0x1c, 0x00, 0x00, 0x00, 0x10, 0xcf, 0x58, 0xcc,
+    0xe0, 0x00, 0x16, 0x00, 0x10, 0x5d, 0x00, 0x29, 0x19, 0x48, 0x87, 0x88,
+    0xec, 0x49, 0x88, 0x73, 0x8b, 0x5d, 0xa6, 0x46, 0xb4, 0x00, 0x00, 0x00,
+    0x00, 0x01, 0x24, 0x17, 0x4a, 0xd1, 0xbd, 0x52, 0x06, 0x72, 0x9e, 0x7a,
+    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x5a,
+};
+
+// Highly redundant data bigger than the internal buffer, generated with:
+// dd if=/dev/zero bs=30K count=1 | tr '\0' 'a' | xz -9 --check=crc32 |
+// hexdump -v -e '"    " 12/1 "0x%02x, " "\n"'
+const uint8_t kCompressed30KiBofA[] = {
+    0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00, 0x01, 0x69, 0x22, 0xde, 0x36,
+    0x02, 0x00, 0x21, 0x01, 0x1c, 0x00, 0x00, 0x00, 0x10, 0xcf, 0x58, 0xcc,
+    0xe0, 0x77, 0xff, 0x00, 0x41, 0x5d, 0x00, 0x30, 0xef, 0xfb, 0xbf, 0xfe,
+    0xa3, 0xb1, 0x5e, 0xe5, 0xf8, 0x3f, 0xb2, 0xaa, 0x26, 0x55, 0xf8, 0x68,
+    0x70, 0x41, 0x70, 0x15, 0x0f, 0x8d, 0xfd, 0x1e, 0x4c, 0x1b, 0x8a, 0x42,
+    0xb7, 0x19, 0xf4, 0x69, 0x18, 0x71, 0xae, 0x66, 0x23, 0x8a, 0x8a, 0x4d,
+    0x2f, 0xa3, 0x0d, 0xd9, 0x7f, 0xa6, 0xe3, 0x8c, 0x23, 0x11, 0x53, 0xe0,
+    0x59, 0x18, 0xc5, 0x75, 0x8a, 0xe2, 0x76, 0x4c, 0xee, 0x30, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xf9, 0x47, 0xb5, 0xee, 0x00, 0x01, 0x59, 0x80,
+    0xf0, 0x01, 0x00, 0x00, 0xe0, 0x41, 0x96, 0xde, 0x3e, 0x30, 0x0d, 0x8b,
+    0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x59, 0x5a,
+};
+
+}  // namespace
+
+class XzExtentWriterTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    fake_extent_writer_ = new FakeExtentWriter();
+    xz_writer_.reset(
+        new XzExtentWriter(brillo::make_unique_ptr(fake_extent_writer_)));
+  }
+
+  void WriteAll(const brillo::Blob& compressed) {
+    EXPECT_TRUE(xz_writer_->Init(fd_, {}, 1024));
+    EXPECT_TRUE(xz_writer_->Write(compressed.data(), compressed.size()));
+    EXPECT_TRUE(xz_writer_->End());
+
+    EXPECT_TRUE(fake_extent_writer_->InitCalled());
+    EXPECT_TRUE(fake_extent_writer_->EndCalled());
+  }
+
+  // Owned by |xz_writer_|. This object is invalidated after |xz_writer_| is
+  // deleted.
+  FakeExtentWriter* fake_extent_writer_{nullptr};
+  std::unique_ptr<XzExtentWriter> xz_writer_;
+
+  const brillo::Blob sample_data_{
+      std::begin(kSampleData),
+      std::begin(kSampleData) + strlen(kSampleData)};
+  FileDescriptorPtr fd_;
+};
+
+TEST_F(XzExtentWriterTest, CreateAndDestroy) {
+  // Test that no Init() or End() called doesn't crash the program.
+  EXPECT_FALSE(fake_extent_writer_->InitCalled());
+  EXPECT_FALSE(fake_extent_writer_->EndCalled());
+}
+
+TEST_F(XzExtentWriterTest, CompressedSampleData) {
+  WriteAll(brillo::Blob(std::begin(kCompressedDataNoCheck),
+                          std::end(kCompressedDataNoCheck)));
+  EXPECT_EQ(sample_data_, fake_extent_writer_->WrittenData());
+}
+
+TEST_F(XzExtentWriterTest, CompressedSampleDataWithCrc) {
+  WriteAll(brillo::Blob(std::begin(kCompressedDataCRC32),
+                          std::end(kCompressedDataCRC32)));
+  EXPECT_EQ(sample_data_, fake_extent_writer_->WrittenData());
+}
+
+TEST_F(XzExtentWriterTest, CompressedDataBiggerThanTheBuffer) {
+  // Test that even if the output data is bigger than the internal buffer, all
+  // the data is written.
+  WriteAll(brillo::Blob(std::begin(kCompressed30KiBofA),
+                          std::end(kCompressed30KiBofA)));
+  brillo::Blob expected_data(30 * 1024, 'a');
+  EXPECT_EQ(expected_data, fake_extent_writer_->WrittenData());
+}
+
+TEST_F(XzExtentWriterTest, GarbageDataRejected) {
+  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()));
+  EXPECT_TRUE(xz_writer_->End());
+
+  EXPECT_TRUE(fake_extent_writer_->EndCalled());
+}
+
+TEST_F(XzExtentWriterTest, PartialDataIsKept) {
+  brillo::Blob compressed(std::begin(kCompressed30KiBofA),
+                            std::end(kCompressed30KiBofA));
+  EXPECT_TRUE(xz_writer_->Init(fd_, {}, 1024));
+  for (uint8_t byte : compressed) {
+    EXPECT_TRUE(xz_writer_->Write(&byte, 1));
+  }
+  EXPECT_TRUE(xz_writer_->End());
+
+  // The sample_data_ is an uncompressed string.
+  brillo::Blob expected_data(30 * 1024, 'a');
+  EXPECT_EQ(expected_data, fake_extent_writer_->WrittenData());
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/ab_generator.cc b/payload_generator/ab_generator.cc
new file mode 100644
index 0000000..7caf897
--- /dev/null
+++ b/payload_generator/ab_generator.cc
@@ -0,0 +1,334 @@
+//
+// 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/ab_generator.h"
+
+#include <algorithm>
+
+#include <base/strings/stringprintf.h>
+
+#include "update_engine/common/hash_calculator.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/payload_constants.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/delta_diff_utils.h"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+bool ABGenerator::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);
+
+  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;
+
+  aops->clear();
+  TEST_AND_RETURN_FALSE(diff_utils::DeltaReadPartition(
+      aops,
+      old_part,
+      new_part,
+      hard_chunk_blocks,
+      soft_chunk_blocks,
+      blob_file,
+      true));  // src_ops_allowed
+  LOG(INFO) << "done reading " << new_part.name;
+
+  TEST_AND_RETURN_FALSE(FragmentOperations(aops,
+                                           new_part.path,
+                                           blob_file));
+  SortOperationsByDestination(aops);
+
+  // Use the soft_chunk_size when merging operations to prevent merging all
+  // the operations into a huge one if there's no hard limit.
+  size_t merge_chunk_blocks = soft_chunk_blocks;
+  if (hard_chunk_blocks != -1 &&
+      static_cast<size_t>(hard_chunk_blocks) < soft_chunk_blocks) {
+    merge_chunk_blocks = hard_chunk_blocks;
+  }
+
+  TEST_AND_RETURN_FALSE(MergeOperations(aops,
+                                        merge_chunk_blocks,
+                                        new_part.path,
+                                        blob_file));
+
+  if (config.minor_version == kOpSrcHashMinorPayloadVersion)
+    TEST_AND_RETURN_FALSE(AddSourceHash(aops, old_part.path));
+
+  return true;
+}
+
+void ABGenerator::SortOperationsByDestination(
+    vector<AnnotatedOperation>* aops) {
+  sort(aops->begin(), aops->end(), diff_utils::CompareAopsByDestination);
+}
+
+bool ABGenerator::FragmentOperations(
+    vector<AnnotatedOperation>* aops,
+    const string& target_part_path,
+    BlobFileWriter* blob_file) {
+  vector<AnnotatedOperation> fragmented_aops;
+  for (const AnnotatedOperation& aop : *aops) {
+    if (aop.op.type() == InstallOperation::SOURCE_COPY) {
+      TEST_AND_RETURN_FALSE(SplitSourceCopy(aop, &fragmented_aops));
+    } else if ((aop.op.type() == InstallOperation::REPLACE) ||
+               (aop.op.type() == InstallOperation::REPLACE_BZ)) {
+      TEST_AND_RETURN_FALSE(SplitReplaceOrReplaceBz(aop, &fragmented_aops,
+                                                    target_part_path,
+                                                    blob_file));
+    } else {
+      fragmented_aops.push_back(aop);
+    }
+  }
+  *aops = fragmented_aops;
+  return true;
+}
+
+bool ABGenerator::SplitSourceCopy(
+    const AnnotatedOperation& original_aop,
+    vector<AnnotatedOperation>* result_aops) {
+  InstallOperation original_op = original_aop.op;
+  TEST_AND_RETURN_FALSE(original_op.type() == InstallOperation::SOURCE_COPY);
+  // Keeps track of the index of curr_src_ext.
+  int curr_src_ext_index = 0;
+  Extent curr_src_ext = original_op.src_extents(curr_src_ext_index);
+  for (int i = 0; i < original_op.dst_extents_size(); i++) {
+    Extent dst_ext = original_op.dst_extents(i);
+    // The new operation which will have only one dst extent.
+    InstallOperation new_op;
+    uint64_t blocks_left = dst_ext.num_blocks();
+    while (blocks_left > 0) {
+      if (curr_src_ext.num_blocks() <= blocks_left) {
+        // If the curr_src_ext is smaller than dst_ext, add it.
+        blocks_left -= curr_src_ext.num_blocks();
+        *(new_op.add_src_extents()) = curr_src_ext;
+        if (curr_src_ext_index + 1 < original_op.src_extents().size()) {
+          curr_src_ext = original_op.src_extents(++curr_src_ext_index);
+        } else {
+          break;
+        }
+      } else {
+        // Split src_exts that are bigger than the dst_ext we're dealing with.
+        Extent first_ext;
+        first_ext.set_num_blocks(blocks_left);
+        first_ext.set_start_block(curr_src_ext.start_block());
+        *(new_op.add_src_extents()) = first_ext;
+        // Keep the second half of the split op.
+        curr_src_ext.set_num_blocks(curr_src_ext.num_blocks() - blocks_left);
+        curr_src_ext.set_start_block(curr_src_ext.start_block() + blocks_left);
+        blocks_left -= first_ext.num_blocks();
+      }
+    }
+    // Fix up our new operation and add it to the results.
+    new_op.set_type(InstallOperation::SOURCE_COPY);
+    *(new_op.add_dst_extents()) = dst_ext;
+    new_op.set_src_length(dst_ext.num_blocks() * kBlockSize);
+    new_op.set_dst_length(dst_ext.num_blocks() * kBlockSize);
+
+    AnnotatedOperation new_aop;
+    new_aop.op = new_op;
+    new_aop.name = base::StringPrintf("%s:%d", original_aop.name.c_str(), i);
+    result_aops->push_back(new_aop);
+  }
+  if (curr_src_ext_index != original_op.src_extents().size() - 1) {
+    LOG(FATAL) << "Incorrectly split SOURCE_COPY operation. Did not use all "
+               << "source extents.";
+  }
+  return true;
+}
+
+bool ABGenerator::SplitReplaceOrReplaceBz(
+    const AnnotatedOperation& original_aop,
+    vector<AnnotatedOperation>* result_aops,
+    const string& target_part_path,
+    BlobFileWriter* blob_file) {
+  InstallOperation original_op = original_aop.op;
+  const bool is_replace = original_op.type() == InstallOperation::REPLACE;
+  TEST_AND_RETURN_FALSE(is_replace ||
+                        original_op.type() == InstallOperation::REPLACE_BZ);
+
+  uint32_t data_offset = original_op.data_offset();
+  for (int i = 0; i < original_op.dst_extents_size(); i++) {
+    Extent dst_ext = original_op.dst_extents(i);
+    // Make a new operation with only one dst extent.
+    InstallOperation new_op;
+    *(new_op.add_dst_extents()) = dst_ext;
+    uint32_t data_size = dst_ext.num_blocks() * kBlockSize;
+    new_op.set_dst_length(data_size);
+    // If this is a REPLACE, attempt to reuse portions of the existing blob.
+    if (is_replace) {
+      new_op.set_type(InstallOperation::REPLACE);
+      new_op.set_data_length(data_size);
+      new_op.set_data_offset(data_offset);
+      data_offset += data_size;
+    }
+
+    AnnotatedOperation new_aop;
+    new_aop.op = new_op;
+    new_aop.name = base::StringPrintf("%s:%d", original_aop.name.c_str(), i);
+    TEST_AND_RETURN_FALSE(AddDataAndSetType(&new_aop, target_part_path,
+                                            blob_file));
+
+    result_aops->push_back(new_aop);
+  }
+  return true;
+}
+
+bool ABGenerator::MergeOperations(vector<AnnotatedOperation>* aops,
+                                  size_t chunk_blocks,
+                                  const string& target_part_path,
+                                  BlobFileWriter* blob_file) {
+  vector<AnnotatedOperation> new_aops;
+  for (const AnnotatedOperation& curr_aop : *aops) {
+    if (new_aops.empty()) {
+      new_aops.push_back(curr_aop);
+      continue;
+    }
+    AnnotatedOperation& last_aop = new_aops.back();
+
+    if (last_aop.op.dst_extents_size() <= 0 ||
+        curr_aop.op.dst_extents_size() <= 0) {
+      new_aops.push_back(curr_aop);
+      continue;
+    }
+    uint32_t last_dst_idx = last_aop.op.dst_extents_size() - 1;
+    uint32_t last_end_block =
+        last_aop.op.dst_extents(last_dst_idx).start_block() +
+        last_aop.op.dst_extents(last_dst_idx).num_blocks();
+    uint32_t curr_start_block = curr_aop.op.dst_extents(0).start_block();
+    uint32_t combined_block_count =
+        last_aop.op.dst_extents(last_dst_idx).num_blocks() +
+        curr_aop.op.dst_extents(0).num_blocks();
+    bool good_op_type = curr_aop.op.type() == InstallOperation::SOURCE_COPY ||
+                        curr_aop.op.type() == InstallOperation::REPLACE ||
+                        curr_aop.op.type() == InstallOperation::REPLACE_BZ;
+    if (good_op_type &&
+        last_aop.op.type() == curr_aop.op.type() &&
+        last_end_block == curr_start_block &&
+        combined_block_count <= chunk_blocks) {
+      // If the operations have the same type (which is a type that we can
+      // merge), are contiguous, are fragmented to have one destination extent,
+      // and their combined block count would be less than chunk size, merge
+      // them.
+      last_aop.name = base::StringPrintf("%s,%s",
+                                         last_aop.name.c_str(),
+                                         curr_aop.name.c_str());
+
+      ExtendExtents(last_aop.op.mutable_src_extents(),
+                    curr_aop.op.src_extents());
+      if (curr_aop.op.src_length() > 0)
+        last_aop.op.set_src_length(last_aop.op.src_length() +
+                                   curr_aop.op.src_length());
+      ExtendExtents(last_aop.op.mutable_dst_extents(),
+                    curr_aop.op.dst_extents());
+      if (curr_aop.op.dst_length() > 0)
+        last_aop.op.set_dst_length(last_aop.op.dst_length() +
+                                   curr_aop.op.dst_length());
+      // Set the data length to zero so we know to add the blob later.
+      if (curr_aop.op.type() == InstallOperation::REPLACE ||
+          curr_aop.op.type() == InstallOperation::REPLACE_BZ) {
+        last_aop.op.set_data_length(0);
+      }
+    } else {
+      // Otherwise just include the extent as is.
+      new_aops.push_back(curr_aop);
+    }
+  }
+
+  // Set the blobs for REPLACE/REPLACE_BZ operations that have been merged.
+  for (AnnotatedOperation& curr_aop : new_aops) {
+    if (curr_aop.op.data_length() == 0 &&
+        (curr_aop.op.type() == InstallOperation::REPLACE ||
+         curr_aop.op.type() == InstallOperation::REPLACE_BZ)) {
+      TEST_AND_RETURN_FALSE(AddDataAndSetType(&curr_aop, target_part_path,
+                                              blob_file));
+    }
+  }
+
+  *aops = new_aops;
+  return true;
+}
+
+bool ABGenerator::AddDataAndSetType(AnnotatedOperation* aop,
+                                    const string& target_part_path,
+                                    BlobFileWriter* blob_file) {
+  TEST_AND_RETURN_FALSE(aop->op.type() == InstallOperation::REPLACE ||
+                        aop->op.type() == InstallOperation::REPLACE_BZ);
+
+  brillo::Blob data(aop->op.dst_length());
+  vector<Extent> dst_extents;
+  ExtentsToVector(aop->op.dst_extents(), &dst_extents);
+  TEST_AND_RETURN_FALSE(utils::ReadExtents(target_part_path,
+                                           dst_extents,
+                                           &data,
+                                           data.size(),
+                                           kBlockSize));
+
+  brillo::Blob data_bz;
+  TEST_AND_RETURN_FALSE(BzipCompress(data, &data_bz));
+  CHECK(!data_bz.empty());
+
+  brillo::Blob* data_p = nullptr;
+  InstallOperation_Type new_op_type;
+  if (data_bz.size() < data.size()) {
+    new_op_type = InstallOperation::REPLACE_BZ;
+    data_p = &data_bz;
+  } else {
+    new_op_type = InstallOperation::REPLACE;
+    data_p = &data;
+  }
+
+  // If the operation doesn't point to a data blob, then we add it.
+  if (aop->op.type() != new_op_type ||
+      aop->op.data_length() != data_p->size()) {
+    aop->op.set_type(new_op_type);
+    aop->SetOperationBlob(data_p, blob_file);
+  }
+
+  return true;
+}
+
+bool ABGenerator::AddSourceHash(vector<AnnotatedOperation>* aops,
+                                const string& source_part_path) {
+  for (AnnotatedOperation& aop : *aops) {
+    if (aop.op.src_extents_size() == 0)
+      continue;
+
+    vector<Extent> src_extents;
+    ExtentsToVector(aop.op.src_extents(), &src_extents);
+    brillo::Blob src_data, src_hash;
+    uint64_t src_length =
+        aop.op.has_src_length()
+            ? aop.op.src_length()
+            : BlocksInExtents(aop.op.src_extents()) * kBlockSize;
+    TEST_AND_RETURN_FALSE(utils::ReadExtents(
+        source_part_path, src_extents, &src_data, src_length, kBlockSize));
+    TEST_AND_RETURN_FALSE(HashCalculator::RawHashOfData(src_data, &src_hash));
+    aop.op.set_src_sha256_hash(src_hash.data(), src_hash.size());
+  }
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/ab_generator.h b/payload_generator/ab_generator.h
new file mode 100644
index 0000000..c2837c0
--- /dev/null
+++ b/payload_generator/ab_generator.h
@@ -0,0 +1,132 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_PAYLOAD_GENERATOR_AB_GENERATOR_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_AB_GENERATOR_H_
+
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <brillo/secure_blob.h>
+
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_generator/blob_file_writer.h"
+#include "update_engine/payload_generator/extent_utils.h"
+#include "update_engine/payload_generator/filesystem_interface.h"
+#include "update_engine/payload_generator/operations_generator.h"
+#include "update_engine/payload_generator/payload_generation_config.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+// The ABGenerator is an operations generator that generates payloads using the
+// A-to-B operations SOURCE_COPY and SOURCE_BSDIFF introduced in the payload
+// minor version 2 format.
+class ABGenerator : public OperationsGenerator {
+ public:
+  ABGenerator() = default;
+
+  // Generate the update payload operations for the given partition using
+  // SOURCE_* operations, used for generating deltas for the minor version
+  // kSourceMinorPayloadVersion. This function will generate operations in the
+  // partition that will read blocks from the source partition in random order
+  // and write the new image on the target partition, also possibly in random
+  // order. The operations are stored in |aops| and should be executed in that
+  // order. 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;
+
+  // Split the operations in the vector of AnnotatedOperations |aops|
+  // such that for every operation there is only one dst extent and updates
+  // |aops| with the new list of operations. All kinds of operations are
+  // fragmented except BSDIFF and SOURCE_BSDIFF operations.
+  // The |target_part_path| is the filename of the new image, where the
+  // destination extents refer to. The blobs of the operations in |aops| should
+  // reference |blob_file|. |blob_file| are updated if needed.
+  static bool FragmentOperations(std::vector<AnnotatedOperation>* aops,
+                                 const std::string& target_part_path,
+                                 BlobFileWriter* blob_file);
+
+  // Takes a vector of AnnotatedOperations |aops| and sorts them by the first
+  // start block in their destination extents. Sets |aops| to a vector of the
+  // sorted operations.
+  static void SortOperationsByDestination(
+      std::vector<AnnotatedOperation>* aops);
+
+  // Takes an SOURCE_COPY install operation, |aop|, and adds one operation for
+  // each dst extent in |aop| to |ops|. The new operations added to |ops| will
+  // have only one dst extent. The src extents are split so the number of blocks
+  // in the src and dst extents are equal.
+  // E.g. we have a SOURCE_COPY operation:
+  //   src extents: [(1, 3), (5, 1), (7, 1)], dst extents: [(2, 2), (6, 3)]
+  // Then we will get 2 new operations:
+  //   1. src extents: [(1, 2)], dst extents: [(2, 2)]
+  //   2. src extents: [(3, 1),(5, 1),(7, 1)], dst extents: [(6, 3)]
+  static bool SplitSourceCopy(const AnnotatedOperation& original_aop,
+                              std::vector<AnnotatedOperation>* result_aops);
+
+  // Takes a REPLACE/REPLACE_BZ operation |aop|, and adds one operation for each
+  // dst extent in |aop| to |ops|. The new operations added to |ops| will have
+  // only one dst extent each, and may be either a REPLACE or REPLACE_BZ
+  // depending on whether compression is advantageous.
+  static bool SplitReplaceOrReplaceBz(
+      const AnnotatedOperation& original_aop,
+      std::vector<AnnotatedOperation>* result_aops,
+      const std::string& target_part,
+      BlobFileWriter* blob_file);
+
+  // Takes a sorted (by first destination extent) vector of operations |aops|
+  // and merges SOURCE_COPY, REPLACE, and REPLACE_BZ operations in that vector.
+  // It will merge two operations if:
+  //   - They are of the same type.
+  //   - They are contiguous.
+  //   - Their combined blocks do not exceed |chunk_blocks| blocks.
+  // Note that unlike other methods, you can't pass a negative number in
+  // |chunk_blocks|.
+  static bool MergeOperations(std::vector<AnnotatedOperation>* aops,
+                              size_t chunk_blocks,
+                              const std::string& target_part,
+                              BlobFileWriter* blob_file);
+
+  // Takes a vector of AnnotatedOperations |aops|, adds source hash to all
+  // operations that have src_extents.
+  static bool AddSourceHash(std::vector<AnnotatedOperation>* aops,
+                            const std::string& source_part_path);
+
+ private:
+  // Adds the data payload for a REPLACE/REPLACE_BZ operation |aop| by reading
+  // its output extents from |target_part_path| and appending a corresponding
+  // data blob to |data_fd|. The blob will be compressed if this is smaller than
+  // the uncompressed form, and the operation type will be set accordingly.
+  // |*blob_file| will be updated as well. If the operation happens to have
+  // the right type and already points to a data blob, nothing is written.
+  // Caller should only set type and data blob if it's valid.
+  static bool AddDataAndSetType(AnnotatedOperation* aop,
+                                const std::string& target_part_path,
+                                BlobFileWriter* blob_file);
+
+  DISALLOW_COPY_AND_ASSIGN(ABGenerator);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_AB_GENERATOR_H_
diff --git a/payload_generator/ab_generator_unittest.cc b/payload_generator/ab_generator_unittest.cc
new file mode 100644
index 0000000..632fc64
--- /dev/null
+++ b/payload_generator/ab_generator_unittest.cc
@@ -0,0 +1,590 @@
+//
+// 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/ab_generator.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#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_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"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+bool ExtentEquals(Extent ext, uint64_t start_block, uint64_t num_blocks) {
+  return ext.start_block() == start_block && ext.num_blocks() == num_blocks;
+}
+
+// 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;
+  const size_t op_ex2_start_block = 6;
+  const size_t op_ex2_num_blocks = 1;
+  const size_t part_num_blocks = 7;
+
+  // Create the target partition data.
+  string part_path;
+  EXPECT_TRUE(utils::MakeTempFile(
+      "SplitReplaceOrReplaceBzTest_part.XXXXXX", &part_path, nullptr));
+  ScopedPathUnlinker part_path_unlinker(part_path);
+  const size_t part_size = part_num_blocks * kBlockSize;
+  brillo::Blob part_data;
+  if (compressible) {
+    part_data.resize(part_size);
+    test_utils::FillWithData(&part_data);
+  } else {
+    std::mt19937 gen(12345);
+    std::uniform_int_distribution<uint8_t> dis(0, 255);
+    for (uint32_t i = 0; i < part_size; i++)
+      part_data.push_back(dis(gen));
+  }
+  ASSERT_EQ(part_size, part_data.size());
+  ASSERT_TRUE(utils::WriteFile(part_path.c_str(), part_data.data(), part_size));
+
+  // Create original operation and blob data.
+  const size_t op_ex1_offset = op_ex1_start_block * kBlockSize;
+  const size_t op_ex1_size = op_ex1_num_blocks * kBlockSize;
+  const size_t op_ex2_offset = op_ex2_start_block * kBlockSize;
+  const size_t op_ex2_size = op_ex2_num_blocks * kBlockSize;
+  InstallOperation op;
+  op.set_type(orig_type);
+  *(op.add_dst_extents()) = ExtentForRange(op_ex1_start_block,
+                                           op_ex1_num_blocks);
+  *(op.add_dst_extents()) = ExtentForRange(op_ex2_start_block,
+                                           op_ex2_num_blocks);
+  op.set_dst_length(op_ex1_num_blocks + op_ex2_num_blocks);
+
+  brillo::Blob op_data;
+  op_data.insert(op_data.end(),
+                 part_data.begin() + op_ex1_offset,
+                 part_data.begin() + op_ex1_offset + op_ex1_size);
+  op_data.insert(op_data.end(),
+                 part_data.begin() + op_ex2_offset,
+                 part_data.begin() + op_ex2_offset + op_ex2_size);
+  brillo::Blob op_blob;
+  if (orig_type == InstallOperation::REPLACE) {
+    op_blob = op_data;
+  } else {
+    ASSERT_TRUE(BzipCompress(op_data, &op_blob));
+  }
+  op.set_data_offset(0);
+  op.set_data_length(op_blob.size());
+
+  AnnotatedOperation aop;
+  aop.op = op;
+  aop.name = "SplitTestOp";
+
+  // Create the data file.
+  string data_path;
+  EXPECT_TRUE(utils::MakeTempFile(
+      "SplitReplaceOrReplaceBzTest_data.XXXXXX", &data_path, nullptr));
+  ScopedPathUnlinker data_path_unlinker(data_path);
+  int data_fd = open(data_path.c_str(), O_RDWR, 000);
+  EXPECT_GE(data_fd, 0);
+  ScopedFdCloser data_fd_closer(&data_fd);
+  EXPECT_TRUE(utils::WriteFile(data_path.c_str(), op_blob.data(),
+                               op_blob.size()));
+  off_t data_file_size = op_blob.size();
+  BlobFileWriter blob_file(data_fd, &data_file_size);
+
+  // Split the operation.
+  vector<AnnotatedOperation> result_ops;
+  ASSERT_TRUE(ABGenerator::SplitReplaceOrReplaceBz(
+          aop, &result_ops, part_path, &blob_file));
+
+  // Check the result.
+  InstallOperation_Type expected_type =
+      compressible ? InstallOperation::REPLACE_BZ : InstallOperation::REPLACE;
+
+  ASSERT_EQ(2, result_ops.size());
+
+  EXPECT_EQ("SplitTestOp:0", result_ops[0].name);
+  InstallOperation first_op = result_ops[0].op;
+  EXPECT_EQ(expected_type, first_op.type());
+  EXPECT_EQ(op_ex1_size, first_op.dst_length());
+  EXPECT_EQ(1, first_op.dst_extents().size());
+  EXPECT_TRUE(ExtentEquals(first_op.dst_extents(0), op_ex1_start_block,
+                           op_ex1_num_blocks));
+  // Obtain the expected blob.
+  brillo::Blob first_expected_data(
+      part_data.begin() + op_ex1_offset,
+      part_data.begin() + op_ex1_offset + op_ex1_size);
+  brillo::Blob first_expected_blob;
+  if (compressible) {
+    ASSERT_TRUE(BzipCompress(first_expected_data, &first_expected_blob));
+  } else {
+    first_expected_blob = first_expected_data;
+  }
+  EXPECT_EQ(first_expected_blob.size(), first_op.data_length());
+  // Check that the actual blob matches what's expected.
+  brillo::Blob first_data_blob(first_op.data_length());
+  ssize_t bytes_read;
+  ASSERT_TRUE(utils::PReadAll(data_fd,
+                              first_data_blob.data(),
+                              first_op.data_length(),
+                              first_op.data_offset(),
+                              &bytes_read));
+  ASSERT_EQ(bytes_read, first_op.data_length());
+  EXPECT_EQ(first_expected_blob, first_data_blob);
+
+  EXPECT_EQ("SplitTestOp:1", result_ops[1].name);
+  InstallOperation second_op = result_ops[1].op;
+  EXPECT_EQ(expected_type, second_op.type());
+  EXPECT_EQ(op_ex2_size, second_op.dst_length());
+  EXPECT_EQ(1, second_op.dst_extents().size());
+  EXPECT_TRUE(ExtentEquals(second_op.dst_extents(0), op_ex2_start_block,
+                           op_ex2_num_blocks));
+  // Obtain the expected blob.
+  brillo::Blob second_expected_data(
+      part_data.begin() + op_ex2_offset,
+      part_data.begin() + op_ex2_offset + op_ex2_size);
+  brillo::Blob second_expected_blob;
+  if (compressible) {
+    ASSERT_TRUE(BzipCompress(second_expected_data, &second_expected_blob));
+  } else {
+    second_expected_blob = second_expected_data;
+  }
+  EXPECT_EQ(second_expected_blob.size(), second_op.data_length());
+  // Check that the actual blob matches what's expected.
+  brillo::Blob second_data_blob(second_op.data_length());
+  ASSERT_TRUE(utils::PReadAll(data_fd,
+                              second_data_blob.data(),
+                              second_op.data_length(),
+                              second_op.data_offset(),
+                              &bytes_read));
+  ASSERT_EQ(bytes_read, second_op.data_length());
+  EXPECT_EQ(second_expected_blob, second_data_blob);
+
+  // Check relative layout of data blobs.
+  EXPECT_EQ(first_op.data_offset() + first_op.data_length(),
+            second_op.data_offset());
+  EXPECT_EQ(second_op.data_offset() + second_op.data_length(), data_file_size);
+  // If we split a REPLACE into multiple ones, ensure reuse of preexisting blob.
+  if (!compressible && orig_type == InstallOperation::REPLACE) {
+    EXPECT_EQ(0, first_op.data_offset());
+  }
+}
+
+// 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;
+  const size_t total_op_num_blocks = first_op_num_blocks + second_op_num_blocks;
+  const size_t part_num_blocks = total_op_num_blocks + 2;
+
+  // Create the target partition data.
+  string part_path;
+  EXPECT_TRUE(utils::MakeTempFile(
+      "MergeReplaceOrReplaceBzTest_part.XXXXXX", &part_path, nullptr));
+  ScopedPathUnlinker part_path_unlinker(part_path);
+  const size_t part_size = part_num_blocks * kBlockSize;
+  brillo::Blob part_data;
+  if (compressible) {
+    part_data.resize(part_size);
+    test_utils::FillWithData(&part_data);
+  } else {
+    std::mt19937 gen(12345);
+    std::uniform_int_distribution<uint8_t> dis(0, 255);
+    for (uint32_t i = 0; i < part_size; i++)
+      part_data.push_back(dis(gen));
+  }
+  ASSERT_EQ(part_size, part_data.size());
+  ASSERT_TRUE(utils::WriteFile(part_path.c_str(), part_data.data(), part_size));
+
+  // Create original operations and blob data.
+  vector<AnnotatedOperation> aops;
+  brillo::Blob blob_data;
+  const size_t total_op_size = total_op_num_blocks * kBlockSize;
+
+  InstallOperation first_op;
+  first_op.set_type(orig_type);
+  const size_t first_op_size = first_op_num_blocks * kBlockSize;
+  first_op.set_dst_length(first_op_size);
+  *(first_op.add_dst_extents()) = ExtentForRange(0, first_op_num_blocks);
+  brillo::Blob first_op_data(part_data.begin(),
+                               part_data.begin() + first_op_size);
+  brillo::Blob first_op_blob;
+  if (orig_type == InstallOperation::REPLACE) {
+    first_op_blob = first_op_data;
+  } else {
+    ASSERT_TRUE(BzipCompress(first_op_data, &first_op_blob));
+  }
+  first_op.set_data_offset(0);
+  first_op.set_data_length(first_op_blob.size());
+  blob_data.insert(blob_data.end(), first_op_blob.begin(), first_op_blob.end());
+  AnnotatedOperation first_aop;
+  first_aop.op = first_op;
+  first_aop.name = "first";
+  aops.push_back(first_aop);
+
+  InstallOperation second_op;
+  second_op.set_type(orig_type);
+  const size_t second_op_size = second_op_num_blocks * kBlockSize;
+  second_op.set_dst_length(second_op_size);
+  *(second_op.add_dst_extents()) = ExtentForRange(first_op_num_blocks,
+                                                  second_op_num_blocks);
+  brillo::Blob second_op_data(part_data.begin() + first_op_size,
+                                part_data.begin() + total_op_size);
+  brillo::Blob second_op_blob;
+  if (orig_type == InstallOperation::REPLACE) {
+    second_op_blob = second_op_data;
+  } else {
+    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());
+  blob_data.insert(blob_data.end(), second_op_blob.begin(),
+                   second_op_blob.end());
+  AnnotatedOperation second_aop;
+  second_aop.op = second_op;
+  second_aop.name = "second";
+  aops.push_back(second_aop);
+
+  // Create the data file.
+  string data_path;
+  EXPECT_TRUE(utils::MakeTempFile(
+      "MergeReplaceOrReplaceBzTest_data.XXXXXX", &data_path, nullptr));
+  ScopedPathUnlinker data_path_unlinker(data_path);
+  int data_fd = open(data_path.c_str(), O_RDWR, 000);
+  EXPECT_GE(data_fd, 0);
+  ScopedFdCloser data_fd_closer(&data_fd);
+  EXPECT_TRUE(utils::WriteFile(data_path.c_str(), blob_data.data(),
+                               blob_data.size()));
+  off_t data_file_size = blob_data.size();
+  BlobFileWriter blob_file(data_fd, &data_file_size);
+
+  // Merge the operations.
+  EXPECT_TRUE(ABGenerator::MergeOperations(
+      &aops, 5, part_path, &blob_file));
+
+  // Check the result.
+  InstallOperation_Type expected_op_type =
+      compressible ? InstallOperation::REPLACE_BZ : InstallOperation::REPLACE;
+  EXPECT_EQ(1, aops.size());
+  InstallOperation new_op = aops[0].op;
+  EXPECT_EQ(expected_op_type, new_op.type());
+  EXPECT_FALSE(new_op.has_src_length());
+  EXPECT_EQ(total_op_num_blocks * kBlockSize, new_op.dst_length());
+  EXPECT_EQ(1, new_op.dst_extents().size());
+  EXPECT_TRUE(ExtentEquals(new_op.dst_extents(0), 0, total_op_num_blocks));
+  EXPECT_EQ("first,second", aops[0].name);
+
+  // Check to see if the blob pointed to in the new extent has what we expect.
+  brillo::Blob expected_data(part_data.begin(),
+                               part_data.begin() + total_op_size);
+  brillo::Blob expected_blob;
+  if (compressible) {
+    ASSERT_TRUE(BzipCompress(expected_data, &expected_blob));
+  } else {
+    expected_blob = expected_data;
+  }
+  ASSERT_EQ(expected_blob.size(), new_op.data_length());
+  ASSERT_EQ(blob_data.size() + expected_blob.size(), data_file_size);
+  brillo::Blob new_op_blob(new_op.data_length());
+  ssize_t bytes_read;
+  ASSERT_TRUE(utils::PReadAll(data_fd,
+                              new_op_blob.data(),
+                              new_op.data_length(),
+                              new_op.data_offset(),
+                              &bytes_read));
+  ASSERT_EQ(new_op.data_length(), bytes_read);
+  EXPECT_EQ(expected_blob, new_op_blob);
+}
+
+}  // namespace
+
+class ABGeneratorTest : public ::testing::Test {};
+
+TEST_F(ABGeneratorTest, SplitSourceCopyTest) {
+  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, 2);
+  *(op.add_dst_extents()) = ExtentForRange(14, 3);
+  *(op.add_dst_extents()) = ExtentForRange(18, 3);
+
+  AnnotatedOperation aop;
+  aop.op = op;
+  aop.name = "SplitSourceCopyTestOp";
+  vector<AnnotatedOperation> result_ops;
+  EXPECT_TRUE(ABGenerator::SplitSourceCopy(aop, &result_ops));
+  EXPECT_EQ(result_ops.size(), 3);
+
+  EXPECT_EQ("SplitSourceCopyTestOp:0", result_ops[0].name);
+  InstallOperation first_op = result_ops[0].op;
+  EXPECT_EQ(InstallOperation::SOURCE_COPY, first_op.type());
+  EXPECT_EQ(kBlockSize * 2, first_op.src_length());
+  EXPECT_EQ(1, first_op.src_extents().size());
+  EXPECT_EQ(2, first_op.src_extents(0).start_block());
+  EXPECT_EQ(2, first_op.src_extents(0).num_blocks());
+  EXPECT_EQ(kBlockSize * 2, first_op.dst_length());
+  EXPECT_EQ(1, first_op.dst_extents().size());
+  EXPECT_EQ(10, first_op.dst_extents(0).start_block());
+  EXPECT_EQ(2, first_op.dst_extents(0).num_blocks());
+
+  EXPECT_EQ("SplitSourceCopyTestOp:1", result_ops[1].name);
+  InstallOperation second_op = result_ops[1].op;
+  EXPECT_EQ(InstallOperation::SOURCE_COPY, second_op.type());
+  EXPECT_EQ(kBlockSize * 3, second_op.src_length());
+  EXPECT_EQ(3, second_op.src_extents().size());
+  EXPECT_EQ(4, second_op.src_extents(0).start_block());
+  EXPECT_EQ(1, second_op.src_extents(0).num_blocks());
+  EXPECT_EQ(6, second_op.src_extents(1).start_block());
+  EXPECT_EQ(1, second_op.src_extents(1).num_blocks());
+  EXPECT_EQ(8, second_op.src_extents(2).start_block());
+  EXPECT_EQ(1, second_op.src_extents(2).num_blocks());
+  EXPECT_EQ(kBlockSize * 3, second_op.dst_length());
+  EXPECT_EQ(1, second_op.dst_extents().size());
+  EXPECT_EQ(14, second_op.dst_extents(0).start_block());
+  EXPECT_EQ(3, second_op.dst_extents(0).num_blocks());
+
+  EXPECT_EQ("SplitSourceCopyTestOp:2", result_ops[2].name);
+  InstallOperation third_op = result_ops[2].op;
+  EXPECT_EQ(InstallOperation::SOURCE_COPY, third_op.type());
+  EXPECT_EQ(kBlockSize * 3, third_op.src_length());
+  EXPECT_EQ(1, third_op.src_extents().size());
+  EXPECT_EQ(9, third_op.src_extents(0).start_block());
+  EXPECT_EQ(3, third_op.src_extents(0).num_blocks());
+  EXPECT_EQ(kBlockSize * 3, third_op.dst_length());
+  EXPECT_EQ(1, third_op.dst_extents().size());
+  EXPECT_EQ(18, third_op.dst_extents(0).start_block());
+  EXPECT_EQ(3, third_op.dst_extents(0).num_blocks());
+}
+
+TEST_F(ABGeneratorTest, SplitReplaceTest) {
+  TestSplitReplaceOrReplaceBzOperation(InstallOperation::REPLACE, false);
+}
+
+TEST_F(ABGeneratorTest, SplitReplaceIntoReplaceBzTest) {
+  TestSplitReplaceOrReplaceBzOperation(InstallOperation::REPLACE, true);
+}
+
+TEST_F(ABGeneratorTest, SplitReplaceBzTest) {
+  TestSplitReplaceOrReplaceBzOperation(InstallOperation::REPLACE_BZ, true);
+}
+
+TEST_F(ABGeneratorTest, SplitReplaceBzIntoReplaceTest) {
+  TestSplitReplaceOrReplaceBzOperation(InstallOperation::REPLACE_BZ, false);
+}
+
+TEST_F(ABGeneratorTest, SortOperationsByDestinationTest) {
+  vector<AnnotatedOperation> aops;
+  // One operation with multiple destination extents.
+  InstallOperation first_op;
+  *(first_op.add_dst_extents()) = ExtentForRange(6, 1);
+  *(first_op.add_dst_extents()) = ExtentForRange(10, 2);
+  AnnotatedOperation first_aop;
+  first_aop.op = first_op;
+  first_aop.name = "first";
+  aops.push_back(first_aop);
+
+  // One with no destination extent. Should end up at the end of the vector.
+  InstallOperation second_op;
+  AnnotatedOperation second_aop;
+  second_aop.op = second_op;
+  second_aop.name = "second";
+  aops.push_back(second_aop);
+
+  // One with one destination extent.
+  InstallOperation third_op;
+  *(third_op.add_dst_extents()) = ExtentForRange(3, 2);
+  AnnotatedOperation third_aop;
+  third_aop.op = third_op;
+  third_aop.name = "third";
+  aops.push_back(third_aop);
+
+  ABGenerator::SortOperationsByDestination(&aops);
+  EXPECT_EQ(aops.size(), 3);
+  EXPECT_EQ(third_aop.name, aops[0].name);
+  EXPECT_EQ(first_aop.name, aops[1].name);
+  EXPECT_EQ(second_aop.name, aops[2].name);
+}
+
+TEST_F(ABGeneratorTest, MergeSourceCopyOperationsTest) {
+  vector<AnnotatedOperation> aops;
+  InstallOperation first_op;
+  first_op.set_type(InstallOperation::SOURCE_COPY);
+  first_op.set_src_length(kBlockSize);
+  first_op.set_dst_length(kBlockSize);
+  *(first_op.add_src_extents()) = ExtentForRange(1, 1);
+  *(first_op.add_dst_extents()) = ExtentForRange(6, 1);
+  AnnotatedOperation first_aop;
+  first_aop.op = first_op;
+  first_aop.name = "1";
+  aops.push_back(first_aop);
+
+  InstallOperation second_op;
+  second_op.set_type(InstallOperation::SOURCE_COPY);
+  second_op.set_src_length(3 * kBlockSize);
+  second_op.set_dst_length(3 * kBlockSize);
+  *(second_op.add_src_extents()) = ExtentForRange(2, 2);
+  *(second_op.add_src_extents()) = ExtentForRange(8, 2);
+  *(second_op.add_dst_extents()) = ExtentForRange(7, 3);
+  *(second_op.add_dst_extents()) = ExtentForRange(11, 1);
+  AnnotatedOperation second_aop;
+  second_aop.op = second_op;
+  second_aop.name = "2";
+  aops.push_back(second_aop);
+
+  InstallOperation third_op;
+  third_op.set_type(InstallOperation::SOURCE_COPY);
+  third_op.set_src_length(kBlockSize);
+  third_op.set_dst_length(kBlockSize);
+  *(third_op.add_src_extents()) = ExtentForRange(11, 1);
+  *(third_op.add_dst_extents()) = ExtentForRange(12, 1);
+  AnnotatedOperation third_aop;
+  third_aop.op = third_op;
+  third_aop.name = "3";
+  aops.push_back(third_aop);
+
+  BlobFileWriter blob_file(0, nullptr);
+  EXPECT_TRUE(ABGenerator::MergeOperations(&aops, 5, "", &blob_file));
+
+  EXPECT_EQ(aops.size(), 1);
+  InstallOperation first_result_op = aops[0].op;
+  EXPECT_EQ(InstallOperation::SOURCE_COPY, first_result_op.type());
+  EXPECT_EQ(kBlockSize * 5, first_result_op.src_length());
+  EXPECT_EQ(3, first_result_op.src_extents().size());
+  EXPECT_TRUE(ExtentEquals(first_result_op.src_extents(0), 1, 3));
+  EXPECT_TRUE(ExtentEquals(first_result_op.src_extents(1), 8, 2));
+  EXPECT_TRUE(ExtentEquals(first_result_op.src_extents(2), 11, 1));
+  EXPECT_EQ(kBlockSize * 5, first_result_op.dst_length());
+  EXPECT_EQ(2, first_result_op.dst_extents().size());
+  EXPECT_TRUE(ExtentEquals(first_result_op.dst_extents(0), 6, 4));
+  EXPECT_TRUE(ExtentEquals(first_result_op.dst_extents(1), 11, 2));
+  EXPECT_EQ(aops[0].name, "1,2,3");
+}
+
+TEST_F(ABGeneratorTest, MergeReplaceOperationsTest) {
+  TestMergeReplaceOrReplaceBzOperations(InstallOperation::REPLACE, false);
+}
+
+TEST_F(ABGeneratorTest, MergeReplaceOperationsToReplaceBzTest) {
+  TestMergeReplaceOrReplaceBzOperations(InstallOperation::REPLACE, true);
+}
+
+TEST_F(ABGeneratorTest, MergeReplaceBzOperationsTest) {
+  TestMergeReplaceOrReplaceBzOperations(InstallOperation::REPLACE_BZ, true);
+}
+
+TEST_F(ABGeneratorTest, MergeReplaceBzOperationsToReplaceTest) {
+  TestMergeReplaceOrReplaceBzOperations(InstallOperation::REPLACE_BZ, false);
+}
+
+TEST_F(ABGeneratorTest, NoMergeOperationsTest) {
+  // Test to make sure we don't merge operations that shouldn't be merged.
+  vector<AnnotatedOperation> aops;
+  InstallOperation first_op;
+  first_op.set_type(InstallOperation::REPLACE_BZ);
+  *(first_op.add_dst_extents()) = ExtentForRange(0, 1);
+  first_op.set_data_length(kBlockSize);
+  AnnotatedOperation first_aop;
+  first_aop.op = first_op;
+  aops.push_back(first_aop);
+
+  // Should merge with first, except op types don't match...
+  InstallOperation second_op;
+  second_op.set_type(InstallOperation::REPLACE);
+  *(second_op.add_dst_extents()) = ExtentForRange(1, 2);
+  second_op.set_data_length(2 * kBlockSize);
+  AnnotatedOperation second_aop;
+  second_aop.op = second_op;
+  aops.push_back(second_aop);
+
+  // Should merge with second, except it would exceed chunk size...
+  InstallOperation third_op;
+  third_op.set_type(InstallOperation::REPLACE);
+  *(third_op.add_dst_extents()) = ExtentForRange(3, 3);
+  third_op.set_data_length(3 * kBlockSize);
+  AnnotatedOperation third_aop;
+  third_aop.op = third_op;
+  aops.push_back(third_aop);
+
+  // Should merge with third, except they aren't contiguous...
+  InstallOperation fourth_op;
+  fourth_op.set_type(InstallOperation::REPLACE);
+  *(fourth_op.add_dst_extents()) = ExtentForRange(7, 2);
+  fourth_op.set_data_length(2 * kBlockSize);
+  AnnotatedOperation fourth_aop;
+  fourth_aop.op = fourth_op;
+  aops.push_back(fourth_aop);
+
+  BlobFileWriter blob_file(0, nullptr);
+  EXPECT_TRUE(ABGenerator::MergeOperations(&aops, 4, "", &blob_file));
+
+  // No operations were merged, the number of ops is the same.
+  EXPECT_EQ(aops.size(), 4);
+}
+
+TEST_F(ABGeneratorTest, AddSourceHashTest) {
+  vector<AnnotatedOperation> aops;
+  InstallOperation first_op;
+  first_op.set_type(InstallOperation::SOURCE_COPY);
+  first_op.set_src_length(kBlockSize);
+  *(first_op.add_src_extents()) = ExtentForRange(0, 1);
+  AnnotatedOperation first_aop;
+  first_aop.op = first_op;
+  aops.push_back(first_aop);
+
+  InstallOperation second_op;
+  second_op.set_type(InstallOperation::REPLACE);
+  AnnotatedOperation second_aop;
+  second_aop.op = second_op;
+  aops.push_back(second_aop);
+
+  string src_part_path;
+  EXPECT_TRUE(utils::MakeTempFile("AddSourceHashTest_src_part.XXXXXX",
+                                  &src_part_path, nullptr));
+  ScopedPathUnlinker src_part_path_unlinker(src_part_path);
+  brillo::Blob src_data(kBlockSize);
+  test_utils::FillWithData(&src_data);
+  ASSERT_TRUE(utils::WriteFile(src_part_path.c_str(), src_data.data(),
+                               src_data.size()));
+
+  EXPECT_TRUE(ABGenerator::AddSourceHash(&aops, src_part_path));
+
+  EXPECT_TRUE(aops[0].op.has_src_sha256_hash());
+  EXPECT_FALSE(aops[1].op.has_src_sha256_hash());
+  brillo::Blob expected_hash;
+  EXPECT_TRUE(HashCalculator::RawHashOfData(src_data, &expected_hash));
+  brillo::Blob result_hash(aops[0].op.src_sha256_hash().begin(),
+                           aops[0].op.src_sha256_hash().end());
+  EXPECT_EQ(expected_hash, result_hash);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/annotated_operation.cc b/payload_generator/annotated_operation.cc
new file mode 100644
index 0000000..984f921
--- /dev/null
+++ b/payload_generator/annotated_operation.cc
@@ -0,0 +1,70 @@
+//
+// 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/annotated_operation.h"
+
+#include <base/format_macros.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/stringprintf.h>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/payload_constants.h"
+
+namespace chromeos_update_engine {
+
+namespace {
+// Output the list of extents as (start_block, num_blocks) in the passed output
+// stream.
+void OutputExtents(std::ostream* os,
+                   const google::protobuf::RepeatedPtrField<Extent>& extents) {
+  for (const auto& extent : extents) {
+    *os << " (" << extent.start_block() << ", " << extent.num_blocks() << ")";
+  }
+}
+}  // namespace
+
+bool AnnotatedOperation::SetOperationBlob(brillo::Blob* blob,
+                                          BlobFileWriter* blob_file) {
+  off_t data_offset = blob_file->StoreBlob(*blob);
+  TEST_AND_RETURN_FALSE(data_offset != -1);
+  op.set_data_offset(data_offset);
+  op.set_data_length(blob->size());
+  return true;
+}
+
+std::ostream& operator<<(std::ostream& os, const AnnotatedOperation& aop) {
+  // For example, this prints:
+  // REPLACE_BZ 500 @3000
+  //   name: /foo/bar
+  //    dst: (123, 3) (127, 2)
+  os << InstallOperationTypeName(aop.op.type()) << " "  << aop.op.data_length();
+  if (aop.op.data_length() > 0)
+    os << " @" << aop.op.data_offset();
+  if (!aop.name.empty()) {
+    os << std::endl << "  name: " << aop.name;
+  }
+  if (aop.op.src_extents_size() != 0) {
+    os << std::endl << "   src:";
+    OutputExtents(&os, aop.op.src_extents());
+  }
+  if (aop.op.dst_extents_size() != 0) {
+    os << std::endl << "   dst:";
+    OutputExtents(&os, aop.op.dst_extents());
+  }
+  return os;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/annotated_operation.h b/payload_generator/annotated_operation.h
new file mode 100644
index 0000000..4076070
--- /dev/null
+++ b/payload_generator/annotated_operation.h
@@ -0,0 +1,49 @@
+//
+// 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_ANNOTATED_OPERATION_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_ANNOTATED_OPERATION_H_
+
+#include <ostream>  // NOLINT(readability/streams)
+#include <string>
+
+#include <brillo/secure_blob.h>
+
+#include "update_engine/payload_generator/blob_file_writer.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+struct AnnotatedOperation {
+  // The name given to the operation, for logging and debugging purposes only.
+  // This normally includes the path to the file and the chunk used, if any.
+  std::string name;
+
+  // The InstallOperation, as defined by the protobuf.
+  InstallOperation op;
+
+  // Writes |blob| to the end of |data_fd|, and updates |data_file_size| to
+  // match the new size of |data_fd|. It sets the data_offset and data_length
+  // in AnnotatedOperation to match the offset and size of |blob| in |data_fd|.
+  bool SetOperationBlob(brillo::Blob* blob, BlobFileWriter* blob_file);
+};
+
+// For logging purposes.
+std::ostream& operator<<(std::ostream& os, const AnnotatedOperation& aop);
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_ANNOTATED_OPERATION_H_
diff --git a/payload_generator/blob_file_writer.cc b/payload_generator/blob_file_writer.cc
new file mode 100644
index 0000000..8225df4
--- /dev/null
+++ b/payload_generator/blob_file_writer.cc
@@ -0,0 +1,47 @@
+//
+// 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/blob_file_writer.h"
+
+#include "update_engine/common/utils.h"
+
+namespace chromeos_update_engine {
+
+off_t BlobFileWriter::StoreBlob(const brillo::Blob& blob) {
+  base::AutoLock auto_lock(blob_mutex_);
+  if (!utils::PWriteAll(blob_fd_, blob.data(), blob.size(), *blob_file_size_))
+    return -1;
+
+  off_t result = *blob_file_size_;
+  *blob_file_size_ += blob.size();
+
+  stored_blobs_++;
+  if (total_blobs_ > 0 &&
+      (10 * (stored_blobs_ - 1) / total_blobs_) !=
+      (10 * stored_blobs_ / total_blobs_)) {
+    LOG(INFO) << (100 * stored_blobs_ / total_blobs_)
+              << "% complete " << stored_blobs_ << "/" << total_blobs_
+              << " ops (output size: " << *blob_file_size_ << ")";
+  }
+  return result;
+}
+
+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
new file mode 100644
index 0000000..cbc13ae
--- /dev/null
+++ b/payload_generator/blob_file_writer.h
@@ -0,0 +1,59 @@
+//
+// 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_BLOB_FILE_WRITER_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_BLOB_FILE_WRITER_H_
+
+#include <base/macros.h>
+
+#include <base/synchronization/lock.h>
+#include <brillo/secure_blob.h>
+
+namespace chromeos_update_engine {
+
+class BlobFileWriter {
+ public:
+  // Create the BlobFileWriter object that will manage the blobs stored to
+  // |blob_fd| in a thread safe way.
+  BlobFileWriter(int blob_fd, off_t* blob_file_size)
+    : blob_fd_(blob_fd),
+      blob_file_size_(blob_file_size) {}
+
+  // Store the passed |blob| in the blob file. Returns the offset at which it
+  // was stored, or -1 in case of failure.
+  off_t StoreBlob(const brillo::Blob& blob);
+
+  // 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};
+  size_t stored_blobs_{0};
+
+  // The file and its size are protected with the |blob_mutex_|.
+  int blob_fd_;
+  off_t* blob_file_size_;
+
+  base::Lock blob_mutex_;
+
+  DISALLOW_COPY_AND_ASSIGN(BlobFileWriter);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_BLOB_FILE_WRITER_H_
diff --git a/payload_generator/blob_file_writer_unittest.cc b/payload_generator/blob_file_writer_unittest.cc
new file mode 100644
index 0000000..5f94ef3
--- /dev/null
+++ b/payload_generator/blob_file_writer_unittest.cc
@@ -0,0 +1,59 @@
+//
+// 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/blob_file_writer.h"
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+
+using chromeos_update_engine::test_utils::FillWithData;
+using std::string;
+
+namespace chromeos_update_engine {
+
+class BlobFileWriterTest : public ::testing::Test {};
+
+TEST(BlobFileWriterTest, SimpleTest) {
+  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(blob_fd, &blob_file_size);
+
+  off_t blob_size = 1024;
+  brillo::Blob blob(blob_size);
+  FillWithData(&blob);
+  EXPECT_EQ(0, blob_file.StoreBlob(blob));
+  EXPECT_EQ(blob_size, blob_file.StoreBlob(blob));
+
+  brillo::Blob stored_blob(blob_size);
+  ssize_t bytes_read;
+  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);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/block_mapping.cc b/payload_generator/block_mapping.cc
new file mode 100644
index 0000000..ff10f0b
--- /dev/null
+++ b/payload_generator/block_mapping.cc
@@ -0,0 +1,161 @@
+//
+// 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/block_mapping.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include "update_engine/common/utils.h"
+
+using std::string;
+using std::vector;
+
+namespace {
+
+size_t HashValue(const brillo::Blob& blob) {
+  std::hash<string> hash_fn;
+  return hash_fn(string(blob.begin(), blob.end()));
+}
+
+}  // namespace
+
+namespace chromeos_update_engine {
+
+BlockMapping::BlockId BlockMapping::AddBlock(const brillo::Blob& block_data) {
+  return AddBlock(-1, 0, block_data);
+}
+
+BlockMapping::BlockId BlockMapping::AddDiskBlock(int fd, off_t byte_offset) {
+  brillo::Blob blob(block_size_);
+  ssize_t bytes_read = 0;
+  if (!utils::PReadAll(fd, blob.data(), block_size_, byte_offset, &bytes_read))
+    return -1;
+  if (static_cast<size_t>(bytes_read) != block_size_)
+    return -1;
+  return AddBlock(fd, byte_offset, blob);
+}
+
+bool BlockMapping::AddManyDiskBlocks(int fd,
+                                     off_t initial_byte_offset,
+                                     size_t num_blocks,
+                                     vector<BlockId>* block_ids) {
+  bool ret = true;
+  block_ids->resize(num_blocks);
+  for (size_t block = 0; block < num_blocks; block++) {
+    (*block_ids)[block] = AddDiskBlock(
+        fd, initial_byte_offset + block * block_size_);
+    ret = ret && (*block_ids)[block] != -1;
+  }
+  return ret;
+}
+
+BlockMapping::BlockId BlockMapping::AddBlock(int fd,
+                                             off_t byte_offset,
+                                             const brillo::Blob& block_data) {
+  if (block_data.size() != block_size_)
+    return -1;
+  size_t h = HashValue(block_data);
+
+  // We either reuse a UniqueBlock or create a new one. If we need a new
+  // UniqueBlock it could also be part of a new or existing bucket (if there is
+  // a hash collision).
+  vector<UniqueBlock> *bucket = nullptr;
+
+  auto mapping_it = mapping_.find(h);
+  if (mapping_it == mapping_.end()) {
+    bucket = &mapping_[h];
+  } else {
+    for (UniqueBlock& existing_block : mapping_it->second) {
+      bool equals = false;
+      if (!existing_block.CompareData(block_data, &equals))
+        return -1;
+      if (equals)
+        return existing_block.block_id;
+    }
+    bucket = &mapping_it->second;
+  }
+
+  // No existing block was found at this point, so we create and fill in a new
+  // one.
+  bucket->emplace_back();
+  UniqueBlock *new_ublock = &bucket->back();
+
+  new_ublock->times_read = 1;
+  new_ublock->fd = fd;
+  new_ublock->byte_offset = byte_offset;
+  new_ublock->block_id = used_block_ids++;
+  // We need to cache blocks that are not referencing any disk location.
+  if (fd == -1)
+    new_ublock->block_data = block_data;
+
+  return new_ublock->block_id;
+}
+
+bool BlockMapping::UniqueBlock::CompareData(const brillo::Blob& other_block,
+                                            bool* equals) {
+  if (!block_data.empty()) {
+    *equals = block_data == other_block;
+    return true;
+  }
+  const size_t block_size = other_block.size();
+  brillo::Blob blob(block_size);
+  ssize_t bytes_read = 0;
+  if (!utils::PReadAll(fd, blob.data(), block_size, byte_offset, &bytes_read))
+    return false;
+  if (static_cast<size_t>(bytes_read) != block_size)
+    return false;
+  *equals = blob == other_block;
+
+  // We increase the number of times we had to read this block from disk and
+  // we cache this block based on that. This caching method is optimized for
+  // the common use case of having two partitions that share blocks between them
+  // but have few repeated blocks inside each partition, such as the block
+  // with all zeros or duplicated files.
+  times_read++;
+  if (times_read > 3)
+    block_data = std::move(blob);
+  return true;
+}
+
+bool MapPartitionBlocks(const string& old_part,
+                        const string& new_part,
+                        size_t old_size,
+                        size_t new_size,
+                        size_t block_size,
+                        vector<BlockMapping::BlockId>* old_block_ids,
+                        vector<BlockMapping::BlockId>* new_block_ids) {
+  BlockMapping mapping(block_size);
+  if (mapping.AddBlock(brillo::Blob(block_size, '\0')) != 0)
+    return false;
+  int old_fd = HANDLE_EINTR(open(old_part.c_str(), O_RDONLY));
+  int new_fd = HANDLE_EINTR(open(new_part.c_str(), O_RDONLY));
+  ScopedFdCloser old_fd_closer(&old_fd);
+  ScopedFdCloser new_fd_closer(&new_fd);
+
+  TEST_AND_RETURN_FALSE(mapping.AddManyDiskBlocks(
+      old_fd, 0, old_size / block_size, old_block_ids));
+  TEST_AND_RETURN_FALSE(mapping.AddManyDiskBlocks(
+      new_fd, 0, new_size / block_size, new_block_ids));
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/block_mapping.h b/payload_generator/block_mapping.h
new file mode 100644
index 0000000..3fe94ab
--- /dev/null
+++ b/payload_generator/block_mapping.h
@@ -0,0 +1,112 @@
+//
+// 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_BLOCK_MAPPING_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_BLOCK_MAPPING_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <brillo/secure_blob.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "update_engine/payload_generator/payload_generation_config.h"
+
+namespace chromeos_update_engine {
+
+// BlockMapping allows to map data blocks (brillo::Blobs of block_size size)
+// into unique integer values called "block ids". This mapping differs from a
+// hash function in that two blocks with the same data will have the same id but
+// also two blocks with the same id will have the same data. This is only valid
+// in the context of the same BlockMapping instance.
+class BlockMapping {
+ public:
+  using BlockId = int64_t;
+
+  explicit BlockMapping(size_t block_size) : block_size_(block_size) {}
+
+  // Add a single data block to the mapping. Returns its unique block id.
+  // In case of error returns -1.
+  BlockId AddBlock(const brillo::Blob& block_data);
+
+  // Add a block from disk reading it from the file descriptor |fd| from the
+  // offset in bytes |byte_offset|. The data block may or may not be cached, so
+  // the file descriptor must be available until the BlockMapping is destroyed.
+  // Returns the unique block id of the added block or -1 in case of error.
+  BlockId AddDiskBlock(int fd, off_t byte_offset);
+
+  // This is a helper method to add |num_blocks| contiguous blocks reading them
+  // from the file descriptor |fd| starting at offset |initial_byte_offset|.
+  // Returns whether it succeeded to add all the disk blocks and stores in
+  // |block_ids| the block id for each one of the added blocks.
+  bool AddManyDiskBlocks(int fd, off_t initial_byte_offset, size_t num_blocks,
+                         std::vector<BlockId>* block_ids);
+
+ private:
+  FRIEND_TEST(BlockMappingTest, BlocksAreNotKeptInMemory);
+
+  // Add a single block passed in |block_data|. If |fd| is not -1, the block
+  // can be discarded to save RAM and retrieved later from |fd| at the position
+  // |byte_offset|.
+  BlockId AddBlock(int fd, off_t byte_offset, const brillo::Blob& block_data);
+
+  size_t block_size_;
+
+  BlockId used_block_ids{0};
+
+  // The UniqueBlock represents the data of a block associated to a unique
+  // block id.
+  struct UniqueBlock {
+    brillo::Blob block_data;
+
+    // The block id assigned to this unique block.
+    BlockId block_id;
+
+    // The location on this unique block on disk (if not cached in block_data).
+    int fd{-1};
+    off_t byte_offset{0};
+
+    // Number of times we have seen this data block. Used for caching.
+    uint32_t times_read{0};
+
+    // Compares the UniqueBlock data with the other_block data and stores if
+    // they are equal in |equals|. Returns whether there was an error reading
+    // the block from disk while comparing it.
+    bool CompareData(const brillo::Blob& other_block, bool* equals);
+  };
+
+  // A mapping from hash values to possible block ids.
+  std::map<size_t, std::vector<UniqueBlock>> mapping_;
+};
+
+// Maps the blocks of the old and new partitions |old_part| and |new_part| whose
+// size in bytes are |old_size| and |new_size| into block ids where two blocks
+// with the same data will have the same block id and vice versa, regardless of
+// the partition they are on.
+// The block ids number 0 corresponds to the block with all zeros, but any
+// other block id number is assigned randomly.
+bool MapPartitionBlocks(const std::string& old_part,
+                        const std::string& new_part,
+                        size_t old_size,
+                        size_t new_size,
+                        size_t block_size,
+                        std::vector<BlockMapping::BlockId>* old_block_ids,
+                        std::vector<BlockMapping::BlockId>* new_block_ids);
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_BLOCK_MAPPING_H_
diff --git a/payload_generator/block_mapping_unittest.cc b/payload_generator/block_mapping_unittest.cc
new file mode 100644
index 0000000..18e48c4
--- /dev/null
+++ b/payload_generator/block_mapping_unittest.cc
@@ -0,0 +1,134 @@
+//
+// 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/block_mapping.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+}  // namespace
+
+class BlockMappingTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    EXPECT_TRUE(utils::MakeTempFile("BlockMappingTest_old.XXXXXX",
+                                    &old_part_path_,
+                                    nullptr));
+    EXPECT_TRUE(utils::MakeTempFile("BlockMappingTest_new.XXXXXX",
+                                    &new_part_path_,
+                                    nullptr));
+
+    old_part_unlinker_.reset(new ScopedPathUnlinker(old_part_path_));
+    new_part_unlinker_.reset(new ScopedPathUnlinker(new_part_path_));
+  }
+
+  // Old new partition files used in testing.
+  string old_part_path_;
+  string new_part_path_;
+  std::unique_ptr<ScopedPathUnlinker> old_part_unlinker_;
+  std::unique_ptr<ScopedPathUnlinker> new_part_unlinker_;
+
+  size_t block_size_{1024};
+  BlockMapping bm_{block_size_};  // BlockMapping under test.
+};
+
+TEST_F(BlockMappingTest, FirstAddedBlockIsZero) {
+  brillo::Blob blob(block_size_);
+  // The BlockMapping just assigns the block ids in order, so it doesn't matter
+  // what are the contents of the first block.
+  blob[0] = 42;
+  EXPECT_EQ(0, bm_.AddBlock(blob));
+  blob[0] = 5;
+  EXPECT_EQ(1, bm_.AddBlock(blob));
+}
+
+TEST_F(BlockMappingTest, BlocksAreNotKeptInMemory) {
+  test_utils::WriteFileString(old_part_path_, string(block_size_, 'a'));
+  int old_fd = HANDLE_EINTR(open(old_part_path_.c_str(), O_RDONLY));
+  ScopedFdCloser old_fd_closer(&old_fd);
+
+  EXPECT_EQ(0, bm_.AddDiskBlock(old_fd, 0));
+
+  // Check that the block_data is not stored on memory if we just used the block
+  // once.
+  for (const auto& it : bm_.mapping_) {
+    for (const BlockMapping::UniqueBlock& ublock : it.second) {
+      EXPECT_TRUE(ublock.block_data.empty());
+    }
+  }
+
+  brillo::Blob block(block_size_, 'a');
+  for (int i = 0; i < 5; ++i) {
+    // Re-add the same block 5 times.
+    EXPECT_EQ(0, bm_.AddBlock(block));
+  }
+
+  for (const auto& it : bm_.mapping_) {
+    for (const BlockMapping::UniqueBlock& ublock : it.second) {
+      EXPECT_FALSE(ublock.block_data.empty());
+      // The block was loaded from disk only 4 times, and after that the counter
+      // is not updated anymore.
+      EXPECT_EQ(4, ublock.times_read);
+    }
+  }
+}
+
+TEST_F(BlockMappingTest, MapPartitionBlocks) {
+  // A string with 10 blocks where all the blocks are different.
+  string old_contents(10 * block_size_, '\0');
+  for (size_t i = 0; i < old_contents.size(); ++i)
+    old_contents[i] = 4 + i / block_size_;
+  test_utils::WriteFileString(old_part_path_, old_contents);
+
+  // A string including the block with all zeros and overlapping some of the
+  // other blocks in old_contents.
+  string new_contents(6 * block_size_, '\0');
+  for (size_t i = 0; i < new_contents.size(); ++i)
+    new_contents[i] = i / block_size_;
+  test_utils::WriteFileString(new_part_path_, new_contents);
+
+  vector<BlockMapping::BlockId> old_ids, new_ids;
+  EXPECT_TRUE(MapPartitionBlocks(old_part_path_,
+                                 new_part_path_,
+                                 old_contents.size(),
+                                 new_contents.size(),
+                                 block_size_,
+                                 &old_ids,
+                                 &new_ids));
+
+  EXPECT_EQ((vector<BlockMapping::BlockId>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}),
+            old_ids);
+  EXPECT_EQ((vector<BlockMapping::BlockId>{0, 11, 12, 13, 1, 2}),
+            new_ids);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/bzip.cc b/payload_generator/bzip.cc
new file mode 100644
index 0000000..9040193
--- /dev/null
+++ b/payload_generator/bzip.cc
@@ -0,0 +1,130 @@
+//
+// 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/bzip.h"
+
+#include <bzlib.h>
+#include <stdlib.h>
+
+#include <algorithm>
+#include <limits>
+
+#include "update_engine/common/utils.h"
+
+using std::string;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+// BzipData compresses or decompresses the input to the output.
+// Returns true on success.
+// Use one of BzipBuffToBuff*ompress as the template parameter to BzipData().
+int BzipBuffToBuffDecompress(uint8_t* out,
+                             uint32_t* out_length,
+                             const void* in,
+                             uint32_t in_length) {
+  return BZ2_bzBuffToBuffDecompress(
+      reinterpret_cast<char*>(out),
+      out_length,
+      reinterpret_cast<char*>(const_cast<void*>(in)),
+      in_length,
+      0,  // Silent verbosity
+      0);  // Normal algorithm
+}
+
+int BzipBuffToBuffCompress(uint8_t* out,
+                           uint32_t* out_length,
+                           const void* in,
+                           uint32_t in_length) {
+  return BZ2_bzBuffToBuffCompress(
+      reinterpret_cast<char*>(out),
+      out_length,
+      reinterpret_cast<char*>(const_cast<void*>(in)),
+      in_length,
+      9,  // Best compression
+      0,  // Silent verbosity
+      0);  // Default work factor
+}
+
+template<int F(uint8_t* out,
+               uint32_t* out_length,
+               const void* in,
+               uint32_t in_length)>
+bool BzipData(const void* const in,
+              const size_t in_size,
+              brillo::Blob* const out) {
+  TEST_AND_RETURN_FALSE(out);
+  out->clear();
+  if (in_size == 0) {
+    return true;
+  }
+  // Try increasing buffer size until it works
+  size_t buf_size = in_size;
+  out->resize(buf_size);
+
+  for (;;) {
+    if (buf_size > std::numeric_limits<uint32_t>::max())
+      return false;
+    uint32_t data_size = buf_size;
+    int rc = F(out->data(), &data_size, in, in_size);
+    TEST_AND_RETURN_FALSE(rc == BZ_OUTBUFF_FULL || rc == BZ_OK);
+    if (rc == BZ_OK) {
+      // we're done!
+      out->resize(data_size);
+      return true;
+    }
+
+    // Data didn't fit; double the buffer size.
+    buf_size *= 2;
+    out->resize(buf_size);
+  }
+}
+
+}  // namespace
+
+bool BzipDecompress(const brillo::Blob& in, brillo::Blob* out) {
+  return BzipData<BzipBuffToBuffDecompress>(in.data(), in.size(), out);
+}
+
+bool BzipCompress(const brillo::Blob& in, brillo::Blob* out) {
+  return BzipData<BzipBuffToBuffCompress>(in.data(), in.size(), out);
+}
+
+namespace {
+template<bool F(const void* const in,
+                const size_t in_size,
+                brillo::Blob* const out)>
+bool BzipString(const string& str,
+                brillo::Blob* out) {
+  TEST_AND_RETURN_FALSE(out);
+  brillo::Blob temp;
+  TEST_AND_RETURN_FALSE(F(str.data(), str.size(), &temp));
+  out->clear();
+  out->insert(out->end(), temp.begin(), temp.end());
+  return true;
+}
+}  // namespace
+
+bool BzipCompressString(const string& str, brillo::Blob* out) {
+  return BzipString<BzipData<BzipBuffToBuffCompress>>(str, out);
+}
+
+bool BzipDecompressString(const string& str, brillo::Blob* out) {
+  return BzipString<BzipData<BzipBuffToBuffDecompress>>(str, out);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/bzip.h b/payload_generator/bzip.h
new file mode 100644
index 0000000..ca9956e
--- /dev/null
+++ b/payload_generator/bzip.h
@@ -0,0 +1,35 @@
+//
+// 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_BZIP_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_BZIP_H_
+
+#include <string>
+#include <vector>
+
+#include <brillo/secure_blob.h>
+
+namespace chromeos_update_engine {
+
+// Bzip2 compresses or decompresses str/in to out.
+bool BzipDecompress(const brillo::Blob& in, brillo::Blob* out);
+bool BzipCompress(const brillo::Blob& in, brillo::Blob* out);
+bool BzipCompressString(const std::string& str, brillo::Blob* out);
+bool BzipDecompressString(const std::string& str, brillo::Blob* out);
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_BZIP_H_
diff --git a/payload_generator/cycle_breaker.cc b/payload_generator/cycle_breaker.cc
new file mode 100644
index 0000000..321732e
--- /dev/null
+++ b/payload_generator/cycle_breaker.cc
@@ -0,0 +1,211 @@
+//
+// 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 <set>
+#include <string>
+#include <utility>
+
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+
+#include "update_engine/common/utils.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 (utils::MapContainsKey(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 = kuint64max;
+  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 (utils::SetContainsKey(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..231d63a
--- /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..4053cf3
--- /dev/null
+++ b/payload_generator/cycle_breaker_unittest.cc
@@ -0,0 +1,278 @@
+//
+// 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 <gtest/gtest.h>
+
+#include "update_engine/common/utils.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(utils::SetContainsKey(broken_edges, make_pair(n_a, n_e)) ||
+              utils::SetContainsKey(broken_edges, make_pair(n_e, n_b)) ||
+              utils::SetContainsKey(broken_edges, make_pair(n_b, n_a)));
+  EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_c, n_d)) ||
+              utils::SetContainsKey(broken_edges, make_pair(n_d, n_e)) ||
+              utils::SetContainsKey(broken_edges, make_pair(n_e, n_c)));
+  EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_g, n_h)) ||
+              utils::SetContainsKey(broken_edges, make_pair(n_h, n_g)));
+  EXPECT_EQ(3, 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) {
+  int 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) {
+  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);
+  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(utils::SetContainsKey(broken_edges, make_pair(n_b, n_a)));
+  EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_b, n_c)));
+  EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_d, n_e)));
+  EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_f, n_g)));
+  EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_h, n_i)));
+}
+
+TEST(CycleBreakerTest, UnblockGraphTest) {
+  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 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(utils::SetContainsKey(broken_edges, make_pair(n_a, n_b)));
+  EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_a, n_c)));
+}
+
+TEST(CycleBreakerTest, SkipOpsTest) {
+  int 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(2, breaker.skipped_ops());
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/delta_diff_generator.cc b/payload_generator/delta_diff_generator.cc
new file mode 100644
index 0000000..ffe0fe9
--- /dev/null
+++ b/payload_generator/delta_diff_generator.cc
@@ -0,0 +1,148 @@
+//
+// 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/delta_diff_generator.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <base/logging.h>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/delta_performer.h"
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_generator/ab_generator.h"
+#include "update_engine/payload_generator/blob_file_writer.h"
+#include "update_engine/payload_generator/delta_diff_utils.h"
+#include "update_engine/payload_generator/full_update_generator.h"
+#include "update_engine/payload_generator/inplace_generator.h"
+#include "update_engine/payload_generator/payload_file.h"
+
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+// bytes
+const size_t kRootFSPartitionSize = static_cast<size_t>(2) * 1024 * 1024 * 1024;
+const size_t kBlockSize = 4096;  // bytes
+
+bool GenerateUpdatePayloadFile(
+    const PayloadGenerationConfig& config,
+    const string& output_path,
+    const string& private_key_path,
+    uint64_t* metadata_size) {
+
+  // Create empty payload file object.
+  PayloadFile payload;
+  TEST_AND_RETURN_FALSE(payload.Init(config));
+
+  const string kTempFileTemplate("CrAU_temp_data.XXXXXX");
+  string temp_file_path;
+  int data_file_fd;
+  TEST_AND_RETURN_FALSE(
+      utils::MakeTempFile(kTempFileTemplate, &temp_file_path, &data_file_fd));
+  ScopedPathUnlinker temp_file_unlinker(temp_file_path);
+  TEST_AND_RETURN_FALSE(data_file_fd >= 0);
+
+  {
+    off_t data_file_size = 0;
+    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("");
+    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];
+      LOG(INFO) << "Partition name: " << new_part.name;
+      LOG(INFO) << "Partition size: " << new_part.size;
+      LOG(INFO) << "Block count: " << new_part.size / config.block_size;
+
+      // Select payload generation strategy based on the config.
+      unique_ptr<OperationsGenerator> strategy;
+      // We don't efficiently support deltas on squashfs. For now, we will
+      // produce full operations in that case.
+      if (!old_part.path.empty() &&
+          !utils::IsSquashfsFilesystem(new_part.path)) {
+        // Delta update.
+        if (utils::IsExtFilesystem(new_part.path)) {
+          LOG_IF(WARNING, old_part.size != new_part.size)
+              << "Old and new filesystems have different size.";
+          // TODO(deymo): Our tools only support growing the filesystem size
+          // during an update. Remove this check when that's fixed.
+          // crbug.com/192136
+          LOG_IF(FATAL, old_part.size > new_part.size)
+              << "Shirking the filesystem size is not supported at the moment.";
+        }
+        if (config.minor_version == kInPlaceMinorPayloadVersion) {
+          LOG(INFO) << "Using generator InplaceGenerator().";
+          strategy.reset(new InplaceGenerator());
+        } else if (config.minor_version == kSourceMinorPayloadVersion ||
+                   config.minor_version == kOpSrcHashMinorPayloadVersion) {
+          LOG(INFO) << "Using generator ABGenerator().";
+          strategy.reset(new ABGenerator());
+        } else {
+          LOG(ERROR) << "Unsupported minor version given for delta payload: "
+                     << config.minor_version;
+          return false;
+        }
+      } else {
+        LOG(INFO) << "Using generator FullUpdateGenerator().";
+        strategy.reset(new FullUpdateGenerator());
+      }
+
+      vector<AnnotatedOperation> aops;
+      // Generate the operations using the strategy we selected above.
+      TEST_AND_RETURN_FALSE(strategy->GenerateOperations(config,
+                                                         old_part,
+                                                         new_part,
+                                                         &blob_file,
+                                                         &aops));
+
+      // 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));
+    }
+  }
+
+  LOG(INFO) << "Writing payload file...";
+  // Write payload file to disk.
+  TEST_AND_RETURN_FALSE(payload.WritePayload(output_path, temp_file_path,
+                                             private_key_path, metadata_size));
+
+  LOG(INFO) << "All done. Successfully created delta file with "
+            << "metadata size = " << *metadata_size;
+  return true;
+}
+
+};  // namespace chromeos_update_engine
diff --git a/payload_generator/delta_diff_generator.h b/payload_generator/delta_diff_generator.h
new file mode 100644
index 0000000..d8bdae2
--- /dev/null
+++ b/payload_generator/delta_diff_generator.h
@@ -0,0 +1,47 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_PAYLOAD_GENERATOR_DELTA_DIFF_GENERATOR_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_DELTA_DIFF_GENERATOR_H_
+
+#include <string>
+
+#include "update_engine/payload_generator/payload_generation_config.h"
+
+namespace chromeos_update_engine {
+
+extern const size_t kBlockSize;
+extern const size_t kRootFSPartitionSize;
+
+// The |config| describes the payload generation request, describing both
+// old and new images for delta payloads and only the new image for full
+// payloads.
+// For delta payloads, the images should be already mounted read-only at
+// the respective rootfs_mountpt.
+// |private_key_path| points to a private key used to sign the update.
+// Pass empty string to not sign the update.
+// |output_path| is the filename where the delta update should be written.
+// Returns true on success. Also writes the size of the metadata into
+// |metadata_size|.
+bool GenerateUpdatePayloadFile(const PayloadGenerationConfig& config,
+                               const std::string& output_path,
+                               const std::string& private_key_path,
+                               uint64_t* metadata_size);
+
+
+};  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_DELTA_DIFF_GENERATOR_H_
diff --git a/payload_generator/delta_diff_utils.cc b/payload_generator/delta_diff_utils.cc
new file mode 100644
index 0000000..ba1aac4
--- /dev/null
+++ b/payload_generator/delta_diff_utils.cc
@@ -0,0 +1,679 @@
+//
+// 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/delta_diff_utils.h"
+
+#include <algorithm>
+#include <map>
+
+#include <base/files/file_util.h>
+#include <base/format_macros.h>
+#include <base/strings/stringprintf.h>
+
+#include "update_engine/common/hash_calculator.h"
+#include "update_engine/common/subprocess.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_generator/block_mapping.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"
+
+using std::map;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+namespace {
+
+const char* const kBsdiffPath = "bsdiff";
+
+// The maximum destination size allowed for bsdiff. In general, bsdiff should
+// work for arbitrary big files, but the payload generation and payload
+// application requires a significant amount of RAM. We put a hard-limit of
+// 200 MiB that should not affect any released board, but will limit the
+// Chrome binary in ASan builders.
+const uint64_t kMaxBsdiffDestinationSize = 200 * 1024 * 1024;  // bytes
+
+// 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;
+  bool new_src = true, new_dst = true;
+  size_t removed_bytes = 0, nonfull_block_bytes;
+  bool do_remove = false;
+  while (src_idx < src_extents->size() && dst_idx < dst_extents->size()) {
+    if (new_src) {
+      src_offset = 0;
+      new_src = false;
+    }
+    if (new_dst) {
+      dst_offset = 0;
+      new_dst = false;
+    }
+
+    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;
+
+    new_src = ProcessExtentBlockRange(src_extents, &src_idx, do_remove,
+                                      prev_src_offset, src_offset);
+    new_dst = ProcessExtentBlockRange(dst_extents, &dst_idx, do_remove,
+                                      prev_dst_offset, dst_offset);
+    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;
+}
+
+}  // namespace
+
+namespace diff_utils {
+
+bool DeltaReadPartition(
+    vector<AnnotatedOperation>* aops,
+    const PartitionConfig& old_part,
+    const PartitionConfig& new_part,
+    ssize_t hard_chunk_blocks,
+    size_t soft_chunk_blocks,
+    BlobFileWriter* blob_file,
+    bool src_ops_allowed) {
+  ExtentRanges old_visited_blocks;
+  ExtentRanges new_visited_blocks;
+
+  TEST_AND_RETURN_FALSE(DeltaMovedAndZeroBlocks(
+      aops,
+      old_part.path,
+      new_part.path,
+      old_part.fs_interface ? old_part.fs_interface->GetBlockCount() : 0,
+      new_part.fs_interface->GetBlockCount(),
+      soft_chunk_blocks,
+      src_ops_allowed,
+      blob_file,
+      &old_visited_blocks,
+      &new_visited_blocks));
+
+  map<string, vector<Extent>> old_files_map;
+  if (old_part.fs_interface) {
+    vector<FilesystemInterface::File> old_files;
+    old_part.fs_interface->GetFiles(&old_files);
+    for (const FilesystemInterface::File& file : old_files)
+      old_files_map[file.name] = file.extents;
+  }
+
+  TEST_AND_RETURN_FALSE(new_part.fs_interface);
+  vector<FilesystemInterface::File> new_files;
+  new_part.fs_interface->GetFiles(&new_files);
+
+  // The processing is very straightforward here, we generate operations for
+  // every file (and pseudo-file such as the metadata) in the new filesystem
+  // based on the file with the same name in the old filesystem, if any.
+  // Files with overlapping data blocks (like hardlinks or filesystems with tail
+  // packing or compression where the blocks store more than one file) are only
+  // generated once in the new image, but are also used only once from the old
+  // image due to some simplifications (see below).
+  for (const FilesystemInterface::File& new_file : new_files) {
+    // Ignore the files in the new filesystem without blocks. Symlinks with
+    // data blocks (for example, symlinks bigger than 60 bytes in ext2) are
+    // handled as normal files. We also ignore blocks that were already
+    // processed by a previous file.
+    vector<Extent> new_file_extents = FilterExtentRanges(
+        new_file.extents, new_visited_blocks);
+    new_visited_blocks.AddExtents(new_file_extents);
+
+    if (new_file_extents.empty())
+      continue;
+
+    LOG(INFO) << "Encoding file " << new_file.name << " ("
+              << BlocksInExtents(new_file_extents) << " blocks)";
+
+    // We can't visit each dst image inode more than once, as that would
+    // duplicate work. Here, we avoid visiting each source image inode
+    // more than once. Technically, we could have multiple operations
+    // that read the same blocks from the source image for diffing, but
+    // we choose not to avoid complexity. Eventually we will move away
+    // from using a graph/cycle detection/etc to generate diffs, and at that
+    // time, it will be easy (non-complex) to have many operations read
+    // from the same source blocks. At that time, this code can die. -adlr
+    vector<Extent> old_file_extents = FilterExtentRanges(
+        old_files_map[new_file.name], old_visited_blocks);
+    old_visited_blocks.AddExtents(old_file_extents);
+
+    TEST_AND_RETURN_FALSE(DeltaReadFile(
+        aops,
+        old_part.path,
+        new_part.path,
+        old_file_extents,
+        new_file_extents,
+        new_file.name,  // operation name
+        hard_chunk_blocks,
+        blob_file,
+        src_ops_allowed));
+  }
+  // Process all the blocks not included in any file. We provided all the unused
+  // blocks in the old partition as available data.
+  vector<Extent> new_unvisited = {
+      ExtentForRange(0, new_part.size / kBlockSize)};
+  new_unvisited = FilterExtentRanges(new_unvisited, new_visited_blocks);
+  if (new_unvisited.empty())
+    return true;
+
+  vector<Extent> old_unvisited;
+  if (old_part.fs_interface) {
+    old_unvisited.push_back(ExtentForRange(0, old_part.size / kBlockSize));
+    old_unvisited = FilterExtentRanges(old_unvisited, old_visited_blocks);
+  }
+
+  LOG(INFO) << "Scanning " << BlocksInExtents(new_unvisited)
+            << " unwritten blocks using chunk size of "
+            << soft_chunk_blocks << " blocks.";
+  // We use the soft_chunk_blocks limit for the <non-file-data> as we don't
+  // really know the structure of this data and we should not expect it to have
+  // redundancy between partitions.
+  TEST_AND_RETURN_FALSE(DeltaReadFile(
+      aops,
+      old_part.path,
+      new_part.path,
+      old_unvisited,
+      new_unvisited,
+      "<non-file-data>",  // operation name
+      soft_chunk_blocks,
+      blob_file,
+      src_ops_allowed));
+
+  return true;
+}
+
+bool DeltaMovedAndZeroBlocks(
+    vector<AnnotatedOperation>* aops,
+    const string& old_part,
+    const string& new_part,
+    size_t old_num_blocks,
+    size_t new_num_blocks,
+    ssize_t chunk_blocks,
+    bool src_ops_allowed,
+    BlobFileWriter* blob_file,
+    ExtentRanges* old_visited_blocks,
+    ExtentRanges* new_visited_blocks) {
+  vector<BlockMapping::BlockId> old_block_ids;
+  vector<BlockMapping::BlockId> new_block_ids;
+  TEST_AND_RETURN_FALSE(MapPartitionBlocks(old_part,
+                                           new_part,
+                                           old_num_blocks * kBlockSize,
+                                           new_num_blocks * kBlockSize,
+                                           kBlockSize,
+                                           &old_block_ids,
+                                           &new_block_ids));
+
+  // For minor-version=1, we map all the blocks that didn't move, regardless of
+  // the contents since they are already copied and no operation is required.
+  if (!src_ops_allowed) {
+    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.
+  map<BlockMapping::BlockId, vector<uint64_t>> old_blocks_map;
+
+  for (uint64_t block = old_num_blocks; block-- > 0; ) {
+    if (old_block_ids[block] != 0 && !old_visited_blocks->ContainsBlock(block))
+      old_blocks_map[old_block_ids[block]].push_back(block);
+  }
+
+  // The collection of blocks in the new partition with just zeros. This is a
+  // common case for free-space that's also problematic for bsdiff, so we want
+  // to optimize it using REPLACE_BZ operations. The blob for a REPLACE_BZ of
+  // just zeros is so small that it doesn't make sense to spend the I/O reading
+  // zeros from the old partition.
+  vector<Extent> new_zeros;
+
+  vector<Extent> old_identical_blocks;
+  vector<Extent> new_identical_blocks;
+
+  for (uint64_t block = 0; block < new_num_blocks; block++) {
+    // Only produce operations for blocks that were not yet visited.
+    if (new_visited_blocks->ContainsBlock(block))
+      continue;
+    if (new_block_ids[block] == 0) {
+      AppendBlockToExtents(&new_zeros, block);
+      continue;
+    }
+
+    auto old_blocks_map_it = old_blocks_map.find(new_block_ids[block]);
+    // Check if the block exists in the old partition at all.
+    if (old_blocks_map_it == old_blocks_map.end() ||
+        old_blocks_map_it->second.empty())
+      continue;
+
+    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 doesn't support that.
+    if (!src_ops_allowed)
+      old_blocks_map_it->second.pop_back();
+  }
+
+  // Produce operations for the zero blocks split per output extent.
+  size_t num_ops = aops->size();
+  new_visited_blocks->AddExtents(new_zeros);
+  for (Extent extent : new_zeros) {
+    TEST_AND_RETURN_FALSE(DeltaReadFile(
+        aops,
+        "",
+        new_part,
+        vector<Extent>(),  // old_extents
+        vector<Extent>{extent},  // new_extents
+        "<zeros>",
+        chunk_blocks,
+        blob_file,
+        src_ops_allowed));
+  }
+  LOG(INFO) << "Produced " << (aops->size() - num_ops) << " operations for "
+            << BlocksInExtents(new_zeros) << " zeroed blocks";
+
+  // Produce MOVE/SOURCE_COPY operations for the moved blocks.
+  num_ops = aops->size();
+  if (chunk_blocks == -1)
+    chunk_blocks = new_num_blocks;
+  uint64_t used_blocks = 0;
+  old_visited_blocks->AddExtents(old_identical_blocks);
+  new_visited_blocks->AddExtents(new_identical_blocks);
+  for (Extent extent : new_identical_blocks) {
+    // We split the operation at the extent boundary or when bigger than
+    // chunk_blocks.
+    for (uint64_t op_block_offset = 0; op_block_offset < extent.num_blocks();
+         op_block_offset += chunk_blocks) {
+      aops->emplace_back();
+      AnnotatedOperation* aop = &aops->back();
+      aop->name = "<identical-blocks>";
+      aop->op.set_type(src_ops_allowed ? InstallOperation::SOURCE_COPY
+                                       : InstallOperation::MOVE);
+
+      uint64_t chunk_num_blocks =
+        std::min(extent.num_blocks() - op_block_offset,
+                 static_cast<uint64_t>(chunk_blocks));
+
+      // The current operation represents the move/copy operation for the
+      // sublist starting at |used_blocks| of length |chunk_num_blocks| where
+      // the src and dst are from |old_identical_blocks| and
+      // |new_identical_blocks| respectively.
+      StoreExtents(
+          ExtentsSublist(old_identical_blocks, used_blocks, chunk_num_blocks),
+          aop->op.mutable_src_extents());
+
+      Extent* op_dst_extent = aop->op.add_dst_extents();
+      op_dst_extent->set_start_block(extent.start_block() + op_block_offset);
+      op_dst_extent->set_num_blocks(chunk_num_blocks);
+      CHECK(
+          vector<Extent>{*op_dst_extent} ==  // NOLINT(whitespace/braces)
+          ExtentsSublist(new_identical_blocks, used_blocks, chunk_num_blocks));
+
+      used_blocks += chunk_num_blocks;
+    }
+  }
+  LOG(INFO) << "Produced " << (aops->size() - num_ops) << " operations for "
+            << used_blocks << " identical blocks moved";
+
+  return true;
+}
+
+bool DeltaReadFile(
+    vector<AnnotatedOperation>* aops,
+    const string& old_part,
+    const string& new_part,
+    const vector<Extent>& old_extents,
+    const vector<Extent>& new_extents,
+    const string& name,
+    ssize_t chunk_blocks,
+    BlobFileWriter* blob_file,
+    bool src_ops_allowed) {
+  brillo::Blob data;
+  InstallOperation operation;
+
+  uint64_t total_blocks = BlocksInExtents(new_extents);
+  if (chunk_blocks == -1)
+    chunk_blocks = total_blocks;
+
+  // If bsdiff breaks again, blacklist the problem file by using:
+  //   bsdiff_allowed = (name != "/foo/bar")
+  //
+  // TODO(dgarrett): chromium-os:15274 connect this test to the command line.
+  bool bsdiff_allowed = true;
+  if (static_cast<uint64_t>(chunk_blocks) * kBlockSize >
+      kMaxBsdiffDestinationSize) {
+    bsdiff_allowed = false;
+  }
+
+  if (!bsdiff_allowed) {
+    LOG(INFO) << "bsdiff blacklisting: " << name;
+  }
+
+  for (uint64_t block_offset = 0; block_offset < total_blocks;
+      block_offset += chunk_blocks) {
+    // Split the old/new file in the same chunks. Note that this could drop
+    // some information from the old file used for the new chunk. If the old
+    // file is smaller (or even empty when there's no old file) the chunk will
+    // also be empty.
+    vector<Extent> old_extents_chunk = ExtentsSublist(
+        old_extents, block_offset, chunk_blocks);
+    vector<Extent> new_extents_chunk = ExtentsSublist(
+        new_extents, block_offset, chunk_blocks);
+    NormalizeExtents(&old_extents_chunk);
+    NormalizeExtents(&new_extents_chunk);
+
+    TEST_AND_RETURN_FALSE(ReadExtentsToDiff(old_part,
+                                            new_part,
+                                            old_extents_chunk,
+                                            new_extents_chunk,
+                                            bsdiff_allowed,
+                                            &data,
+                                            &operation,
+                                            src_ops_allowed));
+
+    // Check if the operation writes nothing.
+    if (operation.dst_extents_size() == 0) {
+      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.
+    AnnotatedOperation aop;
+    aop.name = name;
+    if (static_cast<uint64_t>(chunk_blocks) < total_blocks) {
+      aop.name = base::StringPrintf("%s:%" PRIu64,
+                                    name.c_str(), block_offset / chunk_blocks);
+    }
+    aop.op = operation;
+
+    // Write the data
+    if (operation.type() != InstallOperation::MOVE &&
+        operation.type() != InstallOperation::SOURCE_COPY) {
+      TEST_AND_RETURN_FALSE(aop.SetOperationBlob(&data, blob_file));
+    } else {
+      TEST_AND_RETURN_FALSE(blob_file->StoreBlob(data) != -1);
+    }
+    aops->emplace_back(aop);
+  }
+  return true;
+}
+
+bool ReadExtentsToDiff(const string& old_part,
+                       const string& new_part,
+                       const vector<Extent>& old_extents,
+                       const vector<Extent>& new_extents,
+                       bool bsdiff_allowed,
+                       brillo::Blob* out_data,
+                       InstallOperation* out_op,
+                       bool src_ops_allowed) {
+  InstallOperation operation;
+  // Data blob that will be written to delta file.
+  const brillo::Blob* data_blob = nullptr;
+
+  // We read blocks from old_extents and write blocks to new_extents.
+  uint64_t blocks_to_read = BlocksInExtents(old_extents);
+  uint64_t blocks_to_write = BlocksInExtents(new_extents);
+
+  // Make copies of the extents so we can modify them.
+  vector<Extent> src_extents = old_extents;
+  vector<Extent> dst_extents = new_extents;
+
+  // Read in bytes from new data.
+  brillo::Blob new_data;
+  TEST_AND_RETURN_FALSE(utils::ReadExtents(new_part,
+                                           new_extents,
+                                           &new_data,
+                                           kBlockSize * blocks_to_write,
+                                           kBlockSize));
+  TEST_AND_RETURN_FALSE(!new_data.empty());
+
+
+  // Using a REPLACE is always an option.
+  operation.set_type(InstallOperation::REPLACE);
+  data_blob = &new_data;
+
+  // Try compressing it with bzip2.
+  brillo::Blob new_data_bz;
+  TEST_AND_RETURN_FALSE(BzipCompress(new_data, &new_data_bz));
+  CHECK(!new_data_bz.empty());
+  if (new_data_bz.size() < data_blob->size()) {
+    // A REPLACE_BZ is better.
+    operation.set_type(InstallOperation::REPLACE_BZ);
+    data_blob = &new_data_bz;
+  }
+
+  brillo::Blob old_data;
+  brillo::Blob empty_blob;
+  brillo::Blob bsdiff_delta;
+  if (blocks_to_read > 0) {
+    // Read old data.
+    TEST_AND_RETURN_FALSE(
+        utils::ReadExtents(old_part, src_extents, &old_data,
+                           kBlockSize * blocks_to_read, kBlockSize));
+    if (old_data == new_data) {
+      // No change in data.
+      if (src_ops_allowed) {
+        operation.set_type(InstallOperation::SOURCE_COPY);
+      } else {
+        operation.set_type(InstallOperation::MOVE);
+      }
+      data_blob = &empty_blob;
+    } else if (bsdiff_allowed) {
+      // If the source file is considered bsdiff safe (no bsdiff bugs
+      // triggered), see if BSDIFF encoding is smaller.
+      base::FilePath old_chunk;
+      TEST_AND_RETURN_FALSE(base::CreateTemporaryFile(&old_chunk));
+      ScopedPathUnlinker old_unlinker(old_chunk.value());
+      TEST_AND_RETURN_FALSE(
+          utils::WriteFile(old_chunk.value().c_str(),
+                           old_data.data(), old_data.size()));
+      base::FilePath new_chunk;
+      TEST_AND_RETURN_FALSE(base::CreateTemporaryFile(&new_chunk));
+      ScopedPathUnlinker new_unlinker(new_chunk.value());
+      TEST_AND_RETURN_FALSE(
+          utils::WriteFile(new_chunk.value().c_str(),
+                           new_data.data(), new_data.size()));
+
+      TEST_AND_RETURN_FALSE(
+          BsdiffFiles(old_chunk.value(), new_chunk.value(), &bsdiff_delta));
+      CHECK_GT(bsdiff_delta.size(), static_cast<brillo::Blob::size_type>(0));
+      if (bsdiff_delta.size() < data_blob->size()) {
+        if (src_ops_allowed) {
+          operation.set_type(InstallOperation::SOURCE_BSDIFF);
+        } else {
+          operation.set_type(InstallOperation::BSDIFF);
+        }
+        data_blob = &bsdiff_delta;
+      }
+    }
+  }
+
+  size_t removed_bytes = 0;
+  // Remove identical src/dst block ranges in MOVE operations.
+  if (operation.type() == InstallOperation::MOVE) {
+    removed_bytes = RemoveIdenticalBlockRanges(
+        &src_extents, &dst_extents, new_data.size());
+  }
+  // Set legacy src_length and dst_length fields.
+  operation.set_src_length(old_data.size() - removed_bytes);
+  operation.set_dst_length(new_data.size() - removed_bytes);
+
+  // Embed extents in the operation.
+  StoreExtents(src_extents, operation.mutable_src_extents());
+  StoreExtents(dst_extents, operation.mutable_dst_extents());
+
+  // Replace operations should not have source extents.
+  if (operation.type() == InstallOperation::REPLACE ||
+      operation.type() == InstallOperation::REPLACE_BZ) {
+    operation.clear_src_extents();
+    operation.clear_src_length();
+  }
+
+  *out_data = std::move(*data_blob);
+  *out_op = operation;
+
+  return true;
+}
+
+// Runs the bsdiff tool on two files and returns the resulting delta in
+// 'out'. Returns true on success.
+bool BsdiffFiles(const string& old_file,
+                 const string& new_file,
+                 brillo::Blob* out) {
+  const string kPatchFile = "delta.patchXXXXXX";
+  string patch_file_path;
+
+  TEST_AND_RETURN_FALSE(
+      utils::MakeTempFile(kPatchFile, &patch_file_path, nullptr));
+
+  vector<string> cmd;
+  cmd.push_back(kBsdiffPath);
+  cmd.push_back(old_file);
+  cmd.push_back(new_file);
+  cmd.push_back(patch_file_path);
+
+  int rc = 1;
+  brillo::Blob patch_file;
+  TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &rc, nullptr));
+  TEST_AND_RETURN_FALSE(rc == 0);
+  TEST_AND_RETURN_FALSE(utils::ReadFile(patch_file_path, out));
+  unlink(patch_file_path.c_str());
+  return true;
+}
+
+// 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;
+  TEST_AND_RETURN_FALSE(hasher.UpdateFile(part.path, part.size) ==
+                        static_cast<off_t>(part.size));
+  TEST_AND_RETURN_FALSE(hasher.Finalize());
+  const brillo::Blob& hash = hasher.raw_hash();
+  info->set_hash(hash.data(), hash.size());
+  LOG(INFO) << part.path << ": size=" << part.size << " hash=" << hasher.hash();
+  return true;
+}
+
+bool CompareAopsByDestination(AnnotatedOperation first_aop,
+                              AnnotatedOperation second_aop) {
+  // We want empty operations to be at the end of the payload.
+  if (!first_aop.op.dst_extents().size() || !second_aop.op.dst_extents().size())
+    return ((!first_aop.op.dst_extents().size()) <
+            (!second_aop.op.dst_extents().size()));
+  uint32_t first_dst_start = first_aop.op.dst_extents(0).start_block();
+  uint32_t second_dst_start = second_aop.op.dst_extents(0).start_block();
+  return first_dst_start < second_dst_start;
+}
+
+}  // namespace diff_utils
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/delta_diff_utils.h b/payload_generator/delta_diff_utils.h
new file mode 100644
index 0000000..5ba9bc7
--- /dev/null
+++ b/payload_generator/delta_diff_utils.h
@@ -0,0 +1,137 @@
+//
+// 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_DELTA_DIFF_UTILS_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_DELTA_DIFF_UTILS_H_
+
+#include <string>
+#include <vector>
+
+#include <brillo/secure_blob.h>
+
+#include "update_engine/payload_generator/annotated_operation.h"
+#include "update_engine/payload_generator/extent_ranges.h"
+#include "update_engine/payload_generator/payload_generation_config.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+namespace diff_utils {
+
+// Create operations in |aops| to produce all the blocks in the |new_part|
+// partition using the filesystem opened in that PartitionConfig.
+// It uses the files reported by the filesystem in |old_part| and the data
+// blocks in that partition (if available) to determine the best way to compress
+// the new files (REPLACE, REPLACE_BZ, COPY, BSDIFF) and writes any necessary
+// data to the end of |data_fd| updating |data_file_size| accordingly.
+// |hard_chunk_blocks| and |soft_chunk_blocks| are the hard and soft chunk
+// limits in number of blocks respectively. The soft chunk limit is used to
+// split MOVE and SOURCE_COPY operations and REPLACE_BZ of zeroed blocks, while
+// the hard limit is used to split a file when generating other operations. A
+// value of -1 in |hard_chunk_blocks| means whole files.
+bool DeltaReadPartition(std::vector<AnnotatedOperation>* aops,
+                        const PartitionConfig& old_part,
+                        const PartitionConfig& new_part,
+                        ssize_t hard_chunk_blocks,
+                        size_t soft_chunk_blocks,
+                        BlobFileWriter* blob_file,
+                        bool src_ops_allowed);
+
+// Create operations in |aops| for identical blocks that moved around in the old
+// and new partition and also handle zeroed blocks. The old and new partition
+// are stored in the |old_part| and |new_part| files and have |old_num_blocks|
+// and |new_num_blocks| respectively. The maximum operation size is
+// |chunk_blocks| blocks, or unlimited if |chunk_blocks| is -1. The blobs of the
+// produced operations are stored in the |data_fd| file whose size is updated
+// in the value pointed by |data_file_size|.
+// The collections |old_visited_blocks| and |new_visited_blocks| state what
+// blocks already have operations reading or writing them and only operations
+// for unvisited blocks are produced by this function updating both collections
+// with the used blocks.
+bool DeltaMovedAndZeroBlocks(std::vector<AnnotatedOperation>* aops,
+                             const std::string& old_part,
+                             const std::string& new_part,
+                             size_t old_num_blocks,
+                             size_t new_num_blocks,
+                             ssize_t chunk_blocks,
+                             bool src_ops_allowed,
+                             BlobFileWriter* blob_file,
+                             ExtentRanges* old_visited_blocks,
+                             ExtentRanges* new_visited_blocks);
+
+// For a given file |name| append operations to |aops| to produce it in the
+// |new_part|. The file will be split in chunks of |chunk_blocks| blocks each
+// or treated as a single chunk if |chunk_blocks| is -1. The file data is
+// stored in |new_part| in the blocks described by |new_extents| and, if it
+// exists, the old version exists in |old_part| in the blocks described by
+// |old_extents|. The operations added to |aops| reference the data blob
+// in the file |data_fd|, which has length *data_file_size. *data_file_size is
+// updated appropriately. Returns true on success.
+bool DeltaReadFile(std::vector<AnnotatedOperation>* aops,
+                   const std::string& old_part,
+                   const std::string& new_part,
+                   const std::vector<Extent>& old_extents,
+                   const std::vector<Extent>& new_extents,
+                   const std::string& name,
+                   ssize_t chunk_blocks,
+                   BlobFileWriter* blob_file,
+                   bool src_ops_allowed);
+
+// Reads the blocks |old_extents| from |old_part| (if it exists) and the
+// |new_extents| from |new_part| and determines the smallest way to encode
+// this |new_extents| for the diff. It stores necessary data in |out_data| and
+// fills in |out_op|. If there's no change in old and new files, it creates a
+// MOVE operation. If there is a change, the smallest of REPLACE, REPLACE_BZ,
+// or BSDIFF wins. |new_extents| must not be empty.
+// If |src_ops_allowed| is true, it will emit SOURCE_COPY and SOURCE_BSDIFF
+// operations instead of MOVE and BSDIFF, respectively.
+// Returns true on success.
+bool ReadExtentsToDiff(const std::string& old_part,
+                       const std::string& new_part,
+                       const std::vector<Extent>& old_extents,
+                       const std::vector<Extent>& new_extents,
+                       bool bsdiff_allowed,
+                       brillo::Blob* out_data,
+                       InstallOperation* out_op,
+                       bool src_ops_allowed);
+
+// Runs the bsdiff tool on two files and returns the resulting delta in
+// |out|. Returns true on success.
+bool BsdiffFiles(const std::string& old_file,
+                 const std::string& new_file,
+                 brillo::Blob* out);
+
+// 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);
+
+// Compare two AnnotatedOperations by the start block of the first Extent in
+// their destination extents.
+bool CompareAopsByDestination(AnnotatedOperation first_aop,
+                              AnnotatedOperation second_aop);
+
+}  // namespace diff_utils
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_DELTA_DIFF_UTILS_H_
diff --git a/payload_generator/delta_diff_utils_unittest.cc b/payload_generator/delta_diff_utils_unittest.cc
new file mode 100644
index 0000000..4dc5981
--- /dev/null
+++ b/payload_generator/delta_diff_utils_unittest.cc
@@ -0,0 +1,775 @@
+//
+// 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/delta_diff_utils.h"
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <base/files/scoped_file.h>
+#include <base/format_macros.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/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/fake_filesystem.h"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+// Writes the |data| in the blocks specified by |extents| on the partition
+// |part_path|. The |data| size could be smaller than the size of the blocks
+// passed.
+bool WriteExtents(const string& part_path,
+                  const vector<Extent>& extents,
+                  off_t block_size,
+                  const brillo::Blob& data) {
+  uint64_t offset = 0;
+  base::ScopedFILE fp(fopen(part_path.c_str(), "r+"));
+  TEST_AND_RETURN_FALSE(fp.get());
+
+  for (const Extent& extent : extents) {
+    if (offset >= data.size())
+      break;
+    TEST_AND_RETURN_FALSE(
+        fseek(fp.get(), extent.start_block() * block_size, SEEK_SET) == 0);
+    uint64_t to_write = std::min(extent.num_blocks() * block_size,
+                                 data.size() - offset);
+    TEST_AND_RETURN_FALSE(
+        fwrite(data.data() + offset, 1, to_write, fp.get()) == to_write);
+    offset += extent.num_blocks() * block_size;
+  }
+  return true;
+}
+
+// Create a fake filesystem of the given |size| and initialize the partition
+// holding it in the PartitionConfig |part|.
+void CreatePartition(PartitionConfig* part, const string& pattern,
+                     uint64_t block_size, off_t size) {
+  int fd = -1;
+  ASSERT_TRUE(utils::MakeTempFile(pattern.c_str(), &part->path, &fd));
+  ASSERT_EQ(0, ftruncate(fd, size));
+  ASSERT_EQ(0, close(fd));
+  part->fs_interface.reset(new FakeFilesystem(block_size, size / block_size));
+  part->size = size;
+}
+
+// Writes to the |partition| path blocks such they are all different and they
+// include the tag passed in |tag|, making them also different to any other
+// block on a partition initialized with this function with a different tag.
+// The |block_size| should be a divisor of the partition size.
+// Returns whether the function succeeded writing the partition.
+bool InitializePartitionWithUniqueBlocks(const PartitionConfig& part,
+                                         uint64_t block_size,
+                                         uint64_t tag) {
+  TEST_AND_RETURN_FALSE(part.size % block_size == 0);
+  size_t num_blocks = part.size / block_size;
+  brillo::Blob file_data(part.size);
+  for (size_t i = 0; i < num_blocks; ++i) {
+    string prefix = base::StringPrintf(
+        "block tag 0x%.16" PRIx64 ", block number %16" PRIuS " ", tag, i);
+    brillo::Blob block_data(prefix.begin(), prefix.end());
+    TEST_AND_RETURN_FALSE(prefix.size() <= block_size);
+    block_data.resize(block_size, 'X');
+    std::copy(block_data.begin(), block_data.end(),
+              file_data.begin() + i * block_size);
+  }
+  return test_utils::WriteFileVector(part.path, file_data);
+}
+
+}  // namespace
+
+class DeltaDiffUtilsTest : public ::testing::Test {
+ protected:
+  const uint64_t kDefaultBlockCount = 128;
+
+  void SetUp() override {
+    CreatePartition(&old_part_, "DeltaDiffUtilsTest-old_part-XXXXXX",
+                    block_size_, block_size_ * kDefaultBlockCount);
+    CreatePartition(&new_part_, "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 avoid repeating all the arguments that never change.
+  bool RunDeltaMovedAndZeroBlocks(ssize_t chunk_blocks,
+                                  bool src_ops_allowed) {
+    BlobFileWriter blob_file(blob_fd_, &blob_size_);
+    return diff_utils::DeltaMovedAndZeroBlocks(
+        &aops_,
+        old_part_.path,
+        new_part_.path,
+        old_part_.size / block_size_,
+        new_part_.size / block_size_,
+        chunk_blocks,
+        src_ops_allowed,
+        &blob_file,
+        &old_visited_blocks_,
+        &new_visited_blocks_);
+  }
+
+  // Old and new temporary partitions used in the tests. These are initialized
+  // with
+  PartitionConfig old_part_{"part"};
+  PartitionConfig new_part_{"part"};
+
+  // The file holding the output blob from the various diff utils functions.
+  string blob_path_;
+  int blob_fd_{-1};
+  off_t blob_size_{0};
+
+  size_t block_size_{kBlockSize};
+
+  // Default input/output arguments used when calling DeltaMovedAndZeroBlocks().
+  vector<AnnotatedOperation> aops_;
+  ExtentRanges old_visited_blocks_;
+  ExtentRanges new_visited_blocks_;
+};
+
+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,
+      true,  // bsdiff_allowed
+      &data,
+      &op,
+      false));  // src_ops_allowed
+  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(BlocksInExtents(op.src_extents()),
+            BlocksInExtents(op.dst_extents()));
+  EXPECT_EQ(1, 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 = BlocksInExtents(old_extents);
+  EXPECT_EQ(num_blocks, 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,
+      true,  // bsdiff_allowed
+      &data,
+      &op,
+      false));  // src_ops_allowed
+
+  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 = BlocksInExtents(old_extents);
+
+  EXPECT_EQ(num_blocks * kBlockSize, op.src_length());
+  EXPECT_EQ(num_blocks * kBlockSize, op.dst_length());
+
+  EXPECT_EQ(old_extents.size(), 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(), 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,
+      true,  // bsdiff_allowed
+      &data,
+      &op,
+      false));  // src_ops_allowed
+
+  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(BlocksInExtents(op.src_extents()),
+            BlocksInExtents(op.dst_extents()));
+  EXPECT_EQ(1, BlocksInExtents(op.dst_extents()));
+}
+
+TEST_F(DeltaDiffUtilsTest, BsdiffNotAllowedTest) {
+  // Same setup as the previous test, but this time BSDIFF operations are not
+  // allowed.
+  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,
+      false,  // bsdiff_allowed
+      &data,
+      &op,
+      false));  // src_ops_allowed
+
+  EXPECT_FALSE(data.empty());
+
+  // The point of this test is that we don't use BSDIFF the way the above
+  // did. The rest of the details are to be caught in other tests.
+  EXPECT_TRUE(op.has_type());
+  EXPECT_NE(InstallOperation::BSDIFF, op.type());
+}
+
+TEST_F(DeltaDiffUtilsTest, BsdiffNotAllowedMoveTest) {
+  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));
+  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,
+      false,  // bsdiff_allowed
+      &data,
+      &op,
+      false));  // src_ops_allowed
+
+  EXPECT_TRUE(data.empty());
+
+  // The point of this test is that we can still use a MOVE for a file
+  // that is blacklisted.
+  EXPECT_TRUE(op.has_type());
+  EXPECT_EQ(InstallOperation::MOVE, op.type());
+}
+
+TEST_F(DeltaDiffUtilsTest, ReplaceSmallTest) {
+  // 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) };
+
+  // Make a blob that's just 1's that will compress well.
+  brillo::Blob ones(kBlockSize, 1);
+
+  // Make a blob with random data that won't compress well.
+  brillo::Blob random_data;
+  std::mt19937 gen(12345);
+  std::uniform_int_distribution<uint8_t> dis(0, 255);
+  for (uint32_t i = 0; i < kBlockSize; i++) {
+    random_data.push_back(dis(gen));
+  }
+
+  for (int i = 0; i < 2; i++) {
+    brillo::Blob data_to_test = i == 0 ? random_data : ones;
+    // The old_extents will be initialized with 0.
+    EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize,
+                             data_to_test));
+
+    brillo::Blob data;
+    InstallOperation op;
+    EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
+        old_part_.path,
+        new_part_.path,
+        old_extents,
+        new_extents,
+        true,  // bsdiff_allowed
+        &data,
+        &op,
+        false));  // src_ops_allowed
+    EXPECT_FALSE(data.empty());
+
+    EXPECT_TRUE(op.has_type());
+    const InstallOperation_Type expected_type =
+        (i == 0 ? InstallOperation::REPLACE : InstallOperation::REPLACE_BZ);
+    EXPECT_EQ(expected_type, op.type());
+    EXPECT_FALSE(op.has_data_offset());
+    EXPECT_FALSE(op.has_data_length());
+    EXPECT_EQ(0, op.src_extents_size());
+    EXPECT_FALSE(op.has_src_length());
+    EXPECT_EQ(1, op.dst_extents_size());
+    EXPECT_EQ(data_to_test.size(), op.dst_length());
+    EXPECT_EQ(1, BlocksInExtents(op.dst_extents()));
+  }
+}
+
+TEST_F(DeltaDiffUtilsTest, SourceCopyTest) {
+  // Makes sure SOURCE_COPY operations are emitted whenever src_ops_allowed
+  // is true. It is the same setup as MoveSmallTest, which checks that
+  // the operation is well-formed.
+  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(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,
+      true,  // bsdiff_allowed
+      &data,
+      &op,
+      true));  // src_ops_allowed
+  EXPECT_TRUE(data.empty());
+
+  EXPECT_TRUE(op.has_type());
+  EXPECT_EQ(InstallOperation::SOURCE_COPY, op.type());
+}
+
+TEST_F(DeltaDiffUtilsTest, SourceBsdiffTest) {
+  // Makes sure SOURCE_BSDIFF operations are emitted whenever src_ops_allowed
+  // is true. It is the same setup as BsdiffSmallTest, which checks
+  // that the operation is well-formed.
+  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,
+      true,  // bsdiff_allowed
+      &data,
+      &op,
+      true));  // src_ops_allowed
+
+  EXPECT_FALSE(data.empty());
+  EXPECT_TRUE(op.has_type());
+  EXPECT_EQ(InstallOperation::SOURCE_BSDIFF, 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) {
+  InitializePartitionWithUniqueBlocks(old_part_, block_size_, 5);
+  InitializePartitionWithUniqueBlocks(new_part_, block_size_, 42);
+
+  EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1,  // chunk_blocks
+                                         false));  // src_ops_allowed
+
+  EXPECT_EQ(0, old_visited_blocks_.blocks());
+  EXPECT_EQ(0, 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 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
+                                         false));  // src_ops_allowed
+
+  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) {
+  // We use a smaller partition for this test.
+  old_part_.size = kBlockSize * 50;
+  new_part_.size = kBlockSize * 50;
+
+  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, 5),
+                                    ExtentForRange(25, 7)};
+  old_visited_blocks_.AddExtents(already_visited);
+  new_visited_blocks_.AddExtents(already_visited);
+
+  // Override some of the old blocks with different data.
+  vector<Extent> different_blocks = {ExtentForRange(40, 5)};
+  EXPECT_TRUE(WriteExtents(old_part_.path, different_blocks, kBlockSize,
+                           brillo::Blob(5 * kBlockSize, 'a')));
+
+  EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(10,  // chunk_blocks
+                                         true));  // src_ops_allowed
+
+  ExtentRanges expected_ranges;
+  expected_ranges.AddExtent(ExtentForRange(0, 50));
+  expected_ranges.SubtractExtents(different_blocks);
+
+  EXPECT_EQ(expected_ranges.extent_set(), old_visited_blocks_.extent_set());
+  EXPECT_EQ(expected_ranges.extent_set(), new_visited_blocks_.extent_set());
+  EXPECT_EQ(0, blob_size_);
+
+  // We expect all the blocks that we didn't override with |different_blocks|
+  // and that we didn't mark as visited in |already_visited| to match and have a
+  // SOURCE_COPY operation chunked at 10 blocks.
+  vector<Extent> expected_op_extents = {
+      ExtentForRange(0, 5),
+      ExtentForRange(10, 10),
+      ExtentForRange(20, 5),
+      ExtentForRange(32, 8),
+      ExtentForRange(45, 5),
+  };
+
+  EXPECT_EQ(expected_op_extents.size(), aops_.size());
+  for (size_t i = 0; i < aops_.size() && i < expected_op_extents.size(); ++i) {
+    SCOPED_TRACE(base::StringPrintf("Failed on operation number %" PRIuS, i));
+    const AnnotatedOperation& aop = aops_[i];
+    EXPECT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
+    EXPECT_EQ(1, aop.op.src_extents_size());
+    EXPECT_EQ(expected_op_extents[i], aop.op.src_extents(0));
+    EXPECT_EQ(1, aop.op.dst_extents_size());
+    EXPECT_EQ(expected_op_extents[i], aop.op.dst_extents(0));
+  }
+}
+
+TEST_F(DeltaDiffUtilsTest, IdenticalBlocksAreCopiedInOder) {
+  // We use a smaller partition for this test.
+  old_part_.size = block_size_ * 50;
+  new_part_.size = block_size_ * 50;
+
+  // Create two identical partitions with 5 copies of the same unique "file".
+  brillo::Blob file_data(block_size_ * 10, 'a');
+  for (size_t offset = 0; offset < file_data.size(); offset += block_size_)
+    file_data[offset] = 'a' + offset / block_size_;
+
+  brillo::Blob partition_data(old_part_.size);
+  for (size_t offset = 0; offset < partition_data.size();
+       offset += file_data.size()) {
+    std::copy(file_data.begin(), file_data.end(),
+              partition_data.begin() + offset);
+  }
+  EXPECT_TRUE(test_utils::WriteFileVector(old_part_.path, partition_data));
+  EXPECT_TRUE(test_utils::WriteFileVector(new_part_.path, partition_data));
+
+  EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1,  // chunk_blocks
+                                         true));  // src_ops_allowed
+
+  // There should be only one SOURCE_COPY, for the whole partition and the
+  // source extents should cover only the first copy of the source file since
+  // we prefer to re-read files (maybe cached) instead of continue reading the
+  // rest of the partition.
+  EXPECT_EQ(1, aops_.size());
+  const AnnotatedOperation& aop = aops_[0];
+  EXPECT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
+  EXPECT_EQ(5, aop.op.src_extents_size());
+  for (int i = 0; i < aop.op.src_extents_size(); ++i) {
+    EXPECT_EQ(ExtentForRange(0, 10), aop.op.src_extents(i));
+  }
+
+  EXPECT_EQ(1, aop.op.dst_extents_size());
+  EXPECT_EQ(ExtentForRange(0, 50), aop.op.dst_extents(0));
+
+  EXPECT_EQ(0, blob_size_);
+}
+
+// Test that all blocks with zeros are handled separately using REPLACE_BZ
+// operations unless they are not moved.
+TEST_F(DeltaDiffUtilsTest, ZeroBlocksUseReplaceBz) {
+  InitializePartitionWithUniqueBlocks(old_part_, block_size_, 42);
+  InitializePartitionWithUniqueBlocks(new_part_, block_size_, 5);
+
+  // We set four ranges of blocks with zeros: a single block, a range that fits
+  // in the chunk size, a range that doesn't and finally a range of zeros that
+  // was also zeros in the old image.
+  vector<Extent> new_zeros = {
+      ExtentForRange(10, 1),
+      ExtentForRange(20, 4),
+      // The last range is split since the old image has zeros in part of it.
+      ExtentForRange(30, 20),
+  };
+  brillo::Blob zeros_data(BlocksInExtents(new_zeros) * block_size_, '\0');
+  EXPECT_TRUE(WriteExtents(new_part_.path, new_zeros, block_size_, zeros_data));
+
+  vector<Extent> old_zeros = vector<Extent>{ExtentForRange(43, 7)};
+  EXPECT_TRUE(WriteExtents(old_part_.path, old_zeros, block_size_, zeros_data));
+
+  EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(5,  // chunk_blocks
+                                         false));  // src_ops_allowed
+
+  // 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, part with REPLACE_BZ and part
+  // trivial MOVE operations (not included).
+  EXPECT_EQ(new_zeros,
+            new_visited_blocks_.GetExtentsForBlockCount(
+                new_visited_blocks_.blocks()));
+
+  vector<Extent> expected_op_extents = {
+      ExtentForRange(10, 1),
+      ExtentForRange(20, 4),
+      // This range should be split.
+      ExtentForRange(30, 5),
+      ExtentForRange(35, 5),
+      ExtentForRange(40, 3),
+  };
+
+  EXPECT_EQ(expected_op_extents.size(), aops_.size());
+  for (size_t i = 0; i < aops_.size() && i < expected_op_extents.size(); ++i) {
+    SCOPED_TRACE(base::StringPrintf("Failed on operation number %" PRIuS, i));
+    const AnnotatedOperation& aop = aops_[i];
+    EXPECT_EQ(InstallOperation::REPLACE_BZ, aop.op.type());
+    EXPECT_EQ(0, aop.op.src_extents_size());
+    EXPECT_EQ(1, aop.op.dst_extents_size());
+    EXPECT_EQ(expected_op_extents[i], aop.op.dst_extents(0));
+  }
+  EXPECT_NE(0, blob_size_);
+}
+
+TEST_F(DeltaDiffUtilsTest, ShuffledBlocksAreTracked) {
+  vector<uint64_t> permutation = {0, 1, 5, 6, 7, 2, 3, 4, 9, 10, 11, 12, 8};
+  vector<Extent> perm_extents;
+  for (uint64_t x : permutation)
+    AppendBlockToExtents(&perm_extents, x);
+
+  // We use a smaller partition for this test.
+  old_part_.size = block_size_ * permutation.size();
+  new_part_.size = block_size_ * permutation.size();
+  InitializePartitionWithUniqueBlocks(new_part_, block_size_, 123);
+
+  // We initialize the old_part_ with the blocks from new_part but in the
+  // |permutation| order. Block i in the old_part_ will contain the same data
+  // as block permutation[i] in the new_part_.
+  brillo::Blob new_contents;
+  EXPECT_TRUE(utils::ReadFile(new_part_.path, &new_contents));
+  EXPECT_TRUE(WriteExtents(old_part_.path, perm_extents, block_size_,
+                           new_contents));
+
+  EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1,  // chunk_blocks
+                                         true));  // src_ops_allowed
+
+  EXPECT_EQ(permutation.size(), old_visited_blocks_.blocks());
+  EXPECT_EQ(permutation.size(), new_visited_blocks_.blocks());
+
+  // There should be only one SOURCE_COPY, with a complicate list of extents.
+  EXPECT_EQ(1, aops_.size());
+  const AnnotatedOperation& aop = aops_[0];
+  EXPECT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
+  vector<Extent> aop_src_extents;
+  ExtentsToVector(aop.op.src_extents(), &aop_src_extents);
+  EXPECT_EQ(perm_extents, aop_src_extents);
+
+  EXPECT_EQ(1, aop.op.dst_extents_size());
+  EXPECT_EQ(ExtentForRange(0, permutation.size()), aop.op.dst_extents(0));
+
+  EXPECT_EQ(0, blob_size_);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/ext2_filesystem.cc b/payload_generator/ext2_filesystem.cc
new file mode 100644
index 0000000..b452b41
--- /dev/null
+++ b/payload_generator/ext2_filesystem.cc
@@ -0,0 +1,355 @@
+//
+// 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/ext2_filesystem.h"
+
+#include <et/com_err.h>
+#include <ext2fs/ext2_io.h>
+#include <ext2fs/ext2fs.h>
+
+#include <map>
+#include <set>
+
+#include <base/logging.h>
+#include <base/strings/stringprintf.h>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_generator/extent_ranges.h"
+#include "update_engine/payload_generator/extent_utils.h"
+#include "update_engine/update_metadata.pb.h"
+
+using std::set;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+// Processes all blocks belonging to an inode and adds them to the extent list.
+// This function should match the prototype expected by ext2fs_block_iterate2().
+int ProcessInodeAllBlocks(ext2_filsys fs,
+                          blk_t* blocknr,
+                          e2_blkcnt_t blockcnt,
+                          blk_t ref_blk,
+                          int ref_offset,
+                          void* priv) {
+  vector<Extent>* extents = static_cast<vector<Extent>*>(priv);
+  AppendBlockToExtents(extents, *blocknr);
+  return 0;
+}
+
+// Processes only indirect, double indirect or triple indirect metadata
+// blocks belonging to an inode. This function should match the prototype of
+// ext2fs_block_iterate2().
+int AddMetadataBlocks(ext2_filsys fs,
+                      blk_t* blocknr,
+                      e2_blkcnt_t blockcnt,
+                      blk_t ref_blk,
+                      int ref_offset,
+                      void* priv) {
+  set<uint64_t>* blocks = static_cast<set<uint64_t>*>(priv);
+  // If |blockcnt| is non-negative, |blocknr| points to the physical block
+  // number.
+  // If |blockcnt| is negative, it is one of the values: BLOCK_COUNT_IND,
+  // BLOCK_COUNT_DIND, BLOCK_COUNT_TIND or BLOCK_COUNT_TRANSLATOR and
+  // |blocknr| points to a block in the first three cases. The last case is
+  // only used by GNU Hurd, so we shouldn't see those cases here.
+  if (blockcnt == BLOCK_COUNT_IND || blockcnt == BLOCK_COUNT_DIND ||
+      blockcnt == BLOCK_COUNT_TIND) {
+    blocks->insert(*blocknr);
+  }
+  return 0;
+}
+
+struct UpdateFileAndAppendState {
+  std::map<ext2_ino_t, FilesystemInterface::File>* inodes = nullptr;
+  set<ext2_ino_t>* used_inodes = nullptr;
+  vector<FilesystemInterface::File>* files = nullptr;
+  ext2_filsys filsys;
+};
+
+int UpdateFileAndAppend(ext2_ino_t dir,
+                        int entry,
+                        struct ext2_dir_entry *dirent,
+                        int offset,
+                        int blocksize,
+                        char *buf,
+                        void *priv_data) {
+  UpdateFileAndAppendState* state =
+      static_cast<UpdateFileAndAppendState*>(priv_data);
+  uint32_t file_type = dirent->name_len >> 8;
+  // Directories can't have hard links, and they are added from the outer loop.
+  if (file_type == EXT2_FT_DIR)
+    return 0;
+
+  auto ino_file = state->inodes->find(dirent->inode);
+  if (ino_file == state->inodes->end())
+    return 0;
+  auto dir_file = state->inodes->find(dir);
+  if (dir_file == state->inodes->end())
+    return 0;
+  string basename(dirent->name, dirent->name_len & 0xff);
+  ino_file->second.name = dir_file->second.name;
+  if (dir_file->second.name != "/")
+    ino_file->second.name += "/";
+  ino_file->second.name += basename;
+
+  // Append this file to the output. If the file has a hard link, it will be
+  // added twice to the output, but with different names, which is ok. That will
+  // help identify all the versions of the same file.
+  state->files->push_back(ino_file->second);
+  state->used_inodes->insert(dirent->inode);
+  return 0;
+}
+
+}  // namespace
+
+unique_ptr<Ext2Filesystem> Ext2Filesystem::CreateFromFile(
+    const string& filename) {
+  if (filename.empty())
+    return nullptr;
+  unique_ptr<Ext2Filesystem> result(new Ext2Filesystem());
+  result->filename_ = filename;
+
+  errcode_t err = ext2fs_open(filename.c_str(),
+                              0,  // flags (read only)
+                              0,  // superblock block number
+                              0,  // block_size (autodetect)
+                              unix_io_manager,
+                              &result->filsys_);
+  if (err) {
+    LOG(ERROR) << "Opening ext2fs " << filename;
+    return nullptr;
+  }
+  return result;
+}
+
+Ext2Filesystem::~Ext2Filesystem() {
+  ext2fs_free(filsys_);
+}
+
+size_t Ext2Filesystem::GetBlockSize() const {
+  return filsys_->blocksize;
+}
+
+size_t Ext2Filesystem::GetBlockCount() const {
+  return ext2fs_blocks_count(filsys_->super);
+}
+
+bool Ext2Filesystem::GetFiles(vector<File>* files) const {
+  TEST_AND_RETURN_FALSE_ERRCODE(ext2fs_read_inode_bitmap(filsys_));
+
+  ext2_inode_scan iscan;
+  TEST_AND_RETURN_FALSE_ERRCODE(
+      ext2fs_open_inode_scan(filsys_, 0 /* buffer_blocks */, &iscan));
+
+  std::map<ext2_ino_t, File> inodes;
+
+  // List of directories. We need to first parse all the files in a directory
+  // to later fix the absolute paths.
+  vector<ext2_ino_t> directories;
+
+  set<uint64_t> inode_blocks;
+
+  // Iterator
+  ext2_ino_t it_ino;
+  ext2_inode it_inode;
+
+  bool ok = true;
+  while (true) {
+    errcode_t error = ext2fs_get_next_inode(iscan, &it_ino, &it_inode);
+    if (error) {
+      LOG(ERROR) << "Failed to retrieve next inode (" << error << ")";
+      ok = false;
+      break;
+    }
+    if (it_ino == 0)
+      break;
+
+    // Skip inodes that are not in use.
+    if (!ext2fs_test_inode_bitmap(filsys_->inode_map, it_ino))
+      continue;
+
+    File& file = inodes[it_ino];
+    if (it_ino == EXT2_RESIZE_INO) {
+      file.name = "<group-descriptors>";
+    } else {
+      file.name = base::StringPrintf("<inode-%u>", it_ino);
+    }
+
+    memset(&file.file_stat, 0, sizeof(file.file_stat));
+    file.file_stat.st_ino = it_ino;
+    file.file_stat.st_mode = it_inode.i_mode;
+    file.file_stat.st_nlink = it_inode.i_links_count;
+    file.file_stat.st_uid = it_inode.i_uid;
+    file.file_stat.st_gid = it_inode.i_gid;
+    file.file_stat.st_size = it_inode.i_size;
+    file.file_stat.st_blksize = filsys_->blocksize;
+    file.file_stat.st_blocks = it_inode.i_blocks;
+    file.file_stat.st_atime = it_inode.i_atime;
+    file.file_stat.st_mtime = it_inode.i_mtime;
+    file.file_stat.st_ctime = it_inode.i_ctime;
+
+    bool is_dir = (ext2fs_check_directory(filsys_, it_ino) == 0);
+    if (is_dir)
+      directories.push_back(it_ino);
+
+    if (!ext2fs_inode_has_valid_blocks(&it_inode))
+      continue;
+
+    // Process the inode data and metadata blocks.
+    // For normal files, inode blocks are indirect, double indirect
+    // and triple indirect blocks (no data blocks). For directories and
+    // the journal, all blocks are considered metadata blocks.
+    int flags = it_ino < EXT2_GOOD_OLD_FIRST_INO ? 0 : BLOCK_FLAG_DATA_ONLY;
+    error = ext2fs_block_iterate2(filsys_, it_ino, flags,
+                                  nullptr,  // block_buf
+                                  ProcessInodeAllBlocks,
+                                  &file.extents);
+
+    if (error) {
+      LOG(ERROR) << "Failed to enumerate inode " << it_ino
+                << " blocks (" << error << ")";
+      continue;
+    }
+    if (it_ino >= EXT2_GOOD_OLD_FIRST_INO) {
+      ext2fs_block_iterate2(filsys_, it_ino, 0, nullptr,
+                            AddMetadataBlocks,
+                            &inode_blocks);
+    }
+  }
+  ext2fs_close_inode_scan(iscan);
+  if (!ok)
+    return false;
+
+  // The set of inodes already added to the output. There can be less elements
+  // here than in files since the later can contain repeated inodes due to
+  // hardlink files.
+  set<ext2_ino_t> used_inodes;
+
+  UpdateFileAndAppendState priv_data;
+  priv_data.inodes = &inodes;
+  priv_data.used_inodes = &used_inodes;
+  priv_data.files = files;
+  priv_data.filsys = filsys_;
+
+  files->clear();
+  // Iterate over all the files of each directory to update the name and add it.
+  for (ext2_ino_t dir_ino : directories) {
+    char* dir_name = nullptr;
+    errcode_t error = ext2fs_get_pathname(filsys_, dir_ino, 0, &dir_name);
+    if (error) {
+      // Not being able to read a directory name is not a fatal error, it is
+      // just skiped.
+      LOG(WARNING) << "Reading directory name on inode " << dir_ino
+                   << " (error " << error << ")";
+      inodes[dir_ino].name = base::StringPrintf("<dir-%u>", dir_ino);
+    } else {
+      inodes[dir_ino].name = dir_name;
+      files->push_back(inodes[dir_ino]);
+      used_inodes.insert(dir_ino);
+    }
+    ext2fs_free_mem(&dir_name);
+
+    error = ext2fs_dir_iterate2(
+        filsys_, dir_ino, 0, nullptr /* block_buf */,
+        UpdateFileAndAppend, &priv_data);
+    if (error) {
+      LOG(WARNING) << "Failed to enumerate files in directory "
+                   << inodes[dir_ino].name << " (error " << error << ")";
+    }
+  }
+
+  // Add <inode-blocks> file with the blocks that hold inodes.
+  File inode_file;
+  inode_file.name = "<inode-blocks>";
+  for (uint64_t block : inode_blocks) {
+    AppendBlockToExtents(&inode_file.extents, block);
+  }
+  files->push_back(inode_file);
+
+  // Add <free-spacce> blocs.
+  errcode_t error = ext2fs_read_block_bitmap(filsys_);
+  if (error) {
+    LOG(ERROR) << "Reading the blocks bitmap (error " << error << ")";
+  } else {
+    File free_space;
+    free_space.name = "<free-space>";
+    blk64_t blk_start = ext2fs_get_block_bitmap_start2(filsys_->block_map);
+    blk64_t blk_end = ext2fs_get_block_bitmap_end2(filsys_->block_map);
+    for (blk64_t block = blk_start; block < blk_end; block++) {
+      if (!ext2fs_test_block_bitmap2(filsys_->block_map, block))
+        AppendBlockToExtents(&free_space.extents, block);
+    }
+    files->push_back(free_space);
+  }
+
+  // Add all the unreachable files plus the pseudo-files with an inode. Since
+  // these inodes aren't files in the filesystem, ignore the empty ones.
+  for (const auto& ino_file : inodes) {
+    if (used_inodes.find(ino_file.first) != used_inodes.end())
+      continue;
+    if (ino_file.second.extents.empty())
+      continue;
+
+    File file = ino_file.second;
+    ExtentRanges ranges;
+    ranges.AddExtents(file.extents);
+    file.extents = ranges.GetExtentsForBlockCount(ranges.blocks());
+
+    files->push_back(file);
+  }
+
+  return true;
+}
+
+bool Ext2Filesystem::LoadSettings(brillo::KeyValueStore* store) const {
+  // First search for the settings inode following symlinks if we find some.
+  ext2_ino_t ino_num = 0;
+  errcode_t err = ext2fs_namei_follow(
+      filsys_, EXT2_ROOT_INO /* root */, EXT2_ROOT_INO /* cwd */,
+      "/etc/update_engine.conf", &ino_num);
+  if (err != 0)
+    return false;
+
+  ext2_inode ino_data;
+  if (ext2fs_read_inode(filsys_, ino_num, &ino_data) != 0)
+    return false;
+
+  // Load the list of blocks and then the contents of the inodes.
+  vector<Extent> extents;
+  err = ext2fs_block_iterate2(filsys_, ino_num, BLOCK_FLAG_DATA_ONLY,
+                              nullptr,  // block_buf
+                              ProcessInodeAllBlocks,
+                              &extents);
+  if (err != 0)
+    return false;
+
+  brillo::Blob blob;
+  uint64_t physical_size = BlocksInExtents(extents) * filsys_->blocksize;
+  // Sparse holes in the settings file are not supported.
+  if (EXT2_I_SIZE(&ino_data) > physical_size)
+    return false;
+  if (!utils::ReadExtents(filename_, extents, &blob, physical_size,
+                          filsys_->blocksize))
+    return false;
+
+  string text(blob.begin(), blob.begin() + EXT2_I_SIZE(&ino_data));
+  return store->LoadFromString(text);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/ext2_filesystem.h b/payload_generator/ext2_filesystem.h
new file mode 100644
index 0000000..248e208
--- /dev/null
+++ b/payload_generator/ext2_filesystem.h
@@ -0,0 +1,71 @@
+//
+// 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_EXT2_FILESYSTEM_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_EXT2_FILESYSTEM_H_
+
+#include "update_engine/payload_generator/filesystem_interface.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <ext2fs/ext2fs.h>
+
+namespace chromeos_update_engine {
+
+class Ext2Filesystem : public FilesystemInterface {
+ public:
+  // Creates an Ext2Filesystem from a ext2 formatted filesystem stored in a
+  // file. The file doesn't need to be loop-back mounted.
+  static std::unique_ptr<Ext2Filesystem> CreateFromFile(
+      const std::string& filename);
+  virtual ~Ext2Filesystem();
+
+  // FilesystemInterface overrides.
+  size_t GetBlockSize() const override;
+  size_t GetBlockCount() const override;
+
+  // GetFiles will return one FilesystemInterface::File for every file and every
+  // directory in the filesystem. Hard-linked files will appear in the list
+  // several times with the same list of blocks.
+  // On addition to actual files, it also returns these pseudo-files:
+  //  <free-space>: With all the unallocated data-blocks.
+  //  <inode-blocks>: Will all the data-blocks for second and third level inodes
+  //    of all the files.
+  //  <group-descriptors>: With the block group descriptor and their reserved
+  //    space.
+  //  <metadata>: With the rest of ext2 metadata blocks, such as superblocks
+  //    and bitmap tables.
+  bool GetFiles(std::vector<File>* files) const override;
+
+  bool LoadSettings(brillo::KeyValueStore* store) const override;
+
+ private:
+  Ext2Filesystem() = default;
+
+  // The ext2 main data structure holding the filesystem.
+  ext2_filsys filsys_ = nullptr;
+
+  // The file where the filesystem is stored.
+  std::string filename_;
+
+  DISALLOW_COPY_AND_ASSIGN(Ext2Filesystem);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_EXT2_FILESYSTEM_H_
diff --git a/payload_generator/ext2_filesystem_unittest.cc b/payload_generator/ext2_filesystem_unittest.cc
new file mode 100644
index 0000000..df8b98f
--- /dev/null
+++ b/payload_generator/ext2_filesystem_unittest.cc
@@ -0,0 +1,214 @@
+//
+// 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/ext2_filesystem.h"
+
+#include <unistd.h>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <base/format_macros.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.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/extent_utils.h"
+
+using chromeos_update_engine::test_utils::System;
+using std::map;
+using std::set;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+uint64_t kDefaultFilesystemSize = 4 * 1024 * 1024;
+size_t kDefaultFilesystemBlockCount = 1024;
+size_t kDefaultFilesystemBlockSize = 4096;
+
+// Checks that all the blocks in |extents| are in the range [0, total_blocks).
+void ExpectBlocksInRange(const vector<Extent>& extents, uint64_t total_blocks) {
+  for (const Extent& extent : extents) {
+    EXPECT_LE(0, extent.start_block());
+    EXPECT_LE(extent.start_block() + extent.num_blocks(), total_blocks);
+  }
+}
+
+}  // namespace
+
+
+class Ext2FilesystemTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ASSERT_TRUE(utils::MakeTempFile("Ext2FilesystemTest-XXXXXX",
+                                    &fs_filename_, nullptr));
+    ASSERT_EQ(0, truncate(fs_filename_.c_str(), kDefaultFilesystemSize));
+  }
+
+  void TearDown() override {
+    unlink(fs_filename_.c_str());
+  }
+
+  string fs_filename_;
+};
+
+TEST_F(Ext2FilesystemTest, InvalidFilesystem) {
+  unique_ptr<Ext2Filesystem> fs = Ext2Filesystem::CreateFromFile(fs_filename_);
+  ASSERT_EQ(nullptr, fs.get());
+
+  fs = Ext2Filesystem::CreateFromFile("/path/to/invalid/file");
+  ASSERT_EQ(nullptr, fs.get());
+}
+
+TEST_F(Ext2FilesystemTest, EmptyFilesystem) {
+  EXPECT_EQ(0, System(base::StringPrintf(
+      "/sbin/mkfs.ext2 -q -b %" PRIuS " -F %s",
+      kDefaultFilesystemBlockSize, fs_filename_.c_str())));
+  unique_ptr<Ext2Filesystem> fs = Ext2Filesystem::CreateFromFile(fs_filename_);
+
+  ASSERT_NE(nullptr, fs.get());
+  EXPECT_EQ(kDefaultFilesystemBlockCount, fs->GetBlockCount());
+  EXPECT_EQ(kDefaultFilesystemBlockSize, fs->GetBlockSize());
+
+  vector<FilesystemInterface::File> files;
+  EXPECT_TRUE(fs->GetFiles(&files));
+
+  map<string, FilesystemInterface::File> map_files;
+  for (const auto& file : files) {
+    EXPECT_EQ(map_files.end(), map_files.find(file.name))
+        << "File " << file.name << " repeated in the list.";
+    map_files[file.name] = file;
+    ExpectBlocksInRange(file.extents, fs->GetBlockCount());
+  }
+  EXPECT_EQ(2, map_files["/"].file_stat.st_ino);
+  EXPECT_FALSE(map_files["<free-space>"].extents.empty());
+}
+
+// This test parses the sample images generated during build time with the
+// "generate_image.sh" script. The expected conditions of each file in these
+// images is encoded in the file name, as defined in the mentioned script.
+TEST_F(Ext2FilesystemTest, ParseGeneratedImages) {
+  const vector<string> kGeneratedImages = {
+      "disk_ext2_1k.img",
+      "disk_ext2_4k.img" };
+  base::FilePath build_path = test_utils::GetBuildArtifactsPath().Append("gen");
+  for (const string& fs_name : kGeneratedImages) {
+    LOG(INFO) << "Testing " << fs_name;
+    unique_ptr<Ext2Filesystem> fs = Ext2Filesystem::CreateFromFile(
+        build_path.Append(fs_name).value());
+    ASSERT_NE(nullptr, fs.get());
+
+    vector<FilesystemInterface::File> files;
+    map<string, FilesystemInterface::File> map_files;
+    set<string> filenames;
+    EXPECT_TRUE(fs->GetFiles(&files));
+    for (const auto& file : files) {
+      // Check no repeated files. We should parse hard-links with two different
+      // names.
+      EXPECT_EQ(map_files.end(), map_files.find(file.name))
+          << "File " << file.name << " repeated in the list.";
+      map_files[file.name] = file;
+      filenames.insert(file.name);
+      ExpectBlocksInRange(file.extents, fs->GetBlockCount());
+    }
+
+    // Check that all the files are parsed, and the /removed file should not
+    // be included in the list.
+    set<string> kExpectedFiles = {
+        "/",
+        "/dir1",
+        "/dir1/file",
+        "/dir1/dir2",
+        "/dir1/dir2/file",
+        "/dir1/dir2/dir1",
+        "/empty-file",
+        "/link-hard-regular-16k",
+        "/link-long_symlink",
+        "/link-short_symlink",
+        "/lost+found",
+        "/regular-small",
+        "/regular-16k",
+        "/regular-32k-zeros",
+        "/regular-with_net_cap",
+        "/sparse_empty-10k",
+        "/sparse_empty-2blocks",
+        "/sparse-10000blocks",
+        "/sparse-16k-last_block",
+        "/sparse-16k-first_block",
+        "/sparse-16k-holes",
+        "<inode-blocks>",
+        "<free-space>",
+        "<group-descriptors>",
+    };
+    EXPECT_EQ(kExpectedFiles, filenames);
+
+    FilesystemInterface::File file;
+
+    // Small symlinks don't actually have data blocks.
+    EXPECT_TRUE(map_files["/link-short_symlink"].extents.empty());
+    EXPECT_EQ(1, BlocksInExtents(map_files["/link-long_symlink"].extents));
+
+    // Hard-links report the same list of blocks.
+    EXPECT_EQ(map_files["/link-hard-regular-16k"].extents,
+              map_files["/regular-16k"].extents);
+    EXPECT_FALSE(map_files["/regular-16k"].extents.empty());
+
+    // The number of blocks in these files doesn't depend on the
+    // block size.
+    EXPECT_TRUE(map_files["/empty-file"].extents.empty());
+    EXPECT_EQ(1, BlocksInExtents(map_files["/regular-small"].extents));
+    EXPECT_EQ(1, BlocksInExtents(map_files["/regular-with_net_cap"].extents));
+    EXPECT_TRUE(map_files["/sparse_empty-10k"].extents.empty());
+    EXPECT_TRUE(map_files["/sparse_empty-2blocks"].extents.empty());
+    EXPECT_EQ(1, BlocksInExtents(map_files["/sparse-16k-last_block"].extents));
+    EXPECT_EQ(1, BlocksInExtents(map_files["/sparse-16k-first_block"].extents));
+    EXPECT_EQ(2, BlocksInExtents(map_files["/sparse-16k-holes"].extents));
+  }
+}
+
+TEST_F(Ext2FilesystemTest, LoadSettingsFailsTest) {
+  base::FilePath path = test_utils::GetBuildArtifactsPath().Append(
+      "gen/disk_ext2_1k.img");
+  unique_ptr<Ext2Filesystem> fs = Ext2Filesystem::CreateFromFile(path.value());
+
+  brillo::KeyValueStore store;
+  // disk_ext2_1k.img doesn't have the /etc/update_engine.conf file.
+  EXPECT_FALSE(fs->LoadSettings(&store));
+}
+
+TEST_F(Ext2FilesystemTest, LoadSettingsWorksTest) {
+  base::FilePath path = test_utils::GetBuildArtifactsPath().Append(
+      "gen/disk_ext2_ue_settings.img");
+  unique_ptr<Ext2Filesystem> fs = Ext2Filesystem::CreateFromFile(path.value());
+
+  brillo::KeyValueStore store;
+  EXPECT_TRUE(fs->LoadSettings(&store));
+  string minor_version;
+  EXPECT_TRUE(store.GetString("PAYLOAD_MINOR_VERSION", &minor_version));
+  EXPECT_EQ("1234", minor_version);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/extent_ranges.cc b/payload_generator/extent_ranges.cc
new file mode 100644
index 0000000..848fdc7
--- /dev/null
+++ b/payload_generator/extent_ranges.cc
@@ -0,0 +1,306 @@
+//
+// 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/extent_ranges.h"
+
+#include <algorithm>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include <base/logging.h>
+
+#include "update_engine/payload_consumer/payload_constants.h"
+
+using std::set;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+bool ExtentRanges::ExtentsOverlapOrTouch(const Extent& a, const Extent& b) {
+  if (a.start_block() == b.start_block())
+    return true;
+  if (a.start_block() == kSparseHole || b.start_block() == kSparseHole)
+    return false;
+  if (a.start_block() < b.start_block()) {
+    return a.start_block() + a.num_blocks() >= b.start_block();
+  } else {
+    return b.start_block() + b.num_blocks() >= a.start_block();
+  }
+}
+
+bool ExtentRanges::ExtentsOverlap(const Extent& a, const Extent& b) {
+  if (a.start_block() == b.start_block())
+    return true;
+  if (a.start_block() == kSparseHole || b.start_block() == kSparseHole)
+    return false;
+  if (a.start_block() < b.start_block()) {
+    return a.start_block() + a.num_blocks() > b.start_block();
+  } else {
+    return b.start_block() + b.num_blocks() > a.start_block();
+  }
+}
+
+void ExtentRanges::AddBlock(uint64_t block) {
+  AddExtent(ExtentForRange(block, 1));
+}
+
+void ExtentRanges::SubtractBlock(uint64_t block) {
+  SubtractExtent(ExtentForRange(block, 1));
+}
+
+namespace {
+
+Extent UnionOverlappingExtents(const Extent& first, const Extent& second) {
+  CHECK_NE(kSparseHole, first.start_block());
+  CHECK_NE(kSparseHole, second.start_block());
+  uint64_t start = std::min(first.start_block(), second.start_block());
+  uint64_t end = std::max(first.start_block() + first.num_blocks(),
+                          second.start_block() + second.num_blocks());
+  return ExtentForRange(start, end - start);
+}
+
+}  // namespace
+
+void ExtentRanges::AddExtent(Extent extent) {
+  if (extent.start_block() == kSparseHole || extent.num_blocks() == 0)
+    return;
+
+  ExtentSet::iterator begin_del = extent_set_.end();
+  ExtentSet::iterator end_del = extent_set_.end();
+  uint64_t del_blocks = 0;
+  for (ExtentSet::iterator it = extent_set_.begin(), e = extent_set_.end();
+       it != e; ++it) {
+    if (ExtentsOverlapOrTouch(*it, extent)) {
+      end_del = it;
+      ++end_del;
+      del_blocks += it->num_blocks();
+      if (begin_del == extent_set_.end())
+        begin_del = it;
+
+      extent = UnionOverlappingExtents(extent, *it);
+    }
+  }
+  extent_set_.erase(begin_del, end_del);
+  extent_set_.insert(extent);
+  blocks_ -= del_blocks;
+  blocks_ += extent.num_blocks();
+}
+
+namespace {
+// Returns base - subtractee (set subtraction).
+ExtentRanges::ExtentSet SubtractOverlappingExtents(const Extent& base,
+                                                   const Extent& subtractee) {
+  ExtentRanges::ExtentSet ret;
+  if (subtractee.start_block() > base.start_block()) {
+    ret.insert(ExtentForRange(base.start_block(),
+                              subtractee.start_block() - base.start_block()));
+  }
+  uint64_t base_end = base.start_block() + base.num_blocks();
+  uint64_t subtractee_end = subtractee.start_block() + subtractee.num_blocks();
+  if (base_end > subtractee_end) {
+    ret.insert(ExtentForRange(subtractee_end, base_end - subtractee_end));
+  }
+  return ret;
+}
+}  // namespace
+
+void ExtentRanges::SubtractExtent(const Extent& extent) {
+  if (extent.start_block() == kSparseHole || extent.num_blocks() == 0)
+    return;
+
+  ExtentSet::iterator begin_del = extent_set_.end();
+  ExtentSet::iterator end_del = extent_set_.end();
+  uint64_t del_blocks = 0;
+  ExtentSet new_extents;
+  for (ExtentSet::iterator it = extent_set_.begin(), e = extent_set_.end();
+       it != e; ++it) {
+    if (!ExtentsOverlap(*it, extent))
+      continue;
+
+    if (begin_del == extent_set_.end())
+      begin_del = it;
+    end_del = it;
+    ++end_del;
+
+    del_blocks += it->num_blocks();
+
+    ExtentSet subtraction = SubtractOverlappingExtents(*it, extent);
+    for (ExtentSet::iterator jt = subtraction.begin(), je = subtraction.end();
+         jt != je; ++jt) {
+      new_extents.insert(*jt);
+      del_blocks -= jt->num_blocks();
+    }
+  }
+  extent_set_.erase(begin_del, end_del);
+  extent_set_.insert(new_extents.begin(), new_extents.end());
+  blocks_ -= del_blocks;
+}
+
+void ExtentRanges::AddRanges(const ExtentRanges& ranges) {
+  for (ExtentSet::const_iterator it = ranges.extent_set_.begin(),
+           e = ranges.extent_set_.end(); it != e; ++it) {
+    AddExtent(*it);
+  }
+}
+
+void ExtentRanges::SubtractRanges(const ExtentRanges& ranges) {
+  for (ExtentSet::const_iterator it = ranges.extent_set_.begin(),
+           e = ranges.extent_set_.end(); it != e; ++it) {
+    SubtractExtent(*it);
+  }
+}
+
+void ExtentRanges::AddExtents(const vector<Extent>& extents) {
+  for (vector<Extent>::const_iterator it = extents.begin(), e = extents.end();
+       it != e; ++it) {
+    AddExtent(*it);
+  }
+}
+
+void ExtentRanges::SubtractExtents(const vector<Extent>& extents) {
+  for (vector<Extent>::const_iterator it = extents.begin(), e = extents.end();
+       it != e; ++it) {
+    SubtractExtent(*it);
+  }
+}
+
+void ExtentRanges::AddRepeatedExtents(
+    const ::google::protobuf::RepeatedPtrField<Extent> &exts) {
+  for (int i = 0, e = exts.size(); i != e; ++i) {
+    AddExtent(exts.Get(i));
+  }
+}
+
+void ExtentRanges::SubtractRepeatedExtents(
+    const ::google::protobuf::RepeatedPtrField<Extent> &exts) {
+  for (int i = 0, e = exts.size(); i != e; ++i) {
+    SubtractExtent(exts.Get(i));
+  }
+}
+
+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|.
+  if (lower != extent_set_.begin())
+    lower--;
+  // Any extent starting at block+1 or later is not interesting, so this is the
+  // upper limit.
+  auto upper = extent_set_.lower_bound(ExtentForRange(block + 1, 0));
+  for (auto iter = lower; iter != upper; ++iter) {
+    if (iter->start_block() <= block &&
+        block < iter->start_block() + iter->num_blocks()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+void ExtentRanges::Dump() const {
+  LOG(INFO) << "ExtentRanges Dump. blocks: " << blocks_;
+  for (ExtentSet::const_iterator it = extent_set_.begin(),
+           e = extent_set_.end();
+       it != e; ++it) {
+    LOG(INFO) << "{" << it->start_block() << ", " << it->num_blocks() << "}";
+  }
+}
+
+Extent ExtentForRange(uint64_t start_block, uint64_t num_blocks) {
+  Extent ret;
+  ret.set_start_block(start_block);
+  ret.set_num_blocks(num_blocks);
+  return ret;
+}
+
+vector<Extent> ExtentRanges::GetExtentsForBlockCount(
+    uint64_t count) const {
+  vector<Extent> out;
+  if (count == 0)
+    return out;
+  uint64_t out_blocks = 0;
+  CHECK(count <= blocks_);
+  for (ExtentSet::const_iterator it = extent_set_.begin(),
+           e = extent_set_.end();
+       it != e; ++it) {
+    const uint64_t blocks_needed = count - out_blocks;
+    const Extent& extent = *it;
+    out.push_back(extent);
+    out_blocks += extent.num_blocks();
+    if (extent.num_blocks() < blocks_needed)
+      continue;
+    if (extent.num_blocks() == blocks_needed)
+      break;
+    // If we get here, we just added the last extent needed, but it's too big
+    out_blocks -= extent.num_blocks();
+    out_blocks += blocks_needed;
+    out.back().set_num_blocks(blocks_needed);
+    break;
+  }
+  return out;
+}
+
+vector<Extent> FilterExtentRanges(const vector<Extent>& extents,
+                                  const ExtentRanges& ranges) {
+  vector<Extent> result;
+  const ExtentRanges::ExtentSet& extent_set = ranges.extent_set();
+  for (Extent extent : extents) {
+    // The extents are sorted by the start_block. We want to iterate all the
+    // Extents in the ExtentSet possibly overlapping the current |extent|. This
+    // is achieved by looking from the extent whose start_block is *lower* than
+    // the extent.start_block() up to the greatest extent whose start_block is
+    // lower than extent.start_block() + extent.num_blocks().
+    auto lower = extent_set.lower_bound(extent);
+    // We need to decrement the lower_bound to look at the extent that could
+    // overlap the beginning of the current |extent|.
+    if (lower != extent_set.begin())
+      lower--;
+    auto upper = extent_set.lower_bound(
+        ExtentForRange(extent.start_block() + extent.num_blocks(), 0));
+    for (auto iter = lower; iter != upper; ++iter) {
+      if (!ExtentRanges::ExtentsOverlap(extent, *iter))
+        continue;
+      if (iter->start_block() <= extent.start_block()) {
+        // We need to cut blocks from the beginning of the |extent|.
+        uint64_t cut_blocks = iter->start_block() + iter->num_blocks() -
+            extent.start_block();
+        if (cut_blocks >= extent.num_blocks()) {
+          extent.set_num_blocks(0);
+          break;
+        }
+        extent = ExtentForRange(extent.start_block() + cut_blocks,
+                                extent.num_blocks() - cut_blocks);
+      } else {
+        // We need to cut blocks on the middle of the extent, possible up to the
+        // end of it.
+        result.push_back(
+            ExtentForRange(extent.start_block(),
+                           iter->start_block() - extent.start_block()));
+        uint64_t new_start = iter->start_block() + iter->num_blocks();
+        uint64_t old_end = extent.start_block() + extent.num_blocks();
+        if (new_start >= old_end) {
+          extent.set_num_blocks(0);
+          break;
+        }
+        extent = ExtentForRange(new_start, old_end - new_start);
+      }
+    }
+    if (extent.num_blocks() > 0)
+      result.push_back(extent);
+  }
+  return result;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/extent_ranges.h b/payload_generator/extent_ranges.h
new file mode 100644
index 0000000..198c834
--- /dev/null
+++ b/payload_generator/extent_ranges.h
@@ -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.
+//
+
+#ifndef UPDATE_ENGINE_PAYLOAD_GENERATOR_EXTENT_RANGES_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_EXTENT_RANGES_H_
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include <base/macros.h>
+
+#include "update_engine/update_metadata.pb.h"
+
+// An ExtentRanges object represents an unordered collection of extents (and
+// therefore blocks). Such an object may be modified by adding or subtracting
+// blocks (think: set addition or set subtraction). Note that ExtentRanges
+// ignores sparse hole extents mostly to avoid confusion between extending a
+// sparse hole range vs. set addition but also to ensure that the delta
+// generator doesn't use sparse holes as scratch space.
+
+namespace chromeos_update_engine {
+
+struct ExtentLess {
+  bool operator()(const Extent& x, const Extent& y) const {
+    return x.start_block() < y.start_block();
+  }
+};
+
+Extent ExtentForRange(uint64_t start_block, uint64_t num_blocks);
+
+class ExtentRanges {
+ public:
+  typedef std::set<Extent, ExtentLess> ExtentSet;
+
+  ExtentRanges() : blocks_(0) {}
+  void AddBlock(uint64_t block);
+  void SubtractBlock(uint64_t block);
+  void AddExtent(Extent extent);
+  void SubtractExtent(const Extent& extent);
+  void AddExtents(const std::vector<Extent>& extents);
+  void SubtractExtents(const std::vector<Extent>& extents);
+  void AddRepeatedExtents(
+      const ::google::protobuf::RepeatedPtrField<Extent> &exts);
+  void SubtractRepeatedExtents(
+      const ::google::protobuf::RepeatedPtrField<Extent> &exts);
+  void AddRanges(const ExtentRanges& ranges);
+  void SubtractRanges(const ExtentRanges& ranges);
+
+  // Returns whether the block |block| is in this ExtentRange.
+  bool ContainsBlock(uint64_t block) const;
+
+  static bool ExtentsOverlapOrTouch(const Extent& a, const Extent& b);
+  static bool ExtentsOverlap(const Extent& a, const Extent& b);
+
+  // Dumps contents to the log file. Useful for debugging.
+  void Dump() const;
+
+  uint64_t blocks() const { return blocks_; }
+  const ExtentSet& extent_set() const { return extent_set_; }
+
+  // Returns an ordered vector of extents for |count| blocks,
+  // using extents in extent_set_. The returned extents are not
+  // removed from extent_set_. |count| must be less than or equal to
+  // the number of blocks in this extent set.
+  std::vector<Extent> GetExtentsForBlockCount(uint64_t count) const;
+
+ private:
+  ExtentSet extent_set_;
+  uint64_t blocks_;
+};
+
+// Filters out from the passed list of extents |extents| all the blocks in the
+// ExtentRanges set. Note that the order of the blocks in |extents| is preserved
+// omitting blocks present in the ExtentRanges |ranges|.
+std::vector<Extent> FilterExtentRanges(const std::vector<Extent>& extents,
+                                       const ExtentRanges& ranges);
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_EXTENT_RANGES_H_
diff --git a/payload_generator/extent_ranges_unittest.cc b/payload_generator/extent_ranges_unittest.cc
new file mode 100644
index 0000000..8b39040
--- /dev/null
+++ b/payload_generator/extent_ranges_unittest.cc
@@ -0,0 +1,345 @@
+//
+// 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/extent_ranges.h"
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "update_engine/common/test_utils.h"
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_generator/extent_utils.h"
+
+using std::vector;
+
+namespace chromeos_update_engine {
+
+class ExtentRangesTest : public ::testing::Test {};
+
+namespace {
+void ExpectRangeEq(const ExtentRanges& ranges,
+                   const uint64_t* expected,
+                   size_t sz,
+                   int line) {
+  uint64_t blocks = 0;
+  for (size_t i = 1; i < sz; i += 2) {
+    blocks += expected[i];
+  }
+  EXPECT_EQ(blocks, ranges.blocks()) << "line: " << line;
+
+  const ExtentRanges::ExtentSet& result = ranges.extent_set();
+  ExtentRanges::ExtentSet::const_iterator it = result.begin();
+  for (size_t i = 0; i < sz; i += 2) {
+    EXPECT_FALSE(it == result.end()) << "line: " << line;
+    EXPECT_EQ(expected[i], it->start_block()) << "line: " << line;
+    EXPECT_EQ(expected[i + 1], it->num_blocks()) << "line: " << line;
+    ++it;
+  }
+}
+
+#define EXPECT_RANGE_EQ(ranges, var)                            \
+  do {                                                          \
+    ExpectRangeEq(ranges, var, arraysize(var), __LINE__);       \
+  } while (0)
+
+void ExpectRangesOverlapOrTouch(uint64_t a_start, uint64_t a_num,
+                                uint64_t b_start, uint64_t b_num) {
+  EXPECT_TRUE(ExtentRanges::ExtentsOverlapOrTouch(ExtentForRange(a_start,
+                                                                 a_num),
+                                                  ExtentForRange(b_start,
+                                                                 b_num)));
+  EXPECT_TRUE(ExtentRanges::ExtentsOverlapOrTouch(ExtentForRange(b_start,
+                                                                 b_num),
+                                                  ExtentForRange(a_start,
+                                                                 a_num)));
+}
+
+void ExpectFalseRangesOverlapOrTouch(uint64_t a_start, uint64_t a_num,
+                                     uint64_t b_start, uint64_t b_num) {
+  EXPECT_FALSE(ExtentRanges::ExtentsOverlapOrTouch(ExtentForRange(a_start,
+                                                                  a_num),
+                                                   ExtentForRange(b_start,
+                                                                  b_num)));
+  EXPECT_FALSE(ExtentRanges::ExtentsOverlapOrTouch(ExtentForRange(b_start,
+                                                                  b_num),
+                                                   ExtentForRange(a_start,
+                                                                  a_num)));
+  EXPECT_FALSE(ExtentRanges::ExtentsOverlap(ExtentForRange(a_start,
+                                                           a_num),
+                                            ExtentForRange(b_start,
+                                                           b_num)));
+  EXPECT_FALSE(ExtentRanges::ExtentsOverlap(ExtentForRange(b_start,
+                                                           b_num),
+                                            ExtentForRange(a_start,
+                                                           a_num)));
+}
+
+void ExpectRangesOverlap(uint64_t a_start, uint64_t a_num,
+                         uint64_t b_start, uint64_t b_num) {
+  EXPECT_TRUE(ExtentRanges::ExtentsOverlap(ExtentForRange(a_start,
+                                                          a_num),
+                                           ExtentForRange(b_start,
+                                                          b_num)));
+  EXPECT_TRUE(ExtentRanges::ExtentsOverlap(ExtentForRange(b_start,
+                                                          b_num),
+                                           ExtentForRange(a_start,
+                                                          a_num)));
+  EXPECT_TRUE(ExtentRanges::ExtentsOverlapOrTouch(ExtentForRange(a_start,
+                                                                 a_num),
+                                                  ExtentForRange(b_start,
+                                                                 b_num)));
+  EXPECT_TRUE(ExtentRanges::ExtentsOverlapOrTouch(ExtentForRange(b_start,
+                                                                 b_num),
+                                                  ExtentForRange(a_start,
+                                                                 a_num)));
+}
+
+void ExpectFalseRangesOverlap(uint64_t a_start, uint64_t a_num,
+                              uint64_t b_start, uint64_t b_num) {
+  EXPECT_FALSE(ExtentRanges::ExtentsOverlap(ExtentForRange(a_start,
+                                                           a_num),
+                                            ExtentForRange(b_start,
+                                                           b_num)));
+  EXPECT_FALSE(ExtentRanges::ExtentsOverlap(ExtentForRange(b_start,
+                                                           b_num),
+                                            ExtentForRange(a_start,
+                                                           a_num)));
+}
+
+}  // namespace
+
+TEST(ExtentRangesTest, ExtentsOverlapTest) {
+  ExpectRangesOverlapOrTouch(10, 20, 30, 10);
+  ExpectRangesOverlap(10, 20, 25, 10);
+  ExpectFalseRangesOverlapOrTouch(10, 20, 35, 10);
+  ExpectFalseRangesOverlap(10, 20, 30, 10);
+  ExpectRangesOverlap(12, 4, 12, 3);
+
+  ExpectRangesOverlapOrTouch(kSparseHole, 2, kSparseHole, 3);
+  ExpectRangesOverlap(kSparseHole, 2, kSparseHole, 3);
+  ExpectFalseRangesOverlapOrTouch(kSparseHole, 2, 10, 3);
+  ExpectFalseRangesOverlapOrTouch(10, 2, kSparseHole, 3);
+  ExpectFalseRangesOverlap(kSparseHole, 2, 10, 3);
+  ExpectFalseRangesOverlap(10, 2, kSparseHole, 3);
+}
+
+TEST(ExtentRangesTest, SimpleTest) {
+  ExtentRanges ranges;
+  {
+    static const uint64_t expected[] = {};
+    // Can't use arraysize() on 0-length arrays:
+    ExpectRangeEq(ranges, expected, 0, __LINE__);
+  }
+  ranges.SubtractBlock(2);
+  {
+    static const uint64_t expected[] = {};
+    // Can't use arraysize() on 0-length arrays:
+    ExpectRangeEq(ranges, expected, 0, __LINE__);
+  }
+
+  ranges.AddBlock(0);
+  ranges.Dump();
+  ranges.AddBlock(1);
+  ranges.AddBlock(3);
+
+  {
+    static const uint64_t expected[] = {0, 2, 3, 1};
+    EXPECT_RANGE_EQ(ranges, expected);
+  }
+  ranges.AddBlock(2);
+  {
+    static const uint64_t expected[] = {0, 4};
+    EXPECT_RANGE_EQ(ranges, expected);
+    ranges.AddBlock(kSparseHole);
+    EXPECT_RANGE_EQ(ranges, expected);
+    ranges.SubtractBlock(kSparseHole);
+    EXPECT_RANGE_EQ(ranges, expected);
+  }
+  ranges.SubtractBlock(2);
+  {
+    static const uint64_t expected[] = {0, 2, 3, 1};
+    EXPECT_RANGE_EQ(ranges, expected);
+  }
+
+  for (uint64_t i = 100; i < 1000; i += 100) {
+    ranges.AddExtent(ExtentForRange(i, 50));
+  }
+  {
+    static const uint64_t expected[] = {
+      0, 2, 3, 1, 100, 50, 200, 50, 300, 50, 400, 50,
+      500, 50, 600, 50, 700, 50, 800, 50, 900, 50
+    };
+    EXPECT_RANGE_EQ(ranges, expected);
+  }
+
+  ranges.SubtractExtent(ExtentForRange(210, 410 - 210));
+  {
+    static const uint64_t expected[] = {
+      0, 2, 3, 1, 100, 50, 200, 10, 410, 40, 500, 50,
+      600, 50, 700, 50, 800, 50, 900, 50
+    };
+    EXPECT_RANGE_EQ(ranges, expected);
+  }
+  ranges.AddExtent(ExtentForRange(100000, 0));
+  {
+    static const uint64_t expected[] = {
+      0, 2, 3, 1, 100, 50, 200, 10, 410, 40, 500, 50,
+      600, 50, 700, 50, 800, 50, 900, 50
+    };
+    EXPECT_RANGE_EQ(ranges, expected);
+  }
+  ranges.SubtractExtent(ExtentForRange(3, 0));
+  {
+    static const uint64_t expected[] = {
+      0, 2, 3, 1, 100, 50, 200, 10, 410, 40, 500, 50,
+      600, 50, 700, 50, 800, 50, 900, 50
+    };
+    EXPECT_RANGE_EQ(ranges, expected);
+  }
+}
+
+TEST(ExtentRangesTest, MultipleRanges) {
+  ExtentRanges ranges_a, ranges_b;
+  ranges_a.AddBlock(0);
+  ranges_b.AddBlock(4);
+  ranges_b.AddBlock(3);
+  {
+    uint64_t expected[] = {3, 2};
+    EXPECT_RANGE_EQ(ranges_b, expected);
+  }
+  ranges_a.AddRanges(ranges_b);
+  {
+    uint64_t expected[] = {0, 1, 3, 2};
+    EXPECT_RANGE_EQ(ranges_a, expected);
+  }
+  ranges_a.SubtractRanges(ranges_b);
+  {
+    uint64_t expected[] = {0, 1};
+    EXPECT_RANGE_EQ(ranges_a, expected);
+  }
+  {
+    uint64_t expected[] = {3, 2};
+    EXPECT_RANGE_EQ(ranges_b, expected);
+  }
+}
+
+TEST(ExtentRangesTest, GetExtentsForBlockCountTest) {
+  ExtentRanges ranges;
+  ranges.AddExtents(vector<Extent>(1, ExtentForRange(10, 30)));
+  {
+    vector<Extent> zero_extents = ranges.GetExtentsForBlockCount(0);
+    EXPECT_TRUE(zero_extents.empty());
+  }
+  ::google::protobuf::RepeatedPtrField<Extent> rep_field;
+  *rep_field.Add() = ExtentForRange(30, 40);
+  ranges.AddRepeatedExtents(rep_field);
+  ranges.SubtractExtents(vector<Extent>(1, ExtentForRange(20, 10)));
+  *rep_field.Mutable(0) = ExtentForRange(50, 10);
+  ranges.SubtractRepeatedExtents(rep_field);
+  EXPECT_EQ(40, ranges.blocks());
+
+  for (int i = 0; i < 2; i++) {
+    vector<Extent> expected(2);
+    expected[0] = ExtentForRange(10, 10);
+    expected[1] = ExtentForRange(30, i == 0 ? 10 : 20);
+    vector<Extent> actual =
+        ranges.GetExtentsForBlockCount(10 + expected[1].num_blocks());
+    EXPECT_EQ(expected.size(), actual.size());
+    for (vector<Extent>::size_type j = 0, e = expected.size(); j != e; ++j) {
+      EXPECT_EQ(expected[j].start_block(), actual[j].start_block())
+          << "j = " << j;
+      EXPECT_EQ(expected[j].num_blocks(), actual[j].num_blocks())
+          << "j = " << j;
+    }
+  }
+}
+
+TEST(ExtentRangesTest, ContainsBlockTest) {
+  ExtentRanges ranges;
+  EXPECT_FALSE(ranges.ContainsBlock(123));
+
+  ranges.AddExtent(ExtentForRange(10, 10));
+  ranges.AddExtent(ExtentForRange(100, 1));
+
+  EXPECT_FALSE(ranges.ContainsBlock(9));
+  EXPECT_TRUE(ranges.ContainsBlock(10));
+  EXPECT_TRUE(ranges.ContainsBlock(15));
+  EXPECT_TRUE(ranges.ContainsBlock(19));
+  EXPECT_FALSE(ranges.ContainsBlock(20));
+
+  // Test for an extent with just the block we are requesting.
+  EXPECT_FALSE(ranges.ContainsBlock(99));
+  EXPECT_TRUE(ranges.ContainsBlock(100));
+  EXPECT_FALSE(ranges.ContainsBlock(101));
+}
+
+TEST(ExtentRangesTest, FilterExtentRangesEmptyRanges) {
+  ExtentRanges ranges;
+  EXPECT_EQ(vector<Extent>(),
+            FilterExtentRanges(vector<Extent>(), ranges));
+  EXPECT_EQ(
+      vector<Extent>{ ExtentForRange(50, 10) },
+      FilterExtentRanges(vector<Extent>{ ExtentForRange(50, 10) }, ranges));
+  // Check that the empty Extents are ignored.
+  EXPECT_EQ(
+      (vector<Extent>{ ExtentForRange(10, 10), ExtentForRange(20, 10) }),
+      FilterExtentRanges(vector<Extent>{
+           ExtentForRange(10, 10),
+           ExtentForRange(3, 0),
+           ExtentForRange(20, 10) }, ranges));
+}
+
+TEST(ExtentRangesTest, FilterExtentRangesMultipleRanges) {
+  // Two overlaping extents, with three ranges to remove.
+  vector<Extent> extents {
+      ExtentForRange(10, 100),
+      ExtentForRange(30, 100) };
+  ExtentRanges ranges;
+  // This overlaps the beginning of the second extent.
+  ranges.AddExtent(ExtentForRange(28, 3));
+  ranges.AddExtent(ExtentForRange(50, 10));
+  ranges.AddExtent(ExtentForRange(70, 10));
+  // This overlaps the end of the second extent.
+  ranges.AddExtent(ExtentForRange(108, 6));
+  EXPECT_EQ(
+      (vector<Extent>{
+           // For the first extent:
+           ExtentForRange(10, 18),
+           ExtentForRange(31, 19),
+           ExtentForRange(60, 10),
+           ExtentForRange(80, 28),
+           // For the second extent:
+           ExtentForRange(31, 19),
+           ExtentForRange(60, 10),
+           ExtentForRange(80, 28),
+           ExtentForRange(114, 16)}),
+      FilterExtentRanges(extents, ranges));
+}
+
+TEST(ExtentRangesTest, FilterExtentRangesOvelapping) {
+  ExtentRanges ranges;
+  ranges.AddExtent(ExtentForRange(10, 3));
+  ranges.AddExtent(ExtentForRange(20, 5));
+  // Requested extent overlaps with one of the ranges.
+  EXPECT_EQ(vector<Extent>(),
+            FilterExtentRanges(vector<Extent>{
+                                   ExtentForRange(10, 1),
+                                   ExtentForRange(22, 1) },
+                               ranges));
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/extent_utils.cc b/payload_generator/extent_utils.cc
new file mode 100644
index 0000000..1093445
--- /dev/null
+++ b/payload_generator/extent_utils.cc
@@ -0,0 +1,154 @@
+//
+// 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/extent_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_ranges.h"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+void AppendBlockToExtents(vector<Extent>* extents, uint64_t block) {
+  // First try to extend the last extent in |extents|, if any.
+  if (!extents->empty()) {
+    Extent& extent = extents->back();
+    uint64_t next_block = extent.start_block() == kSparseHole ?
+        kSparseHole : extent.start_block() + extent.num_blocks();
+    if (next_block == block) {
+      extent.set_num_blocks(extent.num_blocks() + 1);
+      return;
+    }
+  }
+  // If unable to extend the last extent, append a new single-block extent.
+  Extent new_extent;
+  new_extent.set_start_block(block);
+  new_extent.set_num_blocks(1);
+  extents->push_back(new_extent);
+}
+
+Extent GetElement(const vector<Extent>& collection, size_t index) {
+  return collection[index];
+}
+
+Extent GetElement(
+    const google::protobuf::RepeatedPtrField<Extent>& collection,
+    size_t index) {
+  return collection.Get(index);
+}
+
+void ExtendExtents(
+    google::protobuf::RepeatedPtrField<Extent>* extents,
+    const google::protobuf::RepeatedPtrField<Extent>& extents_to_add) {
+  vector<Extent> extents_vector;
+  vector<Extent> extents_to_add_vector;
+  ExtentsToVector(*extents, &extents_vector);
+  ExtentsToVector(extents_to_add, &extents_to_add_vector);
+  extents_vector.insert(extents_vector.end(),
+                        extents_to_add_vector.begin(),
+                        extents_to_add_vector.end());
+  NormalizeExtents(&extents_vector);
+  extents->Clear();
+  StoreExtents(extents_vector, extents);
+}
+
+// Stores all Extents in 'extents' into 'out'.
+void StoreExtents(const vector<Extent>& extents,
+                  google::protobuf::RepeatedPtrField<Extent>* out) {
+  for (const Extent& extent : extents) {
+    Extent* new_extent = out->Add();
+    *new_extent = extent;
+  }
+}
+
+// Stores all extents in |extents| into |out_vector|.
+void ExtentsToVector(const google::protobuf::RepeatedPtrField<Extent>& extents,
+                     vector<Extent>* out_vector) {
+  out_vector->clear();
+  for (int i = 0; i < extents.size(); i++) {
+    out_vector->push_back(extents.Get(i));
+  }
+}
+
+void NormalizeExtents(vector<Extent>* extents) {
+  vector<Extent> new_extents;
+  for (const Extent& curr_ext : *extents) {
+    if (new_extents.empty()) {
+      new_extents.push_back(curr_ext);
+      continue;
+    }
+    Extent& last_ext = new_extents.back();
+    if (last_ext.start_block() + last_ext.num_blocks() ==
+        curr_ext.start_block()) {
+      // If the extents are touching, we want to combine them.
+      last_ext.set_num_blocks(last_ext.num_blocks() + curr_ext.num_blocks());
+    } else {
+      // Otherwise just include the extent as is.
+      new_extents.push_back(curr_ext);
+    }
+  }
+  *extents = new_extents;
+}
+
+vector<Extent> ExtentsSublist(const vector<Extent>& extents,
+                              uint64_t block_offset, uint64_t block_count) {
+  vector<Extent> result;
+  uint64_t scanned_blocks = 0;
+  if (block_count == 0)
+    return result;
+  uint64_t end_block_offset = block_offset + block_count;
+  for (const Extent& extent : extents) {
+    // The loop invariant is that if |extents| has enough blocks, there's
+    // still some extent to add to |result|. This implies that at the beginning
+    // of the loop scanned_blocks < block_offset + block_count.
+    if (scanned_blocks + extent.num_blocks() > block_offset) {
+      // This case implies that |extent| has some overlapping with the requested
+      // subsequence.
+      uint64_t new_start = extent.start_block();
+      uint64_t new_num_blocks = extent.num_blocks();
+      if (scanned_blocks + new_num_blocks > end_block_offset) {
+        // Cut the end part of the extent.
+        new_num_blocks = end_block_offset - scanned_blocks;
+      }
+      if (block_offset > scanned_blocks) {
+        // Cut the begin part of the extent.
+        new_num_blocks -= block_offset - scanned_blocks;
+        new_start += block_offset - scanned_blocks;
+      }
+      result.push_back(ExtentForRange(new_start, new_num_blocks));
+    }
+    scanned_blocks += extent.num_blocks();
+    if (scanned_blocks >= end_block_offset)
+      break;
+  }
+  return result;
+}
+
+bool operator==(const Extent& a, const Extent& b) {
+  return a.start_block() == b.start_block() && a.num_blocks() == b.num_blocks();
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/extent_utils.h b/payload_generator/extent_utils.h
new file mode 100644
index 0000000..91729d5
--- /dev/null
+++ b/payload_generator/extent_utils.h
@@ -0,0 +1,101 @@
+//
+// 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_EXTENT_UTILS_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_EXTENT_UTILS_H_
+
+#include <vector>
+
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/update_metadata.pb.h"
+
+// Utility functions for manipulating Extents and lists of blocks.
+
+namespace chromeos_update_engine {
+
+// |block| must either be the next block in the last extent or a block
+// in the next extent. This function will not handle inserting block
+// into an arbitrary place in the extents.
+void AppendBlockToExtents(std::vector<Extent>* extents, uint64_t block);
+
+// Get/SetElement are intentionally overloaded so that templated functions
+// can accept either type of collection of Extents.
+Extent GetElement(const std::vector<Extent>& collection, size_t index);
+Extent GetElement(
+    const google::protobuf::RepeatedPtrField<Extent>& collection,
+    size_t index);
+
+// Return the total number of blocks in a collection (vector or
+// RepeatedPtrField) of Extents.
+template<typename T>
+uint64_t BlocksInExtents(const T& collection) {
+  uint64_t ret = 0;
+  for (size_t i = 0; i < static_cast<size_t>(collection.size()); ++i) {
+    ret += GetElement(collection, i).num_blocks();
+  }
+  return ret;
+}
+
+// Takes a collection (vector or RepeatedPtrField) of Extent and
+// returns a vector of the blocks referenced, in order.
+template<typename T>
+std::vector<uint64_t> ExpandExtents(const T& extents) {
+  std::vector<uint64_t> ret;
+  for (size_t i = 0, e = static_cast<size_t>(extents.size()); i != e; ++i) {
+    const Extent extent = GetElement(extents, i);
+    if (extent.start_block() == kSparseHole) {
+      ret.resize(ret.size() + extent.num_blocks(), kSparseHole);
+    } else {
+      for (uint64_t block = extent.start_block();
+           block < (extent.start_block() + extent.num_blocks()); block++) {
+        ret.push_back(block);
+      }
+    }
+  }
+  return ret;
+}
+
+// Stores all Extents in 'extents' into 'out'.
+void StoreExtents(const std::vector<Extent>& extents,
+                  google::protobuf::RepeatedPtrField<Extent>* out);
+
+// Stores all extents in |extents| into |out_vector|.
+void ExtentsToVector(const google::protobuf::RepeatedPtrField<Extent>& extents,
+                     std::vector<Extent>* out_vector);
+
+// Takes a pointer to extents |extents| and extents |extents_to_add|, and
+// merges them by adding |extents_to_add| to |extents| and normalizing.
+void ExtendExtents(
+  google::protobuf::RepeatedPtrField<Extent>* extents,
+  const google::protobuf::RepeatedPtrField<Extent>& extents_to_add);
+
+// Takes a vector of extents and normalizes those extents. Expects the extents
+// to be sorted by start block. E.g. if |extents| is [(1, 2), (3, 5), (10, 2)]
+// then |extents| will be changed to [(1, 7), (10, 2)].
+void NormalizeExtents(std::vector<Extent>* extents);
+
+// Return a subsequence of the list of blocks passed. Both the passed list of
+// blocks |extents| and the return value are expressed as a list of Extent, not
+// blocks. The returned list skips the first |block_offset| blocks from the
+// |extents| and cotains |block_count| blocks (or less if |extents| is shorter).
+std::vector<Extent> ExtentsSublist(const std::vector<Extent>& extents,
+                                   uint64_t block_offset, uint64_t block_count);
+
+bool operator==(const Extent& a, const Extent& b);
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_EXTENT_UTILS_H_
diff --git a/payload_generator/extent_utils_unittest.cc b/payload_generator/extent_utils_unittest.cc
new file mode 100644
index 0000000..62c8ff1
--- /dev/null
+++ b/payload_generator/extent_utils_unittest.cc
@@ -0,0 +1,164 @@
+//
+// 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/extent_utils.h"
+
+#include <utility>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "update_engine/common/test_utils.h"
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_generator/extent_ranges.h"
+
+using std::vector;
+
+namespace chromeos_update_engine {
+
+class ExtentUtilsTest : public ::testing::Test {};
+
+TEST(ExtentUtilsTest, AppendSparseToExtentsTest) {
+  vector<Extent> extents;
+
+  EXPECT_EQ(0, extents.size());
+  AppendBlockToExtents(&extents, kSparseHole);
+  EXPECT_EQ(1, extents.size());
+  AppendBlockToExtents(&extents, 0);
+  EXPECT_EQ(2, extents.size());
+  AppendBlockToExtents(&extents, kSparseHole);
+  AppendBlockToExtents(&extents, kSparseHole);
+
+  ASSERT_EQ(3, extents.size());
+  EXPECT_EQ(kSparseHole, extents[0].start_block());
+  EXPECT_EQ(1, extents[0].num_blocks());
+  EXPECT_EQ(0, extents[1].start_block());
+  EXPECT_EQ(1, extents[1].num_blocks());
+  EXPECT_EQ(kSparseHole, extents[2].start_block());
+  EXPECT_EQ(2, extents[2].num_blocks());
+}
+
+TEST(ExtentUtilsTest, BlocksInExtentsTest) {
+  {
+    vector<Extent> extents;
+    EXPECT_EQ(0, BlocksInExtents(extents));
+    extents.push_back(ExtentForRange(0, 1));
+    EXPECT_EQ(1, BlocksInExtents(extents));
+    extents.push_back(ExtentForRange(23, 55));
+    EXPECT_EQ(56, BlocksInExtents(extents));
+    extents.push_back(ExtentForRange(1, 2));
+    EXPECT_EQ(58, BlocksInExtents(extents));
+  }
+  {
+    google::protobuf::RepeatedPtrField<Extent> extents;
+    EXPECT_EQ(0, BlocksInExtents(extents));
+    *extents.Add() = ExtentForRange(0, 1);
+    EXPECT_EQ(1, BlocksInExtents(extents));
+    *extents.Add() = ExtentForRange(23, 55);
+    EXPECT_EQ(56, BlocksInExtents(extents));
+    *extents.Add() = ExtentForRange(1, 2);
+    EXPECT_EQ(58, BlocksInExtents(extents));
+  }
+}
+
+TEST(ExtentUtilsTest, ExtendExtentsTest) {
+  InstallOperation first_op;
+  *(first_op.add_src_extents()) = ExtentForRange(1, 1);
+  *(first_op.add_src_extents()) = ExtentForRange(3, 1);
+
+  InstallOperation second_op;
+  *(second_op.add_src_extents()) = ExtentForRange(4, 2);
+  *(second_op.add_src_extents()) = ExtentForRange(8, 2);
+
+  ExtendExtents(first_op.mutable_src_extents(), second_op.src_extents());
+  vector<Extent> first_op_vec;
+  ExtentsToVector(first_op.src_extents(), &first_op_vec);
+  EXPECT_EQ((vector<Extent>{
+      ExtentForRange(1, 1),
+      ExtentForRange(3, 3),
+      ExtentForRange(8, 2)}), first_op_vec);
+}
+
+TEST(ExtentUtilsTest, NormalizeExtentsSimpleList) {
+  // Make sure it works when there's just one extent.
+  vector<Extent> extents;
+  NormalizeExtents(&extents);
+  EXPECT_EQ(0, extents.size());
+
+  extents = { ExtentForRange(0, 3) };
+  NormalizeExtents(&extents);
+  EXPECT_EQ(1, extents.size());
+  EXPECT_EQ(ExtentForRange(0, 3), extents[0]);
+}
+
+TEST(ExtentUtilsTest, NormalizeExtentsTest) {
+  vector<Extent> extents = {
+      ExtentForRange(0, 3),
+      ExtentForRange(3, 2),
+      ExtentForRange(5, 1),
+      ExtentForRange(8, 4),
+      ExtentForRange(13, 1),
+      ExtentForRange(14, 2)
+  };
+  NormalizeExtents(&extents);
+  EXPECT_EQ(3, extents.size());
+  EXPECT_EQ(ExtentForRange(0, 6), extents[0]);
+  EXPECT_EQ(ExtentForRange(8, 4), extents[1]);
+  EXPECT_EQ(ExtentForRange(13, 3), extents[2]);
+}
+
+TEST(ExtentUtilsTest, ExtentsSublistTest) {
+  vector<Extent> extents = {
+      ExtentForRange(10, 10),
+      ExtentForRange(30, 10),
+      ExtentForRange(50, 10)
+  };
+
+  // Simple empty result cases.
+  EXPECT_EQ(vector<Extent>(),
+            ExtentsSublist(extents, 1000, 20));
+  EXPECT_EQ(vector<Extent>(),
+            ExtentsSublist(extents, 5, 0));
+  EXPECT_EQ(vector<Extent>(),
+            ExtentsSublist(extents, 30, 1));
+
+  // Normal test cases.
+  EXPECT_EQ(vector<Extent>{ ExtentForRange(13, 2) },
+            ExtentsSublist(extents, 3, 2));
+  EXPECT_EQ(vector<Extent>{ ExtentForRange(15, 5) },
+            ExtentsSublist(extents, 5, 5));
+  EXPECT_EQ((vector<Extent>{ ExtentForRange(15, 5), ExtentForRange(30, 5) }),
+            ExtentsSublist(extents, 5, 10));
+  EXPECT_EQ((vector<Extent>{
+                 ExtentForRange(13, 7),
+                 ExtentForRange(30, 10),
+                 ExtentForRange(50, 3), }),
+            ExtentsSublist(extents, 3, 20));
+
+  // Extact match case.
+  EXPECT_EQ(vector<Extent>{ ExtentForRange(30, 10) },
+            ExtentsSublist(extents, 10, 10));
+  EXPECT_EQ(vector<Extent>{ ExtentForRange(50, 10) },
+            ExtentsSublist(extents, 20, 10));
+
+  // Cases where the requested num_blocks is too big.
+  EXPECT_EQ(vector<Extent>{ ExtentForRange(53, 7) },
+            ExtentsSublist(extents, 23, 100));
+  EXPECT_EQ((vector<Extent>{ ExtentForRange(34, 6), ExtentForRange(50, 10) }),
+            ExtentsSublist(extents, 14, 100));
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/fake_filesystem.cc b/payload_generator/fake_filesystem.cc
new file mode 100644
index 0000000..3a6458d
--- /dev/null
+++ b/payload_generator/fake_filesystem.cc
@@ -0,0 +1,60 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/payload_generator/fake_filesystem.h"
+
+#include <gtest/gtest.h>
+
+namespace chromeos_update_engine {
+
+FakeFilesystem::FakeFilesystem(uint64_t block_size, uint64_t block_count) :
+    block_size_(block_size),
+    block_count_(block_count) {
+}
+
+size_t FakeFilesystem::GetBlockSize() const {
+  return block_size_;
+}
+
+size_t FakeFilesystem::GetBlockCount() const {
+  return block_count_;
+}
+
+bool FakeFilesystem::GetFiles(std::vector<File>* files) const {
+  *files = files_;
+  return true;
+}
+
+void FakeFilesystem::AddFile(const std::string& filename,
+                             const std::vector<Extent> extents) {
+  File file;
+  file.name = filename;
+  file.extents = extents;
+  for (const Extent& extent : extents) {
+    EXPECT_LE(0, extent.start_block());
+    EXPECT_LE(extent.start_block() + extent.num_blocks(), block_count_);
+  }
+  files_.push_back(file);
+}
+
+bool FakeFilesystem::LoadSettings(brillo::KeyValueStore* store) const {
+  if (minor_version_ < 0)
+    return false;
+  store->SetString("PAYLOAD_MINOR_VERSION", std::to_string(minor_version_));
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/fake_filesystem.h b/payload_generator/fake_filesystem.h
new file mode 100644
index 0000000..a14b8d3
--- /dev/null
+++ b/payload_generator/fake_filesystem.h
@@ -0,0 +1,68 @@
+//
+// 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_FAKE_FILESYSTEM_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_FAKE_FILESYSTEM_H_
+
+// A fake filesystem interface implementation allowing the user to add arbitrary
+// files/metadata.
+
+#include "update_engine/payload_generator/filesystem_interface.h"
+
+#include <string>
+#include <vector>
+
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+class FakeFilesystem : public FilesystemInterface {
+ public:
+  FakeFilesystem(uint64_t block_size, uint64_t block_count);
+  virtual ~FakeFilesystem() = default;
+
+  // FilesystemInterface overrides.
+  size_t GetBlockSize() const override;
+  size_t GetBlockCount() const override;
+  bool GetFiles(std::vector<File>* files) const override;
+  bool LoadSettings(brillo::KeyValueStore* store) const override;
+
+  // Fake methods.
+
+  // Add a file to the list of fake files.
+  void AddFile(const std::string& filename, const std::vector<Extent> extents);
+
+  // Sets the PAYLOAD_MINOR_VERSION key stored by LoadSettings(). Use a negative
+  // value to produce an error in LoadSettings().
+  void SetMinorVersion(int minor_version) {
+    minor_version_ = minor_version;
+  }
+
+ private:
+  FakeFilesystem() = default;
+
+  uint64_t block_size_;
+  uint64_t block_count_;
+  int minor_version_{-1};
+
+  std::vector<File> files_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeFilesystem);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_FAKE_FILESYSTEM_H_
diff --git a/payload_generator/filesystem_interface.h b/payload_generator/filesystem_interface.h
new file mode 100644
index 0000000..866c46b
--- /dev/null
+++ b/payload_generator/filesystem_interface.h
@@ -0,0 +1,95 @@
+//
+// 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_FILESYSTEM_INTERFACE_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_FILESYSTEM_INTERFACE_H_
+
+// This class is used to abstract a filesystem and iterate the blocks
+// associated with the files and filesystem structures.
+// For the purposes of the update payload generation, a filesystem is a formated
+// partition composed by fixed-size blocks, since that's the interface used in
+// the update payload.
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <brillo/key_value_store.h>
+
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+class FilesystemInterface {
+ public:
+  // This represents a file or pseudo-file in the filesystem. It can include
+  // all sort of files, like symlinks, hardlinks, directories and even a file
+  // entry representing the metadata, free space, journaling data, etc.
+  struct File {
+    File() {
+      memset(&file_stat, 0, sizeof(file_stat));
+    }
+
+    // The stat struct for the file. This is invalid (inode 0) for some
+    // pseudo-files.
+    struct stat file_stat;
+
+    // The absolute path to the file inside the filesystem, for example,
+    // "/usr/bin/bash". For pseudo-files, like blocks associated to internal
+    // filesystem tables or free space, the path doesn't start with a /.
+    std::string name;
+
+    // The list of all physical blocks holding the data of this file in
+    // the same order as the logical data. All the block numbers shall be
+    // between 0 and GetBlockCount() - 1. The blocks are encoded in extents,
+    // indicating the starting block, and the number of consecutive blocks.
+    std::vector<Extent> extents;
+  };
+
+  virtual ~FilesystemInterface() = default;
+
+  // Returns the size of a block in the filesystem.
+  virtual size_t GetBlockSize() const = 0;
+
+  // Returns the number of blocks in the filesystem.
+  virtual size_t GetBlockCount() const = 0;
+
+  // Stores in |files| the list of files and pseudo-files in the filesystem. See
+  // FileInterface for details. The paths returned by this method shall not
+  // be repeated; but the same block could be present in more than one file as
+  // happens for example with hard-linked files, but not limited to those cases.
+  // Returns whether the function succeeded.
+  virtual bool GetFiles(std::vector<File>* files) const = 0;
+
+  // Load the image settings stored in the filesystem in the
+  // /etc/update_engine.conf file. Returns whether the settings were found.
+  virtual bool LoadSettings(brillo::KeyValueStore* store) const = 0;
+
+ protected:
+  FilesystemInterface() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FilesystemInterface);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_FILESYSTEM_INTERFACE_H_
diff --git a/payload_generator/full_update_generator.cc b/payload_generator/full_update_generator.cc
new file mode 100644
index 0000000..56a3ca8
--- /dev/null
+++ b/payload_generator/full_update_generator.cc
@@ -0,0 +1,203 @@
+//
+// 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/full_update_generator.h"
+
+#include <fcntl.h>
+#include <inttypes.h>
+
+#include <algorithm>
+#include <deque>
+#include <memory>
+
+#include <base/format_macros.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <base/synchronization/lock.h>
+#include <base/threading/simple_thread.h>
+#include <brillo/secure_blob.h>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_generator/bzip.h"
+
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+const size_t kDefaultFullChunkSize = 1024 * 1024;  // 1 MiB
+
+// This class encapsulates a full update chunk processing thread work. The
+// processor reads a chunk of data from the input file descriptor and compresses
+// it. The processor will destroy itself when the work is done.
+class ChunkProcessor : public base::DelegateSimpleThread::Delegate {
+ public:
+  // Read a chunk of |size| bytes from |fd| starting at offset |offset|.
+  ChunkProcessor(int fd, off_t offset, size_t size,
+                 BlobFileWriter* blob_file, AnnotatedOperation* aop)
+    : fd_(fd),
+      offset_(offset),
+      size_(size),
+      blob_file_(blob_file),
+      aop_(aop) {}
+  // We use a default move constructor since all the data members are POD types.
+  ChunkProcessor(ChunkProcessor&&) = default;
+  ~ChunkProcessor() override = default;
+
+  // Overrides DelegateSimpleThread::Delegate.
+  // Run() handles the read from |fd| in a thread-safe way, and stores the
+  // new operation to generate the region starting at |offset| of size |size|
+  // in the output operation |aop|. The associated blob data is stored in
+  // |blob_fd| and |blob_file_size| is updated.
+  void Run() override;
+
+ private:
+  bool ProcessChunk();
+
+  // Work parameters.
+  int fd_;
+  off_t offset_;
+  size_t size_;
+  BlobFileWriter* blob_file_;
+  AnnotatedOperation* aop_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChunkProcessor);
+};
+
+void ChunkProcessor::Run() {
+  if (!ProcessChunk()) {
+    LOG(ERROR) << "Error processing region at " << offset_ << " of size "
+               << size_;
+  }
+}
+
+bool ChunkProcessor::ProcessChunk() {
+  brillo::Blob buffer_in_(size_);
+  brillo::Blob op_blob;
+  ssize_t bytes_read = -1;
+  TEST_AND_RETURN_FALSE(utils::PReadAll(fd_,
+                                        buffer_in_.data(),
+                                        buffer_in_.size(),
+                                        offset_,
+                                        &bytes_read));
+  TEST_AND_RETURN_FALSE(bytes_read == static_cast<ssize_t>(size_));
+  TEST_AND_RETURN_FALSE(BzipCompress(buffer_in_, &op_blob));
+
+  InstallOperation_Type op_type = InstallOperation::REPLACE_BZ;
+
+  if (op_blob.size() >= buffer_in_.size()) {
+    // A REPLACE is cheaper than a REPLACE_BZ in this case.
+    op_blob = std::move(buffer_in_);
+    op_type = InstallOperation::REPLACE;
+  }
+
+  TEST_AND_RETURN_FALSE(aop_->SetOperationBlob(&op_blob, blob_file_));
+  aop_->op.set_type(op_type);
+  return true;
+}
+
+}  // namespace
+
+bool FullUpdateGenerator::GenerateOperations(
+    const PayloadGenerationConfig& config,
+    const PartitionConfig& old_part,
+    const PartitionConfig& new_part,
+    BlobFileWriter* blob_file,
+    vector<AnnotatedOperation>* aops) {
+  TEST_AND_RETURN_FALSE(new_part.ValidateExists());
+
+  // FullUpdateGenerator requires a positive chunk_size, otherwise there will
+  // be only one operation with the whole partition which should not be allowed.
+  // For performance reasons, we force a small default hard limit of 1 MiB. This
+  // limit can be changed in the config, and we will use the smaller of the two
+  // soft/hard limits.
+  size_t full_chunk_size;
+  if (config.hard_chunk_size >= 0) {
+    full_chunk_size = std::min(static_cast<size_t>(config.hard_chunk_size),
+                               config.soft_chunk_size);
+  } else {
+    full_chunk_size = std::min(kDefaultFullChunkSize, config.soft_chunk_size);
+    LOG(INFO) << "No chunk_size provided, using the default chunk_size for the "
+              << "full operations: " << full_chunk_size << " bytes.";
+  }
+  TEST_AND_RETURN_FALSE(full_chunk_size > 0);
+  TEST_AND_RETURN_FALSE(full_chunk_size % config.block_size == 0);
+
+  size_t chunk_blocks = full_chunk_size / config.block_size;
+  size_t max_threads = std::max(sysconf(_SC_NPROCESSORS_ONLN), 4L);
+  LOG(INFO) << "Compressing partition " << new_part.name
+            << " from " << new_part.path << " splitting in chunks of "
+            << chunk_blocks << " blocks (" << config.block_size
+            << " bytes each) using " << max_threads << " threads";
+
+  int in_fd = open(new_part.path.c_str(), O_RDONLY, 0);
+  TEST_AND_RETURN_FALSE(in_fd >= 0);
+  ScopedFdCloser in_fd_closer(&in_fd);
+
+  // We potentially have all the ChunkProcessors in memory but only
+  // |max_threads| will actually hold a block in memory while we process.
+  size_t partition_blocks = new_part.size / config.block_size;
+  size_t num_chunks = (partition_blocks + chunk_blocks - 1) / chunk_blocks;
+  aops->resize(num_chunks);
+  vector<ChunkProcessor> chunk_processors;
+  chunk_processors.reserve(num_chunks);
+  blob_file->SetTotalBlobs(num_chunks);
+
+  for (size_t i = 0; i < num_chunks; ++i) {
+    size_t start_block = i * chunk_blocks;
+    // The last chunk could be smaller.
+    size_t num_blocks = std::min(chunk_blocks,
+                                 partition_blocks - i * chunk_blocks);
+
+    // Preset all the static information about the operations. The
+    // ChunkProcessor will set the rest.
+    AnnotatedOperation* aop = aops->data() + i;
+    aop->name = base::StringPrintf("<%s-operation-%" PRIuS ">",
+                                   new_part.name.c_str(), i);
+    Extent* dst_extent = aop->op.add_dst_extents();
+    dst_extent->set_start_block(start_block);
+    dst_extent->set_num_blocks(num_blocks);
+
+    chunk_processors.emplace_back(
+        in_fd,
+        static_cast<off_t>(start_block) * config.block_size,
+        num_blocks * config.block_size,
+        blob_file,
+        aop);
+  }
+
+  // Thread pool used for worker threads.
+  base::DelegateSimpleThreadPool thread_pool("full-update-generator",
+                                             max_threads);
+  thread_pool.Start();
+  for (ChunkProcessor& processor : chunk_processors)
+    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) {
+    if (!aop.op.has_type())
+      return false;
+  }
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/full_update_generator.h b/payload_generator/full_update_generator.h
new file mode 100644
index 0000000..d722028
--- /dev/null
+++ b/payload_generator/full_update_generator.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_FULL_UPDATE_GENERATOR_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_FULL_UPDATE_GENERATOR_H_
+
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+
+#include "update_engine/payload_generator/blob_file_writer.h"
+#include "update_engine/payload_generator/operations_generator.h"
+#include "update_engine/payload_generator/payload_generation_config.h"
+
+namespace chromeos_update_engine {
+
+class FullUpdateGenerator : public OperationsGenerator {
+ public:
+  FullUpdateGenerator() = default;
+
+  // OperationsGenerator override.
+  // Creates a full update for the target image defined in |config|. |config|
+  // must be a valid payload generation configuration for a full payload.
+  // Populates |aops|, with data about the update operations, and writes
+  // relevant data 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(FullUpdateGenerator);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_FULL_UPDATE_GENERATOR_H_
diff --git a/payload_generator/full_update_generator_unittest.cc b/payload_generator/full_update_generator_unittest.cc
new file mode 100644
index 0000000..d46346d
--- /dev/null
+++ b/payload_generator/full_update_generator_unittest.cc
@@ -0,0 +1,142 @@
+//
+// 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/full_update_generator.h"
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "update_engine/common/test_utils.h"
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_generator/extent_utils.h"
+
+using chromeos_update_engine::test_utils::FillWithData;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+class FullUpdateGeneratorTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    config_.is_delta = false;
+    config_.minor_version = kFullPayloadMinorVersion;
+    config_.hard_chunk_size = 128 * 1024;
+    config_.block_size = 4096;
+
+    EXPECT_TRUE(utils::MakeTempFile("FullUpdateTest_partition.XXXXXX",
+                                    &new_part_conf.path,
+                                    nullptr));
+    EXPECT_TRUE(utils::MakeTempFile("FullUpdateTest_blobs.XXXXXX",
+                                    &out_blobs_path_,
+                                    &out_blobs_fd_));
+
+    blob_file_.reset(new BlobFileWriter(out_blobs_fd_, &out_blobs_length_));
+    part_path_unlinker_.reset(new ScopedPathUnlinker(new_part_conf.path));
+    out_blobs_unlinker_.reset(new ScopedPathUnlinker(out_blobs_path_));
+  }
+
+  PayloadGenerationConfig config_;
+  PartitionConfig new_part_conf{"part"};
+
+  vector<AnnotatedOperation> aops;
+
+  // Output file holding the payload blobs.
+  string out_blobs_path_;
+  int out_blobs_fd_{-1};
+  off_t out_blobs_length_{0};
+  ScopedFdCloser out_blobs_fd_closer_{&out_blobs_fd_};
+
+  std::unique_ptr<BlobFileWriter> blob_file_;
+  std::unique_ptr<ScopedPathUnlinker> part_path_unlinker_;
+  std::unique_ptr<ScopedPathUnlinker> out_blobs_unlinker_;
+
+  // FullUpdateGenerator under test.
+  FullUpdateGenerator generator_;
+};
+
+TEST_F(FullUpdateGeneratorTest, RunTest) {
+  brillo::Blob new_part(9 * 1024 * 1024);
+  FillWithData(&new_part);
+  new_part_conf.size = new_part.size();
+
+  EXPECT_TRUE(test_utils::WriteFileVector(new_part_conf.path, new_part));
+
+  EXPECT_TRUE(generator_.GenerateOperations(config_,
+                                            new_part_conf,  // this is ignored
+                                            new_part_conf,
+                                            blob_file_.get(),
+                                            &aops));
+  int64_t new_part_chunks = new_part_conf.size / config_.hard_chunk_size;
+  EXPECT_EQ(new_part_chunks, aops.size());
+  for (off_t i = 0; i < new_part_chunks; ++i) {
+    EXPECT_EQ(1, aops[i].op.dst_extents_size());
+    EXPECT_EQ(i * config_.hard_chunk_size / config_.block_size,
+              aops[i].op.dst_extents(0).start_block()) << "i = " << i;
+    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_BZ, aops[i].op.type());
+    }
+  }
+}
+
+// Test that if the chunk size is not a divisor of the image size, it handles
+// correctly the last chunk of the partition.
+TEST_F(FullUpdateGeneratorTest, ChunkSizeTooBig) {
+  config_.hard_chunk_size = 1024 * 1024;
+  config_.soft_chunk_size = config_.hard_chunk_size;
+  brillo::Blob new_part(1536 * 1024);  // 1.5 MiB
+  new_part_conf.size = new_part.size();
+
+  EXPECT_TRUE(test_utils::WriteFileVector(new_part_conf.path, new_part));
+
+  EXPECT_TRUE(generator_.GenerateOperations(config_,
+                                            new_part_conf,  // this is ignored
+                                            new_part_conf,
+                                            blob_file_.get(),
+                                            &aops));
+  // new_part has one chunk and a half.
+  EXPECT_EQ(2, aops.size());
+  EXPECT_EQ(config_.hard_chunk_size / config_.block_size,
+            BlocksInExtents(aops[0].op.dst_extents()));
+  EXPECT_EQ((new_part.size() - config_.hard_chunk_size) / config_.block_size,
+            BlocksInExtents(aops[1].op.dst_extents()));
+}
+
+// Test that if the image size is much smaller than the chunk size, it handles
+// correctly the only chunk of the partition.
+TEST_F(FullUpdateGeneratorTest, ImageSizeTooSmall) {
+  brillo::Blob new_part(16 * 1024);
+  new_part_conf.size = new_part.size();
+
+  EXPECT_TRUE(test_utils::WriteFileVector(new_part_conf.path, new_part));
+
+  EXPECT_TRUE(generator_.GenerateOperations(config_,
+                                            new_part_conf,  // this is ignored
+                                            new_part_conf,
+                                            blob_file_.get(),
+                                            &aops));
+
+  // new_part has less than one chunk.
+  EXPECT_EQ(1, aops.size());
+  EXPECT_EQ(new_part.size() / config_.block_size,
+            BlocksInExtents(aops[0].op.dst_extents()));
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc
new file mode 100644
index 0000000..f462347
--- /dev/null
+++ b/payload_generator/generate_delta_main.cc
@@ -0,0 +1,546 @@
+//
+// 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 <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_split.h>
+#include <brillo/flag_helper.h>
+#include <brillo/key_value_store.h>
+
+#include "update_engine/common/prefs.h"
+#include "update_engine/common/terminator.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/delta_performer.h"
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_generator/delta_diff_generator.h"
+#include "update_engine/payload_generator/delta_diff_utils.h"
+#include "update_engine/payload_generator/payload_generation_config.h"
+#include "update_engine/payload_generator/payload_signer.h"
+#include "update_engine/update_metadata.pb.h"
+
+// This file contains a simple program that takes an old path, a new path,
+// 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::set;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+void ParseSignatureSizes(const string& signature_sizes_flag,
+                         vector<int>* signature_sizes) {
+  signature_sizes->clear();
+  vector<string> split_strings;
+
+  base::SplitString(signature_sizes_flag, ':', &split_strings);
+  for (const string& str : split_strings) {
+    int size = 0;
+    bool parsing_successful = base::StringToInt(str, &size);
+    LOG_IF(FATAL, !parsing_successful)
+        << "Invalid signature size: " << str;
+
+    LOG_IF(FATAL, size != (2048 / 8)) <<
+        "Only signature sizes of 256 bytes are supported.";
+
+    signature_sizes->push_back(size);
+  }
+}
+
+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<int> &sizes,
+                             const string& out_hash_file,
+                             const string& out_metadata_hash_file,
+                             const string& in_file) {
+  LOG(INFO) << "Calculating hash for signing.";
+  LOG_IF(FATAL, in_file.empty())
+      << "Must pass --in_file to calculate hash for signing.";
+  LOG_IF(FATAL, out_hash_file.empty())
+      << "Must pass --out_hash_file to calculate hash for signing.";
+
+  brillo::Blob payload_hash, metadata_hash;
+  CHECK(PayloadSigner::HashPayloadForSigning(in_file, sizes, &payload_hash,
+                                             &metadata_hash));
+  CHECK(utils::WriteFile(out_hash_file.c_str(), payload_hash.data(),
+                         payload_hash.size()));
+  if (!out_metadata_hash_file.empty())
+    CHECK(utils::WriteFile(out_metadata_hash_file.c_str(), metadata_hash.data(),
+                           metadata_hash.size()));
+
+  LOG(INFO) << "Done calculating hash for signing.";
+}
+
+void SignatureFileFlagToBlobs(const string& signature_file_flag,
+                              vector<brillo::Blob>* signatures) {
+  vector<string> signature_files;
+  base::SplitString(signature_file_flag, ':', &signature_files);
+  for (const string& signature_file : signature_files) {
+    brillo::Blob signature;
+    CHECK(utils::ReadFile(signature_file, &signature));
+    signatures->push_back(signature);
+  }
+}
+
+void SignPayload(const string& in_file,
+                 const string& out_file,
+                 const string& payload_signature_file,
+                 const string& metadata_signature_file,
+                 const string& out_metadata_size_file) {
+  LOG(INFO) << "Signing payload.";
+  LOG_IF(FATAL, in_file.empty())
+      << "Must pass --in_file to sign payload.";
+  LOG_IF(FATAL, out_file.empty())
+      << "Must pass --out_file to sign payload.";
+  LOG_IF(FATAL, payload_signature_file.empty())
+      << "Must pass --signature_file to sign payload.";
+  vector<brillo::Blob> signatures, metadata_signatures;
+  SignatureFileFlagToBlobs(payload_signature_file, &signatures);
+  SignatureFileFlagToBlobs(metadata_signature_file, &metadata_signatures);
+  uint64_t final_metadata_size;
+  CHECK(PayloadSigner::AddSignatureToPayload(in_file, signatures,
+      metadata_signatures, out_file, &final_metadata_size));
+  LOG(INFO) << "Done signing payload. Final metadata size = "
+            << final_metadata_size;
+  if (!out_metadata_size_file.empty()) {
+    string metadata_size_string = std::to_string(final_metadata_size);
+    CHECK(utils::WriteFile(out_metadata_size_file.c_str(),
+                           metadata_size_string.data(),
+                           metadata_size_string.size()));
+  }
+}
+
+void VerifySignedPayload(const string& in_file,
+                         const string& public_key) {
+  LOG(INFO) << "Verifying signed payload.";
+  LOG_IF(FATAL, in_file.empty())
+      << "Must pass --in_file to verify signed payload.";
+  LOG_IF(FATAL, public_key.empty())
+      << "Must pass --public_key to verify signed payload.";
+  CHECK(PayloadSigner::VerifySignedPayload(in_file, public_key));
+  LOG(INFO) << "Done verifying signed payload.";
+}
+
+// TODO(deymo): This function is likely broken for deltas minor version 2 or
+// newer. Move this function to a new file and make the delta_performer
+// integration tests use this instead.
+void ApplyDelta(const string& in_file,
+                const string& old_kernel,
+                const string& old_rootfs,
+                const string& prefs_dir) {
+  LOG(INFO) << "Applying delta.";
+  LOG_IF(FATAL, old_rootfs.empty())
+      << "Must pass --old_image to apply delta.";
+  Prefs prefs;
+  InstallPlan install_plan;
+  LOG(INFO) << "Setting up preferences under: " << prefs_dir;
+  LOG_IF(ERROR, !prefs.Init(base::FilePath(prefs_dir)))
+      << "Failed to initialize preferences.";
+  // Get original checksums
+  LOG(INFO) << "Calculating original checksums";
+  ImageConfig old_image;
+  old_image.partitions.emplace_back(kLegacyPartitionNameRoot);
+  old_image.partitions.back().path = old_rootfs;
+  old_image.partitions.emplace_back(kLegacyPartitionNameKernel);
+  old_image.partitions.back().path = old_kernel;
+  CHECK(old_image.LoadImageSize());
+  for (const auto& old_part : old_image.partitions) {
+    PartitionInfo part_info;
+    CHECK(diff_utils::InitializePartitionInfo(old_part, &part_info));
+    InstallPlan::Partition part;
+    part.name = old_part.name;
+    part.source_hash.assign(part_info.hash().begin(),
+                            part_info.hash().end());
+    part.source_path = old_part.path;
+    // Apply the delta in-place to the old_part.
+    part.target_path = old_part.path;
+    install_plan.partitions.push_back(part);
+  }
+
+  DeltaPerformer performer(&prefs, nullptr, nullptr, nullptr, &install_plan);
+  brillo::Blob buf(1024 * 1024);
+  int fd = open(in_file.c_str(), O_RDONLY, 0);
+  CHECK_GE(fd, 0);
+  ScopedFdCloser fd_closer(&fd);
+  for (off_t offset = 0;; offset += buf.size()) {
+    ssize_t bytes_read;
+    CHECK(utils::PReadAll(fd, buf.data(), buf.size(), offset, &bytes_read));
+    if (bytes_read == 0)
+      break;
+    CHECK_EQ(performer.Write(buf.data(), bytes_read), bytes_read);
+  }
+  CHECK_EQ(performer.Close(), 0);
+  DeltaPerformer::ResetUpdateProgress(&prefs, false);
+  LOG(INFO) << "Done applying delta.";
+}
+
+int Main(int argc, char** argv) {
+  DEFINE_string(old_image, "", "Path to the old rootfs");
+  DEFINE_string(new_image, "", "Path to the new rootfs");
+  DEFINE_string(old_kernel, "", "Path to the old kernel partition image");
+  DEFINE_string(new_kernel, "", "Path to the new kernel partition image");
+  DEFINE_string(old_partitions, "",
+                "Path to the old partitions. To pass multiple partitions, use "
+                "a single argument with a colon between paths, e.g. "
+                "/path/to/part:/path/to/part2::/path/to/last_part . Path can "
+                "be empty, but it has to match the order of partition_names.");
+  DEFINE_string(new_partitions, "",
+                "Path to the new partitions. To pass multiple partitions, use "
+                "a single argument with a colon between paths, e.g. "
+                "/path/to/part:/path/to/part2:/path/to/last_part . Path has "
+                "to match the order of partition_names.");
+  DEFINE_string(partition_names,
+                string(kLegacyPartitionNameRoot) + ":" +
+                kLegacyPartitionNameKernel,
+                "Names of the partitions. To pass multiple names, use a single "
+                "argument with a colon between names, e.g. "
+                "name:name2:name3:last_name . Name can not be empty, and it "
+                "has to match the order of partitions.");
+  DEFINE_string(in_file, "",
+                "Path to input delta payload file used to hash/sign payloads "
+                "and apply delta over old_image (for debugging)");
+  DEFINE_string(out_file, "", "Path to output delta payload file");
+  DEFINE_string(out_hash_file, "", "Path to output hash file");
+  DEFINE_string(out_metadata_hash_file, "",
+                "Path to output metadata hash file");
+  DEFINE_string(out_metadata_size_file, "",
+                "Path to output metadata size file");
+  DEFINE_string(private_key, "", "Path to private key in .pem format");
+  DEFINE_string(public_key, "", "Path to public key in .pem format");
+  DEFINE_int32(public_key_version, -1,
+               "DEPRECATED. Key-check version # of client");
+  DEFINE_string(prefs_dir, "/tmp/update_engine_prefs",
+                "Preferences directory, used with apply_delta");
+  DEFINE_string(signature_size, "",
+                "Raw signature size used for hash calculation. "
+                "You may pass in multiple sizes by colon separating them. E.g. "
+                "2048:2048:4096 will assume 3 signatures, the first two with "
+                "2048 size and the last 4096.");
+  DEFINE_string(signature_file, "",
+                "Raw signature file to sign payload with. To pass multiple "
+                "signatures, use a single argument with a colon between paths, "
+                "e.g. /path/to/sig:/path/to/next:/path/to/last_sig . Each "
+                "signature will be assigned a client version, starting from "
+                "kSignatureOriginalVersion.");
+  DEFINE_string(metadata_signature_file, "",
+                "Raw signature file with the signature of the metadata hash. "
+                "To pass multiple signatures, use a single argument with a "
+                "colon between paths, "
+                "e.g. /path/to/sig:/path/to/next:/path/to/last_sig .");
+  DEFINE_int32(chunk_size, 200 * 1024 * 1024,
+               "Payload chunk size (-1 for whole files)");
+  DEFINE_uint64(rootfs_partition_size,
+               chromeos_update_engine::kRootFSPartitionSize,
+               "RootFS partition size for the image once installed");
+  DEFINE_uint64(major_version, 1,
+               "The major version of the payload being generated.");
+  DEFINE_int32(minor_version, -1,
+               "The minor version of the payload being generated "
+               "(-1 means autodetect).");
+
+  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. "
+                "Only allowed in major version 2 or newer.");
+
+  brillo::FlagHelper::Init(argc, argv,
+      "Generates a payload to provide to ChromeOS' update_engine.\n\n"
+      "This tool can create full payloads and also delta payloads if the src\n"
+      "image is provided. It also provides debugging options to apply, sign\n"
+      "and verify payloads.");
+  Terminator::Init();
+
+  logging::LoggingSettings log_settings;
+  log_settings.log_file     = "delta_generator.log";
+  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;
+
+  logging::InitLogging(log_settings);
+
+  vector<int> signature_sizes;
+  ParseSignatureSizes(FLAGS_signature_size, &signature_sizes);
+
+  if (!FLAGS_out_hash_file.empty() || !FLAGS_out_metadata_hash_file.empty()) {
+    CHECK(FLAGS_out_metadata_size_file.empty());
+    CalculateHashForSigning(signature_sizes, FLAGS_out_hash_file,
+                            FLAGS_out_metadata_hash_file, FLAGS_in_file);
+    return 0;
+  }
+  if (!FLAGS_signature_file.empty()) {
+    SignPayload(FLAGS_in_file, FLAGS_out_file, FLAGS_signature_file,
+                FLAGS_metadata_signature_file, FLAGS_out_metadata_size_file);
+    return 0;
+  }
+  if (!FLAGS_public_key.empty()) {
+    LOG_IF(WARNING, FLAGS_public_key_version != -1)
+        << "--public_key_version is deprecated and ignored.";
+    VerifySignedPayload(FLAGS_in_file, FLAGS_public_key);
+    return 0;
+  }
+  if (!FLAGS_in_file.empty()) {
+    ApplyDelta(FLAGS_in_file, FLAGS_old_kernel, FLAGS_old_image,
+               FLAGS_prefs_dir);
+    return 0;
+  }
+
+  // A payload generation was requested. Convert the flags to a
+  // PayloadGenerationConfig.
+  PayloadGenerationConfig payload_config;
+  vector<string> partition_names, old_partitions, new_partitions;
+
+  base::SplitString(FLAGS_partition_names, ':', &partition_names);
+  CHECK(!partition_names.empty());
+  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] != kLegacyPartitionNameRoot ||
+                  partition_names[1] != kLegacyPartitionNameKernel)
+        << "To support non-default partition name, please use the "
+        << "--new_partitions flag and major version 2.";
+  }
+
+  if (!FLAGS_new_partitions.empty()) {
+    LOG_IF(FATAL, !FLAGS_new_image.empty() || !FLAGS_new_kernel.empty())
+        << "--new_image and --new_kernel are deprecated, please use "
+        << "--new_partitions for all partitions.";
+    base::SplitString(FLAGS_new_partitions, ':', &new_partitions);
+    CHECK(partition_names.size() == new_partitions.size());
+
+    payload_config.is_delta = !FLAGS_old_partitions.empty();
+    LOG_IF(FATAL, !FLAGS_old_image.empty() || !FLAGS_old_kernel.empty())
+        << "--old_image and --old_kernel are deprecated, please use "
+        << "--old_partitions if you are using --new_partitions.";
+  } else {
+    new_partitions = {FLAGS_new_image, FLAGS_new_kernel};
+    LOG(WARNING) << "--new_partitions is empty, using deprecated --new_image "
+                 << "and --new_kernel flags.";
+
+    payload_config.is_delta = !FLAGS_old_image.empty() ||
+                              !FLAGS_old_kernel.empty();
+    LOG_IF(FATAL, !FLAGS_old_partitions.empty())
+        << "Please use --new_partitions if you are using --old_partitions.";
+  }
+  for (size_t i = 0; i < partition_names.size(); i++) {
+    LOG_IF(FATAL, partition_names[i].empty())
+        << "Partition name can't be empty, see --partition_names.";
+    payload_config.target.partitions.emplace_back(partition_names[i]);
+    payload_config.target.partitions.back().path = new_partitions[i];
+  }
+
+  if (payload_config.is_delta) {
+    if (!FLAGS_old_partitions.empty()) {
+      base::SplitString(FLAGS_old_partitions, ':', &old_partitions);
+      CHECK(old_partitions.size() == new_partitions.size());
+    } else {
+      old_partitions = {FLAGS_old_image, FLAGS_old_kernel};
+      LOG(WARNING) << "--old_partitions is empty, using deprecated --old_image "
+                   << "and --old_kernel flags.";
+    }
+    for (size_t i = 0; i < partition_names.size(); i++) {
+      payload_config.source.partitions.emplace_back(partition_names[i]);
+      payload_config.source.partitions.back().path = old_partitions[i];
+    }
+  }
+
+  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));
+  }
+
+  // Use the default soft_chunk_size defined in the config.
+  payload_config.hard_chunk_size = FLAGS_chunk_size;
+  payload_config.block_size = kBlockSize;
+
+  // The partition size is never passed to the delta_generator, so we
+  // need to detect those from the provided files.
+  if (payload_config.is_delta) {
+    CHECK(payload_config.source.LoadImageSize());
+  }
+  CHECK(payload_config.target.LoadImageSize());
+
+  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) {
+    // Avoid opening the filesystem interface for full payloads.
+    for (PartitionConfig& part : payload_config.target.partitions)
+      CHECK(part.OpenFilesystem());
+    for (PartitionConfig& part : payload_config.source.partitions)
+      CHECK(part.OpenFilesystem());
+  }
+
+  payload_config.major_version = FLAGS_major_version;
+  LOG(INFO) << "Using provided major_version=" << FLAGS_major_version;
+
+  if (FLAGS_minor_version == -1) {
+    // Autodetect minor_version by looking at the update_engine.conf in the old
+    // image.
+    if (payload_config.is_delta) {
+      payload_config.minor_version = kInPlaceMinorPayloadVersion;
+      brillo::KeyValueStore store;
+      uint32_t minor_version;
+      for (const PartitionConfig& part : payload_config.source.partitions) {
+        if (part.fs_interface && part.fs_interface->LoadSettings(&store) &&
+            utils::GetMinorVersion(store, &minor_version)) {
+          payload_config.minor_version = minor_version;
+          break;
+        }
+      }
+    } else {
+      payload_config.minor_version = kFullPayloadMinorVersion;
+    }
+    LOG(INFO) << "Auto-detected minor_version=" << payload_config.minor_version;
+  } else {
+    payload_config.minor_version = FLAGS_minor_version;
+    LOG(INFO) << "Using provided minor_version=" << FLAGS_minor_version;
+  }
+
+  if (payload_config.is_delta) {
+    LOG(INFO) << "Generating delta update";
+  } else {
+    LOG(INFO) << "Generating full update";
+  }
+
+  // From this point, all the options have been parsed.
+  if (!payload_config.Validate()) {
+    LOG(ERROR) << "Invalid options passed. See errors above.";
+    return 1;
+  }
+
+  uint64_t metadata_size;
+  if (!GenerateUpdatePayloadFile(payload_config,
+                                 FLAGS_out_file,
+                                 FLAGS_private_key,
+                                 &metadata_size)) {
+    return 1;
+  }
+  if (!FLAGS_out_metadata_size_file.empty()) {
+    string metadata_size_string = std::to_string(metadata_size);
+    CHECK(utils::WriteFile(FLAGS_out_metadata_size_file.c_str(),
+                           metadata_size_string.data(),
+                           metadata_size_string.size()));
+  }
+  return 0;
+}
+
+}  // namespace
+
+}  // namespace chromeos_update_engine
+
+int main(int argc, char** argv) {
+  return chromeos_update_engine::Main(argc, argv);
+}
diff --git a/payload_generator/graph_types.cc b/payload_generator/graph_types.cc
new file mode 100644
index 0000000..7da76f7
--- /dev/null
+++ b/payload_generator/graph_types.cc
@@ -0,0 +1,23 @@
+//
+// 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/graph_types.h"
+
+namespace chromeos_update_engine {
+
+const Vertex::Index Vertex::kInvalidIndex = static_cast<Vertex::Index>(-1);
+
+}  // chromeos_update_engine
diff --git a/payload_generator/graph_types.h b/payload_generator/graph_types.h
new file mode 100644
index 0000000..beee4eb
--- /dev/null
+++ b/payload_generator/graph_types.h
@@ -0,0 +1,90 @@
+//
+// 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;
+COMPILE_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..2d5fb63
--- /dev/null
+++ b/payload_generator/graph_utils.cc
@@ -0,0 +1,141 @@
+//
+// 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 (int i = 0, e = field.size(); i != e; ++i) {
+    LOG(INFO) << header << "(" << GetElement(field, i).start_block() << ", "
+              << GetElement(field, i).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..b32e666
--- /dev/null
+++ b/payload_generator/graph_utils.h
@@ -0,0 +1,56 @@
+//
+// 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..7d3dbe3
--- /dev/null
+++ b/payload_generator/graph_utils_unittest.cc
@@ -0,0 +1,95 @@
+//
+// 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(0, extents.size());
+  AppendBlockToExtents(&extents, 0);
+  EXPECT_EQ(1, extents.size());
+  AppendBlockToExtents(&extents, 1);
+  AppendBlockToExtents(&extents, 2);
+  EXPECT_EQ(1, extents.size());
+  AppendBlockToExtents(&extents, 4);
+
+  EXPECT_EQ(2, extents.size());
+  EXPECT_EQ(0, extents[0].start_block());
+  EXPECT_EQ(3, extents[0].num_blocks());
+  EXPECT_EQ(4, extents[1].start_block());
+  EXPECT_EQ(1, extents[1].num_blocks());
+
+  EXPECT_EQ(4, graph_utils::EdgeWeight(graph, make_pair(0, 1)));
+}
+
+
+TEST(GraphUtilsTest, DepsTest) {
+  Graph graph(3);
+
+  graph_utils::AddReadBeforeDep(&graph[0], 1, 3);
+  EXPECT_EQ(1, graph[0].out_edges.size());
+  {
+    Extent& extent = graph[0].out_edges[1].extents[0];
+    EXPECT_EQ(3, extent.start_block());
+    EXPECT_EQ(1, extent.num_blocks());
+  }
+  graph_utils::AddReadBeforeDep(&graph[0], 1, 4);
+  EXPECT_EQ(1, graph[0].out_edges.size());
+  {
+    Extent& extent = graph[0].out_edges[1].extents[0];
+    EXPECT_EQ(3, extent.start_block());
+    EXPECT_EQ(2, extent.num_blocks());
+  }
+  graph_utils::AddReadBeforeDepExtents(&graph[2], 1,
+    vector<Extent>(1, ExtentForRange(5, 2)));
+  EXPECT_EQ(1, graph[2].out_edges.size());
+  {
+    Extent& extent = graph[2].out_edges[1].extents[0];
+    EXPECT_EQ(5, extent.start_block());
+    EXPECT_EQ(2, 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(0, graph[2].out_edges.size());
+
+  EXPECT_EQ(1, graph[0].out_edges.size());
+  graph_utils::DropIncomingEdgesTo(&graph, 1);
+  EXPECT_EQ(0, 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..2111748
--- /dev/null
+++ b/payload_generator/inplace_generator.cc
@@ -0,0 +1,823 @@
+//
+// 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 "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 {
+
+// 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 (int i = 0, e = extents.size(); i < e; ++i) {
+    Extent extent = GetElement(extents, i);
+    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 (utils::SetContainsKey(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,
+        (*graph)[cut.old_dst].aop.name,
+        -1,  // chunk_blocks, forces to have a single operation.
+        blob_file,
+        false));  // src_ops_allowed
+    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 int extents_size =
+        (field == READER) ? operation.src_extents_size() :
+        operation.dst_extents_size();
+    const char* past_participle = (field == READER) ? "read" : "written";
+    const google::protobuf::RepeatedPtrField<Extent>& extents =
+        (field == READER) ? operation.src_extents() : operation.dst_extents();
+    Vertex::Index Block::*access_type = (field == READER) ?
+        &Block::reader : &Block::writer;
+
+    for (int i = 0; i < extents_size; i++) {
+      const Extent& extent = extents.Get(i);
+      for (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& 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(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);
+
+  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 == kLegacyPartitionNameRoot)
+    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,
+                                   blob_file,
+                                   false));  // src_ops_allowed
+  LOG(INFO) << "Done reading " << new_part.name;
+
+  TEST_AND_RETURN_FALSE(
+    ResolveReadAfterWriteDependencies(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..4839824
--- /dev/null
+++ b/payload_generator/inplace_generator.h
@@ -0,0 +1,242 @@
+//
+// 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& 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..9932ef9
--- /dev/null
+++ b/payload_generator/inplace_generator_unittest.cc
@@ -0,0 +1,658 @@
+//
+// 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 <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/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_));
+  }
+
+  // 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(11, op.src_extents(0).start_block());
+  EXPECT_EQ(1, op.src_extents(0).num_blocks());
+  EXPECT_EQ(13, op.src_extents(1).start_block());
+  EXPECT_EQ(1, op.src_extents(1).num_blocks());
+  EXPECT_EQ(6, op.src_extents(2).start_block());
+  EXPECT_EQ(1, op.src_extents(2).num_blocks());
+  EXPECT_EQ(kSparseHole, op.src_extents(3).start_block());
+  EXPECT_EQ(4, op.src_extents(3).num_blocks());
+  EXPECT_EQ(10, op.src_extents(4).start_block());
+  EXPECT_EQ(1, op.src_extents(4).num_blocks());
+  EXPECT_EQ(14, op.src_extents(5).start_block());
+  EXPECT_EQ(1, op.src_extents(5).num_blocks());
+  EXPECT_EQ(8, op.src_extents(6).start_block());
+  EXPECT_EQ(2, 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(1, 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(3, 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(2, 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(2, graph[0].aop.op.src_extents(0).num_blocks());
+  EXPECT_EQ(7, graph[0].aop.op.src_extents(1).start_block());
+  EXPECT_EQ(1, 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(1, graph[0].aop.op.dst_extents(0).start_block());
+  EXPECT_EQ(2, graph[0].aop.op.dst_extents(0).num_blocks());
+  EXPECT_EQ(4, graph[0].aop.op.dst_extents(1).start_block());
+  EXPECT_EQ(1, graph[0].aop.op.dst_extents(1).num_blocks());
+
+  // Ensure it only depends on the next node and the new temp node
+  EXPECT_EQ(2, 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(1, graph[1].aop.op.src_extents(0).start_block());
+  EXPECT_EQ(2, graph[1].aop.op.src_extents(0).num_blocks());
+  EXPECT_EQ(4, graph[1].aop.op.src_extents(1).start_block());
+  EXPECT_EQ(1, graph[1].aop.op.src_extents(1).num_blocks());
+
+  EXPECT_EQ(2, graph[1].aop.op.dst_extents_size());
+  EXPECT_EQ(3, graph[1].aop.op.dst_extents(0).start_block());
+  EXPECT_EQ(1, graph[1].aop.op.dst_extents(0).num_blocks());
+  EXPECT_EQ(5, graph[1].aop.op.dst_extents(1).start_block());
+  EXPECT_EQ(2, graph[1].aop.op.dst_extents(1).num_blocks());
+
+  // Ensure it only depends on the next node
+  EXPECT_EQ(1, 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(2, graph[1].aop.op.src_extents(0).start_block());
+  EXPECT_EQ(1, 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(12, 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(0, vertex.aop.op.data_offset());
+  EXPECT_EQ(0, vertex.aop.op.data_length());
+  EXPECT_EQ(1, vertex.aop.op.dst_extents_size());
+  EXPECT_EQ(12, vertex.aop.op.dst_extents(0).start_block());
+  EXPECT_EQ(34, 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_blocs=%" PRIu64,
+                                    part_blocks));
+    vector<AnnotatedOperation> result_aops = aops;
+    EXPECT_TRUE(InplaceGenerator::ResolveReadAfterWriteDependencies(
+      part, part_blocks * block_size, block_size, blob_file_.get(),
+      &result_aops));
+
+    size_t full_ops = 0;
+    for (const auto& aop : result_aops) {
+      if (aop.op.type() == InstallOperation::REPLACE ||
+          aop.op.type() == InstallOperation::REPLACE_BZ) {
+        full_ops++;
+      }
+
+      if (aop.op.type() != InstallOperation::MOVE)
+        continue;
+      for (const Extent& extent : aop.op.src_extents()) {
+        EXPECT_NE(0, extent.start_block()) << "On src extents for aop: " << aop;
+      }
+      for (const Extent& extent : aop.op.dst_extents()) {
+        EXPECT_NE(0, 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 ? 2 : 1, full_ops);
+
+    if (HasNonfatalFailure()) {
+      LOG(INFO) << "Result operation list:";
+      for (const auto& aop : result_aops) {
+        LOG(INFO) << aop;
+      }
+    }
+  }
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/operations_generator.h b/payload_generator/operations_generator.h
new file mode 100644
index 0000000..9127d7b
--- /dev/null
+++ b/payload_generator/operations_generator.h
@@ -0,0 +1,59 @@
+//
+// 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_OPERATIONS_GENERATOR_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_OPERATIONS_GENERATOR_H_
+
+#include <vector>
+
+#include <base/macros.h>
+
+#include "update_engine/payload_generator/annotated_operation.h"
+#include "update_engine/payload_generator/blob_file_writer.h"
+#include "update_engine/payload_generator/payload_generation_config.h"
+
+namespace chromeos_update_engine {
+
+class OperationsGenerator {
+ public:
+  virtual ~OperationsGenerator() = default;
+
+  // This method generates a list of operations to update from the partition
+  // |old_part| to |new_part| and stores the generated operations in |aops|.
+  // These operations are generated based on the given |config|.
+  // The operations should be applied in the order specified in the list, and
+  // they respect the payload version and type (delta or full) specified in
+  // |config|.
+  // The operations generated will refer to offsets in the file |blob_file|,
+  // where this function stores the output, but not necessarily in the same
+  // order as they appear in the |aops|.
+  virtual bool GenerateOperations(
+      const PayloadGenerationConfig& config,
+      const PartitionConfig& old_part,
+      const PartitionConfig& new_part,
+      BlobFileWriter* blob_file,
+      std::vector<AnnotatedOperation>* aops) = 0;
+
+ protected:
+  OperationsGenerator() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(OperationsGenerator);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_OPERATIONS_GENERATOR_H_
diff --git a/payload_generator/payload_file.cc b/payload_generator/payload_file.cc
new file mode 100644
index 0000000..d268ab8
--- /dev/null
+++ b/payload_generator/payload_file.cc
@@ -0,0 +1,356 @@
+//
+// 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/payload_file.h"
+
+#include <endian.h>
+
+#include <algorithm>
+
+#include "update_engine/common/hash_calculator.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"
+#include "update_engine/payload_generator/annotated_operation.h"
+#include "update_engine/payload_generator/delta_diff_utils.h"
+#include "update_engine/payload_generator/payload_signer.h"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+struct DeltaObject {
+  DeltaObject(const string& in_name, const int in_type, const off_t in_size)
+      : name(in_name),
+        type(in_type),
+        size(in_size) {}
+  bool operator <(const DeltaObject& object) const {
+    return (size != object.size) ? (size < object.size) : (name < object.name);
+  }
+  string name;
+  int type;
+  off_t size;
+};
+
+// Writes the uint64_t passed in in host-endian to the file as big-endian.
+// Returns true on success.
+bool WriteUint64AsBigEndian(FileWriter* writer, const uint64_t value) {
+  uint64_t value_be = htobe64(value);
+  TEST_AND_RETURN_FALSE(writer->Write(&value_be, sizeof(value_be)));
+  return true;
+}
+
+}  // namespace
+
+bool PayloadFile::Init(const PayloadGenerationConfig& config) {
+  major_version_ = config.major_version;
+  TEST_AND_RETURN_FALSE(major_version_ == kChromeOSMajorPayloadVersion ||
+                        major_version_ == kBrilloMajorPayloadVersion);
+  manifest_.set_minor_version(config.minor_version);
+
+  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);
+  return true;
+}
+
+bool PayloadFile::AddPartition(const PartitionConfig& old_conf,
+                               const PartitionConfig& new_conf,
+                               const vector<AnnotatedOperation>& aops) {
+  // Check partitions order for Chrome OS
+  if (major_version_ == kChromeOSMajorPayloadVersion) {
+    const vector<const char*> part_order = { kLegacyPartitionNameRoot,
+                                             kLegacyPartitionNameKernel };
+    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.name = new_conf.name;
+  part.aops = aops;
+  part.postinstall = new_conf.postinstall;
+  // Initialize the PartitionInfo objects if present.
+  if (!old_conf.path.empty())
+    TEST_AND_RETURN_FALSE(diff_utils::InitializePartitionInfo(old_conf,
+                                                              &part.old_info));
+  TEST_AND_RETURN_FALSE(diff_utils::InitializePartitionInfo(new_conf,
+                                                            &part.new_info));
+  part_vec_.push_back(std::move(part));
+  return true;
+}
+
+bool PayloadFile::WritePayload(const string& payload_file,
+                               const string& data_blobs_path,
+                               const string& private_key_path,
+                               uint64_t* metadata_size_out) {
+  // Reorder the data blobs with the manifest_.
+  string ordered_blobs_path;
+  TEST_AND_RETURN_FALSE(utils::MakeTempFile(
+      "CrAU_temp_data.ordered.XXXXXX",
+      &ordered_blobs_path,
+      nullptr));
+  ScopedPathUnlinker ordered_blobs_unlinker(ordered_blobs_path);
+  TEST_AND_RETURN_FALSE(ReorderDataBlobs(data_blobs_path, ordered_blobs_path));
+
+  // Check that install op blobs are in order.
+  uint64_t next_blob_offset = 0;
+  for (const auto& part : part_vec_) {
+    for (const auto& aop : part.aops) {
+      if (!aop.op.has_data_offset())
+        continue;
+      if (aop.op.data_offset() != next_blob_offset) {
+        LOG(FATAL) << "bad blob offset! " << aop.op.data_offset() << " != "
+                   << next_blob_offset;
+      }
+      next_blob_offset += aop.op.data_length();
+    }
+  }
+
+  // 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_) {
+    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);
+      }
+      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 == kLegacyPartitionNameKernel) {
+        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;
+      }
+    }
+  }
+
+  // Signatures appear at the end of the blobs. Note the offset in the
+  // manifest_.
+  uint64_t signature_blob_length = 0;
+  if (!private_key_path.empty()) {
+    TEST_AND_RETURN_FALSE(
+        PayloadSigner::SignatureBlobLength(vector<string>(1, private_key_path),
+                                           &signature_blob_length));
+    PayloadSigner::AddSignatureToManifest(
+        next_blob_offset, signature_blob_length,
+        major_version_ == kChromeOSMajorPayloadVersion, &manifest_);
+  }
+
+  // Serialize protobuf
+  string serialized_manifest;
+  TEST_AND_RETURN_FALSE(manifest_.AppendToString(&serialized_manifest));
+
+  uint64_t metadata_size =
+      sizeof(kDeltaMagic) + 2 * sizeof(uint64_t) + serialized_manifest.size();
+
+  LOG(INFO) << "Writing final delta file header...";
+  DirectFileWriter writer;
+  TEST_AND_RETURN_FALSE_ERRNO(writer.Open(payload_file.c_str(),
+                                          O_WRONLY | O_CREAT | O_TRUNC,
+                                          0644) == 0);
+  ScopedFileWriterCloser writer_closer(&writer);
+
+  // Write header
+  TEST_AND_RETURN_FALSE(writer.Write(kDeltaMagic, sizeof(kDeltaMagic)));
+
+  // Write major version number
+  TEST_AND_RETURN_FALSE(WriteUint64AsBigEndian(&writer, major_version_));
+
+  // Write protobuf length
+  TEST_AND_RETURN_FALSE(WriteUint64AsBigEndian(&writer,
+                                               serialized_manifest.size()));
+
+  // 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(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... "
+            << serialized_manifest.size();
+  TEST_AND_RETURN_FALSE(writer.Write(serialized_manifest.data(),
+                                     serialized_manifest.size()));
+
+  // Write metadata signature blob.
+  if (major_version_ == kBrilloMajorPayloadVersion &&
+      !private_key_path.empty()) {
+    brillo::Blob metadata_hash, metadata_signature;
+    TEST_AND_RETURN_FALSE(HashCalculator::RawHashOfFile(payload_file,
+                                                             metadata_size,
+                                                             &metadata_hash));
+    TEST_AND_RETURN_FALSE(
+        PayloadSigner::SignHashWithKeys(metadata_hash,
+                                        vector<string>(1, private_key_path),
+                                        &metadata_signature));
+    TEST_AND_RETURN_FALSE(writer.Write(metadata_signature.data(),
+                                       metadata_signature.size()));
+  }
+
+  // Append the data blobs
+  LOG(INFO) << "Writing final delta file data blobs...";
+  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 (;;) {
+    vector<char> buf(1024 * 1024);
+    ssize_t rc = read(blobs_fd, buf.data(), buf.size());
+    if (0 == rc) {
+      // EOF
+      break;
+    }
+    TEST_AND_RETURN_FALSE_ERRNO(rc > 0);
+    TEST_AND_RETURN_FALSE(writer.Write(buf.data(), rc));
+  }
+
+  // Write payload signature blob.
+  if (!private_key_path.empty()) {
+    LOG(INFO) << "Signing the update...";
+    brillo::Blob signature_blob;
+    TEST_AND_RETURN_FALSE(PayloadSigner::SignPayload(
+        payload_file,
+        vector<string>(1, private_key_path),
+        metadata_size,
+        metadata_signature_size,
+        metadata_size + metadata_signature_size + manifest_.signatures_offset(),
+        &signature_blob));
+    TEST_AND_RETURN_FALSE(writer.Write(signature_blob.data(),
+                                       signature_blob.size()));
+  }
+
+  ReportPayloadUsage(metadata_size);
+  *metadata_size_out = metadata_size;
+  return true;
+}
+
+bool PayloadFile::ReorderDataBlobs(
+    const string& data_blobs_path,
+    const string& new_data_blobs_path) {
+  int in_fd = open(data_blobs_path.c_str(), O_RDONLY, 0);
+  TEST_AND_RETURN_FALSE_ERRNO(in_fd >= 0);
+  ScopedFdCloser in_fd_closer(&in_fd);
+
+  DirectFileWriter writer;
+  TEST_AND_RETURN_FALSE(
+      writer.Open(new_data_blobs_path.c_str(),
+                  O_WRONLY | O_TRUNC | O_CREAT,
+                  0644) == 0);
+  ScopedFileWriterCloser writer_closer(&writer);
+  uint64_t out_file_size = 0;
+
+  for (auto& part: part_vec_) {
+    for (AnnotatedOperation& aop : part.aops) {
+      if (!aop.op.has_data_offset())
+        continue;
+      CHECK(aop.op.has_data_length());
+      brillo::Blob buf(aop.op.data_length());
+      ssize_t rc = pread(in_fd, buf.data(), buf.size(), aop.op.data_offset());
+      TEST_AND_RETURN_FALSE(rc == static_cast<ssize_t>(buf.size()));
+
+      // Add the hash of the data blobs for this operation
+      TEST_AND_RETURN_FALSE(AddOperationHash(&aop.op, buf));
+
+      aop.op.set_data_offset(out_file_size);
+      TEST_AND_RETURN_FALSE(writer.Write(buf.data(), buf.size()));
+      out_file_size += buf.size();
+    }
+  }
+  return true;
+}
+
+bool PayloadFile::AddOperationHash(InstallOperation* op,
+                                   const brillo::Blob& buf) {
+  HashCalculator hasher;
+  TEST_AND_RETURN_FALSE(hasher.Update(buf.data(), buf.size()));
+  TEST_AND_RETURN_FALSE(hasher.Finalize());
+  const brillo::Blob& hash = hasher.raw_hash();
+  op->set_data_sha256_hash(hash.data(), hash.size());
+  return true;
+}
+
+void PayloadFile::ReportPayloadUsage(uint64_t metadata_size) const {
+  vector<DeltaObject> objects;
+  off_t total_size = 0;
+
+  for (const auto& part : part_vec_) {
+    for (const AnnotatedOperation& aop : part.aops) {
+      objects.push_back(DeltaObject(aop.name,
+                                    aop.op.type(),
+                                    aop.op.data_length()));
+      total_size += aop.op.data_length();
+    }
+  }
+
+  objects.push_back(DeltaObject("<manifest-metadata>",
+                                -1,
+                                metadata_size));
+  total_size += metadata_size;
+
+  std::sort(objects.begin(), objects.end());
+
+  static const char kFormatString[] = "%6.2f%% %10jd %-10s %s\n";
+  for (const DeltaObject& object : objects) {
+    fprintf(
+        stderr, kFormatString,
+        object.size * 100.0 / total_size,
+        static_cast<intmax_t>(object.size),
+        (object.type >= 0 ? InstallOperationTypeName(
+                                static_cast<InstallOperation_Type>(object.type))
+                          : "-"),
+        object.name.c_str());
+  }
+  fprintf(stderr, kFormatString,
+          100.0, static_cast<intmax_t>(total_size), "", "<total>");
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/payload_file.h b/payload_generator/payload_file.h
new file mode 100644
index 0000000..7cc792a
--- /dev/null
+++ b/payload_generator/payload_file.h
@@ -0,0 +1,105 @@
+//
+// 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_PAYLOAD_FILE_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_PAYLOAD_FILE_H_
+
+#include <string>
+#include <vector>
+
+#include <brillo/secure_blob.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "update_engine/payload_generator/annotated_operation.h"
+#include "update_engine/payload_generator/payload_generation_config.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+// Class to handle the creation of a payload file. This class is the only one
+// dealing with writing the payload and its format, but has no logic about what
+// should be on it.
+class PayloadFile {
+ public:
+  // Initialize the payload file with the payload generation config. It computes
+  // required hashes of the requested partitions.
+  bool Init(const PayloadGenerationConfig& config);
+
+  // Add a partition to the payload manifest. Including partition name, list of
+  // operations and partition info. The operations in |aops|
+  // reference a blob stored in the file provided to WritePayload().
+  bool AddPartition(const PartitionConfig& old_conf,
+                    const PartitionConfig& new_conf,
+                    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
+  // payload file to match the order of the operations. The size of the metadata
+  // section of the payload is stored in |metadata_size_out|.
+  bool WritePayload(const std::string& payload_file,
+                    const std::string& data_blobs_path,
+                    const std::string& private_key_path,
+                    uint64_t* metadata_size_out);
+
+ private:
+  FRIEND_TEST(PayloadFileTest, ReorderBlobsTest);
+
+  // 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
+  // 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 dummy signature operation.
+  static bool AddOperationHash(InstallOperation* op, const brillo::Blob& buf);
+
+  // Install operations in the manifest may reference data blobs, which
+  // are in data_blobs_path. This function creates a new data blobs file
+  // with the data blobs in the same order as the referencing install
+  // operations in the manifest. E.g. if manifest[0] has a data blob
+  // "X" at offset 1, manifest[1] has a data blob "Y" at offset 0,
+  // and data_blobs_path's file contains "YX", new_data_blobs_path
+  // will set to be a file that contains "XY".
+  bool ReorderDataBlobs(const std::string& data_blobs_path,
+                        const std::string& new_data_blobs_path);
+
+  // Print in stderr the Payload usage report.
+  void ReportPayloadUsage(uint64_t metadata_size) const;
+
+  // The major_version of the requested payload.
+  uint64_t major_version_;
+
+  DeltaArchiveManifest manifest_;
+
+  // Struct has necessary information to write PartitionUpdate in protobuf.
+  struct Partition {
+    // The name of the partition.
+    std::string name;
+
+    // The operations to be performed to this partition.
+    std::vector<AnnotatedOperation> aops;
+
+    PartitionInfo old_info;
+    PartitionInfo new_info;
+
+    PostInstallConfig postinstall;
+  };
+
+  std::vector<Partition> part_vec_;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_PAYLOAD_FILE_H_
diff --git a/payload_generator/payload_file_unittest.cc b/payload_generator/payload_file_unittest.cc
new file mode 100644
index 0000000..4d18adf
--- /dev/null
+++ b/payload_generator/payload_file_unittest.cc
@@ -0,0 +1,94 @@
+//
+// 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/payload_file.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "update_engine/common/test_utils.h"
+#include "update_engine/payload_generator/extent_ranges.h"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+class PayloadFileTest : public ::testing::Test {
+ protected:
+  PayloadFile payload_;
+};
+
+TEST_F(PayloadFileTest, ReorderBlobsTest) {
+  string orig_blobs;
+  EXPECT_TRUE(utils::MakeTempFile("ReorderBlobsTest.orig.XXXXXX", &orig_blobs,
+                                  nullptr));
+  ScopedPathUnlinker orig_blobs_unlinker(orig_blobs);
+
+  // The operations have three blob and one gap (the whitespace):
+  // Rootfs operation 1: [8, 3] bcd
+  // Rootfs operation 2: [7, 1] a
+  // Kernel operation 1: [0, 6] kernel
+  string orig_data = "kernel abcd";
+  EXPECT_TRUE(
+      utils::WriteFile(orig_blobs.c_str(), orig_data.data(), orig_data.size()));
+
+  string new_blobs;
+  EXPECT_TRUE(
+      utils::MakeTempFile("ReorderBlobsTest.new.XXXXXX", &new_blobs, nullptr));
+  ScopedPathUnlinker new_blobs_unlinker(new_blobs);
+
+  payload_.part_vec_.resize(2);
+
+  vector<AnnotatedOperation> aops;
+  AnnotatedOperation aop;
+  aop.op.set_data_offset(8);
+  aop.op.set_data_length(3);
+  aops.push_back(aop);
+
+  aop.op.set_data_offset(7);
+  aop.op.set_data_length(1);
+  aops.push_back(aop);
+  payload_.part_vec_[0].aops = aops;
+
+  aop.op.set_data_offset(0);
+  aop.op.set_data_length(6);
+  payload_.part_vec_[1].aops = {aop};
+
+  EXPECT_TRUE(payload_.ReorderDataBlobs(orig_blobs, new_blobs));
+
+  const vector<AnnotatedOperation>& part0_aops = payload_.part_vec_[0].aops;
+  const vector<AnnotatedOperation>& part1_aops = payload_.part_vec_[1].aops;
+  string new_data;
+  EXPECT_TRUE(utils::ReadFile(new_blobs, &new_data));
+  // Kernel blobs should appear at the end.
+  EXPECT_EQ("bcdakernel", new_data);
+
+  EXPECT_EQ(2, part0_aops.size());
+  EXPECT_EQ(0, part0_aops[0].op.data_offset());
+  EXPECT_EQ(3, part0_aops[0].op.data_length());
+  EXPECT_EQ(3, part0_aops[1].op.data_offset());
+  EXPECT_EQ(1, part0_aops[1].op.data_length());
+
+  EXPECT_EQ(1, part1_aops.size());
+  EXPECT_EQ(4, part1_aops[0].op.data_offset());
+  EXPECT_EQ(6, part1_aops[0].op.data_length());
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/payload_generation_config.cc b/payload_generator/payload_generation_config.cc
new file mode 100644
index 0000000..813e33c
--- /dev/null
+++ b/payload_generator/payload_generation_config.cc
@@ -0,0 +1,160 @@
+//
+// 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/payload_generation_config.h"
+
+#include <base/logging.h>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/delta_performer.h"
+#include "update_engine/payload_generator/delta_diff_generator.h"
+#include "update_engine/payload_generator/ext2_filesystem.h"
+#include "update_engine/payload_generator/raw_filesystem.h"
+
+namespace chromeos_update_engine {
+
+bool PostInstallConfig::IsEmpty() const {
+  return run == false && path.empty() && filesystem_type.empty();
+}
+
+bool PartitionConfig::ValidateExists() const {
+  TEST_AND_RETURN_FALSE(!path.empty());
+  TEST_AND_RETURN_FALSE(utils::FileExists(path.c_str()));
+  TEST_AND_RETURN_FALSE(size > 0);
+  // The requested size is within the limits of the file.
+  TEST_AND_RETURN_FALSE(static_cast<off_t>(size) <=
+                        utils::FileSize(path.c_str()));
+  // TODO(deymo): The delta generator algorithm doesn't support a block size
+  // different than 4 KiB. Remove this check once that's fixed. crbug.com/455045
+  int block_count, block_size;
+  if (utils::GetFilesystemSize(path, &block_count, &block_size) &&
+      block_size != 4096) {
+   LOG(ERROR) << "The filesystem provided in " << path
+              << " has a block size of " << block_size
+              << " but delta_generator only supports 4096.";
+   return false;
+  }
+  return true;
+}
+
+bool PartitionConfig::OpenFilesystem() {
+  if (path.empty())
+    return true;
+  fs_interface.reset();
+  if (utils::IsExtFilesystem(path)) {
+    fs_interface = Ext2Filesystem::CreateFromFile(path);
+  }
+
+  if (!fs_interface) {
+    // Fall back to a RAW filesystem.
+    TEST_AND_RETURN_FALSE(size % kBlockSize == 0);
+    fs_interface = RawFilesystem::Create(
+      "<" + name + "-partition>",
+      kBlockSize,
+      size / kBlockSize);
+  }
+  return true;
+}
+
+bool ImageConfig::ValidateIsEmpty() const {
+  TEST_AND_RETURN_FALSE(ImageInfoIsEmpty());
+  return partitions.empty();
+}
+
+bool ImageConfig::LoadImageSize() {
+  for (PartitionConfig& part : partitions) {
+    if (part.path.empty())
+      continue;
+    part.size = utils::FileSize(part.path);
+  }
+  return true;
+}
+
+bool ImageConfig::LoadPostInstallConfig(const brillo::KeyValueStore& store) {
+  bool found_postinstall = false;
+  for (PartitionConfig& part : partitions) {
+    bool run_postinstall;
+    if (!store.GetBoolean("RUN_POSTINSTALL_" + part.name, &run_postinstall) ||
+        !run_postinstall)
+      continue;
+    found_postinstall = true;
+    part.postinstall.run = true;
+    store.GetString("POSTINSTALL_PATH_" + part.name, &part.postinstall.path);
+    store.GetString("FILESYSTEM_TYPE_" + part.name,
+                    &part.postinstall.filesystem_type);
+  }
+  if (!found_postinstall) {
+    LOG(ERROR) << "No valid postinstall config found.";
+    return false;
+  }
+  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();
+}
+
+bool PayloadGenerationConfig::Validate() const {
+  if (is_delta) {
+    for (const PartitionConfig& part : source.partitions) {
+      if (!part.path.empty()) {
+        TEST_AND_RETURN_FALSE(part.ValidateExists());
+        TEST_AND_RETURN_FALSE(part.size % block_size == 0);
+      }
+      // Source partition should not have postinstall.
+      TEST_AND_RETURN_FALSE(part.postinstall.IsEmpty());
+    }
+
+    // Check for the supported minor_version values.
+    TEST_AND_RETURN_FALSE(minor_version == kInPlaceMinorPayloadVersion ||
+                          minor_version == kSourceMinorPayloadVersion ||
+                          minor_version == kOpSrcHashMinorPayloadVersion);
+
+    // 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());
+    TEST_AND_RETURN_FALSE(minor_version == kFullPayloadMinorVersion);
+  }
+
+  // In all cases, the target image must exists.
+  for (const PartitionConfig& part : target.partitions) {
+    TEST_AND_RETURN_FALSE(part.ValidateExists());
+    TEST_AND_RETURN_FALSE(part.size % block_size == 0);
+    if (minor_version == kInPlaceMinorPayloadVersion &&
+        part.name == kLegacyPartitionNameRoot)
+      TEST_AND_RETURN_FALSE(rootfs_partition_size >= part.size);
+    if (major_version == kChromeOSMajorPayloadVersion)
+      TEST_AND_RETURN_FALSE(part.postinstall.IsEmpty());
+  }
+
+  TEST_AND_RETURN_FALSE(hard_chunk_size == -1 ||
+                        hard_chunk_size % block_size == 0);
+  TEST_AND_RETURN_FALSE(soft_chunk_size % block_size == 0);
+
+  TEST_AND_RETURN_FALSE(rootfs_partition_size % block_size == 0);
+
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/payload_generation_config.h b/payload_generator/payload_generation_config.h
new file mode 100644
index 0000000..21bb89b
--- /dev/null
+++ b/payload_generator/payload_generation_config.h
@@ -0,0 +1,164 @@
+//
+// 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_PAYLOAD_GENERATION_CONFIG_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_PAYLOAD_GENERATION_CONFIG_H_
+
+#include <cstddef>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <brillo/key_value_store.h>
+
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_generator/filesystem_interface.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+struct PostInstallConfig {
+  // Whether the postinstall config is empty.
+  bool IsEmpty() const;
+
+  // Whether this partition carries a filesystem with post-install program that
+  // must be run to finalize the update process.
+  bool run = false;
+
+  // The path to the post-install program relative to the root of this
+  // filesystem.
+  std::string path;
+
+  // The filesystem type used to mount the partition in order to run the
+  // post-install program.
+  std::string filesystem_type;
+};
+
+struct PartitionConfig {
+  explicit PartitionConfig(std::string name) : name(name) {}
+
+  // Returns whether the PartitionConfig is not an empty image and all the
+  // fields are set correctly to a valid image file.
+  bool ValidateExists() const;
+
+  // Open then filesystem stored in this partition and stores it in
+  // |fs_interface|. Returns whether opening the filesystem worked.
+  bool OpenFilesystem();
+
+  // The path to the partition file. This can be a regular file or a block
+  // device such as a loop device.
+  std::string path;
+
+  // The size of the data in |path|. If rootfs verification is used (verity)
+  // this value should match the size of the verity device for the rootfs, and
+  // the size of the whole kernel. This value could be smaller than the
+  // partition and is the size of the data update_engine assumes verified for
+  // the source image, and the size of that data it should generate for the
+  // target image.
+  uint64_t size = 0;
+
+  // The FilesystemInterface implementation used to access this partition's
+  // files.
+  std::unique_ptr<FilesystemInterface> fs_interface;
+
+  std::string name;
+
+  PostInstallConfig postinstall;
+};
+
+// The ImageConfig struct describes a pair of binaries kernel and rootfs and the
+// metadata associated with the image they are part of, like build number, size,
+// etc.
+struct ImageConfig {
+  // Returns whether the ImageConfig is an empty image.
+  bool ValidateIsEmpty() const;
+
+  // Load |rootfs_size| and |kernel.size| from the respective image files. For
+  // the kernel, the whole |kernel.path| file is assumed. For the rootfs, the
+  // size is detected from the filesystem.
+  // Returns whether the image size was properly detected.
+  bool LoadImageSize();
+
+  // Load postinstall config from a key value store.
+  bool LoadPostInstallConfig(const brillo::KeyValueStore& store);
+
+  // 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;
+};
+
+// The PayloadGenerationConfig struct encapsulates all the configuration to
+// build the requested payload. This includes information about the old and new
+// image as well as the restrictions applied to the payload (like minor-version
+// and full/delta payload).
+struct PayloadGenerationConfig {
+  // Returns whether the PayloadGenerationConfig is valid.
+  bool Validate() const;
+
+  // Image information about the new image that's the target of this payload.
+  ImageConfig target;
+
+  // Image information pertaining the old image, if any. This is only valid
+  // if is_full is false, so we are requested a delta payload.
+  ImageConfig source;
+
+  // Wheter the requested payload is a delta payload.
+  bool is_delta = false;
+
+  // The major_version of the requested payload.
+  uint64_t major_version;
+
+  // The minor_version of the requested payload.
+  uint32_t minor_version;
+
+  // The size of the rootfs partition, that not necessarily is the same as the
+  // filesystem in either source or target version, since there is some space
+  // after the partition used to store the verity hashes and or the bootcache.
+  uint64_t rootfs_partition_size = 0;
+
+  // The |hard_chunk_size| is the maximum size that a single operation should
+  // write in the destination. Operations bigger than chunk_size should be
+  // split. A value of -1 means no hard chunk size limit. A very low limit
+  // means more operations, and less of a chance to reuse the data.
+  ssize_t hard_chunk_size = -1;
+
+  // The |soft_chunk_size| is the preferred chunk size to use when there's no
+  // significant impact to the operations. For example, REPLACE, MOVE and
+  // SOURCE_COPY operations are not significantly impacted by the chunk size,
+  // except for a few bytes overhead in the manifest to describe extra
+  // operations. On the other hand, splitting BSDIFF operations impacts the
+  // payload size since it is not possible to use the redundancy *between*
+  // chunks.
+  size_t soft_chunk_size = 2 * 1024 * 1024;
+
+  // TODO(deymo): Remove the block_size member and maybe replace it with a
+  // minimum alignment size for blocks (if needed). Algorithms should be able to
+  // pick the block_size they want, but for now only 4 KiB is supported.
+
+  // The block size used for all the operations in the manifest.
+  size_t block_size = 4096;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_PAYLOAD_GENERATION_CONFIG_H_
diff --git a/payload_generator/payload_generation_config_unittest.cc b/payload_generator/payload_generation_config_unittest.cc
new file mode 100644
index 0000000..122d94a
--- /dev/null
+++ b/payload_generator/payload_generation_config_unittest.cc
@@ -0,0 +1,52 @@
+//
+// 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/payload_generation_config.h"
+
+#include <gtest/gtest.h>
+
+namespace chromeos_update_engine {
+
+class PayloadGenerationConfigTest : public ::testing::Test {};
+
+TEST_F(PayloadGenerationConfigTest, SimpleLoadPostInstallConfigTest) {
+  ImageConfig image_config;
+  image_config.partitions.emplace_back("root");
+  brillo::KeyValueStore store;
+  EXPECT_TRUE(
+      store.LoadFromString("RUN_POSTINSTALL_root=true\n"
+                           "POSTINSTALL_PATH_root=postinstall\n"
+                           "FILESYSTEM_TYPE_root=ext4"));
+  EXPECT_TRUE(image_config.LoadPostInstallConfig(store));
+  EXPECT_FALSE(image_config.partitions[0].postinstall.IsEmpty());
+  EXPECT_EQ(true, image_config.partitions[0].postinstall.run);
+  EXPECT_EQ("postinstall", image_config.partitions[0].postinstall.path);
+  EXPECT_EQ("ext4", image_config.partitions[0].postinstall.filesystem_type);
+}
+
+TEST_F(PayloadGenerationConfigTest, LoadPostInstallConfigNameMismatchTest) {
+  ImageConfig image_config;
+  image_config.partitions.emplace_back("system");
+  brillo::KeyValueStore store;
+  EXPECT_TRUE(
+      store.LoadFromString("RUN_POSTINSTALL_root=true\n"
+                           "POSTINSTALL_PATH_root=postinstall\n"
+                           "FILESYSTEM_TYPE_root=ext4"));
+  EXPECT_FALSE(image_config.LoadPostInstallConfig(store));
+  EXPECT_TRUE(image_config.partitions[0].postinstall.IsEmpty());
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/payload_signer.cc b/payload_generator/payload_signer.cc
new file mode 100644
index 0000000..a0c61b1
--- /dev/null
+++ b/payload_generator/payload_signer.cc
@@ -0,0 +1,484 @@
+//
+// 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/payload_generator/payload_signer.h"
+
+#include <endian.h>
+
+#include <base/logging.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <brillo/data_encoding.h>
+#include <openssl/pem.h>
+
+#include "update_engine/common/hash_calculator.h"
+#include "update_engine/common/subprocess.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/delta_performer.h"
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_consumer/payload_verifier.h"
+#include "update_engine/payload_generator/delta_diff_generator.h"
+#include "update_engine/payload_generator/payload_file.h"
+#include "update_engine/update_metadata.pb.h"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+// The payload verifier will check all the signatures included in the payload
+// regardless of the version field. Old version of the verifier require the
+// version field to be included and be 1.
+const uint32_t kSignatureMessageLegacyVersion = 1;
+
+// Given raw |signatures|, packs them into a protobuf and serializes it into a
+// binary blob. Returns true on success, false otherwise.
+bool ConvertSignatureToProtobufBlob(const vector<brillo::Blob>& signatures,
+                                    brillo::Blob* out_signature_blob) {
+  // Pack it into a protobuf
+  Signatures out_message;
+  for (const brillo::Blob& signature : signatures) {
+    Signatures_Signature* sig_message = out_message.add_signatures();
+    // Set all the signatures with the same version number.
+    sig_message->set_version(kSignatureMessageLegacyVersion);
+    sig_message->set_data(signature.data(), signature.size());
+  }
+
+  // Serialize protobuf
+  string serialized;
+  TEST_AND_RETURN_FALSE(out_message.AppendToString(&serialized));
+  out_signature_blob->insert(out_signature_blob->end(),
+                             serialized.begin(),
+                             serialized.end());
+  LOG(INFO) << "Signature blob size: " << out_signature_blob->size();
+  return true;
+}
+
+// Given an unsigned payload under |payload_path| and the |signature_blob| and
+// |metadata_signature_blob| generates an updated payload that includes the
+// signatures. It populates |out_metadata_size| with the size of the final
+// 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_blob|
+// if the payload major version supports metadata signature, 0 otherwise.
+// Returns true on success, false otherwise.
+bool AddSignatureBlobToPayload(const string& payload_path,
+                               const brillo::Blob& signature_blob,
+                               const brillo::Blob& metadata_signature_blob,
+                               brillo::Blob* out_payload,
+                               uint64_t* out_metadata_size,
+                               uint32_t* out_metadata_signature_size,
+                               uint64_t* out_signatures_offset) {
+  uint64_t manifest_offset = 20;
+  const int kProtobufSizeOffset = 12;
+
+  // Loads the payload.
+  brillo::Blob payload;
+  DeltaArchiveManifest manifest;
+  uint64_t metadata_size, major_version;
+  uint32_t metadata_signature_size;
+  TEST_AND_RETURN_FALSE(PayloadSigner::LoadPayload(payload_path, &payload,
+      &manifest, &major_version, &metadata_size, &metadata_signature_size));
+
+  if (major_version == kBrilloMajorPayloadVersion) {
+    // Write metadata signature size in header.
+    uint32_t metadata_signature_size_be =
+        htobe32(metadata_signature_blob.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_blob.begin(),
+                   metadata_signature_blob.end());
+    metadata_signature_size = metadata_signature_blob.size();
+    LOG(INFO) << "Metadata signature size: " << metadata_signature_size;
+  }
+
+  // Is there already a signature op in place?
+  if (manifest.has_signatures_size()) {
+    // The signature op is tied to the size of the signature blob, but not it's
+    // contents. We don't allow the manifest to change if there is already an op
+    // present, because that might invalidate previously generated
+    // hashes/signatures.
+    if (manifest.signatures_size() != signature_blob.size()) {
+      LOG(ERROR) << "Attempt to insert different signature sized blob. "
+                 << "(current:" << manifest.signatures_size()
+                 << "new:" << signature_blob.size() << ")";
+      return false;
+    }
+
+    LOG(INFO) << "Matching signature sizes already present.";
+  } else {
+    // Updates the manifest to include the signature operation.
+    PayloadSigner::AddSignatureToManifest(
+        payload.size() - metadata_size - metadata_signature_size,
+        signature_blob.size(),
+        major_version == kChromeOSMajorPayloadVersion,
+        &manifest);
+
+    // Updates the payload to include the new manifest.
+    string serialized_manifest;
+    TEST_AND_RETURN_FALSE(manifest.AppendToString(&serialized_manifest));
+    LOG(INFO) << "Updated protobuf size: " << serialized_manifest.size();
+    payload.erase(payload.begin() + manifest_offset,
+                  payload.begin() + metadata_size);
+    payload.insert(payload.begin() + manifest_offset,
+                   serialized_manifest.begin(),
+                   serialized_manifest.end());
+
+    // Updates the protobuf size.
+    uint64_t size_be = htobe64(serialized_manifest.size());
+    memcpy(&payload[kProtobufSizeOffset], &size_be, sizeof(size_be));
+    metadata_size = serialized_manifest.size() + manifest_offset;
+
+    LOG(INFO) << "Updated payload size: " << payload.size();
+    LOG(INFO) << "Updated metadata size: " << metadata_size;
+  }
+  uint64_t signatures_offset = metadata_size + metadata_signature_size +
+                               manifest.signatures_offset();
+  LOG(INFO) << "Signature Blob Offset: " << signatures_offset;
+  payload.resize(signatures_offset);
+  payload.insert(payload.begin() + signatures_offset,
+                 signature_blob.begin(),
+                 signature_blob.end());
+
+  *out_payload = std::move(payload);
+  *out_metadata_size = metadata_size;
+  *out_metadata_signature_size = metadata_signature_size;
+  *out_signatures_offset = signatures_offset;
+  return true;
+}
+
+// Given a |payload| with correct signature op and metadata signature size in
+// header and |metadata_size|, |metadata_signature_size|, |signatures_offset|,
+// calculate hash for payload and metadata, save it to |out_hash_data| and
+// |out_metadata_hash|.
+bool CalculateHashFromPayload(const brillo::Blob& payload,
+                              const uint64_t metadata_size,
+                              const uint32_t metadata_signature_size,
+                              const uint64_t signatures_offset,
+                              brillo::Blob* out_hash_data,
+                              brillo::Blob* out_metadata_hash) {
+  if (out_metadata_hash) {
+    // Calculates the hash on the manifest.
+    TEST_AND_RETURN_FALSE(
+        HashCalculator::RawHashOfBytes(payload.data(), metadata_size,
+                                       out_metadata_hash));
+  }
+  if (out_hash_data) {
+    // Calculates the hash on the updated payload. Note that we skip metadata
+    // signature and payload signature.
+    HashCalculator calc;
+    TEST_AND_RETURN_FALSE(calc.Update(payload.data(), metadata_size));
+    TEST_AND_RETURN_FALSE(calc.Update(
+        payload.data() + metadata_size + metadata_signature_size,
+        signatures_offset - metadata_size - metadata_signature_size));
+    TEST_AND_RETURN_FALSE(calc.Finalize());
+    *out_hash_data = calc.raw_hash();
+  }
+  return true;
+}
+
+}  // namespace
+
+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((signature_blob_length + kBlockSize - 1) /
+                                 kBlockSize);
+  }
+}
+
+bool PayloadSigner::LoadPayload(const string& payload_path,
+                                brillo::Blob* out_payload,
+                                DeltaArchiveManifest* out_manifest,
+                                uint64_t* out_major_version,
+                                uint64_t* out_metadata_size,
+                                uint32_t* out_metadata_signature_size) {
+  brillo::Blob payload;
+  TEST_AND_RETURN_FALSE(utils::ReadFile(payload_path, &payload));
+  TEST_AND_RETURN_FALSE(payload.size() >=
+                        DeltaPerformer::kMaxPayloadHeaderSize);
+  const uint8_t* read_pointer = payload.data();
+  TEST_AND_RETURN_FALSE(
+      memcmp(read_pointer, kDeltaMagic, sizeof(kDeltaMagic)) == 0);
+  read_pointer += sizeof(kDeltaMagic);
+
+  uint64_t major_version;
+  memcpy(&major_version, read_pointer, sizeof(major_version));
+  read_pointer += sizeof(major_version);
+  major_version = be64toh(major_version);
+  TEST_AND_RETURN_FALSE(major_version == kChromeOSMajorPayloadVersion ||
+                        major_version == kBrilloMajorPayloadVersion);
+  if (out_major_version)
+    *out_major_version = major_version;
+
+  uint64_t manifest_size = 0;
+  memcpy(&manifest_size, read_pointer, sizeof(manifest_size));
+  read_pointer += sizeof(manifest_size);
+  manifest_size = be64toh(manifest_size);
+
+  uint32_t metadata_signature_size = 0;
+  if (major_version == kBrilloMajorPayloadVersion) {
+    memcpy(&metadata_signature_size, read_pointer,
+           sizeof(metadata_signature_size));
+    read_pointer += sizeof(metadata_signature_size);
+    metadata_signature_size = be32toh(metadata_signature_size);
+  }
+  if (out_metadata_signature_size)
+    *out_metadata_signature_size = metadata_signature_size;
+
+  *out_metadata_size = read_pointer - payload.data() + manifest_size;
+  TEST_AND_RETURN_FALSE(payload.size() >= *out_metadata_size);
+  if (out_manifest)
+    TEST_AND_RETURN_FALSE(
+        out_manifest->ParseFromArray(read_pointer, manifest_size));
+  *out_payload = std::move(payload);
+  return true;
+}
+
+bool PayloadSigner::VerifySignedPayload(const string& payload_path,
+                                        const string& public_key_path) {
+  brillo::Blob payload;
+  DeltaArchiveManifest manifest;
+  uint64_t metadata_size;
+  uint32_t metadata_signature_size;
+  TEST_AND_RETURN_FALSE(LoadPayload(payload_path, &payload, &manifest, nullptr,
+      &metadata_size, &metadata_signature_size));
+  TEST_AND_RETURN_FALSE(manifest.has_signatures_offset() &&
+                        manifest.has_signatures_size());
+  uint64_t signatures_offset = metadata_size + metadata_signature_size +
+                               manifest.signatures_offset();
+  CHECK_EQ(payload.size(), signatures_offset + manifest.signatures_size());
+  brillo::Blob payload_hash, metadata_hash;
+  TEST_AND_RETURN_FALSE(CalculateHashFromPayload(payload,
+                                                 metadata_size,
+                                                 metadata_signature_size,
+                                                 signatures_offset,
+                                                 &payload_hash,
+                                                 &metadata_hash));
+  brillo::Blob signature_blob(payload.begin() + signatures_offset,
+                              payload.end());
+  TEST_AND_RETURN_FALSE(PayloadVerifier::PadRSA2048SHA256Hash(&payload_hash));
+  TEST_AND_RETURN_FALSE(PayloadVerifier::VerifySignature(
+      signature_blob, public_key_path, payload_hash));
+  if (metadata_signature_size) {
+    signature_blob.assign(payload.begin() + metadata_size,
+                          payload.begin() + metadata_size +
+                          metadata_signature_size);
+    TEST_AND_RETURN_FALSE(
+        PayloadVerifier::PadRSA2048SHA256Hash(&metadata_hash));
+    TEST_AND_RETURN_FALSE(PayloadVerifier::VerifySignature(
+        signature_blob, public_key_path, metadata_hash));
+  }
+  return true;
+}
+
+bool PayloadSigner::SignHash(const brillo::Blob& hash,
+                             const string& private_key_path,
+                             brillo::Blob* out_signature) {
+  LOG(INFO) << "Signing hash with private key: " << private_key_path;
+  string sig_path;
+  TEST_AND_RETURN_FALSE(
+      utils::MakeTempFile("signature.XXXXXX", &sig_path, nullptr));
+  ScopedPathUnlinker sig_path_unlinker(sig_path);
+
+  string hash_path;
+  TEST_AND_RETURN_FALSE(
+      utils::MakeTempFile("hash.XXXXXX", &hash_path, nullptr));
+  ScopedPathUnlinker hash_path_unlinker(hash_path);
+  // We expect unpadded SHA256 hash coming in
+  TEST_AND_RETURN_FALSE(hash.size() == 32);
+  brillo::Blob padded_hash(hash);
+  PayloadVerifier::PadRSA2048SHA256Hash(&padded_hash);
+  TEST_AND_RETURN_FALSE(utils::WriteFile(hash_path.c_str(),
+                                         padded_hash.data(),
+                                         padded_hash.size()));
+
+  // This runs on the server, so it's okay to copy out and call openssl
+  // executable rather than properly use the library.
+  vector<string> cmd = {"openssl", "rsautl", "-raw", "-sign", "-inkey",
+                        private_key_path, "-in", hash_path, "-out", sig_path};
+  int return_code = 0;
+  TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &return_code,
+                                                    nullptr));
+  TEST_AND_RETURN_FALSE(return_code == 0);
+
+  brillo::Blob signature;
+  TEST_AND_RETURN_FALSE(utils::ReadFile(sig_path, &signature));
+  out_signature->swap(signature);
+  return true;
+}
+
+bool PayloadSigner::SignHashWithKeys(const brillo::Blob& hash_data,
+                                     const vector<string>& private_key_paths,
+                                     brillo::Blob* out_signature_blob) {
+  vector<brillo::Blob> signatures;
+  for (const string& path : private_key_paths) {
+    brillo::Blob signature;
+    TEST_AND_RETURN_FALSE(SignHash(hash_data, path, &signature));
+    signatures.push_back(signature);
+  }
+  TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(signatures,
+                                                       out_signature_blob));
+  return true;
+}
+
+bool PayloadSigner::SignPayload(const string& unsigned_payload_path,
+                                const vector<string>& private_key_paths,
+                                const uint64_t metadata_size,
+                                const uint32_t metadata_signature_size,
+                                const uint64_t signatures_offset,
+                                brillo::Blob* out_signature_blob) {
+  brillo::Blob payload;
+  TEST_AND_RETURN_FALSE(utils::ReadFile(unsigned_payload_path, &payload));
+  brillo::Blob hash_data;
+  TEST_AND_RETURN_FALSE(CalculateHashFromPayload(payload,
+                                                 metadata_size,
+                                                 metadata_signature_size,
+                                                 signatures_offset,
+                                                 &hash_data,
+                                                 nullptr));
+  TEST_AND_RETURN_FALSE(SignHashWithKeys(hash_data,
+                                         private_key_paths,
+                                         out_signature_blob));
+  return true;
+}
+
+bool PayloadSigner::SignatureBlobLength(const vector<string>& private_key_paths,
+                                        uint64_t* out_length) {
+  DCHECK(out_length);
+  brillo::Blob x_blob(1, 'x'), hash_blob, sig_blob;
+  TEST_AND_RETURN_FALSE(HashCalculator::RawHashOfBytes(x_blob.data(),
+                                                       x_blob.size(),
+                                                       &hash_blob));
+  TEST_AND_RETURN_FALSE(
+      SignHashWithKeys(hash_blob, private_key_paths, &sig_blob));
+  *out_length = sig_blob.size();
+  return true;
+}
+
+bool PayloadSigner::HashPayloadForSigning(const string& payload_path,
+                                          const vector<int>& signature_sizes,
+                                          brillo::Blob* out_payload_hash_data,
+                                          brillo::Blob* out_metadata_hash) {
+  // Create a signature blob with signatures filled with 0.
+  // Will be used for both payload signature and metadata signature.
+  vector<brillo::Blob> signatures;
+  for (int signature_size : signature_sizes) {
+    signatures.emplace_back(signature_size, 0);
+  }
+  brillo::Blob signature_blob;
+  TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(signatures,
+                                                       &signature_blob));
+
+  brillo::Blob payload;
+  uint64_t metadata_size, signatures_offset;
+  uint32_t metadata_signature_size;
+  // Prepare payload for hashing.
+  TEST_AND_RETURN_FALSE(AddSignatureBlobToPayload(payload_path,
+                                                  signature_blob,
+                                                  signature_blob,
+                                                  &payload,
+                                                  &metadata_size,
+                                                  &metadata_signature_size,
+                                                  &signatures_offset));
+  TEST_AND_RETURN_FALSE(CalculateHashFromPayload(payload,
+                                                 metadata_size,
+                                                 metadata_signature_size,
+                                                 signatures_offset,
+                                                 out_payload_hash_data,
+                                                 out_metadata_hash));
+  return true;
+}
+
+bool PayloadSigner::AddSignatureToPayload(
+    const string& payload_path,
+    const vector<brillo::Blob>& payload_signatures,
+    const vector<brillo::Blob>& metadata_signatures,
+    const string& signed_payload_path,
+    uint64_t *out_metadata_size) {
+  // TODO(petkov): Reduce memory usage -- the payload is manipulated in memory.
+
+  // Loads the payload and adds the signature op to it.
+  brillo::Blob signature_blob, metadata_signature_blob;
+  TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(payload_signatures,
+                                                       &signature_blob));
+  if (!metadata_signatures.empty()) {
+    TEST_AND_RETURN_FALSE(
+        ConvertSignatureToProtobufBlob(metadata_signatures,
+                                       &metadata_signature_blob));
+  }
+  brillo::Blob payload;
+  uint64_t signatures_offset;
+  uint32_t metadata_signature_size;
+  TEST_AND_RETURN_FALSE(AddSignatureBlobToPayload(payload_path,
+                                                  signature_blob,
+                                                  metadata_signature_blob,
+                                                  &payload,
+                                                  out_metadata_size,
+                                                  &metadata_signature_size,
+                                                  &signatures_offset));
+
+  LOG(INFO) << "Signed payload size: " << payload.size();
+  TEST_AND_RETURN_FALSE(utils::WriteFile(signed_payload_path.c_str(),
+                                         payload.data(),
+                                         payload.size()));
+  return true;
+}
+
+bool PayloadSigner::GetMetadataSignature(const void* const metadata,
+                                         size_t metadata_size,
+                                         const string& private_key_path,
+                                         string* out_signature) {
+  // Calculates the hash on the updated payload. Note that the payload includes
+  // the signature op but doesn't include the signature blob at the end.
+  brillo::Blob metadata_hash;
+  TEST_AND_RETURN_FALSE(HashCalculator::RawHashOfBytes(metadata,
+                                                       metadata_size,
+                                                       &metadata_hash));
+
+  brillo::Blob signature;
+  TEST_AND_RETURN_FALSE(SignHash(metadata_hash,
+                                 private_key_path,
+                                 &signature));
+
+  *out_signature = brillo::data_encoding::Base64Encode(signature);
+  return true;
+}
+
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/payload_signer.h b/payload_generator/payload_signer.h
new file mode 100644
index 0000000..e7351dd
--- /dev/null
+++ b/payload_generator/payload_signer.h
@@ -0,0 +1,142 @@
+//
+// 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_PAYLOAD_SIGNER_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_PAYLOAD_SIGNER_H_
+
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <brillo/secure_blob.h>
+
+#include "update_engine/update_metadata.pb.h"
+
+// This class encapsulates methods used for payload signing.
+// See update_metadata.proto for more info.
+
+namespace chromeos_update_engine {
+
+class PayloadSigner {
+ public:
+  // Reads the payload from the given |payload_path| into the |out_payload|
+  // vector. It also parses the manifest protobuf in the payload and returns it
+  // in |out_manifest| if not null, along with the major version of the payload
+  // in |out_major_version| if not null, the size of the entire metadata in
+  // |out_metadata_size| and the size of metadata signature in
+  // |out_metadata_signature_size| if not null.
+  static bool LoadPayload(const std::string& payload_path,
+                          brillo::Blob* out_payload,
+                          DeltaArchiveManifest* out_manifest,
+                          uint64_t* out_major_version,
+                          uint64_t* out_metadata_size,
+                          uint32_t* out_metadata_signature_size);
+
+  // Returns true if the payload in |payload_path| is signed and its hash can be
+  // verified using the public key in |public_key_path| with the signature
+  // of a given version in the signature blob. Returns false otherwise.
+  static bool VerifySignedPayload(const std::string& payload_path,
+                                  const std::string& public_key_path);
+
+  // 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
+  // raw signature in |out_signature|. Returns true on success, false otherwise.
+  static bool SignHash(const brillo::Blob& hash,
+                       const std::string& private_key_path,
+                       brillo::Blob* out_signature);
+
+  // Sign |hash_data| blob with all private keys in |private_key_paths|, then
+  // convert the signatures to protobuf blob.
+  static bool SignHashWithKeys(
+      const brillo::Blob& hash_data,
+      const std::vector<std::string>& private_key_paths,
+      brillo::Blob* out_signature_blob);
+
+  // Given an unsigned payload in |unsigned_payload_path|, private keys in
+  // |private_key_path|, metadata size in |metadata_size|, metadata signature
+  // size in |metadata_signature_size| and signatures offset in
+  // |signatures_offset|, calculates the payload signature blob into
+  // |out_signature_blob|. Note that the payload must already have an
+  // 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,
+                          const uint64_t metadata_size,
+                          const uint32_t metadata_signature_size,
+                          const uint64_t signatures_offset,
+                          brillo::Blob* out_signature_blob);
+
+  // Returns the length of out_signature_blob that will result in a call
+  // to SignPayload with the given private keys. Returns true on success.
+  static bool SignatureBlobLength(
+      const std::vector<std::string>& private_key_paths,
+      uint64_t* out_length);
+
+  // Given an unsigned payload in |payload_path|,
+  // this method does two things:
+  // 1. It loads the payload into memory, and inserts placeholder signature
+  //    operations and placeholder metadata signature to make the header and
+  //    the manifest match what the final signed payload will look like based
+  //    on |signatures_sizes|, if needed.
+  // 2. It calculates the raw SHA256 hash of the payload and the metadata in
+  //    |payload_path| (except signatures) and returns the result in
+  //    |out_hash_data| and |out_metadata_hash| respectively.
+  //
+  // The changes to payload are not preserved or written to disk.
+  static bool HashPayloadForSigning(const std::string& payload_path,
+                                    const std::vector<int>& signature_sizes,
+                                    brillo::Blob* out_payload_hash_data,
+                                    brillo::Blob* out_metadata_hash);
+
+  // 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
+  // |signed_payload_path| can point to the same file. Populates
+  // |out_metadata_size| with the size of the metadata after adding the
+  // signature operation in the manifest. Returns true on success, false
+  // otherwise.
+  static bool AddSignatureToPayload(
+      const std::string& payload_path,
+      const std::vector<brillo::Blob>& payload_signatures,
+      const std::vector<brillo::Blob>& metadata_signatures,
+      const std::string& signed_payload_path,
+      uint64_t* out_metadata_size);
+
+  // Computes the SHA256 hash of the first metadata_size bytes of |metadata|
+  // and signs the hash with the given private_key_path and writes the signed
+  // hash in |out_signature|. Returns true if successful or false if there was
+  // any error in the computations.
+  static bool GetMetadataSignature(const void* const metadata,
+                                   size_t metadata_size,
+                                   const std::string& private_key_path,
+                                   std::string* out_signature);
+
+ private:
+  // This should never be constructed
+  DISALLOW_IMPLICIT_CONSTRUCTORS(PayloadSigner);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_PAYLOAD_SIGNER_H_
diff --git a/payload_generator/payload_signer_unittest.cc b/payload_generator/payload_signer_unittest.cc
new file mode 100644
index 0000000..b302f52
--- /dev/null
+++ b/payload_generator/payload_signer_unittest.cc
@@ -0,0 +1,245 @@
+//
+// 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/payload_signer.h"
+
+#include <string>
+#include <vector>
+
+#include <base/logging.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/hash_calculator.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_consumer/payload_verifier.h"
+#include "update_engine/payload_generator/payload_file.h"
+#include "update_engine/update_metadata.pb.h"
+
+using std::string;
+using std::vector;
+
+// Note: the test key was generated with the following command:
+// openssl genrsa -out unittest_key.pem 2048
+// The public-key version is created by the build system.
+
+namespace chromeos_update_engine {
+
+const char* kUnittestPrivateKeyPath = "unittest_key.pem";
+const char* kUnittestPublicKeyPath = "unittest_key.pub.pem";
+const char* kUnittestPrivateKey2Path = "unittest_key2.pem";
+const char* kUnittestPublicKey2Path = "unittest_key2.pub.pem";
+
+// Some data and its corresponding hash and signature:
+const char kDataToSign[] = "This is some data to sign.";
+
+// Generated by:
+// echo -n 'This is some data to sign.' | openssl dgst -sha256 -binary |
+//   hexdump -v -e '" " 8/1 "0x%02x, " "\n"'
+const uint8_t kDataHash[] = {
+  0x7a, 0x07, 0xa6, 0x44, 0x08, 0x86, 0x20, 0xa6,
+  0xc1, 0xf8, 0xd9, 0x02, 0x05, 0x63, 0x0d, 0xb7,
+  0xfc, 0x2b, 0xa0, 0xa9, 0x7c, 0x9d, 0x1d, 0x8c,
+  0x01, 0xf5, 0x78, 0x6d, 0xc5, 0x11, 0xb4, 0x06
+};
+
+// Generated with openssl 1.0, which at the time of this writing, you need
+// to download and install yourself. Here's my command:
+// echo -n 'This is some data to sign.' | openssl dgst -sha256 -binary |
+//    ~/local/bin/openssl pkeyutl -sign -inkey unittest_key.pem -pkeyopt
+//    digest:sha256 | hexdump -v -e '" " 8/1 "0x%02x, " "\n"'
+const uint8_t kDataSignature[] = {
+  0x9f, 0x86, 0x25, 0x8b, 0xf3, 0xcc, 0xe3, 0x95,
+  0x5f, 0x45, 0x83, 0xb2, 0x66, 0xf0, 0x2a, 0xcf,
+  0xb7, 0xaa, 0x52, 0x25, 0x7a, 0xdd, 0x9d, 0x65,
+  0xe5, 0xd6, 0x02, 0x4b, 0x37, 0x99, 0x53, 0x06,
+  0xc2, 0xc9, 0x37, 0x36, 0x25, 0x62, 0x09, 0x4f,
+  0x6b, 0x22, 0xf8, 0xb3, 0x89, 0x14, 0x98, 0x1a,
+  0xbc, 0x30, 0x90, 0x4a, 0x43, 0xf5, 0xea, 0x2e,
+  0xf0, 0xa4, 0xba, 0xc3, 0xa7, 0xa3, 0x44, 0x70,
+  0xd6, 0xc4, 0x89, 0xd8, 0x45, 0x71, 0xbb, 0xee,
+  0x59, 0x87, 0x3d, 0xd5, 0xe5, 0x40, 0x22, 0x3d,
+  0x73, 0x7e, 0x2a, 0x58, 0x93, 0x8e, 0xcb, 0x9c,
+  0xf2, 0xbb, 0x4a, 0xc9, 0xd2, 0x2c, 0x52, 0x42,
+  0xb0, 0xd1, 0x13, 0x22, 0xa4, 0x78, 0xc7, 0xc6,
+  0x3e, 0xf1, 0xdc, 0x4c, 0x7b, 0x2d, 0x40, 0xda,
+  0x58, 0xac, 0x4a, 0x11, 0x96, 0x3d, 0xa0, 0x01,
+  0xf6, 0x96, 0x74, 0xf6, 0x6c, 0x0c, 0x49, 0x69,
+  0x4e, 0xc1, 0x7e, 0x9f, 0x2a, 0x42, 0xdd, 0x15,
+  0x6b, 0x37, 0x2e, 0x3a, 0xa7, 0xa7, 0x6d, 0x91,
+  0x13, 0xe8, 0x59, 0xde, 0xfe, 0x99, 0x07, 0xd9,
+  0x34, 0x0f, 0x17, 0xb3, 0x05, 0x4c, 0xd2, 0xc6,
+  0x82, 0xb7, 0x38, 0x36, 0x63, 0x1d, 0x9e, 0x21,
+  0xa6, 0x32, 0xef, 0xf1, 0x65, 0xe6, 0xed, 0x95,
+  0x25, 0x9b, 0x61, 0xe0, 0xba, 0x86, 0xa1, 0x7f,
+  0xf8, 0xa5, 0x4a, 0x32, 0x1f, 0x15, 0x20, 0x8a,
+  0x41, 0xc5, 0xb0, 0xd9, 0x4a, 0xda, 0x85, 0xf3,
+  0xdc, 0xa0, 0x98, 0x5d, 0x1d, 0x18, 0x9d, 0x2e,
+  0x42, 0xea, 0x69, 0x13, 0x74, 0x3c, 0x74, 0xf7,
+  0x6d, 0x43, 0xb0, 0x63, 0x90, 0xdb, 0x04, 0xd5,
+  0x05, 0xc9, 0x73, 0x1f, 0x6c, 0xd6, 0xfa, 0x46,
+  0x4e, 0x0f, 0x33, 0x58, 0x5b, 0x0d, 0x1b, 0x55,
+  0x39, 0xb9, 0x0f, 0x43, 0x37, 0xc0, 0x06, 0x0c,
+  0x29, 0x93, 0x43, 0xc7, 0x43, 0xb9, 0xab, 0x7d
+};
+
+namespace {
+void SignSampleData(brillo::Blob* out_signature_blob,
+                    const vector<string>& private_keys) {
+  brillo::Blob data_blob(std::begin(kDataToSign),
+                         std::begin(kDataToSign) + strlen(kDataToSign));
+  uint64_t length = 0;
+  EXPECT_TRUE(PayloadSigner::SignatureBlobLength(private_keys, &length));
+  EXPECT_GT(length, 0);
+  brillo::Blob hash_blob;
+  EXPECT_TRUE(HashCalculator::RawHashOfBytes(data_blob.data(),
+                                             data_blob.size(),
+                                             &hash_blob));
+  EXPECT_TRUE(PayloadSigner::SignHashWithKeys(
+      hash_blob,
+      private_keys,
+      out_signature_blob));
+  EXPECT_EQ(length, out_signature_blob->size());
+}
+}  // namespace
+
+class PayloadSignerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    PayloadVerifier::PadRSA2048SHA256Hash(&padded_hash_data_);
+  }
+
+  void DoWriteAndLoadPayloadTest(const PayloadGenerationConfig& config) {
+    PayloadFile payload;
+    payload.Init(config);
+    string payload_path;
+    EXPECT_TRUE(utils::MakeTempFile("payload.XXXXXX", &payload_path, nullptr));
+    ScopedPathUnlinker payload_path_unlinker(payload_path);
+    uint64_t metadata_size;
+    EXPECT_TRUE(
+        payload.WritePayload(payload_path, "/dev/null", "", &metadata_size));
+    brillo::Blob payload_blob;
+    DeltaArchiveManifest manifest;
+    uint64_t load_metadata_size, load_major_version;
+    EXPECT_TRUE(PayloadSigner::LoadPayload(payload_path, &payload_blob,
+        &manifest, &load_major_version, &load_metadata_size, nullptr));
+    EXPECT_EQ(utils::FileSize(payload_path), payload_blob.size());
+    EXPECT_EQ(config.major_version, load_major_version);
+    EXPECT_EQ(metadata_size, load_metadata_size);
+  }
+
+  brillo::Blob padded_hash_data_{std::begin(kDataHash), std::end(kDataHash)};
+};
+
+TEST_F(PayloadSignerTest, LoadPayloadV1Test) {
+  PayloadGenerationConfig config;
+  config.major_version = kChromeOSMajorPayloadVersion;
+  DoWriteAndLoadPayloadTest(config);
+}
+
+TEST_F(PayloadSignerTest, LoadPayloadV2Test) {
+  PayloadGenerationConfig config;
+  config.major_version = kBrilloMajorPayloadVersion;
+  DoWriteAndLoadPayloadTest(config);
+}
+
+TEST_F(PayloadSignerTest, SignSimpleTextTest) {
+  brillo::Blob signature_blob;
+  SignSampleData(&signature_blob, {kUnittestPrivateKeyPath});
+
+  // Check the signature itself
+  Signatures signatures;
+  EXPECT_TRUE(signatures.ParseFromArray(signature_blob.data(),
+                                        signature_blob.size()));
+  EXPECT_EQ(1, signatures.signatures_size());
+  const Signatures_Signature& signature = signatures.signatures(0);
+  EXPECT_EQ(1, signature.version());
+  const string sig_data = signature.data();
+  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]));
+  }
+}
+
+TEST_F(PayloadSignerTest, VerifyAllSignatureTest) {
+  brillo::Blob signature_blob;
+  SignSampleData(&signature_blob,
+                 {kUnittestPrivateKeyPath, kUnittestPrivateKey2Path});
+
+  // Either public key should pass the verification.
+  EXPECT_TRUE(PayloadVerifier::VerifySignature(signature_blob,
+                                               kUnittestPublicKeyPath,
+                                               padded_hash_data_));
+  EXPECT_TRUE(PayloadVerifier::VerifySignature(signature_blob,
+                                               kUnittestPublicKey2Path,
+                                               padded_hash_data_));
+}
+
+TEST_F(PayloadSignerTest, VerifySignatureTest) {
+  brillo::Blob signature_blob;
+  SignSampleData(&signature_blob, {kUnittestPrivateKeyPath});
+
+  EXPECT_TRUE(PayloadVerifier::VerifySignature(signature_blob,
+                                               kUnittestPublicKeyPath,
+                                               padded_hash_data_));
+  // Passing the invalid key should fail the verification.
+  EXPECT_FALSE(PayloadVerifier::VerifySignature(signature_blob,
+                                                kUnittestPublicKey2Path,
+                                                padded_hash_data_));
+}
+
+TEST_F(PayloadSignerTest, SkipMetadataSignatureTest) {
+  string payload_path;
+  EXPECT_TRUE(utils::MakeTempFile("payload.XXXXXX", &payload_path, nullptr));
+  ScopedPathUnlinker payload_path_unlinker(payload_path);
+
+  PayloadGenerationConfig config;
+  config.major_version = kBrilloMajorPayloadVersion;
+  PayloadFile payload;
+  EXPECT_TRUE(payload.Init(config));
+  uint64_t metadata_size;
+  EXPECT_TRUE(
+      payload.WritePayload(payload_path, "/dev/null", "", &metadata_size));
+  const vector<int> sizes = {256};
+  brillo::Blob unsigned_payload_hash, unsigned_metadata_hash;
+  EXPECT_TRUE(PayloadSigner::HashPayloadForSigning(
+      payload_path, sizes, &unsigned_payload_hash, &unsigned_metadata_hash));
+  EXPECT_TRUE(payload.WritePayload(
+      payload_path, "/dev/null", kUnittestPrivateKeyPath, &metadata_size));
+  brillo::Blob signed_payload_hash, signed_metadata_hash;
+  EXPECT_TRUE(PayloadSigner::HashPayloadForSigning(
+      payload_path, sizes, &signed_payload_hash, &signed_metadata_hash));
+  EXPECT_EQ(unsigned_payload_hash, signed_payload_hash);
+  EXPECT_EQ(unsigned_metadata_hash, signed_metadata_hash);
+}
+
+TEST_F(PayloadSignerTest, VerifySignedPayloadTest) {
+  string payload_path;
+  EXPECT_TRUE(utils::MakeTempFile("payload.XXXXXX", &payload_path, nullptr));
+  ScopedPathUnlinker payload_path_unlinker(payload_path);
+
+  PayloadGenerationConfig config;
+  config.major_version = kBrilloMajorPayloadVersion;
+  PayloadFile payload;
+  EXPECT_TRUE(payload.Init(config));
+  uint64_t metadata_size;
+  EXPECT_TRUE(payload.WritePayload(
+      payload_path, "/dev/null", kUnittestPrivateKeyPath, &metadata_size));
+  EXPECT_TRUE(PayloadSigner::VerifySignedPayload(payload_path,
+                                                 kUnittestPublicKeyPath));
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/raw_filesystem.cc b/payload_generator/raw_filesystem.cc
new file mode 100644
index 0000000..2fb1400
--- /dev/null
+++ b/payload_generator/raw_filesystem.cc
@@ -0,0 +1,53 @@
+//
+// 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/raw_filesystem.h"
+
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_generator/extent_ranges.h"
+#include "update_engine/update_metadata.pb.h"
+
+using std::unique_ptr;
+
+namespace chromeos_update_engine {
+
+unique_ptr<RawFilesystem> RawFilesystem::Create(
+      const std::string& filename, uint64_t block_size, uint64_t block_count) {
+  unique_ptr<RawFilesystem> result(new RawFilesystem());
+  result->filename_ = filename;
+  result->block_size_ = block_size;
+  result->block_count_ = block_count;
+  return result;
+}
+
+size_t RawFilesystem::GetBlockSize() const {
+  return block_size_;
+}
+
+size_t RawFilesystem::GetBlockCount() const {
+  return block_count_;
+}
+
+bool RawFilesystem::GetFiles(std::vector<File>* files) const {
+  files->clear();
+  File file;
+  file.name = filename_;
+  file.extents = { ExtentForRange(0, block_count_) };
+  files->push_back(file);
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/raw_filesystem.h b/payload_generator/raw_filesystem.h
new file mode 100644
index 0000000..0aecd81
--- /dev/null
+++ b/payload_generator/raw_filesystem.h
@@ -0,0 +1,60 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_PAYLOAD_GENERATOR_RAW_FILESYSTEM_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_RAW_FILESYSTEM_H_
+
+// A simple filesystem interface implementation used for unknown filesystem
+// format such as the kernel.
+
+#include "update_engine/payload_generator/filesystem_interface.h"
+
+#include <string>
+#include <vector>
+
+namespace chromeos_update_engine {
+
+class RawFilesystem : public FilesystemInterface {
+ public:
+  static std::unique_ptr<RawFilesystem> Create(
+      const std::string& filename, uint64_t block_size, uint64_t block_count);
+  virtual ~RawFilesystem() = default;
+
+  // FilesystemInterface overrides.
+  size_t GetBlockSize() const override;
+  size_t GetBlockCount() const override;
+
+  // GetFiles will return only one file with all the blocks of the filesystem
+  // with the name passed during construction.
+  bool GetFiles(std::vector<File>* files) const override;
+
+  bool LoadSettings(brillo::KeyValueStore* store) const override {
+    return false;
+  }
+
+ private:
+  RawFilesystem() = default;
+
+  std::string filename_;
+  uint64_t block_count_;
+  uint64_t block_size_;
+
+  DISALLOW_COPY_AND_ASSIGN(RawFilesystem);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_RAW_FILESYSTEM_H_
diff --git a/payload_generator/tarjan.cc b/payload_generator/tarjan.cc
new file mode 100644
index 0000000..98e29f9
--- /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 "update_engine/common/utils.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 (utils::VectorContainsValue(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 (utils::VectorContainsValue(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..50cf563
--- /dev/null
+++ b/payload_generator/tarjan.h
@@ -0,0 +1,52 @@
+//
+// 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..e40a7ff
--- /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 <gtest/gtest.h>
+
+#include "update_engine/common/utils.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(5, vertex_indexes.size());
+    EXPECT_TRUE(utils::VectorContainsValue(vertex_indexes, n_a));
+    EXPECT_TRUE(utils::VectorContainsValue(vertex_indexes, n_b));
+    EXPECT_TRUE(utils::VectorContainsValue(vertex_indexes, n_c));
+    EXPECT_TRUE(utils::VectorContainsValue(vertex_indexes, n_d));
+    EXPECT_TRUE(utils::VectorContainsValue(vertex_indexes, n_e));
+  }
+
+  {
+    vector<Vertex::Index> vertex_indexes;
+    tarjan.Execute(n_f, &graph, &vertex_indexes);
+
+    EXPECT_EQ(1, vertex_indexes.size());
+    EXPECT_TRUE(utils::VectorContainsValue(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(2, vertex_indexes.size());
+    EXPECT_TRUE(utils::VectorContainsValue(vertex_indexes, n_g));
+    EXPECT_TRUE(utils::VectorContainsValue(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..f164336
--- /dev/null
+++ b/payload_generator/topological_sort.cc
@@ -0,0 +1,56 @@
+//
+// 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..1d866a7
--- /dev/null
+++ b/payload_generator/topological_sort_unittest.cc
@@ -0,0 +1,95 @@
+//
+// 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
new file mode 100644
index 0000000..49b08b0
--- /dev/null
+++ b/payload_generator/zip_unittest.cc
@@ -0,0 +1,114 @@
+//
+// 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 <string.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "update_engine/common/test_utils.h"
+#include "update_engine/payload_generator/bzip.h"
+
+using chromeos_update_engine::test_utils::kRandomString;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+template <typename T>
+class ZipTest : public ::testing::Test {
+ public:
+  bool ZipDecompress(const brillo::Blob& in, brillo::Blob* out) const = 0;
+  bool ZipCompress(const brillo::Blob& in, brillo::Blob* out) const = 0;
+  bool ZipCompressString(const string& str, brillo::Blob* out) const = 0;
+  bool ZipDecompressString(const string& str, brillo::Blob* out) const = 0;
+};
+
+class BzipTest {};
+
+template <>
+class ZipTest<BzipTest> : public ::testing::Test {
+ public:
+  bool ZipDecompress(const brillo::Blob& in, brillo::Blob* out) const {
+    return BzipDecompress(in, out);
+  }
+  bool ZipCompress(const brillo::Blob& in, brillo::Blob* out) const {
+    return BzipCompress(in, out);
+  }
+  bool ZipCompressString(const string& str, brillo::Blob* out) const {
+    return BzipCompressString(str, out);
+  }
+  bool ZipDecompressString(const string& str, brillo::Blob* out) const {
+    return BzipDecompressString(str, out);
+  }
+};
+
+typedef ::testing::Types<BzipTest> ZipTestTypes;
+TYPED_TEST_CASE(ZipTest, ZipTestTypes);
+
+
+
+TYPED_TEST(ZipTest, SimpleTest) {
+  string in("this should compress well xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+            "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+            "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+            "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+            "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+            "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
+  brillo::Blob out;
+  EXPECT_TRUE(this->ZipCompressString(in, &out));
+  EXPECT_LT(out.size(), in.size());
+  EXPECT_GT(out.size(), 0);
+  brillo::Blob decompressed;
+  EXPECT_TRUE(this->ZipDecompress(out, &decompressed));
+  EXPECT_EQ(in.size(), decompressed.size());
+  EXPECT_TRUE(!memcmp(in.data(), decompressed.data(), in.size()));
+}
+
+TYPED_TEST(ZipTest, PoorCompressionTest) {
+  string in(reinterpret_cast<const char*>(kRandomString),
+            sizeof(kRandomString));
+  brillo::Blob out;
+  EXPECT_TRUE(this->ZipCompressString(in, &out));
+  EXPECT_GT(out.size(), in.size());
+  string out_string(out.begin(), out.end());
+  brillo::Blob decompressed;
+  EXPECT_TRUE(this->ZipDecompressString(out_string, &decompressed));
+  EXPECT_EQ(in.size(), decompressed.size());
+  EXPECT_TRUE(!memcmp(in.data(), decompressed.data(), in.size()));
+}
+
+TYPED_TEST(ZipTest, MalformedZipTest) {
+  string in(reinterpret_cast<const char*>(kRandomString),
+            sizeof(kRandomString));
+  brillo::Blob out;
+  EXPECT_FALSE(this->ZipDecompressString(in, &out));
+}
+
+TYPED_TEST(ZipTest, EmptyInputsTest) {
+  string in;
+  brillo::Blob out;
+  EXPECT_TRUE(this->ZipDecompressString(in, &out));
+  EXPECT_EQ(0, out.size());
+
+  EXPECT_TRUE(this->ZipCompressString(in, &out));
+  EXPECT_EQ(0, out.size());
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_state.cc b/payload_state.cc
new file mode 100644
index 0000000..8594f28
--- /dev/null
+++ b/payload_state.cc
@@ -0,0 +1,1382 @@
+//
+// 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_state.h"
+
+#include <algorithm>
+#include <string>
+
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <metrics/metrics_library.h>
+#include <policy/device_policy.h>
+
+#include "update_engine/common/clock.h"
+#include "update_engine/common/constants.h"
+#include "update_engine/common/hardware_interface.h"
+#include "update_engine/common/prefs.h"
+#include "update_engine/common/utils.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;
+using std::min;
+using std::string;
+
+namespace chromeos_update_engine {
+
+const TimeDelta PayloadState::kDurationSlack = TimeDelta::FromSeconds(600);
+
+// We want to upperbound backoffs to 16 days
+static const int kMaxBackoffDays = 16;
+
+// We want to randomize retry attempts after the backoff by +/- 6 hours.
+static const uint32_t kMaxBackoffFuzzMinutes = 12 * 60;
+
+PayloadState::PayloadState()
+    : prefs_(nullptr),
+      using_p2p_for_downloading_(false),
+      p2p_num_attempts_(0),
+      payload_attempt_number_(0),
+      full_payload_attempt_number_(0),
+      url_index_(0),
+      url_failure_count_(0),
+      url_switch_count_(0),
+      attempt_num_bytes_downloaded_(0),
+      attempt_connection_type_(metrics::ConnectionType::kUnknown),
+      attempt_type_(AttemptType::kUpdate) {
+  for (int i = 0; i <= kNumDownloadSources; i++)
+    total_bytes_downloaded_[i] = current_bytes_downloaded_[i] = 0;
+}
+
+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();
+  LoadUrlIndex();
+  LoadUrlFailureCount();
+  LoadUrlSwitchCount();
+  LoadBackoffExpiryTime();
+  LoadUpdateTimestampStart();
+  // The LoadUpdateDurationUptime() method relies on LoadUpdateTimestampStart()
+  // being called before it. Don't reorder.
+  LoadUpdateDurationUptime();
+  for (int i = 0; i < kNumDownloadSources; i++) {
+    DownloadSource source = static_cast<DownloadSource>(i);
+    LoadCurrentBytesDownloaded(source);
+    LoadTotalBytesDownloaded(source);
+  }
+  LoadNumReboots();
+  LoadNumResponsesSeen();
+  LoadRollbackVersion();
+  LoadP2PFirstAttemptTimestamp();
+  LoadP2PNumAttempts();
+  return true;
+}
+
+void PayloadState::SetResponse(const OmahaResponse& omaha_response) {
+  // Always store the latest response.
+  response_ = omaha_response;
+
+  // Compute the candidate URLs first as they are used to calculate the
+  // response signature so that a change in enterprise policy for
+  // HTTP downloads being enabled or not could be honored as soon as the
+  // next update check happens.
+  ComputeCandidateUrls();
+
+  // Check if the "signature" of this response (i.e. the fields we care about)
+  // has changed.
+  string new_response_signature = CalculateResponseSignature();
+  bool has_response_changed = (response_signature_ != new_response_signature);
+
+  // If the response has changed, we should persist the new signature and
+  // clear away all the existing state.
+  if (has_response_changed) {
+    LOG(INFO) << "Resetting all persisted state as this is a new response";
+    SetNumResponsesSeen(num_responses_seen_ + 1);
+    SetResponseSignature(new_response_signature);
+    ResetPersistedState();
+    return;
+  }
+
+  // This is the earliest point at which we can validate whether the URL index
+  // we loaded from the persisted state is a valid value. If the response
+  // hasn't changed but the URL index is invalid, it's indicative of some
+  // tampering of the persisted state.
+  if (static_cast<uint32_t>(url_index_) >= candidate_urls_.size()) {
+    LOG(INFO) << "Resetting all payload state as the url index seems to have "
+                 "been tampered with";
+    ResetPersistedState();
+    return;
+  }
+
+  // Update the current download source which depends on the latest value of
+  // the response.
+  UpdateCurrentDownloadSource();
+}
+
+void PayloadState::SetUsingP2PForDownloading(bool value) {
+  using_p2p_for_downloading_ = value;
+  // Update the current download source which depends on whether we are
+  // using p2p or not.
+  UpdateCurrentDownloadSource();
+}
+
+void PayloadState::DownloadComplete() {
+  LOG(INFO) << "Payload downloaded successfully";
+  IncrementPayloadAttemptNumber();
+  IncrementFullPayloadAttemptNumber();
+}
+
+void PayloadState::DownloadProgress(size_t count) {
+  if (count == 0)
+    return;
+
+  CalculateUpdateDurationUptime();
+  UpdateBytesDownloaded(count);
+
+  // We've received non-zero bytes from a recent download operation.  Since our
+  // URL failure count is meant to penalize a URL only for consecutive
+  // failures, downloading bytes successfully means we should reset the failure
+  // count (as we know at least that the URL is working). In future, we can
+  // design this to be more sophisticated to check for more intelligent failure
+  // patterns, but right now, even 1 byte downloaded will mark the URL to be
+  // good unless it hits 10 (or configured number of) consecutive failures
+  // again.
+
+  if (GetUrlFailureCount() == 0)
+    return;
+
+  LOG(INFO) << "Resetting failure count of Url" << GetUrlIndex()
+            << " to 0 as we received " << count << " bytes successfully";
+  SetUrlFailureCount(0);
+}
+
+void PayloadState::AttemptStarted(AttemptType attempt_type) {
+  // Flush previous state from abnormal attempt failure, if any.
+  ReportAndClearPersistedAttemptMetrics();
+
+  attempt_type_ = attempt_type;
+
+  ClockInterface *clock = system_state_->clock();
+  attempt_start_time_boot_ = clock->GetBootTime();
+  attempt_start_time_monotonic_ = clock->GetMonotonicTime();
+  attempt_num_bytes_downloaded_ = 0;
+
+  metrics::ConnectionType type;
+  NetworkConnectionType network_connection_type;
+  NetworkTethering tethering;
+  ConnectionManagerInterface* connection_manager =
+      system_state_->connection_manager();
+  if (!connection_manager->GetConnectionProperties(&network_connection_type,
+                                                   &tethering)) {
+    LOG(ERROR) << "Failed to determine connection type.";
+    type = metrics::ConnectionType::kUnknown;
+  } else {
+    type = metrics_utils::GetConnectionType(network_connection_type, tethering);
+  }
+  attempt_connection_type_ = type;
+
+  if (attempt_type == AttemptType::kUpdate)
+    PersistAttemptMetrics();
+}
+
+void PayloadState::UpdateResumed() {
+  LOG(INFO) << "Resuming an update that was previously started.";
+  UpdateNumReboots();
+  AttemptStarted(AttemptType::kUpdate);
+}
+
+void PayloadState::UpdateRestarted() {
+  LOG(INFO) << "Starting a new update";
+  ResetDownloadSourcesOnNewUpdate();
+  SetNumReboots(0);
+  AttemptStarted(AttemptType::kUpdate);
+}
+
+void PayloadState::UpdateSucceeded() {
+  // Send the relevant metrics that are tracked in this class to UMA.
+  CalculateUpdateDurationUptime();
+  SetUpdateTimestampEnd(system_state_->clock()->GetWallclockTime());
+
+  switch (attempt_type_) {
+    case AttemptType::kUpdate:
+      CollectAndReportAttemptMetrics(ErrorCode::kSuccess);
+      CollectAndReportSuccessfulUpdateMetrics();
+      ClearPersistedAttemptMetrics();
+      break;
+
+    case AttemptType::kRollback:
+      metrics::ReportRollbackMetrics(system_state_,
+                                     metrics::RollbackResult::kSuccess);
+      break;
+  }
+
+  // Reset the number of responses seen since it counts from the last
+  // successful update, e.g. now.
+  SetNumResponsesSeen(0);
+
+  CreateSystemUpdatedMarkerFile();
+}
+
+void PayloadState::UpdateFailed(ErrorCode error) {
+  ErrorCode base_error = utils::GetBaseErrorCode(error);
+  LOG(INFO) << "Updating payload state for error code: " << base_error
+            << " (" << utils::CodeToString(base_error) << ")";
+
+  if (candidate_urls_.size() == 0) {
+    // This means we got this error even before we got a valid Omaha response
+    // or don't have any valid candidates in the Omaha response.
+    // So we should not advance the url_index_ in such cases.
+    LOG(INFO) << "Ignoring failures until we get a valid Omaha response.";
+    return;
+  }
+
+  switch (attempt_type_) {
+    case AttemptType::kUpdate:
+      CollectAndReportAttemptMetrics(base_error);
+      ClearPersistedAttemptMetrics();
+      break;
+
+    case AttemptType::kRollback:
+      metrics::ReportRollbackMetrics(system_state_,
+                                     metrics::RollbackResult::kFailed);
+      break;
+  }
+
+  switch (base_error) {
+    // Errors which are good indicators of a problem with a particular URL or
+    // the protocol used in the URL or entities in the communication channel
+    // (e.g. proxies). We should try the next available URL in the next update
+    // check to quickly recover from these errors.
+    case ErrorCode::kPayloadHashMismatchError:
+    case ErrorCode::kPayloadSizeMismatchError:
+    case ErrorCode::kDownloadPayloadVerificationError:
+    case ErrorCode::kDownloadPayloadPubKeyVerificationError:
+    case ErrorCode::kSignedDeltaPayloadExpectedError:
+    case ErrorCode::kDownloadInvalidMetadataMagicString:
+    case ErrorCode::kDownloadSignatureMissingInManifest:
+    case ErrorCode::kDownloadManifestParseError:
+    case ErrorCode::kDownloadMetadataSignatureError:
+    case ErrorCode::kDownloadMetadataSignatureVerificationError:
+    case ErrorCode::kDownloadMetadataSignatureMismatch:
+    case ErrorCode::kDownloadOperationHashVerificationError:
+    case ErrorCode::kDownloadOperationExecutionError:
+    case ErrorCode::kDownloadOperationHashMismatch:
+    case ErrorCode::kDownloadInvalidMetadataSize:
+    case ErrorCode::kDownloadInvalidMetadataSignature:
+    case ErrorCode::kDownloadOperationHashMissingError:
+    case ErrorCode::kDownloadMetadataSignatureMissingError:
+    case ErrorCode::kPayloadMismatchedType:
+    case ErrorCode::kUnsupportedMajorPayloadVersion:
+    case ErrorCode::kUnsupportedMinorPayloadVersion:
+      IncrementUrlIndex();
+      break;
+
+    // Errors which seem to be just transient network/communication related
+    // failures and do not indicate any inherent problem with the URL itself.
+    // So, we should keep the current URL but just increment the
+    // failure count to give it more chances. This way, while we maximize our
+    // chances of downloading from the URLs that appear earlier in the response
+    // (because download from a local server URL that appears earlier in a
+    // response is preferable than downloading from the next URL which could be
+    // a internet URL and thus could be more expensive).
+
+    case ErrorCode::kError:
+    case ErrorCode::kDownloadTransferError:
+    case ErrorCode::kDownloadWriteError:
+    case ErrorCode::kDownloadStateInitializationError:
+    case ErrorCode::kOmahaErrorInHTTPResponse:  // Aggregate for HTTP errors.
+      IncrementFailureCount();
+      break;
+
+    // Errors which are not specific to a URL and hence shouldn't result in
+    // the URL being penalized. This can happen in two cases:
+    // 1. We haven't started downloading anything: These errors don't cost us
+    // anything in terms of actual payload bytes, so we should just do the
+    // regular retries at the next update check.
+    // 2. We have successfully downloaded the payload: In this case, the
+    // payload attempt number would have been incremented and would take care
+    // of the backoff at the next update check.
+    // In either case, there's no need to update URL index or failure count.
+    case ErrorCode::kOmahaRequestError:
+    case ErrorCode::kOmahaResponseHandlerError:
+    case ErrorCode::kPostinstallRunnerError:
+    case ErrorCode::kFilesystemCopierError:
+    case ErrorCode::kInstallDeviceOpenError:
+    case ErrorCode::kKernelDeviceOpenError:
+    case ErrorCode::kDownloadNewPartitionInfoError:
+    case ErrorCode::kNewRootfsVerificationError:
+    case ErrorCode::kNewKernelVerificationError:
+    case ErrorCode::kPostinstallBootedFromFirmwareB:
+    case ErrorCode::kPostinstallFirmwareRONotUpdatable:
+    case ErrorCode::kOmahaRequestEmptyResponseError:
+    case ErrorCode::kOmahaRequestXMLParseError:
+    case ErrorCode::kOmahaResponseInvalid:
+    case ErrorCode::kOmahaUpdateIgnoredPerPolicy:
+    case ErrorCode::kOmahaUpdateDeferredPerPolicy:
+    case ErrorCode::kOmahaUpdateDeferredForBackoff:
+    case ErrorCode::kPostinstallPowerwashError:
+    case ErrorCode::kUpdateCanceledByChannelChange:
+    case ErrorCode::kOmahaRequestXMLHasEntityDecl:
+    case ErrorCode::kFilesystemVerifierError:
+      LOG(INFO) << "Not incrementing URL index or failure count for this error";
+      break;
+
+    case ErrorCode::kSuccess:                            // success code
+    case ErrorCode::kUmaReportedMax:                     // not an error code
+    case ErrorCode::kOmahaRequestHTTPResponseBase:       // aggregated already
+    case ErrorCode::kDevModeFlag:                       // not an error code
+    case ErrorCode::kResumedFlag:                        // not an error code
+    case ErrorCode::kTestImageFlag:                      // not an error code
+    case ErrorCode::kTestOmahaUrlFlag:                   // not an error code
+    case ErrorCode::kSpecialFlags:                       // not an error code
+      // These shouldn't happen. Enumerating these  explicitly here so that we
+      // can let the compiler warn about new error codes that are added to
+      // action_processor.h but not added here.
+      LOG(WARNING) << "Unexpected error code for UpdateFailed";
+      break;
+
+    // Note: Not adding a default here so as to let the compiler warn us of
+    // any new enums that were added in the .h but not listed in this switch.
+  }
+}
+
+bool PayloadState::ShouldBackoffDownload() {
+  if (response_.disable_payload_backoff) {
+    LOG(INFO) << "Payload backoff logic is disabled. "
+                 "Can proceed with the download";
+    return false;
+  }
+  if (GetUsingP2PForDownloading() && !GetP2PUrl().empty()) {
+    LOG(INFO) << "Payload backoff logic is disabled because download "
+              << "will happen from local peer (via p2p).";
+    return false;
+  }
+  if (system_state_->request_params()->interactive()) {
+    LOG(INFO) << "Payload backoff disabled for interactive update checks.";
+    return false;
+  }
+  if (response_.is_delta_payload) {
+    // If delta payloads fail, we want to fallback quickly to full payloads as
+    // they are more likely to succeed. Exponential backoffs would greatly
+    // slow down the fallback to full payloads.  So we don't backoff for delta
+    // payloads.
+    LOG(INFO) << "No backoffs for delta payloads. "
+              << "Can proceed with the download";
+    return false;
+  }
+
+  if (!system_state_->hardware()->IsOfficialBuild()) {
+    // Backoffs are needed only for official builds. We do not want any delays
+    // or update failures due to backoffs during testing or development.
+    LOG(INFO) << "No backoffs for test/dev images. "
+              << "Can proceed with the download";
+    return false;
+  }
+
+  if (backoff_expiry_time_.is_null()) {
+    LOG(INFO) << "No backoff expiry time has been set. "
+              << "Can proceed with the download";
+    return false;
+  }
+
+  if (backoff_expiry_time_ < Time::Now()) {
+    LOG(INFO) << "The backoff expiry time ("
+              << utils::ToString(backoff_expiry_time_)
+              << ") has elapsed. Can proceed with the download";
+    return false;
+  }
+
+  LOG(INFO) << "Cannot proceed with downloads as we need to backoff until "
+            << utils::ToString(backoff_expiry_time_);
+  return true;
+}
+
+void PayloadState::Rollback() {
+  SetRollbackVersion(system_state_->request_params()->app_version());
+  AttemptStarted(AttemptType::kRollback);
+}
+
+void PayloadState::IncrementPayloadAttemptNumber() {
+  // Update the payload attempt number for both payload types: full and delta.
+  SetPayloadAttemptNumber(GetPayloadAttemptNumber() + 1);
+}
+
+void PayloadState::IncrementFullPayloadAttemptNumber() {
+  // Update the payload attempt number for full payloads and the backoff time.
+  if (response_.is_delta_payload) {
+    LOG(INFO) << "Not incrementing payload attempt number for delta payloads";
+    return;
+  }
+
+  LOG(INFO) << "Incrementing the full payload attempt number";
+  SetFullPayloadAttemptNumber(GetFullPayloadAttemptNumber() + 1);
+  UpdateBackoffExpiryTime();
+}
+
+void PayloadState::IncrementUrlIndex() {
+  uint32_t next_url_index = GetUrlIndex() + 1;
+  if (next_url_index < candidate_urls_.size()) {
+    LOG(INFO) << "Incrementing the URL index for next attempt";
+    SetUrlIndex(next_url_index);
+  } else {
+    LOG(INFO) << "Resetting the current URL index (" << GetUrlIndex() << ") to "
+              << "0 as we only have " << candidate_urls_.size()
+              << " candidate URL(s)";
+    SetUrlIndex(0);
+    IncrementPayloadAttemptNumber();
+    IncrementFullPayloadAttemptNumber();
+  }
+
+  // If we have multiple URLs, record that we just switched to another one
+  if (candidate_urls_.size() > 1)
+    SetUrlSwitchCount(url_switch_count_ + 1);
+
+  // Whenever we update the URL index, we should also clear the URL failure
+  // count so we can start over fresh for the new URL.
+  SetUrlFailureCount(0);
+}
+
+void PayloadState::IncrementFailureCount() {
+  uint32_t next_url_failure_count = GetUrlFailureCount() + 1;
+  if (next_url_failure_count < response_.max_failure_count_per_url) {
+    LOG(INFO) << "Incrementing the URL failure count";
+    SetUrlFailureCount(next_url_failure_count);
+  } else {
+    LOG(INFO) << "Reached max number of failures for Url" << GetUrlIndex()
+              << ". Trying next available URL";
+    IncrementUrlIndex();
+  }
+}
+
+void PayloadState::UpdateBackoffExpiryTime() {
+  if (response_.disable_payload_backoff) {
+    LOG(INFO) << "Resetting backoff expiry time as payload backoff is disabled";
+    SetBackoffExpiryTime(Time());
+    return;
+  }
+
+  if (GetFullPayloadAttemptNumber() == 0) {
+    SetBackoffExpiryTime(Time());
+    return;
+  }
+
+  // Since we're doing left-shift below, make sure we don't shift more
+  // than this. E.g. if int is 4-bytes, don't left-shift more than 30 bits,
+  // since we don't expect value of kMaxBackoffDays to be more than 100 anyway.
+  int num_days = 1;  // the value to be shifted.
+  const int kMaxShifts = (sizeof(num_days) * 8) - 2;
+
+  // Normal backoff days is 2 raised to (payload_attempt_number - 1).
+  // E.g. if payload_attempt_number is over 30, limit power to 30.
+  int power = min(GetFullPayloadAttemptNumber() - 1, kMaxShifts);
+
+  // The number of days is the minimum of 2 raised to (payload_attempt_number
+  // - 1) or kMaxBackoffDays.
+  num_days = min(num_days << power, kMaxBackoffDays);
+
+  // We don't want all retries to happen exactly at the same time when
+  // retrying after backoff. So add some random minutes to fuzz.
+  int fuzz_minutes = utils::FuzzInt(0, kMaxBackoffFuzzMinutes);
+  TimeDelta next_backoff_interval = TimeDelta::FromDays(num_days) +
+                                    TimeDelta::FromMinutes(fuzz_minutes);
+  LOG(INFO) << "Incrementing the backoff expiry time by "
+            << utils::FormatTimeDelta(next_backoff_interval);
+  SetBackoffExpiryTime(Time::Now() + next_backoff_interval);
+}
+
+void PayloadState::UpdateCurrentDownloadSource() {
+  current_download_source_ = kNumDownloadSources;
+
+  if (using_p2p_for_downloading_) {
+    current_download_source_ = kDownloadSourceHttpPeer;
+  } else if (GetUrlIndex() < candidate_urls_.size())  {
+    string current_url = candidate_urls_[GetUrlIndex()];
+    if (base::StartsWithASCII(current_url, "https://", false))
+      current_download_source_ = kDownloadSourceHttpsServer;
+    else if (base::StartsWithASCII(current_url, "http://", false))
+      current_download_source_ = kDownloadSourceHttpServer;
+  }
+
+  LOG(INFO) << "Current download source: "
+            << utils::ToString(current_download_source_);
+}
+
+void PayloadState::UpdateBytesDownloaded(size_t count) {
+  SetCurrentBytesDownloaded(
+      current_download_source_,
+      GetCurrentBytesDownloaded(current_download_source_) + count,
+      false);
+  SetTotalBytesDownloaded(
+      current_download_source_,
+      GetTotalBytesDownloaded(current_download_source_) + count,
+      false);
+
+  attempt_num_bytes_downloaded_ += count;
+}
+
+PayloadType PayloadState::CalculatePayloadType() {
+  PayloadType payload_type;
+  OmahaRequestParams* params = system_state_->request_params();
+  if (response_.is_delta_payload) {
+    payload_type = kPayloadTypeDelta;
+  } else if (params->delta_okay()) {
+    payload_type = kPayloadTypeFull;
+  } else {  // Full payload, delta was not allowed by request.
+    payload_type = kPayloadTypeForcedFull;
+  }
+  return payload_type;
+}
+
+// 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();
+
+  PayloadType payload_type = CalculatePayloadType();
+
+  int64_t payload_size = response_.size;
+
+  int64_t payload_bytes_downloaded = attempt_num_bytes_downloaded_;
+
+  ClockInterface *clock = system_state_->clock();
+  TimeDelta duration = clock->GetBootTime() - attempt_start_time_boot_;
+  TimeDelta duration_uptime = clock->GetMonotonicTime() -
+      attempt_start_time_monotonic_;
+
+  int64_t payload_download_speed_bps = 0;
+  int64_t usec = duration_uptime.InMicroseconds();
+  if (usec > 0) {
+    double sec = static_cast<double>(usec) / Time::kMicrosecondsPerSecond;
+    double bps = static_cast<double>(payload_bytes_downloaded) / sec;
+    payload_download_speed_bps = static_cast<int64_t>(bps);
+  }
+
+  DownloadSource download_source = current_download_source_;
+
+  metrics::DownloadErrorCode payload_download_error_code =
+    metrics::DownloadErrorCode::kUnset;
+  ErrorCode internal_error_code = ErrorCode::kSuccess;
+  metrics::AttemptResult attempt_result = metrics_utils::GetAttemptResult(code);
+
+  // Add additional detail to AttemptResult
+  switch (attempt_result) {
+    case metrics::AttemptResult::kPayloadDownloadError:
+      payload_download_error_code = metrics_utils::GetDownloadErrorCode(code);
+      break;
+
+    case metrics::AttemptResult::kInternalError:
+      internal_error_code = code;
+      break;
+
+    // Explicit fall-through for cases where we do not have additional
+    // detail. We avoid the default keyword to force people adding new
+    // AttemptResult values to visit this code and examine whether
+    // additional detail is needed.
+    case metrics::AttemptResult::kUpdateSucceeded:
+    case metrics::AttemptResult::kMetadataMalformed:
+    case metrics::AttemptResult::kOperationMalformed:
+    case metrics::AttemptResult::kOperationExecutionError:
+    case metrics::AttemptResult::kMetadataVerificationFailed:
+    case metrics::AttemptResult::kPayloadVerificationFailed:
+    case metrics::AttemptResult::kVerificationFailed:
+    case metrics::AttemptResult::kPostInstallFailed:
+    case metrics::AttemptResult::kAbnormalTermination:
+    case metrics::AttemptResult::kNumConstants:
+    case metrics::AttemptResult::kUnset:
+      break;
+  }
+
+  metrics::ReportUpdateAttemptMetrics(system_state_,
+                                      attempt_number,
+                                      payload_type,
+                                      duration,
+                                      duration_uptime,
+                                      payload_size,
+                                      payload_bytes_downloaded,
+                                      payload_download_speed_bps,
+                                      download_source,
+                                      attempt_result,
+                                      internal_error_code,
+                                      payload_download_error_code,
+                                      attempt_connection_type_);
+}
+
+void PayloadState::PersistAttemptMetrics() {
+  // TODO(zeuthen): For now we only persist whether an attempt was in
+  // progress and not values/metrics related to the attempt. This
+  // means that when this happens, of all the UpdateEngine.Attempt.*
+  // metrics, only UpdateEngine.Attempt.Result is reported (with the
+  // value |kAbnormalTermination|). In the future we might want to
+  // persist more data so we can report other metrics in the
+  // UpdateEngine.Attempt.* namespace when this happens.
+  prefs_->SetBoolean(kPrefsAttemptInProgress, true);
+}
+
+void PayloadState::ClearPersistedAttemptMetrics() {
+  prefs_->Delete(kPrefsAttemptInProgress);
+}
+
+void PayloadState::ReportAndClearPersistedAttemptMetrics() {
+  bool attempt_in_progress = false;
+  if (!prefs_->GetBoolean(kPrefsAttemptInProgress, &attempt_in_progress))
+    return;
+  if (!attempt_in_progress)
+    return;
+
+  metrics::ReportAbnormallyTerminatedUpdateAttemptMetrics(system_state_);
+
+  ClearPersistedAttemptMetrics();
+}
+
+void PayloadState::CollectAndReportSuccessfulUpdateMetrics() {
+  string metric;
+
+  // Report metrics collected from all known download sources to UMA.
+  int64_t total_bytes_by_source[kNumDownloadSources];
+  int64_t successful_bytes = 0;
+  int64_t total_bytes = 0;
+  int64_t successful_mbs = 0;
+  int64_t total_mbs = 0;
+
+  for (int i = 0; i < kNumDownloadSources; i++) {
+    DownloadSource source = static_cast<DownloadSource>(i);
+    int64_t bytes;
+
+    // Only consider this download source (and send byte counts) as
+    // having been used if we downloaded a non-trivial amount of bytes
+    // (e.g. at least 1 MiB) that contributed to the final success of
+    // the update. Otherwise we're going to end up with a lot of
+    // zero-byte events in the histogram.
+
+    bytes = GetCurrentBytesDownloaded(source);
+    successful_bytes += bytes;
+    successful_mbs += bytes / kNumBytesInOneMiB;
+    SetCurrentBytesDownloaded(source, 0, true);
+
+    bytes = GetTotalBytesDownloaded(source);
+    total_bytes_by_source[i] = bytes;
+    total_bytes += bytes;
+    total_mbs += bytes / kNumBytesInOneMiB;
+    SetTotalBytesDownloaded(source, 0, true);
+  }
+
+  int download_overhead_percentage = 0;
+  if (successful_bytes > 0) {
+    download_overhead_percentage = (total_bytes - successful_bytes) * 100ULL /
+                                   successful_bytes;
+  }
+
+  int url_switch_count = static_cast<int>(url_switch_count_);
+
+  int reboot_count = GetNumReboots();
+
+  SetNumReboots(0);
+
+  TimeDelta duration = GetUpdateDuration();
+
+  prefs_->Delete(kPrefsUpdateTimestampStart);
+  prefs_->Delete(kPrefsUpdateDurationUptime);
+
+  PayloadType payload_type = CalculatePayloadType();
+
+  int64_t payload_size = response_.size;
+
+  int attempt_count = GetPayloadAttemptNumber();
+
+  int updates_abandoned_count = num_responses_seen_ - 1;
+
+  metrics::ReportSuccessfulUpdateMetrics(system_state_,
+                                         attempt_count,
+                                         updates_abandoned_count,
+                                         payload_type,
+                                         payload_size,
+                                         total_bytes_by_source,
+                                         download_overhead_percentage,
+                                         duration,
+                                         reboot_count,
+                                         url_switch_count);
+}
+
+void PayloadState::UpdateNumReboots() {
+  // We only update the reboot count when the system has been detected to have
+  // been rebooted.
+  if (!system_state_->system_rebooted()) {
+    return;
+  }
+
+  SetNumReboots(GetNumReboots() + 1);
+}
+
+void PayloadState::SetNumReboots(uint32_t num_reboots) {
+  CHECK(prefs_);
+  num_reboots_ = num_reboots;
+  prefs_->SetInt64(kPrefsNumReboots, num_reboots);
+  LOG(INFO) << "Number of Reboots during current update attempt = "
+            << num_reboots_;
+}
+
+void PayloadState::ResetPersistedState() {
+  SetPayloadAttemptNumber(0);
+  SetFullPayloadAttemptNumber(0);
+  SetUrlIndex(0);
+  SetUrlFailureCount(0);
+  SetUrlSwitchCount(0);
+  UpdateBackoffExpiryTime();  // This will reset the backoff expiry time.
+  SetUpdateTimestampStart(system_state_->clock()->GetWallclockTime());
+  SetUpdateTimestampEnd(Time());  // Set to null time
+  SetUpdateDurationUptime(TimeDelta::FromSeconds(0));
+  ResetDownloadSourcesOnNewUpdate();
+  ResetRollbackVersion();
+  SetP2PNumAttempts(0);
+  SetP2PFirstAttemptTimestamp(Time());  // Set to null time
+  SetScatteringWaitPeriod(TimeDelta());
+}
+
+void PayloadState::ResetRollbackVersion() {
+  CHECK(powerwash_safe_prefs_);
+  rollback_version_ = "";
+  powerwash_safe_prefs_->Delete(kPrefsRollbackVersion);
+}
+
+void PayloadState::ResetDownloadSourcesOnNewUpdate() {
+  for (int i = 0; i < kNumDownloadSources; i++) {
+    DownloadSource source = static_cast<DownloadSource>(i);
+    SetCurrentBytesDownloaded(source, 0, true);
+    // Note: Not resetting the TotalBytesDownloaded as we want that metric
+    // to count the bytes downloaded across various update attempts until
+    // we have successfully applied the update.
+  }
+}
+
+int64_t PayloadState::GetPersistedValue(const string& key) {
+  CHECK(prefs_);
+  if (!prefs_->Exists(key))
+    return 0;
+
+  int64_t stored_value;
+  if (!prefs_->GetInt64(key, &stored_value))
+    return 0;
+
+  if (stored_value < 0) {
+    LOG(ERROR) << key << ": Invalid value (" << stored_value
+               << ") in persisted state. Defaulting to 0";
+    return 0;
+  }
+
+  return stored_value;
+}
+
+string PayloadState::CalculateResponseSignature() {
+  string response_sign = base::StringPrintf(
+      "NumURLs = %d\n", static_cast<int>(candidate_urls_.size()));
+
+  for (size_t i = 0; i < candidate_urls_.size(); i++)
+    response_sign += base::StringPrintf("Candidate Url%d = %s\n",
+                                        static_cast<int>(i),
+                                        candidate_urls_[i].c_str());
+
+  response_sign += base::StringPrintf(
+      "Payload Size = %ju\n"
+      "Payload Sha256 Hash = %s\n"
+      "Metadata Size = %ju\n"
+      "Metadata Signature = %s\n"
+      "Is Delta Payload = %d\n"
+      "Max Failure Count Per Url = %d\n"
+      "Disable Payload Backoff = %d\n",
+      static_cast<uintmax_t>(response_.size),
+      response_.hash.c_str(),
+      static_cast<uintmax_t>(response_.metadata_size),
+      response_.metadata_signature.c_str(),
+      response_.is_delta_payload,
+      response_.max_failure_count_per_url,
+      response_.disable_payload_backoff);
+  return response_sign;
+}
+
+void PayloadState::LoadResponseSignature() {
+  CHECK(prefs_);
+  string stored_value;
+  if (prefs_->Exists(kPrefsCurrentResponseSignature) &&
+      prefs_->GetString(kPrefsCurrentResponseSignature, &stored_value)) {
+    SetResponseSignature(stored_value);
+  }
+}
+
+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_);
+}
+
+void PayloadState::LoadPayloadAttemptNumber() {
+  SetPayloadAttemptNumber(GetPersistedValue(kPrefsPayloadAttemptNumber));
+}
+
+void PayloadState::LoadFullPayloadAttemptNumber() {
+  SetFullPayloadAttemptNumber(GetPersistedValue(
+      kPrefsFullPayloadAttemptNumber));
+}
+
+void PayloadState::SetPayloadAttemptNumber(int payload_attempt_number) {
+  CHECK(prefs_);
+  payload_attempt_number_ = payload_attempt_number;
+  LOG(INFO) << "Payload Attempt Number = " << payload_attempt_number_;
+  prefs_->SetInt64(kPrefsPayloadAttemptNumber, payload_attempt_number_);
+}
+
+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,
+      full_payload_attempt_number_);
+}
+
+void PayloadState::LoadUrlIndex() {
+  SetUrlIndex(GetPersistedValue(kPrefsCurrentUrlIndex));
+}
+
+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_);
+
+  // Also update the download source, which is purely dependent on the
+  // current URL index alone.
+  UpdateCurrentDownloadSource();
+}
+
+void PayloadState::LoadScatteringWaitPeriod() {
+  SetScatteringWaitPeriod(
+      TimeDelta::FromSeconds(GetPersistedValue(kPrefsWallClockWaitPeriod)));
+}
+
+void PayloadState::SetScatteringWaitPeriod(TimeDelta wait_period) {
+  CHECK(prefs_);
+  scattering_wait_period_ = wait_period;
+  LOG(INFO) << "Scattering Wait Period (seconds) = "
+            << scattering_wait_period_.InSeconds();
+  if (scattering_wait_period_.InSeconds() > 0) {
+    prefs_->SetInt64(kPrefsWallClockWaitPeriod,
+                     scattering_wait_period_.InSeconds());
+  } else {
+    prefs_->Delete(kPrefsWallClockWaitPeriod);
+  }
+}
+
+void PayloadState::LoadUrlSwitchCount() {
+  SetUrlSwitchCount(GetPersistedValue(kPrefsUrlSwitchCount));
+}
+
+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_);
+}
+
+void PayloadState::LoadUrlFailureCount() {
+  SetUrlFailureCount(GetPersistedValue(kPrefsCurrentUrlFailureCount));
+}
+
+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_;
+  prefs_->SetInt64(kPrefsCurrentUrlFailureCount, url_failure_count_);
+}
+
+void PayloadState::LoadBackoffExpiryTime() {
+  CHECK(prefs_);
+  int64_t stored_value;
+  if (!prefs_->Exists(kPrefsBackoffExpiryTime))
+    return;
+
+  if (!prefs_->GetInt64(kPrefsBackoffExpiryTime, &stored_value))
+    return;
+
+  Time stored_time = Time::FromInternalValue(stored_value);
+  if (stored_time > Time::Now() + TimeDelta::FromDays(kMaxBackoffDays)) {
+    LOG(ERROR) << "Invalid backoff expiry time ("
+               << utils::ToString(stored_time)
+               << ") in persisted state. Resetting.";
+    stored_time = Time();
+  }
+  SetBackoffExpiryTime(stored_time);
+}
+
+void PayloadState::SetBackoffExpiryTime(const Time& new_time) {
+  CHECK(prefs_);
+  backoff_expiry_time_ = new_time;
+  LOG(INFO) << "Backoff Expiry Time = "
+            << utils::ToString(backoff_expiry_time_);
+  prefs_->SetInt64(kPrefsBackoffExpiryTime,
+                   backoff_expiry_time_.ToInternalValue());
+}
+
+TimeDelta PayloadState::GetUpdateDuration() {
+  Time end_time = update_timestamp_end_.is_null()
+    ? system_state_->clock()->GetWallclockTime() :
+      update_timestamp_end_;
+  return end_time - update_timestamp_start_;
+}
+
+void PayloadState::LoadUpdateTimestampStart() {
+  int64_t stored_value;
+  Time stored_time;
+
+  CHECK(prefs_);
+
+  Time now = system_state_->clock()->GetWallclockTime();
+
+  if (!prefs_->Exists(kPrefsUpdateTimestampStart)) {
+    // The preference missing is not unexpected - in that case, just
+    // use the current time as start time
+    stored_time = now;
+  } else if (!prefs_->GetInt64(kPrefsUpdateTimestampStart, &stored_value)) {
+    LOG(ERROR) << "Invalid UpdateTimestampStart value. Resetting.";
+    stored_time = now;
+  } else {
+    stored_time = Time::FromInternalValue(stored_value);
+  }
+
+  // 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.
+  TimeDelta duration_according_to_stored_time = now - stored_time;
+  if (duration_according_to_stored_time < -kDurationSlack) {
+    LOG(ERROR) << "The UpdateTimestampStart value ("
+               << utils::ToString(stored_time)
+               << ") in persisted state is "
+               << utils::FormatTimeDelta(duration_according_to_stored_time)
+               << " in the future. Resetting.";
+    stored_time = now;
+  }
+
+  SetUpdateTimestampStart(stored_time);
+}
+
+void PayloadState::SetUpdateTimestampStart(const Time& value) {
+  CHECK(prefs_);
+  update_timestamp_start_ = value;
+  prefs_->SetInt64(kPrefsUpdateTimestampStart,
+                   update_timestamp_start_.ToInternalValue());
+  LOG(INFO) << "Update Timestamp Start = "
+            << utils::ToString(update_timestamp_start_);
+}
+
+void PayloadState::SetUpdateTimestampEnd(const Time& value) {
+  update_timestamp_end_ = value;
+  LOG(INFO) << "Update Timestamp End = "
+            << utils::ToString(update_timestamp_end_);
+}
+
+TimeDelta PayloadState::GetUpdateDurationUptime() {
+  return update_duration_uptime_;
+}
+
+void PayloadState::LoadUpdateDurationUptime() {
+  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
+  } else if (!prefs_->GetInt64(kPrefsUpdateDurationUptime, &stored_value)) {
+    LOG(ERROR) << "Invalid UpdateDurationUptime value. Resetting.";
+    stored_delta = TimeDelta::FromSeconds(0);
+  } else {
+    stored_delta = TimeDelta::FromInternalValue(stored_value);
+  }
+
+  // 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;
+  if (diff < -kDurationSlack) {
+    LOG(ERROR) << "The UpdateDurationUptime value ("
+               << utils::FormatTimeDelta(stored_delta)
+               << ") in persisted state is "
+               << utils::FormatTimeDelta(diff)
+               << " larger than the wall-clock delta. Resetting.";
+    stored_delta = update_duration_current_;
+  }
+
+  SetUpdateDurationUptime(stored_delta);
+}
+
+void PayloadState::LoadNumReboots() {
+  SetNumReboots(GetPersistedValue(kPrefsNumReboots));
+}
+
+void PayloadState::LoadRollbackVersion() {
+  CHECK(powerwash_safe_prefs_);
+  string rollback_version;
+  if (powerwash_safe_prefs_->GetString(kPrefsRollbackVersion,
+                                       &rollback_version)) {
+    SetRollbackVersion(rollback_version);
+  }
+}
+
+void PayloadState::SetRollbackVersion(const string& rollback_version) {
+  CHECK(powerwash_safe_prefs_);
+  LOG(INFO) << "Blacklisting version "<< rollback_version;
+  rollback_version_ = rollback_version;
+  powerwash_safe_prefs_->SetString(kPrefsRollbackVersion, rollback_version);
+}
+
+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,
+                   update_duration_uptime_.ToInternalValue());
+  if (use_logging) {
+    LOG(INFO) << "Update Duration Uptime = "
+              << utils::FormatTimeDelta(update_duration_uptime_);
+  }
+}
+
+void PayloadState::SetUpdateDurationUptime(const TimeDelta& value) {
+  Time now = system_state_->clock()->GetMonotonicTime();
+  SetUpdateDurationUptimeExtended(value, now, true);
+}
+
+void PayloadState::CalculateUpdateDurationUptime() {
+  Time now = system_state_->clock()->GetMonotonicTime();
+  TimeDelta uptime_since_last_update = now - update_duration_uptime_timestamp_;
+  TimeDelta new_uptime = update_duration_uptime_ + uptime_since_last_update;
+  // We're frequently called so avoid logging this write
+  SetUpdateDurationUptimeExtended(new_uptime, now, false);
+}
+
+string PayloadState::GetPrefsKey(const string& prefix, DownloadSource source) {
+  return prefix + "-from-" + utils::ToString(source);
+}
+
+void PayloadState::LoadCurrentBytesDownloaded(DownloadSource source) {
+  string key = GetPrefsKey(kPrefsCurrentBytesDownloaded, source);
+  SetCurrentBytesDownloaded(source, GetPersistedValue(key), true);
+}
+
+void PayloadState::SetCurrentBytesDownloaded(
+    DownloadSource source,
+    uint64_t current_bytes_downloaded,
+    bool log) {
+  CHECK(prefs_);
+
+  if (source >= kNumDownloadSources)
+    return;
+
+  // Update the in-memory value.
+  current_bytes_downloaded_[source] = current_bytes_downloaded;
+
+  string prefs_key = GetPrefsKey(kPrefsCurrentBytesDownloaded, source);
+  prefs_->SetInt64(prefs_key, current_bytes_downloaded);
+  LOG_IF(INFO, log) << "Current bytes downloaded for "
+                    << utils::ToString(source) << " = "
+                    << GetCurrentBytesDownloaded(source);
+}
+
+void PayloadState::LoadTotalBytesDownloaded(DownloadSource source) {
+  string key = GetPrefsKey(kPrefsTotalBytesDownloaded, source);
+  SetTotalBytesDownloaded(source, GetPersistedValue(key), true);
+}
+
+void PayloadState::SetTotalBytesDownloaded(
+    DownloadSource source,
+    uint64_t total_bytes_downloaded,
+    bool log) {
+  CHECK(prefs_);
+
+  if (source >= kNumDownloadSources)
+    return;
+
+  // Update the in-memory value.
+  total_bytes_downloaded_[source] = total_bytes_downloaded;
+
+  // Persist.
+  string prefs_key = GetPrefsKey(kPrefsTotalBytesDownloaded, source);
+  prefs_->SetInt64(prefs_key, total_bytes_downloaded);
+  LOG_IF(INFO, log) << "Total bytes downloaded for "
+                    << utils::ToString(source) << " = "
+                    << GetTotalBytesDownloaded(source);
+}
+
+void PayloadState::LoadNumResponsesSeen() {
+  SetNumResponsesSeen(GetPersistedValue(kPrefsNumResponsesSeen));
+}
+
+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_);
+}
+
+void PayloadState::ComputeCandidateUrls() {
+  bool http_url_ok = true;
+
+  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 {
+    LOG(INFO) << "Allowing HTTP downloads for unofficial builds";
+    http_url_ok = true;
+  }
+
+  candidate_urls_.clear();
+  for (size_t i = 0; i < response_.payload_urls.size(); i++) {
+    string candidate_url = response_.payload_urls[i];
+    if (base::StartsWithASCII(candidate_url, "http://", false) && !http_url_ok)
+      continue;
+    candidate_urls_.push_back(candidate_url);
+    LOG(INFO) << "Candidate Url" << (candidate_urls_.size() - 1)
+              << ": " << candidate_url;
+  }
+
+  LOG(INFO) << "Found " << candidate_urls_.size() << " candidate URLs "
+            << "out of " << response_.payload_urls.size() << " URLs supplied";
+}
+
+void PayloadState::CreateSystemUpdatedMarkerFile() {
+  CHECK(prefs_);
+  int64_t value = system_state_->clock()->GetWallclockTime().ToInternalValue();
+  prefs_->SetInt64(kPrefsSystemUpdatedMarker, value);
+}
+
+void PayloadState::BootedIntoUpdate(TimeDelta time_to_reboot) {
+  // Send |time_to_reboot| as a UMA stat.
+  string metric = metrics::kMetricTimeToRebootMinutes;
+  system_state_->metrics_lib()->SendToUMA(metric,
+                                          time_to_reboot.InMinutes(),
+                                          0,         // min: 0 minute
+                                          30*24*60,  // max: 1 month (approx)
+                                          kNumDefaultUmaBuckets);
+  LOG(INFO) << "Uploading " << utils::FormatTimeDelta(time_to_reboot)
+            << " for metric " <<  metric;
+}
+
+void PayloadState::UpdateEngineStarted() {
+  // Flush previous state from abnormal attempt failure, if any.
+  ReportAndClearPersistedAttemptMetrics();
+
+  // Avoid the UpdateEngineStarted actions if this is not the first time we
+  // run the update engine since reboot.
+  if (!system_state_->system_rebooted())
+    return;
+
+  // Figure out if we just booted into a new update
+  if (prefs_->Exists(kPrefsSystemUpdatedMarker)) {
+    int64_t stored_value;
+    if (prefs_->GetInt64(kPrefsSystemUpdatedMarker, &stored_value)) {
+      Time system_updated_at = Time::FromInternalValue(stored_value);
+      if (!system_updated_at.is_null()) {
+        TimeDelta time_to_reboot =
+            system_state_->clock()->GetWallclockTime() - system_updated_at;
+        if (time_to_reboot.ToInternalValue() < 0) {
+          LOG(ERROR) << "time_to_reboot is negative - system_updated_at: "
+                     << utils::ToString(system_updated_at);
+        } else {
+          BootedIntoUpdate(time_to_reboot);
+        }
+      }
+    }
+    prefs_->Delete(kPrefsSystemUpdatedMarker);
+  }
+  // Check if it is needed to send metrics about a failed reboot into a new
+  // version.
+  ReportFailedBootIfNeeded();
+}
+
+void PayloadState::ReportFailedBootIfNeeded() {
+  // If the kPrefsTargetVersionInstalledFrom is present, a successfully applied
+  // payload was marked as ready immediately before the last reboot, and we
+  // need to check if such payload successfully rebooted or not.
+  if (prefs_->Exists(kPrefsTargetVersionInstalledFrom)) {
+    int64_t installed_from = 0;
+    if (!prefs_->GetInt64(kPrefsTargetVersionInstalledFrom, &installed_from)) {
+      LOG(ERROR) << "Error reading TargetVersionInstalledFrom on reboot.";
+      return;
+    }
+    // Old Chrome OS devices will write 2 or 4 in this setting, with the
+    // partition number. We are now using slot numbers (0 or 1) instead, so
+    // the following comparison will not match if we are comparing an old
+    // partition number against a new slot number, which is the correct outcome
+    // 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 == 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;
+      if (!prefs_->GetInt64(kPrefsTargetVersionAttempt, &target_attempt)) {
+        LOG(ERROR) << "Error reading TargetVersionAttempt when "
+                      "TargetVersionInstalledFrom was present.";
+        target_attempt = 1;
+      }
+
+      // Report the UMA metric of the current boot failure.
+      string metric = metrics::kMetricFailedUpdateCount;
+      LOG(INFO) << "Uploading " << target_attempt
+                << " (count) for metric " <<  metric;
+      system_state_->metrics_lib()->SendToUMA(
+           metric,
+           target_attempt,
+           1,    // min value
+           50,   // max value
+           kNumDefaultUmaBuckets);
+    } else {
+      prefs_->Delete(kPrefsTargetVersionAttempt);
+      prefs_->Delete(kPrefsTargetVersionUniqueId);
+    }
+    prefs_->Delete(kPrefsTargetVersionInstalledFrom);
+  }
+}
+
+void PayloadState::ExpectRebootInNewVersion(const string& target_version_uid) {
+  // Expect to boot into the new partition in the next reboot setting the
+  // TargetVersion* flags in the Prefs.
+  string stored_target_version_uid;
+  string target_version_id;
+  string target_partition;
+  int64_t target_attempt;
+
+  if (prefs_->Exists(kPrefsTargetVersionUniqueId) &&
+      prefs_->GetString(kPrefsTargetVersionUniqueId,
+                        &stored_target_version_uid) &&
+      stored_target_version_uid == target_version_uid) {
+    if (!prefs_->GetInt64(kPrefsTargetVersionAttempt, &target_attempt))
+      target_attempt = 0;
+  } else {
+    prefs_->SetString(kPrefsTargetVersionUniqueId, target_version_uid);
+    target_attempt = 0;
+  }
+  prefs_->SetInt64(kPrefsTargetVersionAttempt, target_attempt + 1);
+
+  prefs_->SetInt64(kPrefsTargetVersionInstalledFrom,
+                   system_state_->boot_control()->GetCurrentSlot());
+}
+
+void PayloadState::ResetUpdateStatus() {
+  // Remove the TargetVersionInstalledFrom pref so that if the machine is
+  // rebooted the next boot is not flagged as failed to rebooted into the
+  // new applied payload.
+  prefs_->Delete(kPrefsTargetVersionInstalledFrom);
+
+  // Also decrement the attempt number if it exists.
+  int64_t target_attempt;
+  if (prefs_->GetInt64(kPrefsTargetVersionAttempt, &target_attempt))
+    prefs_->SetInt64(kPrefsTargetVersionAttempt, target_attempt - 1);
+}
+
+int PayloadState::GetP2PNumAttempts() {
+  return p2p_num_attempts_;
+}
+
+void PayloadState::SetP2PNumAttempts(int value) {
+  p2p_num_attempts_ = value;
+  LOG(INFO) << "p2p Num Attempts = " << p2p_num_attempts_;
+  CHECK(prefs_);
+  prefs_->SetInt64(kPrefsP2PNumAttempts, value);
+}
+
+void PayloadState::LoadP2PNumAttempts() {
+  SetP2PNumAttempts(GetPersistedValue(kPrefsP2PNumAttempts));
+}
+
+Time PayloadState::GetP2PFirstAttemptTimestamp() {
+  return p2p_first_attempt_timestamp_;
+}
+
+void PayloadState::SetP2PFirstAttemptTimestamp(const Time& time) {
+  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);
+}
+
+void PayloadState::LoadP2PFirstAttemptTimestamp() {
+  int64_t stored_value = GetPersistedValue(kPrefsP2PFirstAttemptTimestamp);
+  Time stored_time = Time::FromInternalValue(stored_value);
+  SetP2PFirstAttemptTimestamp(stored_time);
+}
+
+void PayloadState::P2PNewAttempt() {
+  CHECK(prefs_);
+  // Set timestamp, if it hasn't been set already
+  if (p2p_first_attempt_timestamp_.is_null()) {
+    SetP2PFirstAttemptTimestamp(system_state_->clock()->GetWallclockTime());
+  }
+  // Increase number of attempts
+  SetP2PNumAttempts(GetP2PNumAttempts() + 1);
+}
+
+bool PayloadState::P2PAttemptAllowed() {
+  if (p2p_num_attempts_ > kMaxP2PAttempts) {
+    LOG(INFO) << "Number of p2p attempts is " << p2p_num_attempts_
+              << " which is greater than "
+              << kMaxP2PAttempts
+              << " - disallowing p2p.";
+    return false;
+  }
+
+  if (!p2p_first_attempt_timestamp_.is_null()) {
+    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"
+                 << " - disallowing p2p.";
+      return false;
+    }
+    if (time_spent_attempting_p2p.InSeconds() > kMaxP2PAttemptTimeSeconds) {
+      LOG(INFO) << "Time spent attempting p2p is "
+                << utils::FormatTimeDelta(time_spent_attempting_p2p)
+                << " which is greater than "
+                << utils::FormatTimeDelta(TimeDelta::FromSeconds(
+                       kMaxP2PAttemptTimeSeconds))
+                << " - disallowing p2p.";
+      return false;
+    }
+  }
+
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_state.h b/payload_state.h
new file mode 100644
index 0000000..bec5823
--- /dev/null
+++ b/payload_state.h
@@ -0,0 +1,573 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_PAYLOAD_STATE_H_
+#define UPDATE_ENGINE_PAYLOAD_STATE_H_
+
+#include <string>
+#include <vector>
+
+#include <base/time/time.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "update_engine/common/prefs_interface.h"
+#include "update_engine/metrics.h"
+#include "update_engine/payload_state_interface.h"
+
+namespace chromeos_update_engine {
+
+class SystemState;
+
+// Encapsulates all the payload state required for download. This includes the
+// state necessary for handling multiple URLs in Omaha response, the backoff
+// state, etc. All state is persisted so that we use the most recently saved
+// value when resuming the update_engine process. All state is also cached in
+// memory so that we ensure we always make progress based on last known good
+// state even when there's any issue in reading/writing from the file system.
+class PayloadState : public PayloadStateInterface {
+ public:
+  PayloadState();
+  ~PayloadState() override {}
+
+  // Initializes a payload state object using the given global system state.
+  // 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(SystemState* system_state);
+
+  // Implementation of PayloadStateInterface methods.
+  void SetResponse(const OmahaResponse& response) override;
+  void DownloadComplete() override;
+  void DownloadProgress(size_t count) override;
+  void UpdateResumed() override;
+  void UpdateRestarted() override;
+  void UpdateSucceeded() override;
+  void UpdateFailed(ErrorCode error) override;
+  void ResetUpdateStatus() override;
+  bool ShouldBackoffDownload() override;
+  void Rollback() override;
+  void ExpectRebootInNewVersion(const std::string& target_version_uid) override;
+  void SetUsingP2PForDownloading(bool value) override;
+
+  void SetUsingP2PForSharing(bool value) override {
+    using_p2p_for_sharing_ = value;
+  }
+
+  inline std::string GetResponseSignature() override {
+    return response_signature_;
+  }
+
+  inline int GetFullPayloadAttemptNumber() override {
+    return full_payload_attempt_number_;
+  }
+
+  inline int GetPayloadAttemptNumber() override {
+    return payload_attempt_number_;
+  }
+
+  inline std::string GetCurrentUrl() override {
+    return candidate_urls_.size() ? candidate_urls_[url_index_] : "";
+  }
+
+  inline uint32_t GetUrlFailureCount() override {
+    return url_failure_count_;
+  }
+
+  inline uint32_t GetUrlSwitchCount() override {
+    return url_switch_count_;
+  }
+
+  inline int GetNumResponsesSeen() override {
+    return num_responses_seen_;
+  }
+
+  inline base::Time GetBackoffExpiryTime() override {
+    return backoff_expiry_time_;
+  }
+
+  base::TimeDelta GetUpdateDuration() override;
+
+  base::TimeDelta GetUpdateDurationUptime() override;
+
+  inline uint64_t GetCurrentBytesDownloaded(DownloadSource source) override {
+    return source < kNumDownloadSources ? current_bytes_downloaded_[source] : 0;
+  }
+
+  inline uint64_t GetTotalBytesDownloaded(DownloadSource source) override {
+    return source < kNumDownloadSources ? total_bytes_downloaded_[source] : 0;
+  }
+
+  inline uint32_t GetNumReboots() override {
+    return num_reboots_;
+  }
+
+  void UpdateEngineStarted() override;
+
+  inline std::string GetRollbackVersion() override {
+    return rollback_version_;
+  }
+
+  int GetP2PNumAttempts() override;
+  base::Time GetP2PFirstAttemptTimestamp() override;
+  void P2PNewAttempt() override;
+  bool P2PAttemptAllowed() override;
+
+  bool GetUsingP2PForDownloading() const override {
+    return using_p2p_for_downloading_;
+  }
+
+  bool GetUsingP2PForSharing() const override {
+    return using_p2p_for_sharing_;
+  }
+
+  base::TimeDelta GetScatteringWaitPeriod() override {
+    return scattering_wait_period_;
+  }
+
+  void SetScatteringWaitPeriod(base::TimeDelta wait_period) override;
+
+  void SetP2PUrl(const std::string& url) override {
+    p2p_url_ = url;
+  }
+
+  std::string GetP2PUrl() const override {
+    return p2p_url_;
+  }
+
+ private:
+  enum class AttemptType {
+    kUpdate,
+    kRollback,
+  };
+
+  friend class PayloadStateTest;
+  FRIEND_TEST(PayloadStateTest, RebootAfterUpdateFailedMetric);
+  FRIEND_TEST(PayloadStateTest, RebootAfterUpdateSucceed);
+  FRIEND_TEST(PayloadStateTest, RebootAfterCanceledUpdate);
+  FRIEND_TEST(PayloadStateTest, RollbackVersion);
+  FRIEND_TEST(PayloadStateTest, UpdateSuccessWithWipedPrefs);
+
+  // Helper called when an attempt has begun, is called by
+  // UpdateResumed(), UpdateRestarted() and Rollback().
+  void AttemptStarted(AttemptType attempt_type);
+
+  // Increments the payload attempt number used for metrics.
+  void IncrementPayloadAttemptNumber();
+
+  // Increments the payload attempt number which governs the backoff behavior
+  // at the time of the next update check.
+  void IncrementFullPayloadAttemptNumber();
+
+  // Advances the current URL index to the next available one. If all URLs have
+  // been exhausted during the current payload download attempt (as indicated
+  // by the payload attempt number), then it will increment the payload attempt
+  // number and wrap around again with the first URL in the list. This also
+  // updates the URL switch count, if needed.
+  void IncrementUrlIndex();
+
+  // Increments the failure count of the current URL. If the configured max
+  // failure count is reached for this URL, it advances the current URL index
+  // to the next URL and resets the failure count for that URL.
+  void IncrementFailureCount();
+
+  // Updates the backoff expiry time exponentially based on the current
+  // payload attempt number.
+  void UpdateBackoffExpiryTime();
+
+  // Updates the value of current download source based on the current URL
+  // index. If the download source is not one of the known sources, it's set
+  // to kNumDownloadSources.
+  void UpdateCurrentDownloadSource();
+
+  // Updates the various metrics corresponding with the given number of bytes
+  // that were downloaded recently.
+  void UpdateBytesDownloaded(size_t count);
+
+  // Calculates the PayloadType we're using.
+  PayloadType CalculatePayloadType();
+
+  // Collects and reports the various metrics related to an update attempt.
+  void CollectAndReportAttemptMetrics(ErrorCode code);
+
+  // Persists values related to the UpdateEngine.Attempt.* metrics so
+  // we can identify later if an update attempt ends abnormally.
+  void PersistAttemptMetrics();
+
+  // Clears persistent state previously set using AttemptMetricsPersist().
+  void ClearPersistedAttemptMetrics();
+
+  // Checks if persistent state previously set using AttemptMetricsPersist()
+  // exists and, if so, emits it with |attempt_result| set to
+  // metrics::AttemptResult::kAbnormalTermination.
+  void ReportAndClearPersistedAttemptMetrics();
+
+  // Collects and reports the various metrics related to a successful update.
+  void CollectAndReportSuccessfulUpdateMetrics();
+
+  // Checks if we were expecting to be running in the new version but the
+  // boot into the new version failed for some reason. If that's the case, an
+  // UMA metric is sent reporting the number of attempts the same applied
+  // payload was attempted to reboot. This function is called by UpdateAttempter
+  // every time the update engine starts and there's no reboot pending.
+  void ReportFailedBootIfNeeded();
+
+  // Resets all the persisted state values which are maintained relative to the
+  // current response signature. The response signature itself is not reset.
+  void ResetPersistedState();
+
+  // Resets the appropriate state related to download sources that need to be
+  // reset on a new update.
+  void ResetDownloadSourcesOnNewUpdate();
+
+  // 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);
+
+  // Calculates the response "signature", which is basically a string composed
+  // of the subset of the fields in the current response that affect the
+  // behavior of the PayloadState.
+  std::string CalculateResponseSignature();
+
+  // Initializes the current response signature from the persisted state.
+  void LoadResponseSignature();
+
+  // Sets the response signature to the given value. Also persists the value
+  // being set so that we resume from the save value in case of a process
+  // restart.
+  void SetResponseSignature(const std::string& response_signature);
+
+  // Initializes the payload attempt number from the persisted state.
+  void LoadPayloadAttemptNumber();
+
+  // Initializes the payload attempt number for full payloads from the persisted
+  // state.
+  void LoadFullPayloadAttemptNumber();
+
+  // Sets the payload attempt number to the given value. Also persists the
+  // value being set so that we resume from the same value in case of a process
+  // restart.
+  void SetPayloadAttemptNumber(int payload_attempt_number);
+
+  // Sets the payload attempt number for full updates to the given value. Also
+  // persists the value being set so that we resume from the same value in case
+  // of a process restart.
+  void SetFullPayloadAttemptNumber(int payload_attempt_number);
+
+  // Initializes the current URL index from the persisted state.
+  void LoadUrlIndex();
+
+  // Sets the current URL index to the given value. Also persists the value
+  // being set so that we resume from the same value in case of a process
+  // restart.
+  void SetUrlIndex(uint32_t url_index);
+
+  // Initializes the current URL's failure count from the persisted stae.
+  void LoadUrlFailureCount();
+
+  // Sets the current URL's failure count to the given value. Also persists the
+  // value being set so that we resume from the same value in case of a process
+  // restart.
+  void SetUrlFailureCount(uint32_t url_failure_count);
+
+  // Sets |url_switch_count_| to the given value and persists the value.
+  void SetUrlSwitchCount(uint32_t url_switch_count);
+
+  // Initializes |url_switch_count_| from the persisted stae.
+  void LoadUrlSwitchCount();
+
+  // Initializes the backoff expiry time from the persisted state.
+  void LoadBackoffExpiryTime();
+
+  // Sets the backoff expiry time to the given value. Also persists the value
+  // being set so that we resume from the same value in case of a process
+  // restart.
+  void SetBackoffExpiryTime(const base::Time& new_time);
+
+  // Initializes |update_timestamp_start_| from the persisted state.
+  void LoadUpdateTimestampStart();
+
+  // Sets |update_timestamp_start_| to the given value and persists the value.
+  void SetUpdateTimestampStart(const base::Time& value);
+
+  // Sets |update_timestamp_end_| to the given value. This is not persisted
+  // as it happens at the end of the update process where state is deleted
+  // anyway.
+  void SetUpdateTimestampEnd(const base::Time& value);
+
+  // Initializes |update_duration_uptime_| from the persisted state.
+  void LoadUpdateDurationUptime();
+
+  // Helper method used in SetUpdateDurationUptime() and
+  // CalculateUpdateDurationUptime().
+  void SetUpdateDurationUptimeExtended(const base::TimeDelta& value,
+                                       const base::Time& timestamp,
+                                       bool use_logging);
+
+  // Sets |update_duration_uptime_| to the given value and persists
+  // the value and sets |update_duration_uptime_timestamp_| to the
+  // current monotonic time.
+  void SetUpdateDurationUptime(const base::TimeDelta& value);
+
+  // Adds the difference between current monotonic time and
+  // |update_duration_uptime_timestamp_| to |update_duration_uptime_| and
+  // sets |update_duration_uptime_timestamp_| to current monotonic time.
+  void CalculateUpdateDurationUptime();
+
+  // Returns the full key for a download source given the prefix.
+  std::string GetPrefsKey(const std::string& prefix, DownloadSource source);
+
+  // Loads the number of bytes that have been currently downloaded through the
+  // previous attempts from the persisted state for the given source. It's
+  // reset to 0 everytime we begin a full update and is continued from previous
+  // attempt if we're resuming the update.
+  void LoadCurrentBytesDownloaded(DownloadSource source);
+
+  // Sets the number of bytes that have been currently downloaded for the
+  // given source. This value is also persisted.
+  void SetCurrentBytesDownloaded(DownloadSource source,
+                                 uint64_t current_bytes_downloaded,
+                                 bool log);
+
+  // Loads the total number of bytes that have been downloaded (since the last
+  // successful update) from the persisted state for the given source. It's
+  // reset to 0 everytime we successfully apply an update and counts the bytes
+  // downloaded for both successful and failed attempts since then.
+  void LoadTotalBytesDownloaded(DownloadSource source);
+
+  // Sets the total number of bytes that have been downloaded so far for the
+  // given source. This value is also persisted.
+  void SetTotalBytesDownloaded(DownloadSource source,
+                               uint64_t total_bytes_downloaded,
+                               bool log);
+
+  // Loads the blacklisted version from our prefs file.
+  void LoadRollbackVersion();
+
+  // Blacklists this version from getting AU'd to until we receive a new update
+  // response.
+  void SetRollbackVersion(const std::string& rollback_version);
+
+  // Clears any blacklisted version.
+  void ResetRollbackVersion();
+
+  inline uint32_t GetUrlIndex() {
+    return url_index_;
+  }
+
+  // Computes the list of candidate URLs from the total list of payload URLs in
+  // the Omaha response.
+  void ComputeCandidateUrls();
+
+  // Sets |num_responses_seen_| and persist it to disk.
+  void SetNumResponsesSeen(int num_responses_seen);
+
+  // Initializes |num_responses_seen_| from persisted state.
+  void LoadNumResponsesSeen();
+
+  // Initializes |num_reboots_| from the persisted state.
+  void LoadNumReboots();
+
+  // Sets |num_reboots| for the update attempt. Also persists the
+  // value being set so that we resume from the same value in case of a process
+  // restart.
+  void SetNumReboots(uint32_t num_reboots);
+
+  // Checks to see if the device rebooted since the last call and if so
+  // increments num_reboots.
+  void UpdateNumReboots();
+
+  // Writes the current wall-clock time to the kPrefsSystemUpdatedMarker
+  // state variable.
+  void CreateSystemUpdatedMarkerFile();
+
+  // Called at program startup if the device booted into a new update.
+  // The |time_to_reboot| parameter contains the (wall-clock) duration
+  // from when the update successfully completed (the value written
+  // into the kPrefsSystemUpdatedMarker state variable) until the device
+  // was booted into the update (current wall-clock time).
+  void BootedIntoUpdate(base::TimeDelta time_to_reboot);
+
+  // Loads the |kPrefsP2PFirstAttemptTimestamp| state variable from disk
+  // into |p2p_first_attempt_timestamp_|.
+  void LoadP2PFirstAttemptTimestamp();
+
+  // Loads the |kPrefsP2PNumAttempts| state variable into |p2p_num_attempts_|.
+  void LoadP2PNumAttempts();
+
+  // Sets the |kPrefsP2PNumAttempts| state variable to |value|.
+  void SetP2PNumAttempts(int value);
+
+  // Sets the |kPrefsP2PFirstAttemptTimestamp| state variable to |time|.
+  void SetP2PFirstAttemptTimestamp(const base::Time& time);
+
+  // Loads the persisted scattering wallclock-based wait period.
+  void LoadScatteringWaitPeriod();
+
+  // 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_;
+
+  // Interface object with which we read/write persisted state. This must
+  // be set by calling the Initialize method before calling any other method.
+  // This object persists across powerwashes.
+  PrefsInterface* powerwash_safe_prefs_;
+
+  // This is the current response object from Omaha.
+  OmahaResponse response_;
+
+  // Whether P2P is being used for downloading and sharing.
+  bool using_p2p_for_downloading_;
+  bool using_p2p_for_sharing_;
+
+  // Stores the P2P download URL, if one is used.
+  std::string p2p_url_;
+
+  // The cached value of |kPrefsP2PFirstAttemptTimestamp|.
+  base::Time p2p_first_attempt_timestamp_;
+
+  // The cached value of |kPrefsP2PNumAttempts|.
+  int p2p_num_attempts_;
+
+  // This stores a "signature" of the current response. The signature here
+  // refers to a subset of the current response from Omaha.  Each update to
+  // this value is persisted so we resume from the same value in case of a
+  // process restart.
+  std::string response_signature_;
+
+  // The number of times we've tried to download the payload. This is
+  // incremented each time we download the payload successsfully or when we
+  // exhaust all failure limits for all URLs and are about to wrap around back
+  // to the first URL.  Each update to this value is persisted so we resume from
+  // the same value in case of a process restart.
+  int payload_attempt_number_;
+
+  // The number of times we've tried to download the payload in full. This is
+  // incremented each time we download the payload in full successsfully or
+  // when we exhaust all failure limits for all URLs and are about to wrap
+  // around back to the first URL.  Each update to this value is persisted so
+  // we resume from the same value in case of a process restart.
+  int full_payload_attempt_number_;
+
+  // 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
+  // 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.
+  int64_t url_index_;
+
+  // The count of failures encountered in the current attempt to download using
+  // the current URL (specified by url_index_).  Each update to this value is
+  // persisted so we resume from the same value in case of a process restart.
+  int64_t url_failure_count_;
+
+  // The number of times we've switched URLs.
+  int32_t url_switch_count_;
+
+  // The current download source based on the current URL. This value is
+  // not persisted as it can be recomputed everytime we update the URL.
+  // We're storing this so as not to recompute this on every few bytes of
+  // data we read from the socket.
+  DownloadSource current_download_source_;
+
+  // The number of different Omaha responses seen. Increases every time
+  // a new response is seen. Resets to 0 only when the system has been
+  // successfully updated.
+  int num_responses_seen_;
+
+  // The number of system reboots during an update attempt. Technically since
+  // we don't go out of our way to not update it when not attempting an update,
+  // also records the number of reboots before the next update attempt starts.
+  uint32_t num_reboots_;
+
+  // The timestamp until which we've to wait before attempting to download the
+  // payload again, so as to backoff repeated downloads.
+  base::Time backoff_expiry_time_;
+
+  // The most recently calculated value of the update duration.
+  base::TimeDelta update_duration_current_;
+
+  // The point in time (wall-clock) that the update was started.
+  base::Time update_timestamp_start_;
+
+  // The point in time (wall-clock) that the update ended. If the update
+  // is still in progress, this is set to the Epoch (e.g. 0).
+  base::Time update_timestamp_end_;
+
+  // The update duration uptime
+  base::TimeDelta update_duration_uptime_;
+
+  // The monotonic time when |update_duration_uptime_| was last set
+  base::Time update_duration_uptime_timestamp_;
+
+  // The number of bytes that have been downloaded for each source for each new
+  // update attempt. If we resume an update, we'll continue from the previous
+  // value, but if we get a new response or if the previous attempt failed,
+  // we'll reset this to 0 to start afresh. Each update to this value is
+  // persisted so we resume from the same value in case of a process restart.
+  // The extra index in the array is to no-op accidental access in case the
+  // return value from GetCurrentDownloadSource is used without validation.
+  uint64_t current_bytes_downloaded_[kNumDownloadSources + 1];
+
+  // The number of bytes that have been downloaded for each source since the
+  // the last successful update. This is used to compute the overhead we incur.
+  // Each update to this value is persisted so we resume from the same value in
+  // case of a process restart.
+  // The extra index in the array is to no-op accidental access in case the
+  // return value from GetCurrentDownloadSource is used without validation.
+  uint64_t total_bytes_downloaded_[kNumDownloadSources + 1];
+
+  // A small timespan used when comparing wall-clock times for coping
+  // with the fact that clocks drift and consequently are adjusted
+  // (either forwards or backwards) via NTP.
+  static const base::TimeDelta kDurationSlack;
+
+  // The ordered list of the subset of payload URL candidates which are
+  // allowed as per device policy.
+  std::vector<std::string> candidate_urls_;
+
+  // 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.
+  std::string rollback_version_;
+
+  // The number of bytes downloaded per attempt.
+  int64_t attempt_num_bytes_downloaded_;
+
+  // The boot time when the attempt was started.
+  base::Time attempt_start_time_boot_;
+
+  // The monotonic time when the attempt was started.
+  base::Time attempt_start_time_monotonic_;
+
+  // The connection type when the attempt started.
+  metrics::ConnectionType attempt_connection_type_;
+
+  // Whether we're currently rolling back.
+  AttemptType attempt_type_;
+
+  // The current scattering wallclock-based wait period.
+  base::TimeDelta scattering_wait_period_;
+
+  DISALLOW_COPY_AND_ASSIGN(PayloadState);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_STATE_H_
diff --git a/payload_state_interface.h b/payload_state_interface.h
new file mode 100644
index 0000000..40a13dd
--- /dev/null
+++ b/payload_state_interface.h
@@ -0,0 +1,199 @@
+//
+// 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.
+//
+
+#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/omaha_response.h"
+
+namespace chromeos_update_engine {
+
+// Describes the methods that need to be implemented by the PayloadState class.
+// This interface has been carved out to support mocking of the PayloadState
+// object.
+class PayloadStateInterface {
+ public:
+  virtual ~PayloadStateInterface() = default;
+
+  // Sets the internal payload state based on the given Omaha response. This
+  // response could be the same or different from the one for which we've stored
+  // the internal state. If it's different, then this method resets all the
+  // internal state corresponding to the old response. Since the Omaha response
+  // has a lot of fields that are not related to payload state, it uses only
+  // a subset of the fields in the Omaha response to compare equality.
+  virtual void SetResponse(const OmahaResponse& response) = 0;
+
+  // This method should be called whenever we have completed downloading all
+  // the bytes of a payload and have verified that its size and hash match the
+  // expected values. We use this notificaiton to increment the payload attempt
+  // number so that the throttle the next attempt to download the same payload
+  // (in case there's an error in subsequent steps such as post-install)
+  // appropriately.
+  virtual void DownloadComplete() = 0;
+
+  // This method should be called whenever we receive new bytes from the
+  // network for the current payload. We use this notification to reset the
+  // failure count for a given URL since receipt of some bytes means we are
+  // able to make forward progress with the current URL.
+  virtual void DownloadProgress(size_t count) = 0;
+
+  // This method should be called every time we resume an update attempt.
+  virtual void UpdateResumed() = 0;
+
+  // This method should be called every time we begin a new update. This method
+  // should not be called when we resume an update from the previously
+  // downloaded point. This is used to reset the metrics for each new update.
+  virtual void UpdateRestarted() = 0;
+
+  // This method should be called once after an update attempt succeeds. This
+  // is when the relevant UMA metrics that are tracked on a per-update-basis
+  // are uploaded to the UMA server.
+  virtual void UpdateSucceeded() = 0;
+
+  // This method should be called whenever an update attempt fails with the
+  // given error code. We use this notification to update the payload state
+  // depending on the type of the error that happened.
+  virtual void UpdateFailed(ErrorCode error) = 0;
+
+  // This method should be called whenever a succeeded update is canceled, and
+  // thus can only be called after UpdateSucceeded(). This is currently used
+  // only for manual testing using the update_engine_client.
+  virtual void ResetUpdateStatus() = 0;
+
+  // This method should be called every time we initiate a Rollback.
+  virtual void Rollback() = 0;
+
+  // Sets the expectations to boot into the new version in the next reboot.
+  // This function is called every time a new update is marked as ready by
+  // UpdateSuccess(). |target_version_uid| is an unique identifier of the
+  // applied payload. It can be any string, as long as the same string is used
+  // for the same payload.
+  virtual void ExpectRebootInNewVersion(
+      const std::string& target_version_uid) = 0;
+
+  // Sets whether P2P is being used to download the update payload. This
+  // is used to keep track of download sources being used and should be called
+  // before the transfer begins.
+  virtual void SetUsingP2PForDownloading(bool value) = 0;
+
+  // Sets whether P2P is being used for sharing the update payloads.
+  virtual void SetUsingP2PForSharing(bool value) = 0;
+
+  // Returns true if we should backoff the current download attempt.
+  // False otherwise.
+  virtual bool ShouldBackoffDownload() = 0;
+
+  // Returns the currently stored response "signature". The signature  is a
+  // subset of fields that are of interest to the PayloadState behavior.
+  virtual std::string GetResponseSignature() = 0;
+
+  // Returns the payload attempt number.
+  virtual int GetPayloadAttemptNumber() = 0;
+
+  // Returns the payload attempt number of the attempted full payload. Returns
+  // 0 for delta payloads.
+  virtual int GetFullPayloadAttemptNumber() = 0;
+
+  // Returns the current URL. Returns an empty string if there's no valid URL.
+  virtual std::string GetCurrentUrl() = 0;
+
+  // Returns the current URL's failure count.
+  virtual uint32_t GetUrlFailureCount() = 0;
+
+  // Returns the total number of times a new URL has been switched to
+  // for the current response.
+  virtual uint32_t GetUrlSwitchCount() = 0;
+
+  // Returns the total number of different responses seen since the
+  // last successful update.
+  virtual int GetNumResponsesSeen() = 0;
+
+  // Returns the expiry time for the current backoff period.
+  virtual base::Time GetBackoffExpiryTime() = 0;
+
+  // Returns the elapsed time used for this update, including time
+  // where the device is powered off and sleeping. If the
+  // update has not completed, returns the time spent so far.
+  virtual base::TimeDelta GetUpdateDuration() = 0;
+
+  // Returns the time used for this update not including time when
+  // the device is powered off or sleeping. If the update has not
+  // completed, returns the time spent so far.
+  virtual base::TimeDelta GetUpdateDurationUptime() = 0;
+
+  // Returns the number of bytes that have been downloaded for each source for
+  // each new update attempt. If we resume an update, we'll continue from the
+  // previous value, but if we get a new response or if the previous attempt
+  // failed, we'll reset this to 0 to start afresh.
+  virtual uint64_t GetCurrentBytesDownloaded(DownloadSource source) = 0;
+
+  // Returns the total number of bytes that have been downloaded for each
+  // source since the the last successful update. This is used to compute the
+  // overhead we incur.
+  virtual uint64_t GetTotalBytesDownloaded(DownloadSource source) = 0;
+
+  // Returns the reboot count for this update attempt.
+  virtual uint32_t GetNumReboots() = 0;
+
+  // Called at update_engine startup to do various house-keeping.
+  virtual void UpdateEngineStarted() = 0;
+
+  // Returns the version from before a rollback if our last update was a
+  // rollback.
+  virtual std::string GetRollbackVersion() = 0;
+
+  // Returns the value of number of attempts we've attempted to
+  // download the payload via p2p.
+  virtual int GetP2PNumAttempts() = 0;
+
+  // Returns the value of timestamp of the first time we've attempted
+  // to download the payload via p2p.
+  virtual base::Time GetP2PFirstAttemptTimestamp() = 0;
+
+  // Should be called every time we decide to use p2p for an update
+  // attempt. This is used to increase the p2p attempt counter and
+  // set the timestamp for first attempt.
+  virtual void P2PNewAttempt() = 0;
+
+  // Returns |true| if we are allowed to continue using p2p for
+  // downloading and |false| otherwise. This is done by recording
+  // and examining how many attempts have been done already as well
+  // as when the first attempt was.
+  virtual bool P2PAttemptAllowed() = 0;
+
+  // Gets the values previously set with SetUsingP2PForDownloading() and
+  // SetUsingP2PForSharing().
+  virtual bool GetUsingP2PForDownloading() const = 0;
+  virtual bool GetUsingP2PForSharing() const = 0;
+
+  // Returns the current (persisted) scattering wallclock-based wait period.
+  virtual base::TimeDelta GetScatteringWaitPeriod() = 0;
+
+  // Sets and persists the scattering wallclock-based wait period.
+  virtual void SetScatteringWaitPeriod(base::TimeDelta wait_period) = 0;
+
+  // Sets/gets the P2P download URL, if one is to be used.
+  virtual void SetP2PUrl(const std::string& url) = 0;
+  virtual std::string GetP2PUrl() const = 0;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_STATE_INTERFACE_H_
diff --git a/payload_state_unittest.cc b/payload_state_unittest.cc
new file mode 100644
index 0000000..a8067dc
--- /dev/null
+++ b/payload_state_unittest.cc
@@ -0,0 +1,1663 @@
+//
+// 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_state.h"
+
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/strings/stringprintf.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/constants.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_prefs.h"
+#include "update_engine/common/prefs.h"
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/fake_system_state.h"
+#include "update_engine/omaha_request_action.h"
+
+using base::Time;
+using base::TimeDelta;
+using std::string;
+using testing::AnyNumber;
+using testing::AtLeast;
+using testing::Mock;
+using testing::NiceMock;
+using testing::Return;
+using testing::SetArgumentPointee;
+using testing::_;
+
+namespace chromeos_update_engine {
+
+const char* kCurrentBytesDownloadedFromHttps =
+  "current-bytes-downloaded-from-HttpsServer";
+const char* kTotalBytesDownloadedFromHttps =
+  "total-bytes-downloaded-from-HttpsServer";
+const char* kCurrentBytesDownloadedFromHttp =
+  "current-bytes-downloaded-from-HttpServer";
+const char* kTotalBytesDownloadedFromHttp =
+  "total-bytes-downloaded-from-HttpServer";
+const char* kCurrentBytesDownloadedFromHttpPeer =
+  "current-bytes-downloaded-from-HttpPeer";
+const char* kTotalBytesDownloadedFromHttpPeer =
+  "total-bytes-downloaded-from-HttpPeer";
+
+static void SetupPayloadStateWith2Urls(string hash,
+                                       bool http_enabled,
+                                       PayloadState* payload_state,
+                                       OmahaResponse* response) {
+  response->payload_urls.clear();
+  response->payload_urls.push_back("http://test");
+  response->payload_urls.push_back("https://test");
+  response->size = 523456789;
+  response->hash = hash;
+  response->metadata_size = 558123;
+  response->metadata_signature = "metasign";
+  response->max_failure_count_per_url = 3;
+  payload_state->SetResponse(*response);
+  string stored_response_sign = payload_state->GetResponseSignature();
+
+  string expected_url_https_only =
+      "NumURLs = 1\n"
+      "Candidate Url0 = https://test\n";
+
+  string expected_urls_both =
+      "NumURLs = 2\n"
+      "Candidate Url0 = http://test\n"
+      "Candidate Url1 = https://test\n";
+
+  string expected_response_sign =
+      (http_enabled ? expected_urls_both : expected_url_https_only) +
+      base::StringPrintf("Payload Size = 523456789\n"
+                         "Payload Sha256 Hash = %s\n"
+                         "Metadata Size = 558123\n"
+                         "Metadata Signature = metasign\n"
+                         "Is Delta Payload = %d\n"
+                         "Max Failure Count Per Url = %d\n"
+                         "Disable Payload Backoff = %d\n",
+                         hash.c_str(),
+                         response->is_delta_payload,
+                         response->max_failure_count_per_url,
+                         response->disable_payload_backoff);
+  EXPECT_EQ(expected_response_sign, stored_response_sign);
+}
+
+class PayloadStateTest : public ::testing::Test { };
+
+TEST(PayloadStateTest, DidYouAddANewErrorCode) {
+  if (static_cast<int>(ErrorCode::kUmaReportedMax) != 48) {
+    LOG(ERROR) << "The following failure is intentional. If you added a new "
+               << "ErrorCode enum value, make sure to add it to the "
+               << "PayloadState::UpdateFailed method and then update this test "
+               << "to the new value of ErrorCode::kUmaReportedMax, which is "
+               << ErrorCode::kUmaReportedMax;
+    EXPECT_FALSE("Please see the log line above");
+  }
+}
+
+TEST(PayloadStateTest, SetResponseWorksWithEmptyResponse) {
+  OmahaResponse response;
+  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));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsBackoffExpiryTime, 0)).Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlIndex, 0)).Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlFailureCount, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsUpdateTimestampStart, _))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsUpdateDurationUptime, _))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kCurrentBytesDownloadedFromHttps, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kCurrentBytesDownloadedFromHttp, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kCurrentBytesDownloadedFromHttpPeer, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsNumReboots, 0)).Times(AtLeast(1));
+  PayloadState payload_state;
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+  payload_state.SetResponse(response);
+  string stored_response_sign = payload_state.GetResponseSignature();
+  string expected_response_sign = "NumURLs = 0\n"
+                                  "Payload Size = 0\n"
+                                  "Payload Sha256 Hash = \n"
+                                  "Metadata Size = 0\n"
+                                  "Metadata Signature = \n"
+                                  "Is Delta Payload = 0\n"
+                                  "Max Failure Count Per Url = 0\n"
+                                  "Disable Payload Backoff = 0\n";
+  EXPECT_EQ(expected_response_sign, stored_response_sign);
+  EXPECT_EQ("", payload_state.GetCurrentUrl());
+  EXPECT_EQ(0, payload_state.GetUrlFailureCount());
+  EXPECT_EQ(0, payload_state.GetUrlSwitchCount());
+  EXPECT_EQ(1, payload_state.GetNumResponsesSeen());
+}
+
+TEST(PayloadStateTest, SetResponseWorksWithSingleUrl) {
+  OmahaResponse response;
+  response.payload_urls.push_back("https://single.url.test");
+  response.size = 123456789;
+  response.hash = "hash";
+  response.metadata_size = 58123;
+  response.metadata_signature = "msign";
+  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));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsBackoffExpiryTime, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlIndex, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlFailureCount, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsUpdateTimestampStart, _))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsUpdateDurationUptime, _))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kCurrentBytesDownloadedFromHttps, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kCurrentBytesDownloadedFromHttp, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kCurrentBytesDownloadedFromHttpPeer, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsNumReboots, 0))
+      .Times(AtLeast(1));
+  PayloadState payload_state;
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+  payload_state.SetResponse(response);
+  string stored_response_sign = payload_state.GetResponseSignature();
+  string expected_response_sign = "NumURLs = 1\n"
+                                  "Candidate Url0 = https://single.url.test\n"
+                                  "Payload Size = 123456789\n"
+                                  "Payload Sha256 Hash = hash\n"
+                                  "Metadata Size = 58123\n"
+                                  "Metadata Signature = msign\n"
+                                  "Is Delta Payload = 0\n"
+                                  "Max Failure Count Per Url = 0\n"
+                                  "Disable Payload Backoff = 0\n";
+  EXPECT_EQ(expected_response_sign, stored_response_sign);
+  EXPECT_EQ("https://single.url.test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(0, payload_state.GetUrlFailureCount());
+  EXPECT_EQ(0, payload_state.GetUrlSwitchCount());
+  EXPECT_EQ(1, payload_state.GetNumResponsesSeen());
+}
+
+TEST(PayloadStateTest, SetResponseWorksWithMultipleUrls) {
+  OmahaResponse response;
+  response.payload_urls.push_back("http://multiple.url.test");
+  response.payload_urls.push_back("https://multiple.url.test");
+  response.size = 523456789;
+  response.hash = "rhash";
+  response.metadata_size = 558123;
+  response.metadata_signature = "metasign";
+  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));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsBackoffExpiryTime, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlIndex, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlFailureCount, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kCurrentBytesDownloadedFromHttps, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kCurrentBytesDownloadedFromHttp, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kCurrentBytesDownloadedFromHttpPeer, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsNumReboots, 0))
+      .Times(AtLeast(1));
+
+  PayloadState payload_state;
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+  payload_state.SetResponse(response);
+  string stored_response_sign = payload_state.GetResponseSignature();
+  string expected_response_sign = "NumURLs = 2\n"
+                                  "Candidate Url0 = http://multiple.url.test\n"
+                                  "Candidate Url1 = https://multiple.url.test\n"
+                                  "Payload Size = 523456789\n"
+                                  "Payload Sha256 Hash = rhash\n"
+                                  "Metadata Size = 558123\n"
+                                  "Metadata Signature = metasign\n"
+                                  "Is Delta Payload = 0\n"
+                                  "Max Failure Count Per Url = 0\n"
+                                  "Disable Payload Backoff = 0\n";
+  EXPECT_EQ(expected_response_sign, stored_response_sign);
+  EXPECT_EQ("http://multiple.url.test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(0, payload_state.GetUrlFailureCount());
+  EXPECT_EQ(0, payload_state.GetUrlSwitchCount());
+  EXPECT_EQ(1, payload_state.GetNumResponsesSeen());
+}
+
+TEST(PayloadStateTest, CanAdvanceUrlIndexCorrectly) {
+  OmahaResponse response;
+  FakeSystemState fake_system_state;
+  NiceMock<MockPrefs>* prefs = fake_system_state.mock_prefs();
+  PayloadState payload_state;
+
+  EXPECT_CALL(*prefs, SetInt64(_, _)).Times(AnyNumber());
+  // Payload attempt should start with 0 and then advance to 1.
+  EXPECT_CALL(*prefs, SetInt64(kPrefsPayloadAttemptNumber, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsPayloadAttemptNumber, 1))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 1))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsBackoffExpiryTime, _)).Times(AtLeast(2));
+
+  // Reboots will be set
+  EXPECT_CALL(*prefs, SetInt64(kPrefsNumReboots, _)).Times(AtLeast(1));
+
+  // Url index should go from 0 to 1 twice.
+  EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlIndex, 0)).Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlIndex, 1)).Times(AtLeast(1));
+
+  // Failure count should be called each times url index is set, so that's
+  // 4 times for this test.
+  EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlFailureCount, 0))
+    .Times(AtLeast(4));
+
+  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.
+  SetupPayloadStateWith2Urls("Hash1235", true, &payload_state, &response);
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
+
+  // Verify that on the first error, the URL index advances to 1.
+  ErrorCode error = ErrorCode::kDownloadMetadataSignatureMismatch;
+  payload_state.UpdateFailed(error);
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
+
+  // Verify that on the next error, the URL index wraps around to 0.
+  payload_state.UpdateFailed(error);
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
+
+  // Verify that on the next error, it again advances to 1.
+  payload_state.UpdateFailed(error);
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
+
+  // Verify that we switched URLs three times
+  EXPECT_EQ(3, payload_state.GetUrlSwitchCount());
+}
+
+TEST(PayloadStateTest, NewResponseResetsPayloadState) {
+  OmahaResponse response;
+  FakeSystemState fake_system_state;
+  PayloadState payload_state;
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(_, _, _, _, _))
+    .Times(AnyNumber());
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
+    .Times(AnyNumber());
+
+  // Set the first response.
+  SetupPayloadStateWith2Urls("Hash5823", true, &payload_state, &response);
+  EXPECT_EQ(1, payload_state.GetNumResponsesSeen());
+
+  // Advance the URL index to 1 by faking an error.
+  ErrorCode error = ErrorCode::kDownloadMetadataSignatureMismatch;
+  payload_state.UpdateFailed(error);
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(1, payload_state.GetUrlSwitchCount());
+
+  // Now, slightly change the response and set it again.
+  SetupPayloadStateWith2Urls("Hash8225", true, &payload_state, &response);
+  EXPECT_EQ(2, payload_state.GetNumResponsesSeen());
+
+  // Fake an error again.
+  payload_state.UpdateFailed(error);
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(1, payload_state.GetUrlSwitchCount());
+
+  // Return a third different response.
+  SetupPayloadStateWith2Urls("Hash9999", true, &payload_state, &response);
+  EXPECT_EQ(3, payload_state.GetNumResponsesSeen());
+
+  // Make sure the url index was reset to 0 because of the new response.
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(0, payload_state.GetUrlFailureCount());
+  EXPECT_EQ(0, payload_state.GetUrlSwitchCount());
+  EXPECT_EQ(0,
+            payload_state.GetCurrentBytesDownloaded(kDownloadSourceHttpServer));
+  EXPECT_EQ(0,
+            payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpServer));
+  EXPECT_EQ(0, payload_state.GetCurrentBytesDownloaded(
+                 kDownloadSourceHttpsServer));
+  EXPECT_EQ(0,
+            payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpsServer));
+}
+
+TEST(PayloadStateTest, AllCountersGetUpdatedProperlyOnErrorCodesAndEvents) {
+  OmahaResponse response;
+  PayloadState payload_state;
+  FakeSystemState fake_system_state;
+  int progress_bytes = 100;
+  NiceMock<MockPrefs>* prefs = fake_system_state.mock_prefs();
+
+  EXPECT_CALL(*prefs, SetInt64(_, _)).Times(AnyNumber());
+  EXPECT_CALL(*prefs, SetInt64(kPrefsPayloadAttemptNumber, 0))
+    .Times(AtLeast(2));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsPayloadAttemptNumber, 1))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsPayloadAttemptNumber, 2))
+    .Times(AtLeast(1));
+
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 0))
+    .Times(AtLeast(2));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 1))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 2))
+    .Times(AtLeast(1));
+
+  EXPECT_CALL(*prefs, SetInt64(kPrefsBackoffExpiryTime, _)).Times(AtLeast(4));
+
+  EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlIndex, 0)).Times(AtLeast(4));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlIndex, 1)).Times(AtLeast(2));
+
+  EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlFailureCount, 0))
+    .Times(AtLeast(7));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlFailureCount, 1))
+    .Times(AtLeast(2));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlFailureCount, 2))
+    .Times(AtLeast(1));
+
+  EXPECT_CALL(*prefs, SetInt64(kPrefsUpdateTimestampStart, _))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsUpdateDurationUptime, _))
+    .Times(AtLeast(1));
+
+  EXPECT_CALL(*prefs, SetInt64(kCurrentBytesDownloadedFromHttps, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kCurrentBytesDownloadedFromHttp, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kCurrentBytesDownloadedFromHttpPeer, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kCurrentBytesDownloadedFromHttp, progress_bytes))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kTotalBytesDownloadedFromHttp, progress_bytes))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsNumReboots, 0))
+      .Times(AtLeast(1));
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+
+  SetupPayloadStateWith2Urls("Hash5873", true, &payload_state, &response);
+  EXPECT_EQ(1, payload_state.GetNumResponsesSeen());
+
+  // This should advance the URL index.
+  payload_state.UpdateFailed(ErrorCode::kDownloadMetadataSignatureMismatch);
+  EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(0, payload_state.GetUrlFailureCount());
+  EXPECT_EQ(1, payload_state.GetUrlSwitchCount());
+
+  // This should advance the failure count only.
+  payload_state.UpdateFailed(ErrorCode::kDownloadTransferError);
+  EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(1, payload_state.GetUrlFailureCount());
+  EXPECT_EQ(1, payload_state.GetUrlSwitchCount());
+
+  // This should advance the failure count only.
+  payload_state.UpdateFailed(ErrorCode::kDownloadTransferError);
+  EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(2, payload_state.GetUrlFailureCount());
+  EXPECT_EQ(1, payload_state.GetUrlSwitchCount());
+
+  // This should advance the URL index as we've reached the
+  // max failure count and reset the failure count for the new URL index.
+  // This should also wrap around the URL index and thus cause the payload
+  // attempt number to be incremented.
+  payload_state.UpdateFailed(ErrorCode::kDownloadTransferError);
+  EXPECT_EQ(1, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(1, payload_state.GetFullPayloadAttemptNumber());
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(0, payload_state.GetUrlFailureCount());
+  EXPECT_EQ(2, payload_state.GetUrlSwitchCount());
+  EXPECT_TRUE(payload_state.ShouldBackoffDownload());
+
+  // This should advance the URL index.
+  payload_state.UpdateFailed(ErrorCode::kPayloadHashMismatchError);
+  EXPECT_EQ(1, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(1, payload_state.GetFullPayloadAttemptNumber());
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(0, payload_state.GetUrlFailureCount());
+  EXPECT_EQ(3, payload_state.GetUrlSwitchCount());
+  EXPECT_TRUE(payload_state.ShouldBackoffDownload());
+
+  // This should advance the URL index and payload attempt number due to
+  // wrap-around of URL index.
+  payload_state.UpdateFailed(ErrorCode::kDownloadMetadataSignatureMissingError);
+  EXPECT_EQ(2, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(2, payload_state.GetFullPayloadAttemptNumber());
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(0, payload_state.GetUrlFailureCount());
+  EXPECT_EQ(4, payload_state.GetUrlSwitchCount());
+  EXPECT_TRUE(payload_state.ShouldBackoffDownload());
+
+  // This HTTP error code should only increase the failure count.
+  payload_state.UpdateFailed(static_cast<ErrorCode>(
+      static_cast<int>(ErrorCode::kOmahaRequestHTTPResponseBase) + 404));
+  EXPECT_EQ(2, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(2, payload_state.GetFullPayloadAttemptNumber());
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(1, payload_state.GetUrlFailureCount());
+  EXPECT_EQ(4, payload_state.GetUrlSwitchCount());
+  EXPECT_TRUE(payload_state.ShouldBackoffDownload());
+
+  // And that failure count should be reset when we download some bytes
+  // afterwards.
+  payload_state.DownloadProgress(progress_bytes);
+  EXPECT_EQ(2, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(2, payload_state.GetFullPayloadAttemptNumber());
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(0, payload_state.GetUrlFailureCount());
+  EXPECT_EQ(4, payload_state.GetUrlSwitchCount());
+  EXPECT_TRUE(payload_state.ShouldBackoffDownload());
+
+  // Now, slightly change the response and set it again.
+  SetupPayloadStateWith2Urls("Hash8532", true, &payload_state, &response);
+  EXPECT_EQ(2, payload_state.GetNumResponsesSeen());
+
+  // Make sure the url index was reset to 0 because of the new response.
+  EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(0, payload_state.GetUrlFailureCount());
+  EXPECT_EQ(0, payload_state.GetUrlSwitchCount());
+  EXPECT_FALSE(payload_state.ShouldBackoffDownload());
+}
+
+TEST(PayloadStateTest, PayloadAttemptNumberIncreasesOnSuccessfulFullDownload) {
+  OmahaResponse response;
+  response.is_delta_payload = false;
+  PayloadState payload_state;
+  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));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsPayloadAttemptNumber, 1))
+    .Times(AtLeast(1));
+
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 1))
+    .Times(AtLeast(1));
+
+  EXPECT_CALL(*prefs, SetInt64(kPrefsBackoffExpiryTime, _))
+    .Times(AtLeast(2));
+
+  EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlIndex, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlFailureCount, 0))
+    .Times(AtLeast(1));
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+
+  SetupPayloadStateWith2Urls("Hash8593", true, &payload_state, &response);
+
+  // This should just advance the payload attempt number;
+  EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
+  payload_state.DownloadComplete();
+  EXPECT_EQ(1, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(1, payload_state.GetFullPayloadAttemptNumber());
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(0, payload_state.GetUrlFailureCount());
+  EXPECT_EQ(0, payload_state.GetUrlSwitchCount());
+}
+
+TEST(PayloadStateTest, PayloadAttemptNumberIncreasesOnSuccessfulDeltaDownload) {
+  OmahaResponse response;
+  response.is_delta_payload = true;
+  PayloadState payload_state;
+  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));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsPayloadAttemptNumber, 1))
+    .Times(AtLeast(1));
+
+  // kPrefsFullPayloadAttemptNumber is not incremented for delta payloads.
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 0))
+    .Times(AtLeast(1));
+
+  EXPECT_CALL(*prefs, SetInt64(kPrefsBackoffExpiryTime, _))
+    .Times(1);
+
+  EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlIndex, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlFailureCount, 0))
+    .Times(AtLeast(1));
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+
+  SetupPayloadStateWith2Urls("Hash8593", true, &payload_state, &response);
+
+  // This should just advance the payload attempt number;
+  EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
+  payload_state.DownloadComplete();
+  EXPECT_EQ(1, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(0, payload_state.GetUrlFailureCount());
+  EXPECT_EQ(0, payload_state.GetUrlSwitchCount());
+}
+
+TEST(PayloadStateTest, SetResponseResetsInvalidUrlIndex) {
+  OmahaResponse response;
+  PayloadState payload_state;
+  FakeSystemState fake_system_state;
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+  SetupPayloadStateWith2Urls("Hash4427", true, &payload_state, &response);
+
+  // Generate enough events to advance URL index, failure count and
+  // payload attempt number all to 1.
+  payload_state.DownloadComplete();
+  payload_state.UpdateFailed(ErrorCode::kDownloadMetadataSignatureMismatch);
+  payload_state.UpdateFailed(ErrorCode::kDownloadTransferError);
+  EXPECT_EQ(1, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(1, payload_state.GetFullPayloadAttemptNumber());
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(1, payload_state.GetUrlFailureCount());
+  EXPECT_EQ(1, payload_state.GetUrlSwitchCount());
+
+  // Now, simulate a corrupted url index on persisted store which gets
+  // 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(*prefs2, GetInt64(kPrefsFullPayloadAttemptNumber, _))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs2, GetInt64(kPrefsCurrentUrlIndex, _))
+      .WillRepeatedly(DoAll(SetArgumentPointee<1>(2), Return(true)));
+  EXPECT_CALL(*prefs2, GetInt64(kPrefsCurrentUrlFailureCount, _))
+    .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(&fake_system_state2));
+  SetupPayloadStateWith2Urls("Hash4427", true, &payload_state, &response);
+
+  // Make sure all counters get reset to 0 because of the corrupted URL index
+  // we supplied above.
+  EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(0, payload_state.GetUrlFailureCount());
+  EXPECT_EQ(0, payload_state.GetUrlSwitchCount());
+}
+
+TEST(PayloadStateTest, NoBackoffInteractiveChecks) {
+  OmahaResponse response;
+  response.is_delta_payload = false;
+  PayloadState payload_state;
+  FakeSystemState fake_system_state;
+  OmahaRequestParams params(&fake_system_state);
+  params.Init("", "", true);  // is_interactive = True.
+  fake_system_state.set_request_params(&params);
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+  SetupPayloadStateWith2Urls("Hash6437", true, &payload_state, &response);
+
+  // Simulate two failures (enough to cause payload backoff) and check
+  // again that we're ready to re-download without any backoff as this is
+  // an interactive check.
+  payload_state.UpdateFailed(ErrorCode::kDownloadMetadataSignatureMismatch);
+  payload_state.UpdateFailed(ErrorCode::kDownloadMetadataSignatureMismatch);
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(1, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(1, payload_state.GetFullPayloadAttemptNumber());
+  EXPECT_FALSE(payload_state.ShouldBackoffDownload());
+}
+
+TEST(PayloadStateTest, NoBackoffForP2PUpdates) {
+  OmahaResponse response;
+  response.is_delta_payload = false;
+  PayloadState payload_state;
+  FakeSystemState fake_system_state;
+  OmahaRequestParams params(&fake_system_state);
+  params.Init("", "", false);  // is_interactive = False.
+  fake_system_state.set_request_params(&params);
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+  SetupPayloadStateWith2Urls("Hash6437", true, &payload_state, &response);
+
+  // Simulate two failures (enough to cause payload backoff) and check
+  // again that we're ready to re-download without any backoff as this is
+  // an interactive check.
+  payload_state.UpdateFailed(ErrorCode::kDownloadMetadataSignatureMismatch);
+  payload_state.UpdateFailed(ErrorCode::kDownloadMetadataSignatureMismatch);
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(1, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(1, payload_state.GetFullPayloadAttemptNumber());
+  // Set p2p url.
+  payload_state.SetUsingP2PForDownloading(true);
+  payload_state.SetP2PUrl("http://mypeer:52909/path/to/file");
+  // Should not backoff for p2p updates.
+  EXPECT_FALSE(payload_state.ShouldBackoffDownload());
+
+  payload_state.SetP2PUrl("");
+  // No actual p2p update if no url is provided.
+  EXPECT_TRUE(payload_state.ShouldBackoffDownload());
+}
+
+TEST(PayloadStateTest, NoBackoffForDeltaPayloads) {
+  OmahaResponse response;
+  response.is_delta_payload = true;
+  PayloadState payload_state;
+  FakeSystemState fake_system_state;
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+  SetupPayloadStateWith2Urls("Hash6437", true, &payload_state, &response);
+
+  // Simulate a successful download and see that we're ready to download
+  // again without any backoff as this is a delta payload.
+  payload_state.DownloadComplete();
+  EXPECT_EQ(1, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
+  EXPECT_FALSE(payload_state.ShouldBackoffDownload());
+
+  // Simulate two failures (enough to cause payload backoff) and check
+  // again that we're ready to re-download without any backoff as this is
+  // a delta payload.
+  payload_state.UpdateFailed(ErrorCode::kDownloadMetadataSignatureMismatch);
+  payload_state.UpdateFailed(ErrorCode::kDownloadMetadataSignatureMismatch);
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(2, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
+  EXPECT_FALSE(payload_state.ShouldBackoffDownload());
+}
+
+static void CheckPayloadBackoffState(PayloadState* payload_state,
+                                     int expected_attempt_number,
+                                     TimeDelta expected_days) {
+  payload_state->DownloadComplete();
+  EXPECT_EQ(expected_attempt_number,
+      payload_state->GetFullPayloadAttemptNumber());
+  EXPECT_TRUE(payload_state->ShouldBackoffDownload());
+  Time backoff_expiry_time = payload_state->GetBackoffExpiryTime();
+  // Add 1 hour extra to the 6 hour fuzz check to tolerate edge cases.
+  TimeDelta max_fuzz_delta = TimeDelta::FromHours(7);
+  Time expected_min_time = Time::Now() + expected_days - max_fuzz_delta;
+  Time expected_max_time = Time::Now() + expected_days + max_fuzz_delta;
+  EXPECT_LT(expected_min_time.ToInternalValue(),
+            backoff_expiry_time.ToInternalValue());
+  EXPECT_GT(expected_max_time.ToInternalValue(),
+            backoff_expiry_time.ToInternalValue());
+}
+
+TEST(PayloadStateTest, BackoffPeriodsAreInCorrectRange) {
+  OmahaResponse response;
+  response.is_delta_payload = false;
+  PayloadState payload_state;
+  FakeSystemState fake_system_state;
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+  SetupPayloadStateWith2Urls("Hash8939", true, &payload_state, &response);
+
+  CheckPayloadBackoffState(&payload_state, 1,  TimeDelta::FromDays(1));
+  CheckPayloadBackoffState(&payload_state, 2,  TimeDelta::FromDays(2));
+  CheckPayloadBackoffState(&payload_state, 3,  TimeDelta::FromDays(4));
+  CheckPayloadBackoffState(&payload_state, 4,  TimeDelta::FromDays(8));
+  CheckPayloadBackoffState(&payload_state, 5,  TimeDelta::FromDays(16));
+  CheckPayloadBackoffState(&payload_state, 6,  TimeDelta::FromDays(16));
+  CheckPayloadBackoffState(&payload_state, 7,  TimeDelta::FromDays(16));
+  CheckPayloadBackoffState(&payload_state, 8,  TimeDelta::FromDays(16));
+  CheckPayloadBackoffState(&payload_state, 9,  TimeDelta::FromDays(16));
+  CheckPayloadBackoffState(&payload_state, 10,  TimeDelta::FromDays(16));
+}
+
+TEST(PayloadStateTest, BackoffLogicCanBeDisabled) {
+  OmahaResponse response;
+  response.disable_payload_backoff = true;
+  PayloadState payload_state;
+  FakeSystemState fake_system_state;
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+  SetupPayloadStateWith2Urls("Hash8939", true, &payload_state, &response);
+
+  // Simulate a successful download and see that we are ready to download
+  // again without any backoff.
+  payload_state.DownloadComplete();
+  EXPECT_EQ(1, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(1, payload_state.GetFullPayloadAttemptNumber());
+  EXPECT_FALSE(payload_state.ShouldBackoffDownload());
+
+  // Test again, this time by simulating two errors that would cause
+  // the payload attempt number to increment due to wrap around. And
+  // check that we are still ready to re-download without any backoff.
+  payload_state.UpdateFailed(ErrorCode::kDownloadMetadataSignatureMismatch);
+  payload_state.UpdateFailed(ErrorCode::kDownloadMetadataSignatureMismatch);
+  EXPECT_EQ(2, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(2, payload_state.GetFullPayloadAttemptNumber());
+  EXPECT_FALSE(payload_state.ShouldBackoffDownload());
+}
+
+TEST(PayloadStateTest, BytesDownloadedMetricsGetAddedToCorrectSources) {
+  OmahaResponse response;
+  response.disable_payload_backoff = true;
+  PayloadState payload_state;
+  FakeSystemState fake_system_state;
+  int https_total = 0;
+  int http_total = 0;
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+  SetupPayloadStateWith2Urls("Hash3286", true, &payload_state, &response);
+  EXPECT_EQ(1, payload_state.GetNumResponsesSeen());
+
+  // Simulate a previous attempt with in order to set an initial non-zero value
+  // for the total bytes downloaded for HTTP.
+  int prev_chunk = 323456789;
+  http_total += prev_chunk;
+  payload_state.DownloadProgress(prev_chunk);
+
+  // Ensure that the initial values for HTTP reflect this attempt.
+  EXPECT_EQ(prev_chunk,
+            payload_state.GetCurrentBytesDownloaded(kDownloadSourceHttpServer));
+  EXPECT_EQ(http_total,
+            payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpServer));
+
+  // Change the response hash so as to simulate a new response which will
+  // reset the current bytes downloaded, but not the total bytes downloaded.
+  SetupPayloadStateWith2Urls("Hash9904", true, &payload_state, &response);
+  EXPECT_EQ(2, payload_state.GetNumResponsesSeen());
+
+  // First, simulate successful download of a few bytes over HTTP.
+  int first_chunk = 5000000;
+  http_total += first_chunk;
+  payload_state.DownloadProgress(first_chunk);
+  // Test that first all progress is made on HTTP and none on HTTPS.
+  EXPECT_EQ(first_chunk,
+            payload_state.GetCurrentBytesDownloaded(kDownloadSourceHttpServer));
+  EXPECT_EQ(http_total,
+            payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpServer));
+  EXPECT_EQ(0, payload_state.GetCurrentBytesDownloaded(
+                 kDownloadSourceHttpsServer));
+  EXPECT_EQ(https_total,
+            payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpsServer));
+
+  // Simulate an error that'll cause the url index to point to https.
+  ErrorCode error = ErrorCode::kDownloadMetadataSignatureMismatch;
+  payload_state.UpdateFailed(error);
+
+  // Test that no new progress is made on HTTP and new progress is on HTTPS.
+  int second_chunk = 23456789;
+  https_total += second_chunk;
+  payload_state.DownloadProgress(second_chunk);
+  EXPECT_EQ(first_chunk,
+            payload_state.GetCurrentBytesDownloaded(kDownloadSourceHttpServer));
+  EXPECT_EQ(http_total,
+            payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpServer));
+  EXPECT_EQ(second_chunk, payload_state.GetCurrentBytesDownloaded(
+              kDownloadSourceHttpsServer));
+  EXPECT_EQ(https_total,
+            payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpsServer));
+
+  // Simulate error to go back to http.
+  payload_state.UpdateFailed(error);
+  int third_chunk = 32345678;
+  int http_chunk = first_chunk + third_chunk;
+  http_total += third_chunk;
+  payload_state.DownloadProgress(third_chunk);
+
+  // Test that third chunk is again back on HTTP. HTTPS remains on second chunk.
+  EXPECT_EQ(http_chunk,
+            payload_state.GetCurrentBytesDownloaded(kDownloadSourceHttpServer));
+  EXPECT_EQ(http_total,
+            payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpServer));
+  EXPECT_EQ(second_chunk, payload_state.GetCurrentBytesDownloaded(
+                 kDownloadSourceHttpsServer));
+  EXPECT_EQ(https_total,
+            payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpsServer));
+
+  // Simulate error (will cause URL switch), set p2p is to be used and
+  // then do 42MB worth of progress
+  payload_state.UpdateFailed(error);
+  payload_state.SetUsingP2PForDownloading(true);
+  int p2p_total = 42 * 1000 * 1000;
+  payload_state.DownloadProgress(p2p_total);
+
+  EXPECT_EQ(p2p_total,
+            payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpPeer));
+
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(_, _, _, _, _))
+    .Times(AnyNumber());
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
+    .Times(AnyNumber());
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
+      metrics::kMetricSuccessfulUpdateUrlSwitchCount,
+      3, _, _, _));
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
+      metrics::kMetricSuccessfulUpdateTotalDurationMinutes,
+      _, _, _, _));
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
+      metrics::kMetricSuccessfulUpdateDownloadOverheadPercentage,
+      314, _, _, _));
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
+      metrics::kMetricAttemptPayloadType, kPayloadTypeFull, kNumPayloadTypes));
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
+      metrics::kMetricSuccessfulUpdatePayloadType, kPayloadTypeFull,
+      kNumPayloadTypes));
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
+      metrics::kMetricSuccessfulUpdateAttemptCount, 1, _, _, _));
+
+  payload_state.UpdateSucceeded();
+
+  // Make sure the metrics are reset after a successful update.
+  EXPECT_EQ(0,
+            payload_state.GetCurrentBytesDownloaded(kDownloadSourceHttpServer));
+  EXPECT_EQ(0,
+            payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpServer));
+  EXPECT_EQ(0, payload_state.GetCurrentBytesDownloaded(
+                 kDownloadSourceHttpsServer));
+  EXPECT_EQ(0,
+            payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpsServer));
+  EXPECT_EQ(0, payload_state.GetNumResponsesSeen());
+}
+
+TEST(PayloadStateTest, DownloadSourcesUsedIsCorrect) {
+  OmahaResponse response;
+  PayloadState payload_state;
+  FakeSystemState fake_system_state;
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+  SetupPayloadStateWith2Urls("Hash3286", true, &payload_state, &response);
+
+  // Simulate progress in order to mark HTTP as one of the sources used.
+  int num_bytes = 42 * 1000 * 1000;
+  payload_state.DownloadProgress(num_bytes);
+
+  // Check that this was done via HTTP.
+  EXPECT_EQ(num_bytes,
+            payload_state.GetCurrentBytesDownloaded(kDownloadSourceHttpServer));
+  EXPECT_EQ(num_bytes,
+            payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpServer));
+
+  // Check that only HTTP is reported as a download source.
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(_, _, _, _, _))
+    .Times(AnyNumber());
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
+      metrics::kMetricSuccessfulUpdateDownloadSourcesUsed,
+      (1 << kDownloadSourceHttpServer),
+      _, _, _));
+
+  payload_state.UpdateSucceeded();
+}
+
+TEST(PayloadStateTest, RestartingUpdateResetsMetrics) {
+  OmahaResponse response;
+  FakeSystemState fake_system_state;
+  PayloadState payload_state;
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+
+  // Set the first response.
+  SetupPayloadStateWith2Urls("Hash5823", true, &payload_state, &response);
+
+  int num_bytes = 10000;
+  payload_state.DownloadProgress(num_bytes);
+  EXPECT_EQ(num_bytes,
+            payload_state.GetCurrentBytesDownloaded(kDownloadSourceHttpServer));
+  EXPECT_EQ(num_bytes,
+            payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpServer));
+  EXPECT_EQ(0, payload_state.GetCurrentBytesDownloaded(
+                 kDownloadSourceHttpsServer));
+  EXPECT_EQ(0,
+            payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpsServer));
+
+  payload_state.UpdateRestarted();
+  // Make sure the current bytes downloaded is reset, but not the total bytes.
+  EXPECT_EQ(0,
+            payload_state.GetCurrentBytesDownloaded(kDownloadSourceHttpServer));
+  EXPECT_EQ(num_bytes,
+            payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpServer));
+}
+
+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));
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+
+  payload_state.UpdateRestarted();
+  EXPECT_EQ(0, payload_state.GetNumReboots());
+
+  fake_system_state.set_system_rebooted(true);
+  payload_state.UpdateResumed();
+  // Num reboots should be incremented because system rebooted detected.
+  EXPECT_EQ(1, payload_state.GetNumReboots());
+
+  fake_system_state.set_system_rebooted(false);
+  payload_state.UpdateResumed();
+  // Num reboots should now be 1 as reboot was not detected.
+  EXPECT_EQ(1, payload_state.GetNumReboots());
+
+  // Restart the update again to verify we set the num of reboots back to 0.
+  payload_state.UpdateRestarted();
+  EXPECT_EQ(0, payload_state.GetNumReboots());
+}
+
+TEST(PayloadStateTest, RollbackVersion) {
+  FakeSystemState fake_system_state;
+  PayloadState payload_state;
+
+  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();
+
+  EXPECT_EQ(rollback_version, payload_state.GetRollbackVersion());
+
+  // Change it up a little and verify we load it correctly.
+  rollback_version = "2345.0.1";
+  // Let's verify we can reload it correctly.
+  EXPECT_CALL(*mock_powerwash_safe_prefs, GetString(
+      kPrefsRollbackVersion, _)).WillOnce(DoAll(
+          SetArgumentPointee<1>(rollback_version), Return(true)));
+  EXPECT_CALL(*mock_powerwash_safe_prefs, SetString(kPrefsRollbackVersion,
+                                                    rollback_version));
+  payload_state.LoadRollbackVersion();
+  EXPECT_EQ(rollback_version, payload_state.GetRollbackVersion());
+
+  // Check that we report only UpdateEngine.Rollback.* metrics in
+  // UpdateSucceeded().
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(_, _, _, _, _))
+    .Times(0);
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
+    .Times(0);
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(),
+              SendEnumToUMA(
+                  metrics::kMetricRollbackResult,
+                  static_cast<int>(metrics::RollbackResult::kSuccess),
+                  static_cast<int>(metrics::RollbackResult::kNumConstants)));
+  payload_state.UpdateSucceeded();
+}
+
+TEST(PayloadStateTest, DurationsAreCorrect) {
+  OmahaResponse response;
+  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
+  fake_clock.SetWallclockTime(Time::FromInternalValue(1000000));
+  fake_clock.SetMonotonicTime(Time::FromInternalValue(2000000));
+
+  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, &payload_state, &response);
+  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);
+
+  // Check that durations are reset when a new response comes in.
+  SetupPayloadStateWith2Urls("Hash8594", true, &payload_state, &response);
+  EXPECT_EQ(payload_state.GetUpdateDuration().InMicroseconds(), 0);
+  EXPECT_EQ(payload_state.GetUpdateDurationUptime().InMicroseconds(), 0);
+
+  // 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));
+  payload_state.DownloadProgress(10);
+  EXPECT_EQ(payload_state.GetUpdateDuration().InMicroseconds(), 10000000);
+  EXPECT_EQ(payload_state.GetUpdateDurationUptime().InMicroseconds(), 10000000);
+
+  // 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));
+  PayloadState payload_state2;
+  EXPECT_TRUE(payload_state2.Initialize(&fake_system_state));
+  EXPECT_EQ(payload_state2.GetUpdateDuration().InMicroseconds(), 10000000);
+  EXPECT_EQ(payload_state2.GetUpdateDurationUptime().InMicroseconds(),
+            10000000);
+
+  // 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));
+  payload_state2.UpdateSucceeded();
+  EXPECT_EQ(payload_state2.GetUpdateDuration().InMicroseconds(), 17000000);
+  EXPECT_EQ(payload_state2.GetUpdateDurationUptime().InMicroseconds(),
+            16000000);
+}
+
+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).
+  fake_clock.SetWallclockTime(Time::FromInternalValue(
+      30 * Time::kMicrosecondsPerSecond));
+
+  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, &payload_state, &response);
+  payload_state.UpdateSucceeded();
+
+  // Check that the marker was written.
+  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.SetWallclockTime(Time::FromInternalValue(
+      500 * Time::kMicrosecondsPerSecond));
+  PayloadState payload_state2;
+  EXPECT_TRUE(payload_state2.Initialize(&fake_system_state));
+
+  // Expect 500 - 30 seconds = 470 seconds ~= 7 min 50 sec
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
+      metrics::kMetricTimeToRebootMinutes,
+      7, _, _, _));
+  fake_system_state.set_system_rebooted(true);
+
+  payload_state2.UpdateEngineStarted();
+
+  // Check that the marker was nuked.
+  EXPECT_FALSE(fake_prefs.Exists(kPrefsSystemUpdatedMarker));
+}
+
+TEST(PayloadStateTest, RestartAfterCrash) {
+  PayloadState payload_state;
+  FakeSystemState fake_system_state;
+  NiceMock<MockPrefs>* prefs = fake_system_state.mock_prefs();
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+
+  // Only the |kPrefsAttemptInProgress| state variable should be read.
+  EXPECT_CALL(*prefs, Exists(_)).Times(0);
+  EXPECT_CALL(*prefs, SetString(_, _)).Times(0);
+  EXPECT_CALL(*prefs, SetInt64(_, _)).Times(0);
+  EXPECT_CALL(*prefs, SetBoolean(_, _)).Times(0);
+  EXPECT_CALL(*prefs, GetString(_, _)).Times(0);
+  EXPECT_CALL(*prefs, GetInt64(_, _)).Times(0);
+  EXPECT_CALL(*prefs, GetBoolean(_, _)).Times(0);
+  EXPECT_CALL(*prefs, GetBoolean(kPrefsAttemptInProgress, _));
+
+  // No metrics are reported after a crash.
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(),
+              SendToUMA(_, _, _, _, _)).Times(0);
+
+  // Simulate an update_engine restart without a reboot.
+  fake_system_state.set_system_rebooted(false);
+
+  payload_state.UpdateEngineStarted();
+}
+
+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(&fake_system_state));
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(),
+      SendEnumToUMA(
+          metrics::kMetricAttemptResult,
+          static_cast<int>(metrics::AttemptResult::kAbnormalTermination),
+          _)).Times(0);
+  payload_state.UpdateEngineStarted();
+}
+
+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.
+  fake_system_state.set_prefs(&fake_prefs);
+  fake_prefs.SetBoolean(kPrefsAttemptInProgress, true);
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(),
+      SendEnumToUMA(
+          metrics::kMetricAttemptResult,
+          static_cast<int>(metrics::AttemptResult::kAbnormalTermination),
+          _)).Times(1);
+  payload_state.UpdateEngineStarted();
+
+  EXPECT_FALSE(fake_prefs.Exists(kPrefsAttemptInProgress));
+}
+
+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).
+  fake_system_state.set_prefs(&fake_prefs);
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(_, _, _, _, _))
+    .Times(AnyNumber());
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
+    .Times(AnyNumber());
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(),
+      SendEnumToUMA(
+          metrics::kMetricAttemptResult,
+          static_cast<int>(metrics::AttemptResult::kAbnormalTermination),
+          _)).Times(0);
+
+  // Attempt not in progress, should be clear.
+  EXPECT_FALSE(fake_prefs.Exists(kPrefsAttemptInProgress));
+
+  payload_state.UpdateRestarted();
+
+  // Attempt not in progress, should be set.
+  EXPECT_TRUE(fake_prefs.Exists(kPrefsAttemptInProgress));
+
+  payload_state.UpdateSucceeded();
+
+  // Attempt not in progress, should be clear.
+  EXPECT_FALSE(fake_prefs.Exists(kPrefsAttemptInProgress));
+}
+
+TEST(PayloadStateTest, CandidateUrlsComputedCorrectly) {
+  OmahaResponse response;
+  FakeSystemState fake_system_state;
+  PayloadState payload_state;
+
+  policy::MockDevicePolicy disable_http_policy;
+  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(_))
+      .WillRepeatedly(Return(false));
+
+  // Set the first response.
+  SetupPayloadStateWith2Urls("Hash8433", true, &payload_state, &response);
+
+  // Check that we use the HTTP URL since there is no value set for allowing
+  // http.
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
+
+  // Test with device policy not allowing http updates.
+  EXPECT_CALL(disable_http_policy, GetHttpDownloadsEnabled(_))
+      .WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(true)));
+
+  // Reset state and set again.
+  SetupPayloadStateWith2Urls("Hash8433", false, &payload_state, &response);
+
+  // Check that we skip the HTTP URL and use only the HTTPS url.
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
+
+  // Advance the URL index to 1 by faking an error.
+  ErrorCode error = ErrorCode::kDownloadMetadataSignatureMismatch;
+  payload_state.UpdateFailed(error);
+
+  // Check that we still skip the HTTP URL and use only the HTTPS url.
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(0, payload_state.GetUrlSwitchCount());
+
+  // Now, slightly change the response and set it again.
+  SetupPayloadStateWith2Urls("Hash2399", false, &payload_state, &response);
+
+  // Check that we still skip the HTTP URL and use only the HTTPS url.
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
+
+  // Now, pretend that the HTTP policy is turned on. We want to make sure
+  // the new policy is honored.
+  policy::MockDevicePolicy enable_http_policy;
+  fake_system_state.set_device_policy(&enable_http_policy);
+  EXPECT_CALL(enable_http_policy, GetHttpDownloadsEnabled(_))
+      .WillRepeatedly(DoAll(SetArgumentPointee<0>(true), Return(true)));
+
+  // Now, set the same response using the same hash
+  // so that we can test that the state is reset not because of the
+  // hash but because of the policy change which results in candidate url
+  // list change.
+  SetupPayloadStateWith2Urls("Hash2399", true, &payload_state, &response);
+
+  // Check that we use the HTTP URL now and the failure count is reset.
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(0, payload_state.GetUrlFailureCount());
+
+  // Fake a failure and see if we're moving over to the HTTPS url and update
+  // the URL switch count properly.
+  payload_state.UpdateFailed(error);
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(1, payload_state.GetUrlSwitchCount());
+  EXPECT_EQ(0, payload_state.GetUrlFailureCount());
+}
+
+TEST(PayloadStateTest, PayloadTypeMetricWhenTypeIsDelta) {
+  OmahaResponse response;
+  response.is_delta_payload = true;
+  PayloadState payload_state;
+  FakeSystemState fake_system_state;
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+  SetupPayloadStateWith2Urls("Hash6437", true, &payload_state, &response);
+
+  // Simulate a successful download and update.
+  payload_state.DownloadComplete();
+
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
+    .Times(AnyNumber());
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
+      metrics::kMetricAttemptPayloadType, kPayloadTypeDelta, kNumPayloadTypes));
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
+      metrics::kMetricSuccessfulUpdatePayloadType, kPayloadTypeDelta,
+      kNumPayloadTypes));
+  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(&fake_system_state);
+  params.set_delta_okay(false);
+  fake_system_state.set_request_params(&params);
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+  SetupPayloadStateWith2Urls("Hash6437", true, &payload_state, &response);
+
+  payload_state.DownloadComplete();
+
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
+      metrics::kMetricAttemptPayloadType, kPayloadTypeDelta,
+      kNumPayloadTypes));
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
+      metrics::kMetricSuccessfulUpdatePayloadType, kPayloadTypeDelta,
+      kNumPayloadTypes));
+  payload_state.UpdateSucceeded();
+}
+
+TEST(PayloadStateTest, PayloadTypeMetricWhenTypeIsForcedFull) {
+  OmahaResponse response;
+  response.is_delta_payload = false;
+  PayloadState payload_state;
+  FakeSystemState fake_system_state;
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+  SetupPayloadStateWith2Urls("Hash6437", true, &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(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
+    .Times(AnyNumber());
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
+      metrics::kMetricAttemptPayloadType, kPayloadTypeForcedFull,
+      kNumPayloadTypes));
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
+      metrics::kMetricSuccessfulUpdatePayloadType, kPayloadTypeForcedFull,
+      kNumPayloadTypes));
+  payload_state.UpdateSucceeded();
+}
+
+TEST(PayloadStateTest, PayloadTypeMetricWhenTypeIsFull) {
+  OmahaResponse response;
+  response.is_delta_payload = false;
+  PayloadState payload_state;
+  FakeSystemState fake_system_state;
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+  SetupPayloadStateWith2Urls("Hash6437", true, &payload_state, &response);
+
+  // Mock the request to a request where the delta is enabled, although the
+  // result is full.
+  OmahaRequestParams params(&fake_system_state);
+  params.set_delta_okay(true);
+  fake_system_state.set_request_params(&params);
+
+  // Simulate a successful download and update.
+  payload_state.DownloadComplete();
+
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
+    .Times(AnyNumber());
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
+      metrics::kMetricAttemptPayloadType, kPayloadTypeFull,
+      kNumPayloadTypes));
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
+      metrics::kMetricSuccessfulUpdatePayloadType, kPayloadTypeFull,
+      kNumPayloadTypes));
+  payload_state.UpdateSucceeded();
+}
+
+TEST(PayloadStateTest, RebootAfterUpdateFailedMetric) {
+  FakeSystemState fake_system_state;
+  OmahaResponse response;
+  PayloadState payload_state;
+  FakePrefs fake_prefs;
+  fake_system_state.set_prefs(&fake_prefs);
+
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+  SetupPayloadStateWith2Urls("Hash3141", true, &payload_state, &response);
+
+  // Simulate a successful download and update.
+  payload_state.DownloadComplete();
+  payload_state.UpdateSucceeded();
+  payload_state.ExpectRebootInNewVersion("Version:12345678");
+
+  // Reboot into the same environment to get an UMA metric with a value of 1.
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
+      metrics::kMetricFailedUpdateCount, 1, _, _, _));
+  payload_state.ReportFailedBootIfNeeded();
+  Mock::VerifyAndClearExpectations(fake_system_state.mock_metrics_lib());
+
+  // Simulate a second update and reboot into the same environment, this should
+  // send a value of 2.
+  payload_state.ExpectRebootInNewVersion("Version:12345678");
+
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
+      metrics::kMetricFailedUpdateCount, 2, _, _, _));
+  payload_state.ReportFailedBootIfNeeded();
+  Mock::VerifyAndClearExpectations(fake_system_state.mock_metrics_lib());
+
+  // 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(*fake_system_state.mock_metrics_lib(), SendToUMA(
+      metrics::kMetricFailedUpdateCount, 1, _, _, _));
+  payload_state.ReportFailedBootIfNeeded();
+  Mock::VerifyAndClearExpectations(fake_system_state.mock_metrics_lib());
+}
+
+TEST(PayloadStateTest, RebootAfterUpdateSucceed) {
+  FakeSystemState fake_system_state;
+  OmahaResponse response;
+  PayloadState payload_state;
+  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(&fake_system_state));
+  SetupPayloadStateWith2Urls("Hash3141", true, &payload_state, &response);
+
+  // Simulate a successful download and update.
+  payload_state.DownloadComplete();
+  payload_state.UpdateSucceeded();
+  payload_state.ExpectRebootInNewVersion("Version:12345678");
+
+  // Change the BootDevice to a different one, no metric should be sent.
+  fake_boot_control->SetCurrentSlot(1);
+
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
+      metrics::kMetricFailedUpdateCount, _, _, _, _))
+      .Times(0);
+  payload_state.ReportFailedBootIfNeeded();
+
+  // A second reboot in either partition should not send a metric.
+  payload_state.ReportFailedBootIfNeeded();
+  fake_boot_control->SetCurrentSlot(0);
+  payload_state.ReportFailedBootIfNeeded();
+}
+
+TEST(PayloadStateTest, RebootAfterCanceledUpdate) {
+  FakeSystemState fake_system_state;
+  OmahaResponse response;
+  PayloadState payload_state;
+  FakePrefs fake_prefs;
+
+  fake_system_state.set_prefs(&fake_prefs);
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+  SetupPayloadStateWith2Urls("Hash3141", true, &payload_state, &response);
+
+  // Simulate a successful download and update.
+  payload_state.DownloadComplete();
+  payload_state.UpdateSucceeded();
+  payload_state.ExpectRebootInNewVersion("Version:12345678");
+
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
+      metrics::kMetricFailedUpdateCount, _, _, _, _))
+      .Times(0);
+
+  // Cancel the applied update.
+  payload_state.ResetUpdateStatus();
+
+  // Simulate a reboot.
+  payload_state.ReportFailedBootIfNeeded();
+}
+
+TEST(PayloadStateTest, UpdateSuccessWithWipedPrefs) {
+  FakeSystemState fake_system_state;
+  PayloadState payload_state;
+  FakePrefs fake_prefs;
+
+  fake_system_state.set_prefs(&fake_prefs);
+  EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
+      metrics::kMetricFailedUpdateCount, _, _, _, _))
+      .Times(0);
+
+  // Simulate a reboot in this environment.
+  payload_state.ReportFailedBootIfNeeded();
+}
+
+TEST(PayloadStateTest, DisallowP2PAfterTooManyAttempts) {
+  OmahaResponse response;
+  PayloadState payload_state;
+  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, &payload_state, &response);
+
+  // Should allow exactly kMaxP2PAttempts...
+  for (int n = 0; n < kMaxP2PAttempts; n++) {
+    payload_state.P2PNewAttempt();
+    EXPECT_TRUE(payload_state.P2PAttemptAllowed());
+  }
+  // ... but not more than that.
+  payload_state.P2PNewAttempt();
+  EXPECT_FALSE(payload_state.P2PAttemptAllowed());
+}
+
+TEST(PayloadStateTest, DisallowP2PAfterDeadline) {
+  OmahaResponse response;
+  PayloadState payload_state;
+  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, &payload_state, &response);
+
+  // Set the clock to 1 second.
+  Time epoch = Time::FromInternalValue(1000000);
+  fake_clock.SetWallclockTime(epoch);
+
+  // Do an attempt - this will set the timestamp.
+  payload_state.P2PNewAttempt();
+
+  // Check that the timestamp equals what we just set.
+  EXPECT_EQ(epoch, payload_state.GetP2PFirstAttemptTimestamp());
+
+  // Time hasn't advanced - this should work.
+  EXPECT_TRUE(payload_state.P2PAttemptAllowed());
+
+  // Set clock to half the deadline - this should work.
+  fake_clock.SetWallclockTime(epoch +
+      TimeDelta::FromSeconds(kMaxP2PAttemptTimeSeconds) / 2);
+  EXPECT_TRUE(payload_state.P2PAttemptAllowed());
+
+  // Check that the first attempt timestamp hasn't changed just
+  // because the wall-clock time changed.
+  EXPECT_EQ(epoch, payload_state.GetP2PFirstAttemptTimestamp());
+
+  // Set clock to _just_ before the deadline - this should work.
+  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(epoch +
+      TimeDelta::FromSeconds(kMaxP2PAttemptTimeSeconds + 1));
+  EXPECT_FALSE(payload_state.P2PAttemptAllowed());
+}
+
+TEST(PayloadStateTest, P2PStateVarsInitialValue) {
+  OmahaResponse response;
+  PayloadState payload_state;
+  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, &payload_state, &response);
+
+  Time null_time = Time();
+  EXPECT_EQ(null_time, payload_state.GetP2PFirstAttemptTimestamp());
+  EXPECT_EQ(0, payload_state.GetP2PNumAttempts());
+}
+
+TEST(PayloadStateTest, P2PStateVarsArePersisted) {
+  OmahaResponse response;
+  PayloadState payload_state;
+  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, &payload_state, &response);
+
+  // Set the clock to something known.
+  Time time = Time::FromInternalValue(12345);
+  fake_clock.SetWallclockTime(time);
+
+  // New p2p attempt - as a side-effect this will update the p2p state vars.
+  payload_state.P2PNewAttempt();
+  EXPECT_EQ(1, payload_state.GetP2PNumAttempts());
+  EXPECT_EQ(time, payload_state.GetP2PFirstAttemptTimestamp());
+
+  // Now create a new PayloadState and check that it loads the state
+  // vars correctly.
+  PayloadState payload_state2;
+  EXPECT_TRUE(payload_state2.Initialize(&fake_system_state));
+  EXPECT_EQ(1, payload_state2.GetP2PNumAttempts());
+  EXPECT_EQ(time, payload_state2.GetP2PFirstAttemptTimestamp());
+}
+
+TEST(PayloadStateTest, P2PStateVarsAreClearedOnNewResponse) {
+  OmahaResponse response;
+  PayloadState payload_state;
+  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, &payload_state, &response);
+
+  // Set the clock to something known.
+  Time time = Time::FromInternalValue(12345);
+  fake_clock.SetWallclockTime(time);
+
+  // New p2p attempt - as a side-effect this will update the p2p state vars.
+  payload_state.P2PNewAttempt();
+  EXPECT_EQ(1, payload_state.GetP2PNumAttempts());
+  EXPECT_EQ(time, payload_state.GetP2PFirstAttemptTimestamp());
+
+  // Set a new response...
+  SetupPayloadStateWith2Urls("Hash9904", true, &payload_state, &response);
+
+  // ... and check that it clears the P2P state vars.
+  Time null_time = Time();
+  EXPECT_EQ(0, payload_state.GetP2PNumAttempts());
+  EXPECT_EQ(null_time, payload_state.GetP2PFirstAttemptTimestamp());
+}
+
+}  // namespace chromeos_update_engine
diff --git a/proxy_resolver.cc b/proxy_resolver.cc
new file mode 100644
index 0000000..abd6f76
--- /dev/null
+++ b/proxy_resolver.cc
@@ -0,0 +1,66 @@
+//
+// 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/proxy_resolver.h"
+
+#include <base/bind.h>
+#include <base/location.h>
+
+using brillo::MessageLoop;
+using std::deque;
+using std::string;
+
+namespace chromeos_update_engine {
+
+const char kNoProxy[] = "direct://";
+
+DirectProxyResolver::~DirectProxyResolver() {
+  if (idle_callback_id_ != MessageLoop::kTaskIdNull) {
+    // The DirectProxyResolver is instantiated as part of the UpdateAttempter
+    // which is also instantiated by default by the FakeSystemState, even when
+    // it is not used. We check the manage_shares_id_ before calling the
+    // MessageLoop::current() since the unit test using a FakeSystemState may
+    // have not define a MessageLoop for the current thread.
+    MessageLoop::current()->CancelTask(idle_callback_id_);
+    idle_callback_id_ = MessageLoop::kTaskIdNull;
+  }
+}
+
+bool DirectProxyResolver::GetProxiesForUrl(const string& url,
+                                           ProxiesResolvedFn callback,
+                                           void* data) {
+  idle_callback_id_ = MessageLoop::current()->PostTask(
+      FROM_HERE,
+      base::Bind(
+            &DirectProxyResolver::ReturnCallback,
+            base::Unretained(this),
+            callback,
+            data));
+  return true;
+}
+
+void DirectProxyResolver::ReturnCallback(ProxiesResolvedFn callback,
+                                         void* data) {
+  idle_callback_id_ = MessageLoop::kTaskIdNull;
+
+  // Initialize proxy pool with as many proxies as indicated (all identical).
+  deque<string> proxies(num_proxies_, kNoProxy);
+
+  (*callback)(proxies, data);
+}
+
+
+}  // namespace chromeos_update_engine
diff --git a/proxy_resolver.h b/proxy_resolver.h
new file mode 100644
index 0000000..2c8824f
--- /dev/null
+++ b/proxy_resolver.h
@@ -0,0 +1,88 @@
+//
+// 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_PROXY_RESOLVER_H_
+#define UPDATE_ENGINE_PROXY_RESOLVER_H_
+
+#include <deque>
+#include <string>
+
+#include <base/logging.h>
+#include <brillo/message_loops/message_loop.h>
+
+#include "update_engine/common/utils.h"
+
+namespace chromeos_update_engine {
+
+extern const char kNoProxy[];
+
+// Callback for a call to GetProxiesForUrl().
+// Resultant proxies are in |out_proxy|. Each will be in one of the
+// following forms:
+// http://<host>[:<port>] - HTTP proxy
+// socks{4,5}://<host>[:<port>] - SOCKS4/5 proxy
+// kNoProxy - no proxy
+typedef void (*ProxiesResolvedFn)(const std::deque<std::string>& proxies,
+                                  void* data);
+
+class ProxyResolver {
+ public:
+  ProxyResolver() {}
+  virtual ~ProxyResolver() {}
+
+  // Finds proxies for the given URL and returns them via the callback.
+  // |data| will be passed to the callback.
+  // Returns true on success.
+  virtual bool GetProxiesForUrl(const std::string& url,
+                                ProxiesResolvedFn callback,
+                                void* data) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ProxyResolver);
+};
+
+// Always says to not use a proxy
+class DirectProxyResolver : public ProxyResolver {
+ public:
+  DirectProxyResolver() = default;
+  ~DirectProxyResolver() override;
+  bool GetProxiesForUrl(const std::string& url,
+                        ProxiesResolvedFn callback,
+                        void* data) override;
+
+  // Set the number of direct (non-) proxies to be returned by resolver.
+  // The default value is 1; higher numbers are currently used in testing.
+  inline void set_num_proxies(size_t num_proxies) {
+    num_proxies_ = num_proxies;
+  }
+
+ private:
+  // The ID of the main loop callback.
+  brillo::MessageLoop::TaskId idle_callback_id_{
+      brillo::MessageLoop::kTaskIdNull};
+
+  // Number of direct proxies to return on resolved list; currently used for
+  // testing.
+  size_t num_proxies_{1};
+
+  // The MainLoop callback, from here we return to the client.
+  void ReturnCallback(ProxiesResolvedFn callback, void* data);
+  DISALLOW_COPY_AND_ASSIGN(DirectProxyResolver);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PROXY_RESOLVER_H_
diff --git a/real_system_state.cc b/real_system_state.cc
new file mode 100644
index 0000000..3a90292
--- /dev/null
+++ b/real_system_state.cc
@@ -0,0 +1,151 @@
+//
+// 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/real_system_state.h"
+
+#include <base/files/file_util.h>
+#include <base/time/time.h>
+#include <brillo/make_unique_ptr.h>
+
+#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/hardware.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/update_manager/state_factory.h"
+
+namespace chromeos_update_engine {
+
+RealSystemState::RealSystemState(const scoped_refptr<dbus::Bus>& bus)
+    : debugd_proxy_(bus),
+      power_manager_proxy_(bus),
+      session_manager_proxy_(bus),
+      shill_proxy_(bus),
+      libcros_proxy_(bus) {
+}
+
+bool RealSystemState::Initialize() {
+  metrics_lib_.Init();
+
+  boot_control_ = boot_control::CreateBootControl();
+  if (!boot_control_) {
+    LOG(WARNING) << "Unable to create BootControl instance, using stub "
+                 << "instead. All update attempts will fail.";
+    boot_control_ = brillo::make_unique_ptr(new BootControlStub());
+  }
+
+  hardware_ = hardware::CreateHardware();
+  if (!hardware_) {
+    LOG(ERROR) << "Error intializing the HardwareInterface.";
+    return false;
+  }
+
+  LOG_IF(INFO, !hardware_->IsNormalBootMode()) << "Booted in dev mode.";
+  LOG_IF(INFO, !hardware_->IsOfficialBuild()) << "Booted non-official build.";
+
+  if (!shill_proxy_.Init()) {
+    LOG(ERROR) << "Failed to initialize shill proxy.";
+    return false;
+  }
+
+  // Initialize standard and powerwash-safe 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)) {
+    LOG(ERROR) << "Failed to get a non-volatile directory.";
+    return false;
+  }
+  Prefs* prefs;
+  prefs_.reset(prefs = new Prefs());
+  if (!prefs->Init(non_volatile_path.Append(kPrefsSubDirectory))) {
+    LOG(ERROR) << "Failed to initialize preferences.";
+    return false;
+  }
+
+  base::FilePath powerwash_safe_path;
+  if (!hardware_->GetPowerwashSafeDirectory(&powerwash_safe_path)) {
+    // TODO(deymo): Fall-back to in-memory prefs if there's no powerwash-safe
+    // directory, or disable powerwash feature.
+    powerwash_safe_path = non_volatile_path.Append("powerwash-safe");
+    LOG(WARNING) << "No powerwash-safe directory, using non-volatile one.";
+  }
+  powerwash_safe_prefs_.reset(prefs = new Prefs());
+  if (!prefs->Init(
+          powerwash_safe_path.Append(kPowerwashSafePrefsSubDirectory))) {
+    LOG(ERROR) << "Failed to initialize powerwash preferences.";
+    return false;
+  }
+
+  // Check the system rebooted marker file.
+  std::string boot_id;
+  if (utils::GetBootId(&boot_id)) {
+    std::string prev_boot_id;
+    system_rebooted_ = (!prefs_->GetString(kPrefsBootId, &prev_boot_id) ||
+                        prev_boot_id != boot_id);
+    prefs_->SetString(kPrefsBootId, boot_id);
+  } else {
+    LOG(WARNING) << "Couldn't detect the bootid, assuming system was rebooted.";
+    system_rebooted_ = true;
+  }
+
+  // Initialize the OmahaRequestParams with the default settings. These settings
+  // will be re-initialized before every request using the actual request
+  // options. This initialization here pre-loads current channel and version, so
+  // the DBus service can access it.
+  if (!request_params_.Init("", "", false)) {
+    LOG(WARNING) << "Ignoring OmahaRequestParams initialization error. Some "
+                    "features might not work properly.";
+  }
+
+  certificate_checker_.reset(
+      new CertificateChecker(prefs_.get(), &openssl_wrapper_));
+  certificate_checker_->Init();
+
+  // Initialize the UpdateAttempter before the UpdateManager.
+  update_attempter_.reset(
+      new UpdateAttempter(this, certificate_checker_.get(), &libcros_proxy_,
+                          &debugd_proxy_));
+  update_attempter_->Init();
+
+  // Initialize the Update Manager using the default state factory.
+  chromeos_update_manager::State* um_state =
+      chromeos_update_manager::DefaultStateFactory(
+          &policy_provider_, &shill_proxy_, &session_manager_proxy_, 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));
+
+  // 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(this)) {
+    LOG(ERROR) << "Failed to initialize the payload state object.";
+    return false;
+  }
+
+  // All is well. Initialization successful.
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/real_system_state.h b/real_system_state.h
new file mode 100644
index 0000000..8ca1abc
--- /dev/null
+++ b/real_system_state.h
@@ -0,0 +1,173 @@
+//
+// 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 <debugd/dbus-proxies.h>
+#include <metrics/metrics_library.h>
+#include <policy/device_policy.h>
+#include <power_manager/dbus-proxies.h>
+#include <session_manager/dbus-proxies.h>
+
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/certificate_checker.h"
+#include "update_engine/common/clock.h"
+#include "update_engine/common/hardware_interface.h"
+#include "update_engine/common/prefs.h"
+#include "update_engine/connection_manager.h"
+#include "update_engine/p2p_manager.h"
+#include "update_engine/payload_state.h"
+#include "update_engine/shill_proxy.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:
+  // Constructs all system objects that do not require separate initialization;
+  // see Initialize() below for the remaining ones.
+  explicit RealSystemState(const scoped_refptr<dbus::Bus>& bus);
+
+  // Initializes and sets systems objects that require an initialization
+  // separately from construction. Returns |true| on success.
+  bool Initialize();
+
+  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_;
+  }
+
+  inline HardwareInterface* hardware() override { return hardware_.get(); }
+
+  inline MetricsLibraryInterface* metrics_lib() override {
+    return &metrics_lib_;
+  }
+
+  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 org::chromium::PowerManagerProxyInterface* power_manager_proxy()
+      override {
+    return &power_manager_proxy_;
+  }
+
+  inline bool system_rebooted() override { return system_rebooted_; }
+
+ private:
+  // Real DBus proxies using the DBus connection.
+  org::chromium::debugdProxy debugd_proxy_;
+  org::chromium::PowerManagerProxy power_manager_proxy_;
+  org::chromium::SessionManagerInterfaceProxy session_manager_proxy_;
+  ShillProxy shill_proxy_;
+  LibCrosProxy libcros_proxy_;
+
+  // 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.
+  ConnectionManager connection_manager_{&shill_proxy_, this};
+
+  // Interface for the hardware functions.
+  std::unique_ptr<HardwareInterface> hardware_;
+
+  // The Metrics Library interface for reporting UMA stats.
+  MetricsLibrary metrics_lib_;
+
+  // 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};
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_REAL_SYSTEM_STATE_H_
diff --git a/run_unittests b/run_unittests
new file mode 100755
index 0000000..f07078d
--- /dev/null
+++ b/run_unittests
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+#
+# 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.
+#
+
+# Runs the update engine unit tests, including both userland and run-as-root
+# tests.
+
+if [ ! -e ./update_engine_unittests ]; then
+  echo 'Error: unit test binary missing' >&2
+  exit 1
+fi
+
+user_pass=0
+./update_engine_unittests --gtest_filter='-*.RunAsRoot*' && user_pass=1
+root_pass=0
+sudo ./update_engine_unittests --gtest_filter='*.RunAsRoot*' && root_pass=1
+
+echo -n "User tests: " && [ $user_pass == 1 ] && echo "PASSED" || echo "FAILED"
+echo -n "Root tests: " && [ $root_pass == 1 ] && echo "PASSED" || echo "FAILED"
+
+exit $((2 - user_pass - root_pass))
diff --git a/sample_images/generate_images.sh b/sample_images/generate_images.sh
new file mode 100755
index 0000000..70fc14b
--- /dev/null
+++ b/sample_images/generate_images.sh
@@ -0,0 +1,189 @@
+#!/bin/bash
+
+#
+# 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.
+#
+
+# This script generates some sample images used in unittests and packages them
+# in the sample_images.tar.bz2 file. The list of generated images and their
+# options are described in the main() function. You need to manually run this
+# script to update the generated images whenever you modify this script.
+
+set -e
+
+# cleanup <path>
+# Unmount and remove the mountpoint <path>
+cleanup() {
+  if ! sudo umount "$1" 2>/dev/null; then
+    if mountpoint -q "$1"; then
+      sync && sudo umount "$1"
+    fi
+  fi
+  rmdir "$1"
+}
+
+# add_files_default <mntdir> <block_size>
+# Add several test files to the image mounted in <mntdir>.
+add_files_default() {
+  local mntdir="$1"
+  local block_size="$2"
+
+  ### Generate the files used in unittest with descriptive names.
+  sudo touch "${mntdir}"/empty-file
+
+  # regular: Regular files.
+  echo "small file" | sudo dd of="${mntdir}"/regular-small status=none
+  dd if=/dev/zero bs=1024 count=16 status=none | tr '\0' '\141' |
+    sudo dd of="${mntdir}"/regular-16k status=none
+  sudo dd if=/dev/zero of="${mntdir}"/regular-32k-zeros bs=1024 count=16 \
+    status=none
+
+  echo "with net_cap" | sudo dd of="${mntdir}"/regular-with_net_cap status=none
+  sudo setcap cap_net_raw=ep "${mntdir}"/regular-with_net_cap
+
+  # sparse_empty: Files with no data blocks at all (only sparse holes).
+  sudo truncate --size=10240 "${mntdir}"/sparse_empty-10k
+  sudo truncate --size=$(( block_size * 2 )) "${mntdir}"/sparse_empty-2blocks
+
+  # sparse: Files with some data blocks but also sparse holes.
+  echo -n "foo" |
+    sudo dd of="${mntdir}"/sparse-16k-last_block bs=1 \
+      seek=$(( 16 * 1024 - 3)) status=none
+
+  # ext2 inodes have 12 direct blocks, one indirect, one double indirect and
+  # one triple indirect. 10000 should be enough to have an indirect and double
+  # indirect block.
+  echo -n "foo" |
+    sudo dd of="${mntdir}"/sparse-10000blocks bs=1 \
+      seek=$(( block_size * 10000 )) status=none
+
+  sudo truncate --size=16384 "${mntdir}"/sparse-16k-first_block
+  echo "first block" | sudo dd of="${mntdir}"/sparse-16k-first_block status=none
+
+  sudo truncate --size=16384 "${mntdir}"/sparse-16k-holes
+  echo "a" | sudo dd of="${mntdir}"/sparse-16k-holes bs=1 seek=100 status=none
+  echo "b" | sudo dd of="${mntdir}"/sparse-16k-holes bs=1 seek=10000 status=none
+
+  # link: symlinks and hardlinks.
+  sudo ln -s "broken-link" "${mntdir}"/link-short_symlink
+  sudo ln -s $(dd if=/dev/zero bs=256 count=1 status=none | tr '\0' '\141') \
+    "${mntdir}"/link-long_symlink
+  sudo ln "${mntdir}"/regular-16k "${mntdir}"/link-hard-regular-16k
+
+  # Directories.
+  sudo mkdir -p "${mntdir}"/dir1/dir2/dir1
+  echo "foo" | sudo tee "${mntdir}"/dir1/dir2/file >/dev/null
+  echo "bar" | sudo tee "${mntdir}"/dir1/file >/dev/null
+
+  # removed: removed files that should not be listed.
+  echo "We will remove this file so it's contents will be somewhere in the " \
+    "empty space data but it won't be all zeros." |
+    sudo dd of="${mntdir}"/removed conv=fsync status=none
+  sudo rm "${mntdir}"/removed
+}
+
+# add_files_ue_settings <mntdir> <block_size>
+# Add the update_engine.conf settings file. This file contains the
+add_files_ue_settings() {
+  local mntdir="$1"
+
+  sudo mkdir -p "${mntdir}"/etc >/dev/null
+  sudo tee "${mntdir}"/etc/update_engine.conf >/dev/null <<EOF
+PAYLOAD_MINOR_VERSION=1234
+EOF
+  # Example of a real lsb-release file released on link stable.
+  sudo tee "${mntdir}"/etc/lsb-release >/dev/null <<EOF
+CHROMEOS_AUSERVER=https://tools.google.com/service/update2
+CHROMEOS_BOARD_APPID={F26D159B-52A3-491A-AE25-B23670A66B32}
+CHROMEOS_CANARY_APPID={90F229CE-83E2-4FAF-8479-E368A34938B1}
+CHROMEOS_DEVSERVER=
+CHROMEOS_RELEASE_APPID={F26D159B-52A3-491A-AE25-B23670A66B32}
+CHROMEOS_RELEASE_BOARD=link-signed-mp-v4keys
+CHROMEOS_RELEASE_BRANCH_NUMBER=63
+CHROMEOS_RELEASE_BUILD_NUMBER=6946
+CHROMEOS_RELEASE_BUILD_TYPE=Official Build
+CHROMEOS_RELEASE_CHROME_MILESTONE=43
+CHROMEOS_RELEASE_DESCRIPTION=6946.63.0 (Official Build) stable-channel link
+CHROMEOS_RELEASE_NAME=Chrome OS
+CHROMEOS_RELEASE_PATCH_NUMBER=0
+CHROMEOS_RELEASE_TRACK=stable-channel
+CHROMEOS_RELEASE_VERSION=6946.63.0
+GOOGLE_RELEASE=6946.63.0
+EOF
+}
+
+# generate_fs <filename> <kind> <size> [block_size] [block_groups]
+generate_fs() {
+  local filename="$1"
+  local kind="$2"
+  local size="$3"
+  local block_size="${4:-4096}"
+  local block_groups="${5:-}"
+
+  local mkfs_opts=( -q -F -b "${block_size}" -L "ROOT-TEST" -t ext2 )
+  if [[ -n "${block_groups}" ]]; then
+    mkfs_opts+=( -G "${block_groups}" )
+  fi
+
+  local mntdir=$(mktemp --tmpdir -d generate_ext2.XXXXXX)
+  trap 'cleanup "${mntdir}"; rm -f "${filename}"' INT TERM EXIT
+
+  # Cleanup old image.
+  if [[ -e "${filename}" ]]; then
+    rm -f "${filename}"
+  fi
+  truncate --size="${size}" "${filename}"
+
+  mkfs.ext2 "${mkfs_opts[@]}" "${filename}"
+  sudo mount "${filename}" "${mntdir}" -o loop
+
+  case "${kind}" in
+    ue_settings)
+      add_files_ue_settings "${mntdir}" "${block_size}"
+      ;;
+    default)
+      add_files_default "${mntdir}" "${block_size}"
+      ;;
+  esac
+
+  cleanup "${mntdir}"
+  trap - INT TERM EXIT
+}
+
+OUTPUT_DIR=$(dirname "$0")
+IMAGES=()
+
+# generate_image <image_name> [<image args> ...]
+generate_image() {
+  echo "Generating image $1.img"
+  IMAGES+=( "$1.img" )
+  generate_fs "${OUTPUT_DIR}/$1.img" "${@:2}"
+}
+
+main() {
+  # Add more sample images here.
+  generate_image disk_ext2_1k default 16777216 1024
+  generate_image disk_ext2_4k default 16777216 4096
+  generate_image disk_ext2_ue_settings ue_settings 16777216 4096
+
+  # Generate the tarball and delete temporary images.
+  echo "Packing tar file sample_images.tar.bz2"
+  tar -jcf "${OUTPUT_DIR}/sample_images.tar.bz2" -C "${OUTPUT_DIR}" \
+    "${IMAGES[@]}"
+  cd "${OUTPUT_DIR}"
+  rm "${IMAGES[@]}"
+}
+
+main
diff --git a/sample_images/sample_images.tar.bz2 b/sample_images/sample_images.tar.bz2
new file mode 100644
index 0000000..0982271
--- /dev/null
+++ b/sample_images/sample_images.tar.bz2
Binary files differ
diff --git a/sample_omaha_v3_response.xml b/sample_omaha_v3_response.xml
new file mode 100644
index 0000000..abba523
--- /dev/null
+++ b/sample_omaha_v3_response.xml
@@ -0,0 +1,19 @@
+<response protocol="3.0" server="prod">
+  <daystart elapsed_seconds="56652"/>
+  <app appid="{90f229ce-83e2-4faf-8479-e368a34938b1}" status="ok">
+    <updatecheck status="ok">
+      <urls>
+        <url codebase="https://storage.googleapis.com/chromeos-releases-public/canary-channel/canary-channel/3095.0.0/"/>
+      </urls>
+      <manifest version="3095.0.0">
+        <packages>
+          <package hash="HVOmp67vBjPdvpWmOC2Uw4UDwsc=" name="chromeos_3095.0.0_x86-zgb_canary-channel_full_mp-v2.bin-df37843370ddf1e3819a2afeaa934faa.signed" required="true" size="400752559"/>
+        </packages>
+        <actions>
+          <action event="update" run="chromeos_3095.0.0_x86-zgb_canary-channel_full_mp-v2.bin-df37843370ddf1e3819a2afeaa934faa.signed"/>
+          <action ChromeOSVersion="3095.0.0" ChromeVersion="24.0.1307.0" IsDelta="true" IsDeltaPayload="false" MaxDaysToScatter="14" MetadataSignatureRsa="xXrO/LahHlKk3YmqEf1qE0PN587Sc2IJV+FN7J7x1h49waNQIy/QwYO4LaOySgETe5JZXtkAEzzqakfJwxQ2pVfzj1GkExwjd5LTn1He2GvA73B8fKbS4bfP7dbUFwD5039xCwf1U2gezFViOiOPiVURx/pEsdhv+Cqx/3HbjIuj5au2dooSyDxLC5AnODzAKyYfAcjMuiLON+9SqmctJW+VjzdY9SbJAnkH2qqVjFyBKAXsYT+hOTIJ3MJpg8OSVxMMtGB99PxbOJ52F37d2Y5Fws/AUkNnNEsan/WRJA1kuWoS6rpeR8JQYuVhLiK2u/KpOcvMVRw3Q2VUxtcAGw==" MetadataSize="58315" event="postinstall" sha256="DIAVxoI+8NpsudUawOA5U92VHlaxQBS3ejN4EPM6T2A="/>
+        </actions>
+      </manifest>
+    </updatecheck>
+  </app>
+</response>
diff --git a/shill_proxy.cc b/shill_proxy.cc
new file mode 100644
index 0000000..1c050b4
--- /dev/null
+++ b/shill_proxy.cc
@@ -0,0 +1,43 @@
+//
+// 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/shill_proxy.h"
+
+using org::chromium::flimflam::ManagerProxy;
+using org::chromium::flimflam::ManagerProxyInterface;
+using org::chromium::flimflam::ServiceProxy;
+using org::chromium::flimflam::ServiceProxyInterface;
+
+namespace chromeos_update_engine {
+
+ShillProxy::ShillProxy(const scoped_refptr<dbus::Bus>& bus) : bus_(bus) {}
+
+bool ShillProxy::Init() {
+  manager_proxy_.reset(new ManagerProxy(bus_));
+  return true;
+}
+
+ManagerProxyInterface* ShillProxy::GetManagerProxy() {
+  return manager_proxy_.get();
+}
+
+std::unique_ptr<ServiceProxyInterface> ShillProxy::GetServiceForPath(
+    const dbus::ObjectPath& path) {
+  DCHECK(bus_.get());
+  return std::unique_ptr<ServiceProxyInterface>(new ServiceProxy(bus_, path));
+}
+
+}  // namespace chromeos_update_engine
diff --git a/shill_proxy.h b/shill_proxy.h
new file mode 100644
index 0000000..6d545f6
--- /dev/null
+++ b/shill_proxy.h
@@ -0,0 +1,58 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_SHILL_PROXY_H_
+#define UPDATE_ENGINE_SHILL_PROXY_H_
+
+#include <memory>
+#include <string>
+
+#include <base/macros.h>
+#include <dbus/bus.h>
+#include <dbus/object_path.h>
+#include <shill/dbus-proxies.h>
+
+#include "update_engine/shill_proxy_interface.h"
+
+namespace chromeos_update_engine {
+
+// This class implements the connection to shill using real DBus calls.
+class ShillProxy : public ShillProxyInterface {
+ public:
+  explicit ShillProxy(const scoped_refptr<dbus::Bus>& bus);
+  ~ShillProxy() override = default;
+
+  // Initializes the ShillProxy instance creating the manager proxy from the
+  // |bus_|.
+  bool Init();
+
+  // ShillProxyInterface overrides.
+  org::chromium::flimflam::ManagerProxyInterface* GetManagerProxy() override;
+  std::unique_ptr<org::chromium::flimflam::ServiceProxyInterface>
+  GetServiceForPath(const dbus::ObjectPath& path) override;
+
+ private:
+  // A reference to the main bus for creating new ServiceProxy instances.
+  scoped_refptr<dbus::Bus> bus_;
+  std::unique_ptr<org::chromium::flimflam::ManagerProxyInterface>
+      manager_proxy_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShillProxy);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_SHILL_PROXY_H_
diff --git a/shill_proxy_interface.h b/shill_proxy_interface.h
new file mode 100644
index 0000000..5f6b44e
--- /dev/null
+++ b/shill_proxy_interface.h
@@ -0,0 +1,56 @@
+//
+// 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_SHILL_PROXY_INTERFACE_H_
+#define UPDATE_ENGINE_SHILL_PROXY_INTERFACE_H_
+
+#include <memory>
+#include <string>
+
+#include <base/macros.h>
+#include <dbus/object_path.h>
+#include <shill/dbus-proxies.h>
+
+namespace chromeos_update_engine {
+
+// This class handles the DBus connection with shill daemon. The DBus interface
+// with shill requires to monitor or request the current service by interacting
+// with the org::chromium::flimflam::ManagerProxy and then request or monitor
+// properties on the selected org::chromium::flimflam::ServiceProxy. This class
+// provides a mockable way to access that.
+class ShillProxyInterface {
+ public:
+  virtual ~ShillProxyInterface() = default;
+
+  // Return the ManagerProxy instance of the shill daemon. The instance is owned
+  // by this ShillProxyInterface instance.
+  virtual org::chromium::flimflam::ManagerProxyInterface* GetManagerProxy() = 0;
+
+  // Return a ServiceProxy for the given path. The ownership of the returned
+  // instance is transferred to the caller.
+  virtual std::unique_ptr<org::chromium::flimflam::ServiceProxyInterface>
+  GetServiceForPath(const dbus::ObjectPath& path) = 0;
+
+ protected:
+  ShillProxyInterface() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ShillProxyInterface);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_SHILL_PROXY_INTERFACE_H_
diff --git a/system_state.h b/system_state.h
new file mode 100644
index 0000000..2ba0272
--- /dev/null
+++ b/system_state.h
@@ -0,0 +1,122 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_SYSTEM_STATE_H_
+#define UPDATE_ENGINE_SYSTEM_STATE_H_
+
+namespace org {
+namespace chromium {
+class PowerManagerProxyInterface;
+}  // namespace chromium
+}  // namespace org
+
+class MetricsLibraryInterface;
+
+namespace chromeos_update_manager {
+
+class UpdateManager;
+
+}  // namespace chromeos_update_manager
+
+namespace policy {
+
+class DevicePolicy;
+
+}  // namespace policy
+
+namespace chromeos_update_engine {
+
+// SystemState is the root class within the update engine. So we should avoid
+// any circular references in header file inclusion. Hence forward-declaring
+// the required classes.
+class BootControlInterface;
+class ClockInterface;
+class ConnectionManagerInterface;
+class HardwareInterface;
+class OmahaRequestParams;
+class P2PManager;
+class PayloadStateInterface;
+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:
+  // Destructs this object.
+  virtual ~SystemState() {}
+
+  // Sets or gets the latest device policy.
+  virtual void set_device_policy(const policy::DevicePolicy* device_policy) = 0;
+  virtual const policy::DevicePolicy* device_policy() = 0;
+
+  // Gets the interface object for the bootloader control interface.
+  virtual BootControlInterface* boot_control() = 0;
+
+  // Gets the interface object for the clock.
+  virtual ClockInterface* clock() = 0;
+
+  // Gets the connection manager object.
+  virtual ConnectionManagerInterface* connection_manager() = 0;
+
+  // Gets the hardware interface object.
+  virtual HardwareInterface* hardware() = 0;
+
+  // Gets the Metrics Library interface for reporting UMA stats.
+  virtual MetricsLibraryInterface* metrics_lib() = 0;
+
+  // Gets the interface object for persisted store.
+  virtual PrefsInterface* prefs() = 0;
+
+  // Gets the interface object for the persisted store that persists across
+  // powerwashes. Please note that this should be used very seldomly and must
+  // be forwards and backwards compatible as powerwash is used to go back and
+  // forth in system versions.
+  virtual PrefsInterface* powerwash_safe_prefs() = 0;
+
+  // Gets the interface for the payload state object.
+  virtual PayloadStateInterface* payload_state() = 0;
+
+  // Returns a pointer to the update attempter object.
+  virtual UpdateAttempter* update_attempter() = 0;
+
+  // Returns a pointer to the object that stores the parameters that are
+  // common to all Omaha requests.
+  virtual OmahaRequestParams* request_params() = 0;
+
+  // Returns a pointer to the P2PManager singleton.
+  virtual P2PManager* p2p_manager() = 0;
+
+  // Returns a pointer to the UpdateManager singleton.
+  virtual chromeos_update_manager::UpdateManager* update_manager() = 0;
+
+  // DBus proxies. Mocked during test.
+  virtual org::chromium::PowerManagerProxyInterface* power_manager_proxy() = 0;
+
+  // If true, this is the first instance of the update engine since the system
+  // restarted. Important for tracking whether you are running instance of the
+  // update engine on first boot or due to a crash/restart.
+  virtual bool system_rebooted() = 0;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_SYSTEM_STATE_H_
diff --git a/tar_bunzip2.gypi b/tar_bunzip2.gypi
new file mode 100644
index 0000000..8c6614a
--- /dev/null
+++ b/tar_bunzip2.gypi
@@ -0,0 +1,42 @@
+#
+# 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',
+      'inputs': [
+        '<(RULE_INPUT_PATH)',
+      ],
+      '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_http_server.cc b/test_http_server.cc
new file mode 100644
index 0000000..eaffba8
--- /dev/null
+++ b/test_http_server.cc
@@ -0,0 +1,634 @@
+//
+// 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.
+//
+
+// This file implements a simple HTTP server. It can exhibit odd behavior
+// that's useful for testing. For example, it's useful to test that
+// the updater can continue a connection if it's dropped, or that it
+// handles very slow data transfers.
+
+// To use this, simply make an HTTP connection to localhost:port and
+// GET a url.
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/posix/eintr_wrapper.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+
+#include "update_engine/common/http_common.h"
+
+
+// HTTP end-of-line delimiter; sorry, this needs to be a macro.
+#define EOL "\r\n"
+
+using std::string;
+using std::vector;
+
+
+namespace chromeos_update_engine {
+
+static const char* kListeningMsgPrefix = "listening on port ";
+
+enum {
+  RC_OK = 0,
+  RC_BAD_ARGS,
+  RC_ERR_READ,
+  RC_ERR_SETSOCKOPT,
+  RC_ERR_BIND,
+  RC_ERR_LISTEN,
+  RC_ERR_GETSOCKNAME,
+  RC_ERR_REPORT,
+};
+
+struct HttpRequest {
+  HttpRequest()
+      : start_offset(0), end_offset(0), return_code(kHttpResponseOk) {}
+  string host;
+  string url;
+  off_t start_offset;
+  off_t end_offset;  // non-inclusive, zero indicates unspecified.
+  HttpResponseCode return_code;
+};
+
+bool ParseRequest(int fd, HttpRequest* request) {
+  string headers;
+  do {
+    char buf[1024];
+    ssize_t r = read(fd, buf, sizeof(buf));
+    if (r < 0) {
+      perror("read");
+      exit(RC_ERR_READ);
+    }
+    headers.append(buf, r);
+  } while (!base::EndsWith(headers, EOL EOL, true));
+
+  LOG(INFO) << "got headers:\n--8<------8<------8<------8<----\n"
+            << headers
+            << "\n--8<------8<------8<------8<----";
+
+  // Break header into lines.
+  vector<string> lines;
+  base::SplitStringUsingSubstr(
+      headers.substr(0, headers.length() - strlen(EOL EOL)), EOL, &lines);
+
+  // Decode URL line.
+  vector<string> terms;
+  base::SplitStringAlongWhitespace(lines[0], &terms);
+  CHECK_EQ(terms.size(), static_cast<vector<string>::size_type>(3));
+  CHECK_EQ(terms[0], "GET");
+  request->url = terms[1];
+  LOG(INFO) << "URL: " << request->url;
+
+  // Decode remaining lines.
+  size_t i;
+  for (i = 1; i < lines.size(); i++) {
+    vector<string> terms;
+    base::SplitStringAlongWhitespace(lines[i], &terms);
+
+    if (terms[0] == "Range:") {
+      CHECK_EQ(terms.size(), static_cast<vector<string>::size_type>(2));
+      string &range = terms[1];
+      LOG(INFO) << "range attribute: " << range;
+      CHECK(base::StartsWithASCII(range, "bytes=", true) &&
+            range.find('-') != string::npos);
+      request->start_offset = atoll(range.c_str() + strlen("bytes="));
+      // Decode end offset and increment it by one (so it is non-inclusive).
+      if (range.find('-') < range.length() - 1)
+        request->end_offset = atoll(range.c_str() + range.find('-') + 1) + 1;
+      request->return_code = kHttpResponsePartialContent;
+      string tmp_str = base::StringPrintf("decoded range offsets: "
+                                               "start=%jd end=",
+                                               (intmax_t)request->start_offset);
+      if (request->end_offset > 0)
+        base::StringAppendF(&tmp_str, "%jd (non-inclusive)",
+                            (intmax_t)request->end_offset);
+      else
+        base::StringAppendF(&tmp_str, "unspecified");
+      LOG(INFO) << tmp_str;
+    } else if (terms[0] == "Host:") {
+      CHECK_EQ(terms.size(), static_cast<vector<string>::size_type>(2));
+      request->host = terms[1];
+      LOG(INFO) << "host attribute: " << request->host;
+    } else {
+      LOG(WARNING) << "ignoring HTTP attribute: `" << lines[i] << "'";
+    }
+  }
+
+  return true;
+}
+
+string Itoa(off_t num) {
+  char buf[100] = {0};
+  snprintf(buf, sizeof(buf), "%" PRIi64, num);
+  return buf;
+}
+
+// Writes a string into a file. Returns total number of bytes written or -1 if a
+// write error occurred.
+ssize_t WriteString(int fd, const string& str) {
+  const size_t total_size = str.size();
+  size_t remaining_size = total_size;
+  char const *data = str.data();
+
+  while (remaining_size) {
+    ssize_t written = write(fd, data, remaining_size);
+    if (written < 0) {
+      perror("write");
+      LOG(INFO) << "write failed";
+      return -1;
+    }
+    data += written;
+    remaining_size -= written;
+  }
+
+  return total_size;
+}
+
+// Writes the headers of an HTTP response into a file.
+ssize_t WriteHeaders(int fd, const off_t start_offset, const off_t end_offset,
+                     HttpResponseCode return_code) {
+  ssize_t written = 0, ret;
+
+  ret = WriteString(fd,
+                    string("HTTP/1.1 ") + Itoa(return_code) + " " +
+                    GetHttpResponseDescription(return_code) +
+                    EOL
+                    "Content-Type: application/octet-stream" EOL);
+  if (ret < 0)
+    return -1;
+  written += ret;
+
+  // Compute content legnth.
+  const off_t content_length = end_offset - start_offset;;
+
+  // A start offset that equals the end offset indicates that the response
+  // should contain the full range of bytes in the requested resource.
+  if (start_offset || start_offset == end_offset) {
+    ret = WriteString(fd,
+                      string("Accept-Ranges: bytes" EOL
+                             "Content-Range: bytes ") +
+                      Itoa(start_offset == end_offset ? 0 : start_offset) +
+                      "-" + Itoa(end_offset - 1) + "/" + Itoa(end_offset) +
+                      EOL);
+    if (ret < 0)
+      return -1;
+    written += ret;
+  }
+
+  ret = WriteString(fd, string("Content-Length: ") + Itoa(content_length) +
+                    EOL EOL);
+  if (ret < 0)
+    return -1;
+  written += ret;
+
+  return written;
+}
+
+// Writes a predetermined payload of lines of ascending bytes to a file. The
+// first byte of output is appropriately offset with respect to the request line
+// length.  Returns the number of successfully written bytes.
+size_t WritePayload(int fd, const off_t start_offset, const off_t end_offset,
+                    const char first_byte, const size_t line_len) {
+  CHECK_LE(start_offset, end_offset);
+  CHECK_GT(line_len, static_cast<size_t>(0));
+
+  LOG(INFO) << "writing payload: " << line_len << "-byte lines starting with `"
+            << first_byte << "', offset range " << start_offset << " -> "
+            << end_offset;
+
+  // Populate line of ascending characters.
+  string line;
+  line.reserve(line_len);
+  char byte = first_byte;
+  size_t i;
+  for (i = 0; i < line_len; i++)
+    line += byte++;
+
+  const size_t total_len = end_offset - start_offset;
+  size_t remaining_len = total_len;
+  bool success = true;
+
+  // If start offset is not aligned with line boundary, output partial line up
+  // to the first line boundary.
+  size_t start_modulo = start_offset % line_len;
+  if (start_modulo) {
+    string partial = line.substr(start_modulo, remaining_len);
+    ssize_t ret = WriteString(fd, partial);
+    if ((success = (ret >= 0 && (size_t) ret == partial.length())))
+      remaining_len -= partial.length();
+  }
+
+  // Output full lines up to the maximal line boundary below the end offset.
+  while (success && remaining_len >= line_len) {
+    ssize_t ret = WriteString(fd, line);
+    if ((success = (ret >= 0 && (size_t) ret == line_len)))
+      remaining_len -= line_len;
+  }
+
+  // Output a partial line up to the end offset.
+  if (success && remaining_len) {
+    string partial = line.substr(0, remaining_len);
+    ssize_t ret = WriteString(fd, partial);
+    if ((success = (ret >= 0 && (size_t) ret == partial.length())))
+      remaining_len -= partial.length();
+  }
+
+  return (total_len - remaining_len);
+}
+
+// Write default payload lines of the form 'abcdefghij'.
+inline size_t WritePayload(int fd, const off_t start_offset,
+                           const off_t end_offset) {
+  return WritePayload(fd, start_offset, end_offset, 'a', 10);
+}
+
+// Send an empty response, then kill the server.
+void HandleQuit(int fd) {
+  WriteHeaders(fd, 0, 0, kHttpResponseOk);
+  LOG(INFO) << "pid(" << getpid() <<  "): HTTP server exiting ...";
+  exit(RC_OK);
+}
+
+
+// Generates an HTTP response with payload corresponding to requested offsets
+// and length.  Optionally, truncate the payload at a given length and add a
+// pause midway through the transfer.  Returns the total number of bytes
+// delivered or -1 for error.
+ssize_t HandleGet(int fd, const HttpRequest& request, const size_t total_length,
+                  const size_t truncate_length, const int sleep_every,
+                  const int sleep_secs) {
+  ssize_t ret;
+  size_t written = 0;
+
+  // Obtain start offset, make sure it is within total payload length.
+  const size_t start_offset = request.start_offset;
+  if (start_offset >= total_length) {
+    LOG(WARNING) << "start offset (" << start_offset
+                 << ") exceeds total length (" << total_length
+                 << "), generating error response ("
+                 << kHttpResponseReqRangeNotSat << ")";
+    return WriteHeaders(fd, total_length, total_length,
+                        kHttpResponseReqRangeNotSat);
+  }
+
+  // Obtain end offset, adjust to fit in total payload length and ensure it does
+  // not preceded the start offset.
+  size_t end_offset = (request.end_offset > 0 ?
+                       request.end_offset : total_length);
+  if (end_offset < start_offset) {
+    LOG(WARNING) << "end offset (" << end_offset << ") precedes start offset ("
+                 << start_offset << "), generating error response";
+    return WriteHeaders(fd, 0, 0, kHttpResponseBadRequest);
+  }
+  if (end_offset > total_length) {
+    LOG(INFO) << "requested end offset (" << end_offset
+              << ") exceeds total length (" << total_length << "), adjusting";
+    end_offset = total_length;
+  }
+
+  // Generate headers
+  LOG(INFO) << "generating response header: range=" << start_offset << "-"
+            << (end_offset - 1) << "/" << (end_offset - start_offset)
+            << ", return code=" << request.return_code;
+  if ((ret = WriteHeaders(fd, start_offset, end_offset,
+                          request.return_code)) < 0)
+    return -1;
+  LOG(INFO) << ret << " header bytes written";
+  written += ret;
+
+  // Compute payload length, truncate as necessary.
+  size_t payload_length = end_offset - start_offset;
+  if (truncate_length > 0 && truncate_length < payload_length) {
+    LOG(INFO) << "truncating request payload length (" << payload_length
+              << ") at " << truncate_length;
+    payload_length = truncate_length;
+    end_offset = start_offset + payload_length;
+  }
+
+  LOG(INFO) << "generating response payload: range=" << start_offset << "-"
+            << (end_offset - 1) << "/" << (end_offset - start_offset);
+
+  // Decide about optional midway delay.
+  if (truncate_length > 0 && sleep_every > 0 && sleep_secs >= 0 &&
+      start_offset % (truncate_length * sleep_every) == 0) {
+    const off_t midway_offset = start_offset + payload_length / 2;
+
+    if ((ret = WritePayload(fd, start_offset, midway_offset)) < 0)
+      return -1;
+    LOG(INFO) << ret << " payload bytes written (first chunk)";
+    written += ret;
+
+    LOG(INFO) << "sleeping for " << sleep_secs << " seconds...";
+    sleep(sleep_secs);
+
+    if ((ret = WritePayload(fd, midway_offset, end_offset)) < 0)
+      return -1;
+    LOG(INFO) << ret << " payload bytes written (second chunk)";
+    written += ret;
+  } else {
+    if ((ret = WritePayload(fd, start_offset, end_offset)) < 0)
+      return -1;
+    LOG(INFO) << ret << " payload bytes written";
+    written += ret;
+  }
+
+  LOG(INFO) << "response generation complete, " << written
+            << " total bytes written";
+  return written;
+}
+
+ssize_t HandleGet(int fd, const HttpRequest& request,
+                  const size_t total_length) {
+  return HandleGet(fd, request, total_length, 0, 0, 0);
+}
+
+// Handles /redirect/<code>/<url> requests by returning the specified
+// redirect <code> with a location pointing to /<url>.
+void HandleRedirect(int fd, const HttpRequest& request) {
+  LOG(INFO) << "Redirecting...";
+  string url = request.url;
+  CHECK_EQ(static_cast<size_t>(0), url.find("/redirect/"));
+  url.erase(0, strlen("/redirect/"));
+  string::size_type url_start = url.find('/');
+  CHECK_NE(url_start, string::npos);
+  HttpResponseCode code = StringToHttpResponseCode(url.c_str());
+  url.erase(0, url_start);
+  url = "http://" + request.host + url;
+  const char *status = GetHttpResponseDescription(code);
+  if (!status)
+    CHECK(false) << "Unrecognized redirection code: " << code;
+  LOG(INFO) << "Code: " << code << " " << status;
+  LOG(INFO) << "New URL: " << url;
+
+  ssize_t ret;
+  if ((ret = WriteString(fd, "HTTP/1.1 " + Itoa(code) + " " +
+                         status + EOL)) < 0)
+    return;
+  WriteString(fd, "Location: " + url + EOL);
+}
+
+// Generate a page not found error response with actual text payload. Return
+// number of bytes written or -1 for error.
+ssize_t HandleError(int fd, const HttpRequest& request) {
+  LOG(INFO) << "Generating error HTTP response";
+
+  ssize_t ret;
+  size_t written = 0;
+
+  const string data("This is an error page.");
+
+  if ((ret = WriteHeaders(fd, 0, data.size(), kHttpResponseNotFound)) < 0)
+    return -1;
+  written += ret;
+
+  if ((ret = WriteString(fd, data)) < 0)
+    return -1;
+  written += ret;
+
+  return written;
+}
+
+// Generate an error response if the requested offset is nonzero, up to a given
+// maximal number of successive failures.  The error generated is an "Internal
+// Server Error" (500).
+ssize_t HandleErrorIfOffset(int fd, const HttpRequest& request,
+                            size_t end_offset, int max_fails) {
+  static int num_fails = 0;
+
+  if (request.start_offset > 0 && num_fails < max_fails) {
+    LOG(INFO) << "Generating error HTTP response";
+
+    ssize_t ret;
+    size_t written = 0;
+
+    const string data("This is an error page.");
+
+    if ((ret = WriteHeaders(fd, 0, data.size(),
+                            kHttpResponseInternalServerError)) < 0)
+      return -1;
+    written += ret;
+
+    if ((ret = WriteString(fd, data)) < 0)
+      return -1;
+    written += ret;
+
+    num_fails++;
+    return written;
+  } else {
+    num_fails = 0;
+    return HandleGet(fd, request, end_offset);
+  }
+}
+
+void HandleHang(int fd) {
+  LOG(INFO) << "Hanging until the other side of the connection is closed.";
+  char c;
+  while (HANDLE_EINTR(read(fd, &c, 1)) > 0) {}
+}
+
+void HandleDefault(int fd, const HttpRequest& request) {
+  const off_t start_offset = request.start_offset;
+  const string data("unhandled path");
+  const size_t size = data.size();
+  ssize_t ret;
+
+  if ((ret = WriteHeaders(fd, start_offset, size, request.return_code)) < 0)
+    return;
+  WriteString(fd, (start_offset < static_cast<off_t>(size) ?
+                   data.substr(start_offset) : ""));
+}
+
+
+// Break a URL into terms delimited by slashes.
+class UrlTerms {
+ public:
+  UrlTerms(const string &url, size_t num_terms) {
+    // URL must be non-empty and start with a slash.
+    CHECK_GT(url.size(), static_cast<size_t>(0));
+    CHECK_EQ(url[0], '/');
+
+    // Split it into terms delimited by slashes, omitting the preceeding slash.
+    base::SplitStringDontTrim(url.substr(1), '/', &terms);
+
+    // Ensure expected length.
+    CHECK_EQ(terms.size(), num_terms);
+  }
+
+  inline string Get(const off_t index) const {
+    return terms[index];
+  }
+  inline const char *GetCStr(const off_t index) const {
+    return Get(index).c_str();
+  }
+  inline int GetInt(const off_t index) const {
+    return atoi(GetCStr(index));
+  }
+  inline size_t GetSizeT(const off_t index) const {
+    return static_cast<size_t>(atol(GetCStr(index)));
+  }
+
+ private:
+  vector<string> terms;
+};
+
+void HandleConnection(int fd) {
+  HttpRequest request;
+  ParseRequest(fd, &request);
+
+  string &url = request.url;
+  LOG(INFO) << "pid(" << getpid() <<  "): handling url " << url;
+  if (url == "/quitquitquit") {
+    HandleQuit(fd);
+  } else if (base::StartsWithASCII(url, "/download/", true)) {
+    const UrlTerms terms(url, 2);
+    HandleGet(fd, request, terms.GetSizeT(1));
+  } else if (base::StartsWithASCII(url, "/flaky/", true)) {
+    const UrlTerms terms(url, 5);
+    HandleGet(fd, request, terms.GetSizeT(1), terms.GetSizeT(2),
+              terms.GetInt(3), terms.GetInt(4));
+  } else if (url.find("/redirect/") == 0) {
+    HandleRedirect(fd, request);
+  } else if (url == "/error") {
+    HandleError(fd, request);
+  } else if (base::StartsWithASCII(url, "/error-if-offset/", true)) {
+    const UrlTerms terms(url, 3);
+    HandleErrorIfOffset(fd, request, terms.GetSizeT(1), terms.GetInt(2));
+  } else if (url == "/hang") {
+    HandleHang(fd);
+  } else {
+    HandleDefault(fd, request);
+  }
+
+  close(fd);
+}
+
+}  // namespace chromeos_update_engine
+
+using namespace chromeos_update_engine;  // NOLINT(build/namespaces)
+
+
+void usage(const char *prog_arg) {
+  fprintf(
+      stderr,
+      "Usage: %s [ FILE ]\n"
+      "Once accepting connections, the following is written to FILE (or "
+      "stdout):\n"
+      "\"%sN\" (where N is an integer port number)\n",
+      basename(prog_arg), kListeningMsgPrefix);
+}
+
+int main(int argc, char** argv) {
+  // Check invocation.
+  if (argc > 2)
+    errx(RC_BAD_ARGS, "unexpected number of arguments (use -h for usage)");
+
+  // Parse (optional) argument.
+  int report_fd = STDOUT_FILENO;
+  if (argc == 2) {
+    if (!strcmp(argv[1], "-h")) {
+      usage(argv[0]);
+      exit(RC_OK);
+    }
+
+    report_fd = open(argv[1], O_WRONLY | O_CREAT, 00644);
+  }
+
+  // Ignore SIGPIPE on write() to sockets.
+  signal(SIGPIPE, SIG_IGN);
+
+  int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
+  if (listen_fd < 0)
+    LOG(FATAL) << "socket() failed";
+
+  struct sockaddr_in server_addr = sockaddr_in();
+  server_addr.sin_family = AF_INET;
+  server_addr.sin_addr.s_addr = INADDR_ANY;
+  server_addr.sin_port = 0;
+
+  {
+    // Get rid of "Address in use" error
+    int tr = 1;
+    if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &tr,
+                   sizeof(int)) == -1) {
+      perror("setsockopt");
+      exit(RC_ERR_SETSOCKOPT);
+    }
+  }
+
+  // Bind the socket and set for listening.
+  if (bind(listen_fd, reinterpret_cast<struct sockaddr *>(&server_addr),
+           sizeof(server_addr)) < 0) {
+    perror("bind");
+    exit(RC_ERR_BIND);
+  }
+  if (listen(listen_fd, 5) < 0) {
+    perror("listen");
+    exit(RC_ERR_LISTEN);
+  }
+
+  // Check the actual port.
+  struct sockaddr_in bound_addr = sockaddr_in();
+  socklen_t bound_addr_len = sizeof(bound_addr);
+  if (getsockname(listen_fd, reinterpret_cast<struct sockaddr*>(&bound_addr),
+                  &bound_addr_len) < 0) {
+    perror("getsockname");
+    exit(RC_ERR_GETSOCKNAME);
+  }
+  in_port_t port = ntohs(bound_addr.sin_port);
+
+  // Output the listening port, indicating that the server is processing
+  // requests. IMPORTANT! (a) the format of this message is as expected by some
+  // unit tests, avoid unilateral changes; (b) it is necessary to flush/sync the
+  // file to prevent the spawning process from waiting indefinitely for this
+  // message.
+  string listening_msg = base::StringPrintf("%s%hu", kListeningMsgPrefix, port);
+  LOG(INFO) << listening_msg;
+  CHECK_EQ(write(report_fd, listening_msg.c_str(), listening_msg.length()),
+           static_cast<int>(listening_msg.length()));
+  CHECK_EQ(write(report_fd, "\n", 1), 1);
+  if (report_fd == STDOUT_FILENO)
+    fsync(report_fd);
+  else
+    close(report_fd);
+
+  while (1) {
+    LOG(INFO) << "pid(" << getpid() <<  "): waiting to accept new connection";
+    int client_fd = accept(listen_fd, nullptr, nullptr);
+    LOG(INFO) << "got past accept";
+    if (client_fd < 0)
+      LOG(FATAL) << "ERROR on accept";
+    HandleConnection(client_fd);
+  }
+  return 0;
+}
diff --git a/testrunner.cc b/testrunner.cc
new file mode 100644
index 0000000..9e22798
--- /dev/null
+++ b/testrunner.cc
@@ -0,0 +1,47 @@
+//
+// 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.
+//
+
+// based on pam_google_testrunner.cc
+
+#include <xz.h>
+
+#include <base/at_exit.h>
+#include <base/command_line.h>
+#include <brillo/test_helpers.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/terminator.h"
+
+int main(int argc, char **argv) {
+  LOG(INFO) << "started";
+  base::AtExitManager exit_manager;
+  // xz-embedded requires to initialize its CRC-32 table once on startup.
+  xz_crc32_init();
+  // TODO(garnold) temporarily cause the unittest binary to exit with status
+  // code 2 upon catching a SIGTERM. This will help diagnose why the unittest
+  // binary is perceived as failing by the buildbot.  We should revert it to use
+  // the default exit status of 1.  Corresponding reverts are necessary in
+  // terminator_unittest.cc.
+  chromeos_update_engine::Terminator::Init(2);
+  LOG(INFO) << "parsing command line arguments";
+  base::CommandLine::Init(argc, argv);
+  LOG(INFO) << "initializing gtest";
+  SetUpTests(&argc, argv, true);
+  LOG(INFO) << "running unit tests";
+  int test_result = RUN_ALL_TESTS();
+  LOG(INFO) << "unittest return value: " << test_result;
+  return test_result;
+}
diff --git a/unittest_key.pem b/unittest_key.pem
new file mode 100644
index 0000000..224d3c3
--- /dev/null
+++ b/unittest_key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAx6hqS+Hbc7jN82ek+OBkHJNvlWXktyS2XCuQtGtnHjEV7Ou5
+hHNF96s5wEn8RDtrdLocm0a+SqXXf7KycFRTZxLdDbqWAMUlPXOaPI+PZLWkB7/K
+V7Cdj6vPGbeq7elu+aT/budhzTvq2stYrrAlcr/21tOVDPiWtfCdykh7FCXi6FaY
+HoNy5A6EKQLfLBuJoU/QoCufldmwlFaFDKl+Koos6R1QYJfCNZfgocrW1PIh+8t1
+JIvw6Izo8+fTme7nev7Ol2YZiMWJpRYKx8MgxW2UgTXlRpmQN56pc9LUsnVA8FND
+BN57+gMJj+XmdDmbtMpOsyXfSNEgnuwMPcEcmwIDAQABAoIBABPkwgKhlH4pUcwI
+7bUmlpMKVbnrFyjwbYMtjBOOCA5IEckzi56Y5cXRt8VjGdGqogBVcvg9ykQh1iER
+KxpqLI0+oev2RW/6NMW0uQ+DtmPwfVGQWJb4MBraoZ4MYOmnsrkJKbJhN6t9Zt86
+F7IANxsB6ZRqLJXIRywFt5MqOak+GAnQJ8C8eSQg70NhbEhSOrD8wrD6tfvgIqta
+XxhtlQWUAILIWetnWrJsalMqnreGn7vhc7+iihhMtXh1xNBMTA+gzpov/Cx21iH5
+DM9ppSA6HHDXrMhauryypIRrhjOUWRyDws/kIHgIW4TCbULOlxqsputQeTmdf0ti
+7lpwqAECgYEA7nNKkct3Vs51Ugk4GUPC4NOyYRPNc9UQAfHViB9gSDRacCo9Ax9J
+83hJGqDXlNGzELOnhzMn8jQMyF13eWzOsMozK6Fj3uW7DBvelg5bfgsZDUUO5WUF
+6BYbOheVqf12rIHR9BKBmCfLEKyxbKmw5bnB0uNo7IuBPBNuhPbvkgECgYEA1lo5
+XHWJpQnVl+JzXLHpXBK2nfnFAOtvzlTW+7gteeU12X2HcFASrzp7C1ULVV+i1Kcz
+tDFIA5yiFjEqmSJ/TsO8aqAhL5BXJjylCepQK7XkEOGCR8eQjlt7E4DulAsQbfpt
+k30HVVlIOFqLCWKSW8M3dy/Plodq/Gyq26rntpsCgYAzsNyGdIQfVkxKh2MY3v6c
+/Gdb8g4EwThiI4m1o4+ct3SvggiN57eBRx8Z3ao+QaM+yKNVhLpxH+VxfgmLUhIQ
+cxTarXbX+BcvTc9X2i7tSPyaStEq21aHdFtcoYY5Po/+X3ojHevoDyBPMhCYTMTj
+V/xzegbh2HAglNnNizZuAQKBgQCyOxEpBP5vgS7d/MgJklFEYrb/wjgBnMI5oSek
+5C7EBUdyUUM1qw7uLsUy1gL3eO7uvRxrvvJvNmU76KPP8vRCLNTVH9KYNv+P5qsg
+BHmm7rX1J11pi9Fx3TUIMZOu+0gs+ib0lOhtGjDH0tl680BZFohfDR0hv/XAcCbd
+Qk0q8wKBgQCGbURFFW/5RZUA77MmpY6jsEMw2gJmVtjO5IWZg9VPvLQQPgCr4Ho/
+bS2LIsT6pS+hrLOoz5KI0YueRS0jQYI+WkRqNf5wYNjxPql9632FiDLHO+Xv8PBe
+kHrPHy0GGT1igXScY4eemw4ZC1OSdZfkVn6Ui/JvBHrydJ2LrutMWQ==
+-----END RSA PRIVATE KEY-----
diff --git a/unittest_key2.pem b/unittest_key2.pem
new file mode 100644
index 0000000..d1f9a78
--- /dev/null
+++ b/unittest_key2.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAqdM7gnd43cWfQzyrt16QDHE+T0yxg18OZO+9sc8jWpjC1zFt
+oMFokESiu9TLUp+KUC274f+DxIgAfSCO6U5DJFRPXUzve1vaXYfqljUBxc+JYcGd
+d6PCUl4Ap9f02DhkrGnf/rkHOCEhFNpm53dovZ9A6Q5KBfcg1HeQ088opVceQGtU
+mL+bONq5tLvNFLUU0Ru0Am4ADJ8Xmw4rpvqvmXJaFmVuf/Gx7+TOnaJvZTeOO9AJ
+K6q6V8E7+ZZiMIc5F4EOs5AX/lZfNO3RVgG2O7Dhzza+nOzJ3ZJGK5R4WwZduhn9
+QRYogYPB0c62878qXpf2Bn3NpUPi8Cf/RLMMmQIDAQABAoIBACyLUWKpP7S770hN
+k6TnUtVQps1aCn2w4y+qipEnCdjrlL+pIV43HNwqhJzL9gDYBAl/1XYz9TYJjkdD
+0Ph1JLtUufR5B5/NufsqeWeow6xFAX34sPr+oyvDqFxeEsTcFdv7cVt44OHiHrE/
+kBpKgdiq+vWmX9gsuBnCuuQzxC+Juo6nupwZXcpa/ow9lC4QsgKqcjaUGrXXy2t9
+Er+9aSl8NdTjK76BXQsDgNkDyJZwNN14xrdS8eFsS4twskaOEYI4hEM0g62NOjgd
+Po8Ap/MnPpGSGcAd3d3Fq8KgT1lpyMKedLFU+k0H+/Y4RBl7grz1XXvSTzGi3Qy6
+38F4eVkCgYEA4mo4iiXSfrov9QffsVM17O0t9hUsOJHnUsEScxWLDm4IzaObyTtv
+tWW33iQSeFu4Wsol0nzjqWo3BaqiRidRUd42yZ07LJvfUDxUX9xPaUPFRs25iwhZ
+6tKAVqGk7/CFrN+R44sIwbsSvbExMAyW6gnj93EWUmMWWYp02hLbN0sCgYEAwAQI
+awVoc56OCtRpfYtlAPD/VOP1mbNzRmVl/UyZ4XYmz6f/hEz63Bk5PhYSZftlmK/r
+nj4qnl7HZ8jrJgZn2e97rPNpk7KDVU1+csCgLWZBTOXl/o9tOTyjh9LoRAjKtBB7
+x6CkWyiyd94xIq5VbnXhvL3a4d4o6OxMWdG5aSsCgYEAo44z1afIzP7WkdzkPIZt
+l/8linR1A1BymBccqsHPN9dIyLP9X3puEc2u6uuH5CXtoLgSZmENXF577L38h0zz
+s34gebf4/RqEUMOj97OAMfxgz+rgs4yO19DEINCYAzPufJjsHEFdTAVFXn5Xl+wg
+QGRwp1ir1Uv64yffjYC9ls0CgYEAjvIxpiKniPNvwUYypmDgt5uyKetvCpaaabzQ
++YpOQJep+wuRYFfCpZotkDf0SHGoR8wnd23GYpIilvPvgyZfp9HuW2n2nhrWROnl
+Cd63IDUwxeOcni7+XA71mwb7HLMC3Jws2geQc8DPZAdIww3P0eT2QYGBcobmI8jO
+akuEYXMCgYAm79Kb/r+3Hew5oAS1Whw70DskVlOutSgNsDPfW9MtDcnETBcGep7A
+1jCL5jjdUYRonimVMFjh1K+UFzV/DQHkgNzjxz9Inbh02y67vL2X836dS9esOcbx
+uZhf+8rL+GnSNqYDqCEuP7qCIloDhguJq9NKyTB4yc59qIkY2zPAzQ==
+-----END RSA PRIVATE KEY-----
diff --git a/update_attempter.cc b/update_attempter.cc
new file mode 100644
index 0000000..5704731
--- /dev/null
+++ b/update_attempter.cc
@@ -0,0 +1,1604 @@
+//
+// 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 <algorithm>
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/rand_util.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/bind_lambda.h>
+#include <brillo/make_unique_ptr.h>
+#include <brillo/message_loops/message_loop.h>
+#include <debugd/dbus-constants.h>
+#include <policy/device_policy.h>
+#include <policy/libpolicy.h>
+#include <power_manager/dbus-constants.h>
+#include <power_manager/dbus-proxies.h>
+#include <update_engine/dbus-constants.h>
+
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/certificate_checker.h"
+#include "update_engine/common/clock_interface.h"
+#include "update_engine/common/constants.h"
+#include "update_engine/common/hardware_interface.h"
+#include "update_engine/common/libcurl_http_fetcher.h"
+#include "update_engine/common/multi_range_http_fetcher.h"
+#include "update_engine/common/platform_constants.h"
+#include "update_engine/common/prefs_interface.h"
+#include "update_engine/common/subprocess.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/dbus_service.h"
+#include "update_engine/metrics.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/system_state.h"
+#include "update_engine/update_manager/policy.h"
+#include "update_engine/update_manager/update_manager.h"
+#include "update_engine/update_status_utils.h"
+
+using base::Bind;
+using base::Callback;
+using base::Time;
+using base::TimeDelta;
+using base::TimeTicks;
+using brillo::MessageLoop;
+using chromeos_update_manager::EvalStatus;
+using chromeos_update_manager::Policy;
+using chromeos_update_manager::UpdateCheckParams;
+using std::set;
+using std::shared_ptr;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+const int UpdateAttempter::kMaxDeltaUpdateFailures = 3;
+
+namespace {
+const int kMaxConsecutiveObeyProxyRequests = 20;
+
+// By default autest bypasses scattering. If we want to test scattering,
+// use kScheduledAUTestURLRequest. The URL used is same in both cases, but
+// different params are passed to CheckForUpdate().
+const char kAUTestURLRequest[] = "autest";
+const char kScheduledAUTestURLRequest[] = "autest-scheduled";
+}  // namespace
+
+// Turns a generic ErrorCode::kError to a generic error code specific
+// to |action| (e.g., ErrorCode::kFilesystemVerifierError). If |code| is
+// not ErrorCode::kError, or the action is not matched, returns |code|
+// unchanged.
+ErrorCode GetErrorCodeForAction(AbstractAction* action,
+                                     ErrorCode code) {
+  if (code != ErrorCode::kError)
+    return code;
+
+  const string type = action->Type();
+  if (type == OmahaRequestAction::StaticType())
+    return ErrorCode::kOmahaRequestError;
+  if (type == OmahaResponseHandlerAction::StaticType())
+    return ErrorCode::kOmahaResponseHandlerError;
+  if (type == FilesystemVerifierAction::StaticType())
+    return ErrorCode::kFilesystemVerifierError;
+  if (type == PostinstallRunnerAction::StaticType())
+    return ErrorCode::kPostinstallRunnerError;
+
+  return code;
+}
+
+UpdateAttempter::UpdateAttempter(
+    SystemState* system_state,
+    CertificateChecker* cert_checker,
+    LibCrosProxy* libcros_proxy,
+    org::chromium::debugdProxyInterface* debugd_proxy)
+    : processor_(new ActionProcessor()),
+      system_state_(system_state),
+      cert_checker_(cert_checker),
+      chrome_proxy_resolver_(libcros_proxy),
+      debugd_proxy_(debugd_proxy) {
+}
+
+UpdateAttempter::~UpdateAttempter() {
+  // CertificateChecker might not be initialized in unittests.
+  if (cert_checker_)
+    cert_checker_->SetObserver(nullptr);
+  CleanupCpuSharesManagement();
+  // Release ourselves as the ActionProcessor's delegate to prevent
+  // re-scheduling the updates due to the processing stopped.
+  processor_->set_delegate(nullptr);
+}
+
+void UpdateAttempter::Init() {
+  // 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_ = 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))
+    status_ = UpdateStatus::UPDATED_NEED_REBOOT;
+  else
+    status_ = UpdateStatus::IDLE;
+
+  chrome_proxy_resolver_.Init();
+}
+
+void UpdateAttempter::ScheduleUpdates() {
+  if (IsUpdateRunningOrScheduled())
+    return;
+
+  chromeos_update_manager::UpdateManager* const 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->AsyncPolicyRequest(callback, &Policy::UpdateCheckAllowed);
+  waiting_for_scheduled_check_ = true;
+}
+
+void UpdateAttempter::CertificateChecked(ServerToCheck server_to_check,
+                                         CertificateCheckResult result) {
+  metrics::ReportCertificateCheckMetrics(system_state_,
+                                         server_to_check,
+                                         result);
+}
+
+bool UpdateAttempter::CheckAndReportDailyMetrics() {
+  int64_t 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) {
+      LOG(WARNING) << "Last reported daily metrics "
+                   << utils::FormatTimeDelta(time_reported_since) << " ago "
+                   << "which is negative. Either the system clock is wrong or "
+                   << "the kPrefsDailyMetricsLastReportedAt state variable "
+                   << "is wrong.";
+      // In this case, report daily metrics to reset.
+    } else {
+      if (time_reported_since.InSeconds() < 24*60*60) {
+        LOG(INFO) << "Last reported daily metrics "
+                  << utils::FormatTimeDelta(time_reported_since) << " ago.";
+        return false;
+      }
+      LOG(INFO) << "Last reported daily metrics "
+                << utils::FormatTimeDelta(time_reported_since) << " ago, "
+                << "which is more than 24 hours ago.";
+    }
+  }
+
+  LOG(INFO) << "Reporting daily metrics.";
+  system_state_->prefs()->SetInt64(kPrefsDailyMetricsLastReportedAt,
+                                   now.ToInternalValue());
+
+  ReportOSAge();
+
+  return true;
+}
+
+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)";
+    return;
+  }
+
+  Time lsb_release_timestamp = utils::TimeFromStructTimespec(&sb.st_ctim);
+  Time now = system_state_->clock()->GetWallclockTime();
+  TimeDelta age = now - lsb_release_timestamp;
+  if (age.InSeconds() < 0) {
+    LOG(ERROR) << "The OS age (" << utils::FormatTimeDelta(age)
+               << ") is negative. Maybe the clock is wrong? "
+               << "(Note: this may happen in some unit tests.)";
+    return;
+  }
+
+  metrics::ReportDailyMetrics(system_state_, age);
+}
+
+void UpdateAttempter::Update(const string& app_version,
+                             const string& omaha_url,
+                             const string& target_channel,
+                             const string& target_version_prefix,
+                             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)
+  // timeout event.
+  CheckAndReportDailyMetrics();
+
+  // Notify of the new update attempt, clearing prior interactive requests.
+  if (forced_update_pending_callback_.get())
+    forced_update_pending_callback_->Run(false, false);
+
+  fake_update_success_ = false;
+  if (status_ == UpdateStatus::UPDATED_NEED_REBOOT) {
+    // Although we have applied an update, we still want to ping Omaha
+    // to ensure the number of active statistics is accurate.
+    //
+    // Also convey to the UpdateEngine.Check.Result metric that we're
+    // 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";
+    metrics::ReportUpdateCheckMetrics(system_state_,
+                                      metrics::CheckResult::kRebootPending,
+                                      metrics::CheckReaction::kUnset,
+                                      metrics::DownloadErrorCode::kUnset);
+    PingOmaha();
+    return;
+  }
+  if (status_ != UpdateStatus::IDLE) {
+    // Update in progress. Do nothing
+    return;
+  }
+
+  if (!CalculateUpdateParams(app_version,
+                             omaha_url,
+                             target_channel,
+                             target_version_prefix,
+                             obey_proxies,
+                             interactive)) {
+    return;
+  }
+
+  BuildUpdateActions(interactive);
+
+  SetStatusAndNotify(UpdateStatus::CHECKING_FOR_UPDATE);
+
+  // Update the last check time here; it may be re-updated when an Omaha
+  // response is received, but this will prevent us from repeatedly scheduling
+  // checks in the case where a response is not received.
+  UpdateLastCheckedTime();
+
+  // Just in case we didn't update boot flags yet, make sure they're updated
+  // before any update processing starts.
+  start_action_processor_ = true;
+  UpdateBootFlags();
+}
+
+void UpdateAttempter::RefreshDevicePolicy() {
+  // Lazy initialize the policy provider, or reload the latest policy data.
+  if (!policy_provider_.get())
+    policy_provider_.reset(new policy::PolicyProvider());
+  policy_provider_->Reload();
+
+  const policy::DevicePolicy* device_policy = nullptr;
+  if (policy_provider_->device_policy_is_loaded())
+    device_policy = &policy_provider_->GetDevicePolicy();
+
+  if (device_policy)
+    LOG(INFO) << "Device policies/settings present";
+  else
+    LOG(INFO) << "No device policies/settings present.";
+
+  system_state_->set_device_policy(device_policy);
+  system_state_->p2p_manager()->SetDevicePolicy(device_policy);
+}
+
+void UpdateAttempter::CalculateP2PParams(bool interactive) {
+  bool use_p2p_for_downloading = false;
+  bool use_p2p_for_sharing = false;
+
+  // Never use p2p for downloading in interactive checks unless the
+  // developer has opted in for it via a marker file.
+  //
+  // (Why would a developer want to opt in? If he's working on the
+  // update_engine or p2p codebases so he can actually test his
+  // code.).
+
+  if (system_state_ != nullptr) {
+    if (!system_state_->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;
+      } else {
+        LOG(INFO) << "Forcibly disabling use of p2p for downloading "
+                  << "since this update attempt is interactive.";
+      }
+    }
+  }
+
+  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 string& app_version,
+                                            const string& omaha_url,
+                                            const string& target_channel,
+                                            const string& target_version_prefix,
+                                            bool obey_proxies,
+                                            bool interactive) {
+  http_response_code_ = 0;
+  PayloadStateInterface* const payload_state = system_state_->payload_state();
+
+  // Refresh the policy before computing all the update parameters.
+  RefreshDevicePolicy();
+
+  // Set the target version prefix, if provided.
+  if (!target_version_prefix.empty())
+    omaha_request_params_->set_target_version_prefix(target_version_prefix);
+
+  CalculateScatteringParams(interactive);
+
+  CalculateP2PParams(interactive);
+  if (payload_state->GetUsingP2PForDownloading() ||
+      payload_state->GetUsingP2PForSharing()) {
+    // OK, p2p is to be used - start it and perform housekeeping.
+    if (!StartP2PAndPerformHousekeeping()) {
+      // If this fails, disable p2p for this attempt
+      LOG(INFO) << "Forcibly disabling use of p2p since starting p2p or "
+                << "performing housekeeping failed.";
+      payload_state->SetUsingP2PForDownloading(false);
+      payload_state->SetUsingP2PForSharing(false);
+    }
+  }
+
+  if (!omaha_request_params_->Init(app_version,
+                                   omaha_url,
+                                   interactive)) {
+    LOG(ERROR) << "Unable to initialize Omaha request params.";
+    return false;
+  }
+
+  // Set the target channel, if one was provided.
+  if (target_channel.empty()) {
+    LOG(INFO) << "No target channel mandated by policy.";
+  } else {
+    LOG(INFO) << "Setting target channel as mandated: " << target_channel;
+    // Pass in false for powerwash_allowed until we add it to the policy
+    // protobuf.
+    string error_message;
+    if (!omaha_request_params_->SetTargetChannel(target_channel, false,
+                                                 &error_message)) {
+      LOG(ERROR) << "Setting the channel failed: " << error_message;
+    }
+
+    // Since this is the beginning of a new attempt, update the download
+    // channel. The download channel won't be updated until the next attempt,
+    // even if target channel changes meanwhile, so that how we'll know if we
+    // should cancel the current download attempt if there's such a change in
+    // target channel.
+    omaha_request_params_->UpdateDownloadChannel();
+  }
+
+  LOG(INFO) << "target_version_prefix = "
+            << omaha_request_params_->target_version_prefix()
+            << ", scatter_factor_in_seconds = "
+            << utils::FormatSecs(scatter_factor_.InSeconds());
+
+  LOG(INFO) << "Wall Clock Based Wait Enabled = "
+            << omaha_request_params_->wall_clock_based_wait_enabled()
+            << ", Update Check Count Wait Enabled = "
+            << omaha_request_params_->update_check_count_wait_enabled()
+            << ", Waiting Period = " << utils::FormatSecs(
+               omaha_request_params_->waiting_period().InSeconds());
+
+  LOG(INFO) << "Use p2p For Downloading = "
+            << payload_state->GetUsingP2PForDownloading()
+            << ", Use p2p For Sharing = "
+            << payload_state->GetUsingP2PForSharing();
+
+  obeying_proxies_ = true;
+  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_++;
+    LOG(INFO) << "proxy manual checks: " << proxy_manual_checks_;
+    if (proxy_manual_checks_ >= kMaxConsecutiveObeyProxyRequests) {
+      proxy_manual_checks_ = 0;
+      obeying_proxies_ = false;
+    }
+  } else if (base::RandInt(0, 4) == 0) {
+    obeying_proxies_ = false;
+  }
+  LOG_IF(INFO, !obeying_proxies_) << "To help ensure updates work, this update "
+      "check we are ignoring the proxy settings and using "
+      "direct connections.";
+
+  DisableDeltaUpdateIfNeeded();
+  return true;
+}
+
+void UpdateAttempter::CalculateScatteringParams(bool interactive) {
+  // 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 = system_state_->device_policy();
+  if (device_policy) {
+    int64_t new_scatter_factor_in_secs = 0;
+    device_policy->GetScatterFactorInSeconds(&new_scatter_factor_in_secs);
+    if (new_scatter_factor_in_secs < 0)  // sanitize input, just in case.
+      new_scatter_factor_in_secs  = 0;
+    scatter_factor_ = TimeDelta::FromSeconds(new_scatter_factor_in_secs);
+  }
+
+  bool is_scatter_enabled = false;
+  if (scatter_factor_.InSeconds() == 0) {
+    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 (!system_state_->hardware()->IsOOBEComplete(nullptr)) {
+    LOG(INFO) << "Scattering disabled since OOBE is not complete yet";
+  } else {
+    is_scatter_enabled = true;
+    LOG(INFO) << "Scattering is enabled";
+  }
+
+  if (is_scatter_enabled) {
+    // This means the scattering policy is turned on.
+    // Now check if we need to update the waiting period. The two cases
+    // in which we'd need to update the waiting period are:
+    // 1. First time in process or a scheduled check after a user-initiated one.
+    //    (omaha_request_params_->waiting_period will be zero in this case).
+    // 2. Admin has changed the scattering policy value.
+    //    (new scattering value will be different from old one in this case).
+    int64_t wait_period_in_secs = 0;
+    if (omaha_request_params_->waiting_period().InSeconds() == 0) {
+      // First case. Check if we have a suitable value to set for
+      // the waiting period.
+      if (prefs_->GetInt64(kPrefsWallClockWaitPeriod, &wait_period_in_secs) &&
+          wait_period_in_secs > 0 &&
+          wait_period_in_secs <= scatter_factor_.InSeconds()) {
+        // This means:
+        // 1. There's a persisted value for the waiting period available.
+        // 2. And that persisted value is still valid.
+        // So, in this case, we should reuse the persisted value instead of
+        // generating a new random value to improve the chances of a good
+        // distribution for scattering.
+        omaha_request_params_->set_waiting_period(
+          TimeDelta::FromSeconds(wait_period_in_secs));
+        LOG(INFO) << "Using persisted wall-clock waiting period: " <<
+            utils::FormatSecs(
+                omaha_request_params_->waiting_period().InSeconds());
+      } else {
+        // This means there's no persisted value for the waiting period
+        // available or its value is invalid given the new scatter_factor value.
+        // So, we should go ahead and regenerate a new value for the
+        // waiting period.
+        LOG(INFO) << "Persisted value not present or not valid ("
+                  << utils::FormatSecs(wait_period_in_secs)
+                  << ") for wall-clock waiting period.";
+        GenerateNewWaitingPeriod();
+      }
+    } else if (scatter_factor_ != old_scatter_factor) {
+      // This means there's already a waiting period value, but we detected
+      // a change in the scattering policy value. So, we should regenerate the
+      // waiting period to make sure it's within the bounds of the new scatter
+      // factor value.
+      GenerateNewWaitingPeriod();
+    } else {
+      // Neither the first time scattering is enabled nor the scattering value
+      // changed. Nothing to do.
+      LOG(INFO) << "Keeping current wall-clock waiting period: " <<
+          utils::FormatSecs(
+              omaha_request_params_->waiting_period().InSeconds());
+    }
+
+    // The invariant at this point is that omaha_request_params_->waiting_period
+    // is non-zero no matter which path we took above.
+    LOG_IF(ERROR, omaha_request_params_->waiting_period().InSeconds() == 0)
+        << "Waiting Period should NOT be zero at this point!!!";
+
+    // Since scattering is enabled, wall clock based wait will always be
+    // enabled.
+    omaha_request_params_->set_wall_clock_based_wait_enabled(true);
+
+    // If we don't have any issues in accessing the file system to update
+    // the update check count value, we'll turn that on as well.
+    bool decrement_succeeded = DecrementUpdateCheckCount();
+    omaha_request_params_->set_update_check_count_wait_enabled(
+      decrement_succeeded);
+  } else {
+    // This means the scattering feature is turned off or disabled for
+    // this particular update check. Make sure to disable
+    // all the knobs and artifacts so that we don't invoke any scattering
+    // related code.
+    omaha_request_params_->set_wall_clock_based_wait_enabled(false);
+    omaha_request_params_->set_update_check_count_wait_enabled(false);
+    omaha_request_params_->set_waiting_period(TimeDelta::FromSeconds(0));
+    prefs_->Delete(kPrefsWallClockWaitPeriod);
+    prefs_->Delete(kPrefsUpdateCheckCount);
+    // Don't delete the UpdateFirstSeenAt file as we don't want manual checks
+    // that result in no-updates (e.g. due to server side throttling) to
+    // cause update starvation by having the client generate a new
+    // UpdateFirstSeenAt for each scheduled check that follows a manual check.
+  }
+}
+
+void UpdateAttempter::GenerateNewWaitingPeriod() {
+  omaha_request_params_->set_waiting_period(TimeDelta::FromSeconds(
+      base::RandInt(1, scatter_factor_.InSeconds())));
+
+  LOG(INFO) << "Generated new wall-clock waiting period: " << utils::FormatSecs(
+                omaha_request_params_->waiting_period().InSeconds());
+
+  // Do a best-effort to persist this in all cases. Even if the persistence
+  // 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.
+  system_state_->payload_state()->SetScatteringWaitPeriod(
+      omaha_request_params_->waiting_period());
+}
+
+void UpdateAttempter::BuildPostInstallActions(
+    InstallPlanAction* previous_action) {
+  shared_ptr<PostinstallRunnerAction> postinstall_runner_action(
+      new PostinstallRunnerAction(system_state_->boot_control()));
+  actions_.push_back(shared_ptr<AbstractAction>(postinstall_runner_action));
+  BondActions(previous_action,
+              postinstall_runner_action.get());
+}
+
+void UpdateAttempter::BuildUpdateActions(bool interactive) {
+  CHECK(!processor_->IsRunning());
+  processor_->set_delegate(this);
+
+  // Actions:
+  std::unique_ptr<LibcurlHttpFetcher> update_check_fetcher(
+      new LibcurlHttpFetcher(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);
+  shared_ptr<OmahaRequestAction> update_check_action(
+      new OmahaRequestAction(system_state_,
+                             nullptr,
+                             std::move(update_check_fetcher),
+                             false));
+  shared_ptr<OmahaResponseHandlerAction> response_handler_action(
+      new OmahaResponseHandlerAction(system_state_));
+  shared_ptr<FilesystemVerifierAction> src_filesystem_verifier_action(
+      new FilesystemVerifierAction(system_state_->boot_control(),
+                                   VerifierMode::kComputeSourceHash));
+
+  shared_ptr<OmahaRequestAction> download_started_action(
+      new OmahaRequestAction(system_state_,
+                             new OmahaEvent(
+                                 OmahaEvent::kTypeUpdateDownloadStarted),
+                             brillo::make_unique_ptr(new LibcurlHttpFetcher(
+                                 GetProxyResolver(),
+                                 system_state_->hardware())),
+                             false));
+
+  LibcurlHttpFetcher* download_fetcher =
+      new LibcurlHttpFetcher(GetProxyResolver(), system_state_->hardware());
+  download_fetcher->set_server_to_check(ServerToCheck::kDownload);
+  shared_ptr<DownloadAction> download_action(
+      new DownloadAction(prefs_,
+                         system_state_,
+                         new MultiRangeHttpFetcher(
+                             download_fetcher)));  // passes ownership
+  shared_ptr<OmahaRequestAction> download_finished_action(
+      new OmahaRequestAction(
+          system_state_,
+          new OmahaEvent(OmahaEvent::kTypeUpdateDownloadFinished),
+          brillo::make_unique_ptr(
+              new LibcurlHttpFetcher(GetProxyResolver(),
+                                     system_state_->hardware())),
+          false));
+  shared_ptr<FilesystemVerifierAction> dst_filesystem_verifier_action(
+      new FilesystemVerifierAction(system_state_->boot_control(),
+                                   VerifierMode::kVerifyTargetHash));
+  shared_ptr<OmahaRequestAction> update_complete_action(
+      new OmahaRequestAction(
+          system_state_,
+          new OmahaEvent(OmahaEvent::kTypeUpdateComplete),
+          brillo::make_unique_ptr(
+              new LibcurlHttpFetcher(GetProxyResolver(),
+                                     system_state_->hardware())),
+          false));
+
+  download_action->set_delegate(this);
+  response_handler_action_ = response_handler_action;
+  download_action_ = download_action;
+
+  actions_.push_back(shared_ptr<AbstractAction>(update_check_action));
+  actions_.push_back(shared_ptr<AbstractAction>(response_handler_action));
+  actions_.push_back(shared_ptr<AbstractAction>(
+      src_filesystem_verifier_action));
+  actions_.push_back(shared_ptr<AbstractAction>(download_started_action));
+  actions_.push_back(shared_ptr<AbstractAction>(download_action));
+  actions_.push_back(shared_ptr<AbstractAction>(download_finished_action));
+  actions_.push_back(shared_ptr<AbstractAction>(
+      dst_filesystem_verifier_action));
+
+  // Bond them together. We have to use the leaf-types when calling
+  // BondActions().
+  BondActions(update_check_action.get(),
+              response_handler_action.get());
+  BondActions(response_handler_action.get(),
+              src_filesystem_verifier_action.get());
+  BondActions(src_filesystem_verifier_action.get(),
+              download_action.get());
+  BondActions(download_action.get(),
+              dst_filesystem_verifier_action.get());
+  BuildPostInstallActions(dst_filesystem_verifier_action.get());
+
+  actions_.push_back(shared_ptr<AbstractAction>(update_complete_action));
+
+  // Enqueue the actions
+  for (const shared_ptr<AbstractAction>& action : actions_) {
+    processor_->EnqueueAction(action.get());
+  }
+}
+
+bool UpdateAttempter::Rollback(bool powerwash) {
+  if (!CanRollback()) {
+    return false;
+  }
+
+  // Extra check for enterprise-enrolled devices since they don't support
+  // powerwash.
+  if (powerwash) {
+    // Enterprise-enrolled devices have an empty owner in their device policy.
+    string owner;
+    RefreshDevicePolicy();
+    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.";
+      return false;
+    }
+  }
+
+  processor_->set_delegate(this);
+
+  // Initialize the default request params.
+  if (!omaha_request_params_->Init("", "", true)) {
+    LOG(ERROR) << "Unable to initialize Omaha request params.";
+    return false;
+  }
+
+  LOG(INFO) << "Setting rollback options.";
+  InstallPlan install_plan;
+
+  install_plan.target_slot = GetRollbackSlot();
+  install_plan.source_slot = system_state_->boot_control()->GetCurrentSlot();
+
+  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();
+
+  shared_ptr<InstallPlanAction> install_plan_action(
+      new InstallPlanAction(install_plan));
+  actions_.push_back(shared_ptr<AbstractAction>(install_plan_action));
+
+  BuildPostInstallActions(install_plan_action.get());
+
+  // Enqueue the actions
+  for (const shared_ptr<AbstractAction>& action : actions_) {
+    processor_->EnqueueAction(action.get());
+  }
+
+  // Update the payload state for Rollback.
+  system_state_->payload_state()->Rollback();
+
+  SetStatusAndNotify(UpdateStatus::ATTEMPTING_ROLLBACK);
+
+  // Just in case we didn't update boot flags yet, make sure they're updated
+  // before any update processing starts. This also schedules the start of the
+  // actions we just posted.
+  start_action_processor_ = true;
+  UpdateBootFlags();
+  return true;
+}
+
+bool UpdateAttempter::CanRollback() const {
+  // We can only rollback if the update_engine isn't busy and we have a valid
+  // rollback partition.
+  return (status_ == UpdateStatus::IDLE &&
+          GetRollbackSlot() != BootControlInterface::kInvalidSlot);
+}
+
+BootControlInterface::Slot UpdateAttempter::GetRollbackSlot() const {
+  LOG(INFO) << "UpdateAttempter::GetRollbackSlot";
+  const unsigned int num_slots = system_state_->boot_control()->GetNumSlots();
+  const BootControlInterface::Slot current_slot =
+      system_state_->boot_control()->GetCurrentSlot();
+
+  LOG(INFO) << "  Installed slots: " << num_slots;
+  LOG(INFO) << "  Booted from slot: "
+            << BootControlInterface::SlotName(current_slot);
+
+  if (current_slot == BootControlInterface::kInvalidSlot || num_slots < 2) {
+    LOG(INFO) << "Device is not updateable.";
+    return BootControlInterface::kInvalidSlot;
+  }
+
+  vector<BootControlInterface::Slot> bootable_slots;
+  for(BootControlInterface::Slot slot = 0; slot < num_slots; slot++) {
+    if (slot != current_slot &&
+        system_state_->boot_control()->IsSlotBootable(slot)) {
+      LOG(INFO) << "Found bootable slot "
+                << BootControlInterface::SlotName(slot);
+      return slot;
+    }
+  }
+  LOG(INFO) << "No other bootable slot found.";
+  return BootControlInterface::kInvalidSlot;
+}
+
+void UpdateAttempter::CheckForUpdate(const string& app_version,
+                                     const string& omaha_url,
+                                     bool interactive) {
+  LOG(INFO) << "Forced update check requested.";
+  forced_app_version_.clear();
+  forced_omaha_url_.clear();
+
+  // Certain conditions must be met to allow setting custom version and update
+  // server URLs. However, kScheduledAUTestURLRequest and kAUTestURLRequest are
+  // always allowed regardless of device state.
+  if (IsAnyUpdateSourceAllowed()) {
+    forced_app_version_ = app_version;
+    forced_omaha_url_ = omaha_url;
+  }
+  if (omaha_url == kScheduledAUTestURLRequest) {
+    forced_omaha_url_ = constants::kOmahaDefaultAUTestURL;
+    // Pretend that it's not user-initiated even though it is,
+    // so as to test scattering logic, etc. which get kicked off
+    // only in scheduled update checks.
+    interactive = false;
+  } else if (omaha_url == kAUTestURLRequest) {
+    forced_omaha_url_ = constants::kOmahaDefaultAUTestURL;
+  }
+
+  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, interactive);
+  }
+}
+
+bool UpdateAttempter::RebootIfNeeded() {
+  if (status_ != UpdateStatus::UPDATED_NEED_REBOOT) {
+    LOG(INFO) << "Reboot requested, but status is "
+              << UpdateStatusToString(status_) << ", so not rebooting.";
+    return false;
+  }
+
+  if (USE_POWER_MANAGEMENT && RequestPowerManagerReboot())
+    return true;
+
+  return RebootDirectly();
+}
+
+void UpdateAttempter::WriteUpdateCompletedMarker() {
+  string boot_id;
+  if (!utils::GetBootId(&boot_id))
+    return;
+  prefs_->SetString(kPrefsUpdateCompletedOnBootId, boot_id);
+
+  int64_t value = system_state_->clock()->GetBootTime().ToInternalValue();
+  prefs_->SetInt64(kPrefsUpdateCompletedBootTime, value);
+}
+
+bool UpdateAttempter::RequestPowerManagerReboot() {
+  org::chromium::PowerManagerProxyInterface* power_manager_proxy =
+      system_state_->power_manager_proxy();
+  if (!power_manager_proxy) {
+    LOG(WARNING) << "No PowerManager proxy defined, skipping reboot.";
+    return false;
+  }
+  LOG(INFO) << "Calling " << power_manager::kPowerManagerInterface << "."
+            << power_manager::kRequestRestartMethod;
+  brillo::ErrorPtr error;
+  return power_manager_proxy->RequestRestart(
+      power_manager::REQUEST_RESTART_FOR_UPDATE, &error);
+}
+
+bool UpdateAttempter::RebootDirectly() {
+  vector<string> command;
+  command.push_back("/sbin/shutdown");
+  command.push_back("-r");
+  command.push_back("now");
+  LOG(INFO) << "Running \"" << JoinString(command, ' ') << "\"";
+  int rc = 0;
+  Subprocess::SynchronousExec(command, &rc, nullptr);
+  return rc == 0;
+}
+
+void UpdateAttempter::OnUpdateScheduled(EvalStatus status,
+                                        const UpdateCheckParams& params) {
+  waiting_for_scheduled_check_ = false;
+
+  if (status == EvalStatus::kSucceeded) {
+    if (!params.updates_enabled) {
+      LOG(WARNING) << "Updates permanently disabled.";
+      // Signal disabled status, then switch right back to idle. This is
+      // necessary for ensuring that observers waiting for a signal change will
+      // actually notice one on subsequent calls. Note that we don't need to
+      // re-schedule a check in this case as updates are permanently disabled;
+      // further (forced) checks may still initiate a scheduling call.
+      SetStatusAndNotify(UpdateStatus::DISABLED);
+      SetStatusAndNotify(UpdateStatus::IDLE);
+      return;
+    }
+
+    LOG(INFO) << "Running "
+              << (params.is_interactive ? "interactive" : "periodic")
+              << " update.";
+
+    string app_version, omaha_url;
+    if (params.is_interactive) {
+      app_version = forced_app_version_;
+      omaha_url = forced_omaha_url_;
+    }
+
+    Update(app_version, omaha_url, params.target_channel,
+           params.target_version_prefix, false, params.is_interactive);
+  } else {
+    LOG(WARNING)
+        << "Update check scheduling failed (possibly timed out); retrying.";
+    ScheduleUpdates();
+  }
+
+  // This check ensures that future update checks will be or are already
+  // scheduled. The check should never fail. A check failure means that there's
+  // 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(IsUpdateRunningOrScheduled());
+}
+
+void UpdateAttempter::UpdateLastCheckedTime() {
+  last_checked_time_ = system_state_->clock()->GetWallclockTime().ToTimeT();
+}
+
+// Delegate methods:
+void UpdateAttempter::ProcessingDone(const ActionProcessor* processor,
+                                     ErrorCode code) {
+  LOG(INFO) << "Processing Done.";
+  actions_.clear();
+
+  // Reset cpu shares back to normal.
+  CleanupCpuSharesManagement();
+
+  if (status_ == UpdateStatus::REPORTING_ERROR_EVENT) {
+    LOG(INFO) << "Error event sent.";
+
+    // Inform scheduler of new status;
+    SetStatusAndNotify(UpdateStatus::IDLE);
+    ScheduleUpdates();
+
+    if (!fake_update_success_) {
+      return;
+    }
+    LOG(INFO) << "Booted from FW B and tried to install new firmware, "
+        "so requesting reboot from user.";
+  }
+
+  if (code == ErrorCode::kSuccess) {
+    WriteUpdateCompletedMarker();
+    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());
+    prefs_->Delete(kPrefsUpdateFirstSeenAt);
+
+    SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT);
+    ScheduleUpdates();
+    LOG(INFO) << "Update successfully applied, waiting to reboot.";
+
+    // This pointer is null during rollback operations, and the stats
+    // don't make much sense then anyway.
+    if (response_handler_action_) {
+      const InstallPlan& install_plan =
+          response_handler_action_->install_plan();
+
+      // Generate an unique payload identifier.
+      const string target_version_uid =
+          install_plan.payload_hash + ":" + install_plan.metadata_signature;
+
+      // 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 expected "
+            "response_handler_action_ missing.";
+      }
+    }
+    return;
+  }
+
+  if (ScheduleErrorEventAction()) {
+    return;
+  }
+  LOG(INFO) << "No update.";
+  SetStatusAndNotify(UpdateStatus::IDLE);
+  ScheduleUpdates();
+}
+
+void UpdateAttempter::ProcessingStopped(const ActionProcessor* processor) {
+  // Reset cpu shares back to normal.
+  CleanupCpuSharesManagement();
+  download_progress_ = 0.0;
+  SetStatusAndNotify(UpdateStatus::IDLE);
+  ScheduleUpdates();
+  actions_.clear();
+  error_event_.reset(nullptr);
+}
+
+// Called whenever an action has finished processing, either successfully
+// or otherwise.
+void UpdateAttempter::ActionCompleted(ActionProcessor* processor,
+                                      AbstractAction* action,
+                                      ErrorCode code) {
+  // Reset download progress regardless of whether or not the download
+  // action succeeded. Also, get the response code from HTTP request
+  // actions (update download as well as the initial update check
+  // actions).
+  const string type = action->Type();
+  if (type == DownloadAction::StaticType()) {
+    download_progress_ = 0.0;
+    DownloadAction* download_action = static_cast<DownloadAction*>(action);
+    http_response_code_ = download_action->GetHTTPResponseCode();
+  } else if (type == OmahaRequestAction::StaticType()) {
+    OmahaRequestAction* omaha_request_action =
+        static_cast<OmahaRequestAction*>(action);
+    // If the request is not an event, then it's the update-check.
+    if (!omaha_request_action->IsEvent()) {
+      http_response_code_ = omaha_request_action->GetHTTPResponseCode();
+
+      // Record the number of consecutive failed update checks.
+      if (http_response_code_ == kHttpResponseInternalServerError ||
+          http_response_code_ == kHttpResponseServiceUnavailable) {
+        consecutive_failed_update_checks_++;
+      } else {
+        consecutive_failed_update_checks_ = 0;
+      }
+
+      // Store the server-dictated poll interval, if any.
+      server_dictated_poll_interval_ =
+          std::max(0, omaha_request_action->GetOutputObject().poll_interval);
+    }
+  }
+  if (code != ErrorCode::kSuccess) {
+    // If the current state is at or past the download phase, count the failure
+    // in case a switch to full update becomes necessary. Ignore network
+    // transfer timeouts and failures.
+    if (status_ >= UpdateStatus::DOWNLOADING &&
+        code != ErrorCode::kDownloadTransferError) {
+      MarkDeltaUpdateFailure();
+    }
+    // On failure, schedule an error event to be sent to Omaha.
+    CreatePendingErrorEvent(action, code);
+    return;
+  }
+  // Find out which action completed.
+  if (type == OmahaResponseHandlerAction::StaticType()) {
+    // Note that the status will be updated to DOWNLOADING when some bytes get
+    // actually downloaded from the server and the BytesReceived callback is
+    // invoked. This avoids notifying the user that a download has started in
+    // cases when the server and the client are unable to initiate the download.
+    CHECK(action == response_handler_action_.get());
+    const InstallPlan& plan = response_handler_action_->install_plan();
+    UpdateLastCheckedTime();
+    new_version_ = plan.version;
+    new_payload_size_ = plan.payload_size;
+    SetupDownload();
+    SetupCpuSharesManagement();
+    SetStatusAndNotify(UpdateStatus::UPDATE_AVAILABLE);
+  } else if (type == DownloadAction::StaticType()) {
+    SetStatusAndNotify(UpdateStatus::FINALIZING);
+  }
+}
+
+void UpdateAttempter::BytesReceived(uint64_t bytes_progressed,
+                                    uint64_t bytes_received,
+                                    uint64_t total) {
+  // The PayloadState keeps track of how many bytes were actually downloaded
+  // from a given URL for the URL skipping logic.
+  system_state_->payload_state()->DownloadProgress(bytes_progressed);
+
+  double progress = static_cast<double>(bytes_received) /
+      static_cast<double>(total);
+  // Self throttle based on progress. Also send notifications if
+  // progress is too slow.
+  const double kDeltaPercent = 0.01;  // 1%
+  if (status_ != UpdateStatus::DOWNLOADING ||
+      bytes_received == total ||
+      progress - download_progress_ >= kDeltaPercent ||
+      TimeTicks::Now() - last_notify_time_ >= TimeDelta::FromSeconds(10)) {
+    download_progress_ = progress;
+    SetStatusAndNotify(UpdateStatus::DOWNLOADING);
+  }
+}
+
+void UpdateAttempter::DownloadComplete() {
+  system_state_->payload_state()->DownloadComplete();
+}
+
+bool UpdateAttempter::ResetStatus() {
+  LOG(INFO) << "Attempting to reset state from "
+            << UpdateStatusToString(status_) << " to UpdateStatus::IDLE";
+
+  switch (status_) {
+    case UpdateStatus::IDLE:
+      // no-op.
+      return true;
+
+    case UpdateStatus::UPDATED_NEED_REBOOT:  {
+      bool ret_value = true;
+      status_ = UpdateStatus::IDLE;
+
+      // Remove the reboot marker so that if the machine is rebooted
+      // after resetting to idle state, it doesn't go back to
+      // UpdateStatus::UPDATED_NEED_REBOOT state.
+      ret_value = prefs_->Delete(kPrefsUpdateCompletedOnBootId) && ret_value;
+      ret_value = prefs_->Delete(kPrefsUpdateCompletedBootTime) && ret_value;
+
+      // Update the boot flags so the current slot has higher priority.
+      BootControlInterface* boot_control = system_state_->boot_control();
+      if (!boot_control->SetActiveBootSlot(boot_control->GetCurrentSlot()))
+        ret_value = false;
+
+      // Notify the PayloadState that the successful payload was canceled.
+      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
+      // need to clear out this value now to prevent it being sent on the next
+      // updatecheck request.
+      ret_value = prefs_->SetString(kPrefsPreviousVersion, "") && ret_value;
+
+      LOG(INFO) << "Reset status " << (ret_value ? "successful" : "failed");
+      return ret_value;
+    }
+
+    default:
+      LOG(ERROR) << "Reset not allowed in this state.";
+      return false;
+  }
+}
+
+bool UpdateAttempter::GetStatus(int64_t* last_checked_time,
+                                double* progress,
+                                string* current_operation,
+                                string* new_version,
+                                int64_t* new_payload_size) {
+  *last_checked_time = last_checked_time_;
+  *progress = download_progress_;
+  *current_operation = UpdateStatusToString(status_);
+  *new_version = new_version_;
+  *new_payload_size = new_payload_size_;
+  return true;
+}
+
+void UpdateAttempter::UpdateBootFlags() {
+  if (update_boot_flags_running_) {
+    LOG(INFO) << "Update boot flags running, nothing to do.";
+    return;
+  }
+  if (updated_boot_flags_) {
+    LOG(INFO) << "Already updated boot flags. Skipping.";
+    if (start_action_processor_) {
+      ScheduleProcessingStart();
+    }
+    return;
+  }
+  // This is purely best effort. Failures should be logged by Subprocess. Run
+  // the script asynchronously to avoid blocking the event loop regardless of
+  // the script runtime.
+  update_boot_flags_running_ = true;
+  LOG(INFO) << "Marking booted slot as good.";
+  if (!system_state_->boot_control()->MarkBootSuccessfulAsync(Bind(
+          &UpdateAttempter::CompleteUpdateBootFlags, base::Unretained(this)))) {
+    LOG(ERROR) << "Failed to mark current boot as successful.";
+    CompleteUpdateBootFlags(false);
+  }
+}
+
+void UpdateAttempter::CompleteUpdateBootFlags(bool successful) {
+  update_boot_flags_running_ = false;
+  updated_boot_flags_ = true;
+  if (start_action_processor_) {
+    ScheduleProcessingStart();
+  }
+}
+
+void UpdateAttempter::BroadcastStatus() {
+  if (!dbus_adaptor_)
+    return;
+  last_notify_time_ = TimeTicks::Now();
+  dbus_adaptor_->SendStatusUpdateSignal(
+      last_checked_time_,
+      download_progress_,
+      UpdateStatusToString(status_),
+      new_version_.c_str(),
+      new_payload_size_);
+}
+
+uint32_t UpdateAttempter::GetErrorCodeFlags()  {
+  uint32_t flags = 0;
+
+  if (!system_state_->hardware()->IsNormalBootMode())
+    flags |= static_cast<uint32_t>(ErrorCode::kDevModeFlag);
+
+  if (response_handler_action_.get() &&
+      response_handler_action_->install_plan().is_resume)
+    flags |= static_cast<uint32_t>(ErrorCode::kResumedFlag);
+
+  if (!system_state_->hardware()->IsOfficialBuild())
+    flags |= static_cast<uint32_t>(ErrorCode::kTestImageFlag);
+
+  if (omaha_request_params_->update_url() !=
+      constants::kOmahaDefaultProductionURL) {
+    flags |= static_cast<uint32_t>(ErrorCode::kTestOmahaUrlFlag);
+  }
+
+  return flags;
+}
+
+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 = system_state_->request_params();
+  if (params->download_channel() != params->target_channel()) {
+    LOG(ERROR) << "Aborting download as target channel: "
+               << params->target_channel()
+               << " is different from the download channel: "
+               << params->download_channel();
+    *cancel_reason = ErrorCode::kUpdateCanceledByChannelChange;
+    return true;
+  }
+
+  return false;
+}
+
+void UpdateAttempter::SetStatusAndNotify(UpdateStatus status) {
+  status_ = status;
+  BroadcastStatus();
+}
+
+void UpdateAttempter::CreatePendingErrorEvent(AbstractAction* action,
+                                              ErrorCode code) {
+  if (error_event_.get()) {
+    // This shouldn't really happen.
+    LOG(WARNING) << "There's already an existing pending error event.";
+    return;
+  }
+
+  // For now assume that a generic Omaha response action failure means that
+  // there's no update so don't send an event. Also, double check that the
+  // failure has not occurred while sending an error event -- in which case
+  // don't schedule another. This shouldn't really happen but just in case...
+  if ((action->Type() == OmahaResponseHandlerAction::StaticType() &&
+       code == ErrorCode::kError) ||
+      status_ == UpdateStatus::REPORTING_ERROR_EVENT) {
+    return;
+  }
+
+  // Classify the code to generate the appropriate result so that
+  // the Borgmon charts show up the results correctly.
+  // Do this before calling GetErrorCodeForAction which could potentially
+  // augment the bit representation of code and thus cause no matches for
+  // the switch cases below.
+  OmahaEvent::Result event_result;
+  switch (code) {
+    case ErrorCode::kOmahaUpdateIgnoredPerPolicy:
+    case ErrorCode::kOmahaUpdateDeferredPerPolicy:
+    case ErrorCode::kOmahaUpdateDeferredForBackoff:
+      event_result = OmahaEvent::kResultUpdateDeferred;
+      break;
+    default:
+      event_result = OmahaEvent::kResultError;
+      break;
+  }
+
+  code = GetErrorCodeForAction(action, code);
+  fake_update_success_ = code == ErrorCode::kPostinstallBootedFromFirmwareB;
+
+  // Compute the final error code with all the bit flags to be sent to Omaha.
+  code = static_cast<ErrorCode>(
+      static_cast<uint32_t>(code) | GetErrorCodeFlags());
+  error_event_.reset(new OmahaEvent(OmahaEvent::kTypeUpdateComplete,
+                                    event_result,
+                                    code));
+}
+
+bool UpdateAttempter::ScheduleErrorEventAction() {
+  if (error_event_.get() == nullptr)
+    return false;
+
+  LOG(ERROR) << "Update failed.";
+  system_state_->payload_state()->UpdateFailed(error_event_->error_code);
+
+  // Send it to Omaha.
+  LOG(INFO) << "Reporting the error event";
+  shared_ptr<OmahaRequestAction> error_event_action(
+      new OmahaRequestAction(system_state_,
+                             error_event_.release(),  // Pass ownership.
+                             brillo::make_unique_ptr(new LibcurlHttpFetcher(
+                                 GetProxyResolver(),
+                                 system_state_->hardware())),
+                             false));
+  actions_.push_back(shared_ptr<AbstractAction>(error_event_action));
+  processor_->EnqueueAction(error_event_action.get());
+  SetStatusAndNotify(UpdateStatus::REPORTING_ERROR_EVENT);
+  processor_->StartProcessing();
+  return true;
+}
+
+void UpdateAttempter::SetCpuShares(utils::CpuShares shares) {
+  if (shares_ == shares) {
+    return;
+  }
+  if (utils::SetCpuShares(shares)) {
+    shares_ = shares;
+    LOG(INFO) << "CPU shares = " << shares_;
+  }
+}
+
+void UpdateAttempter::SetupCpuSharesManagement() {
+  if (manage_shares_id_ != MessageLoop::kTaskIdNull) {
+    LOG(ERROR) << "Cpu shares timeout source hasn't been destroyed.";
+    CleanupCpuSharesManagement();
+  }
+  const int kCpuSharesTimeout = 2 * 60 * 60;  // 2 hours
+  manage_shares_id_ = MessageLoop::current()->PostDelayedTask(
+      FROM_HERE,
+      Bind(&UpdateAttempter::ManageCpuSharesCallback, base::Unretained(this)),
+      TimeDelta::FromSeconds(kCpuSharesTimeout));
+  SetCpuShares(utils::kCpuSharesLow);
+}
+
+void UpdateAttempter::CleanupCpuSharesManagement() {
+  if (manage_shares_id_ != MessageLoop::kTaskIdNull) {
+    // The UpdateAttempter is instantiated by default by the FakeSystemState,
+    // even when it is not used. We check the manage_shares_id_ before calling
+    // the MessageLoop::current() since the unit test using a FakeSystemState
+    // may have not define a MessageLoop for the current thread.
+    MessageLoop::current()->CancelTask(manage_shares_id_);
+    manage_shares_id_ = MessageLoop::kTaskIdNull;
+  }
+  SetCpuShares(utils::kCpuSharesNormal);
+}
+
+void UpdateAttempter::ScheduleProcessingStart() {
+  LOG(INFO) << "Scheduling an action processor start.";
+  start_action_processor_ = false;
+  MessageLoop::current()->PostTask(
+      FROM_HERE,
+      Bind([this] { this->processor_->StartProcessing(); }));
+}
+
+void UpdateAttempter::ManageCpuSharesCallback() {
+  SetCpuShares(utils::kCpuSharesNormal);
+  manage_shares_id_ = MessageLoop::kTaskIdNull;
+}
+
+void UpdateAttempter::DisableDeltaUpdateIfNeeded() {
+  int64_t delta_failures;
+  if (omaha_request_params_->delta_okay() &&
+      prefs_->GetInt64(kPrefsDeltaUpdateFailures, &delta_failures) &&
+      delta_failures >= kMaxDeltaUpdateFailures) {
+    LOG(WARNING) << "Too many delta update failures, forcing full update.";
+    omaha_request_params_->set_delta_okay(false);
+  }
+}
+
+void UpdateAttempter::MarkDeltaUpdateFailure() {
+  // Don't try to resume a failed delta update.
+  DeltaPerformer::ResetUpdateProgress(prefs_, false);
+  int64_t delta_failures;
+  if (!prefs_->GetInt64(kPrefsDeltaUpdateFailures, &delta_failures) ||
+      delta_failures < 0) {
+    delta_failures = 0;
+  }
+  prefs_->SetInt64(kPrefsDeltaUpdateFailures, ++delta_failures);
+}
+
+void UpdateAttempter::SetupDownload() {
+  MultiRangeHttpFetcher* fetcher =
+      static_cast<MultiRangeHttpFetcher*>(download_action_->http_fetcher());
+  fetcher->ClearRanges();
+  if (response_handler_action_->install_plan().is_resume) {
+    // Resuming an update so fetch the update manifest metadata first.
+    int64_t manifest_metadata_size = 0;
+    prefs_->GetInt64(kPrefsManifestMetadataSize, &manifest_metadata_size);
+    fetcher->AddRange(0, manifest_metadata_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 + next_data_offset;
+    if (resume_offset < response_handler_action_->install_plan().payload_size) {
+      fetcher->AddRange(resume_offset);
+    }
+  } else {
+    fetcher->AddRange(0);
+  }
+}
+
+void UpdateAttempter::PingOmaha() {
+  if (!processor_->IsRunning()) {
+    shared_ptr<OmahaRequestAction> ping_action(new OmahaRequestAction(
+        system_state_,
+        nullptr,
+        brillo::make_unique_ptr(new LibcurlHttpFetcher(
+            GetProxyResolver(),
+            system_state_->hardware())),
+        true));
+    actions_.push_back(shared_ptr<OmahaRequestAction>(ping_action));
+    processor_->set_delegate(nullptr);
+    processor_->EnqueueAction(ping_action.get());
+    // Call StartProcessing() synchronously here to avoid any race conditions
+    // caused by multiple outstanding ping Omaha requests.  If we call
+    // StartProcessing() asynchronously, the device can be suspended before we
+    // get a chance to callback to StartProcessing().  When the device resumes
+    // (assuming the device sleeps longer than the next update check period),
+    // StartProcessing() is called back and at the same time, the next update
+    // check is fired which eventually invokes StartProcessing().  A crash
+    // can occur because StartProcessing() checks to make sure that the
+    // processor is idle which it isn't due to the two concurrent ping Omaha
+    // requests.
+    processor_->StartProcessing();
+  } else {
+    LOG(WARNING) << "Action processor running, Omaha ping suppressed.";
+  }
+
+  // Update the last check time here; it may be re-updated when an Omaha
+  // response is received, but this will prevent us from repeatedly scheduling
+  // checks in the case where a response is not received.
+  UpdateLastCheckedTime();
+
+  // Update the status which will schedule the next update check
+  SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT);
+  ScheduleUpdates();
+}
+
+
+bool UpdateAttempter::DecrementUpdateCheckCount() {
+  int64_t update_check_count_value;
+
+  if (!prefs_->Exists(kPrefsUpdateCheckCount)) {
+    // This file does not exist. This means we haven't started our update
+    // check count down yet, so nothing more to do. This file will be created
+    // later when we first satisfy the wall-clock-based-wait period.
+    LOG(INFO) << "No existing update check count. That's normal.";
+    return true;
+  }
+
+  if (prefs_->GetInt64(kPrefsUpdateCheckCount, &update_check_count_value)) {
+    // Only if we're able to read a proper integer value, then go ahead
+    // and decrement and write back the result in the same file, if needed.
+    LOG(INFO) << "Update check count = " << update_check_count_value;
+
+    if (update_check_count_value == 0) {
+      // It could be 0, if, for some reason, the file didn't get deleted
+      // when we set our status to waiting for reboot. so we just leave it
+      // as is so that we can prevent another update_check wait for this client.
+      LOG(INFO) << "Not decrementing update check count as it's already 0.";
+      return true;
+    }
+
+    if (update_check_count_value > 0)
+      update_check_count_value--;
+    else
+      update_check_count_value = 0;
+
+    // Write out the new value of update_check_count_value.
+    if (prefs_->SetInt64(kPrefsUpdateCheckCount, update_check_count_value)) {
+      // We successfully wrote out te new value, so enable the
+      // update check based wait.
+      LOG(INFO) << "New update check count = " << update_check_count_value;
+      return true;
+    }
+  }
+
+  LOG(INFO) << "Deleting update check count state due to read/write errors.";
+
+  // We cannot read/write to the file, so disable the update check based wait
+  // so that we don't get stuck in this OS version by any chance (which could
+  // happen if there's some bug that causes to read/write incorrectly).
+  // Also attempt to delete the file to do our best effort to cleanup.
+  prefs_->Delete(kPrefsUpdateCheckCount);
+  return false;
+}
+
+
+void UpdateAttempter::UpdateEngineStarted() {
+  // If we just booted into a new update, keep the previous OS version
+  // 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
+  // system_state_->payload_state()->UpdateEngineStarted() since it will
+  // delete SystemUpdated marker file.
+  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.
+      prev_version_.clear();
+    }
+  }
+
+  system_state_->payload_state()->UpdateEngineStarted();
+  StartP2PAtStartup();
+}
+
+bool UpdateAttempter::StartP2PAtStartup() {
+  if (system_state_ == nullptr ||
+      !system_state_->p2p_manager()->IsP2PEnabled()) {
+    LOG(INFO) << "Not starting p2p at startup since it's not enabled.";
+    return false;
+  }
+
+  if (system_state_->p2p_manager()->CountSharedFiles() < 1) {
+    LOG(INFO) << "Not starting p2p at startup since our application "
+              << "is not sharing any files.";
+    return false;
+  }
+
+  return StartP2PAndPerformHousekeeping();
+}
+
+bool UpdateAttempter::StartP2PAndPerformHousekeeping() {
+  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 (!system_state_->p2p_manager()->EnsureP2PRunning()) {
+    LOG(ERROR) << "Error starting p2p.";
+    return false;
+  }
+
+  LOG(INFO) << "Performing p2p housekeeping.";
+  if (!system_state_->p2p_manager()->PerformHousekeeping()) {
+    LOG(ERROR) << "Error performing housekeeping for p2p.";
+    return false;
+  }
+
+  LOG(INFO) << "Done performing p2p housekeeping.";
+  return true;
+}
+
+bool UpdateAttempter::GetBootTimeAtUpdate(Time *out_boot_time) {
+  // In case of an update_engine restart without a reboot, we stored the boot_id
+  // when the update was completed by setting a pref, so we can check whether
+  // the last update was on this boot or a previous one.
+  string boot_id;
+  TEST_AND_RETURN_FALSE(utils::GetBootId(&boot_id));
+
+  string update_completed_on_boot_id;
+  if (!prefs_->Exists(kPrefsUpdateCompletedOnBootId) ||
+      !prefs_->GetString(kPrefsUpdateCompletedOnBootId,
+                         &update_completed_on_boot_id) ||
+      update_completed_on_boot_id != boot_id)
+    return false;
+
+  // Short-circuit avoiding the read in case out_boot_time is nullptr.
+  if (out_boot_time) {
+    int64_t boot_time = 0;
+    // Since the kPrefsUpdateCompletedOnBootId was correctly set, this pref
+    // should not fail.
+    TEST_AND_RETURN_FALSE(
+        prefs_->GetInt64(kPrefsUpdateCompletedBootTime, &boot_time));
+    *out_boot_time = Time::FromInternalValue(boot_time);
+  }
+  return true;
+}
+
+bool UpdateAttempter::IsUpdateRunningOrScheduled() {
+  return ((status_ != UpdateStatus::IDLE &&
+           status_ != UpdateStatus::UPDATED_NEED_REBOOT) ||
+          waiting_for_scheduled_check_);
+}
+
+bool UpdateAttempter::IsAnyUpdateSourceAllowed() {
+  // We allow updates from any source if either of these are true:
+  //  * The device is running an unofficial (dev/test) image.
+  //  * 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 (!system_state_->hardware()->IsOfficialBuild()) {
+    LOG(INFO) << "Non-official build; allowing any update source.";
+    return true;
+  }
+
+  // Even though the debugd tools are also gated on devmode, checking here can
+  // save us a D-Bus call so it's worth doing explicitly.
+  if (system_state_->hardware()->IsNormalBootMode()) {
+    LOG(INFO) << "Not in devmode; disallowing custom update sources.";
+    return false;
+  }
+
+  // Official images in devmode are allowed a custom update source iff the
+  // debugd dev tools are enabled.
+  if (!debugd_proxy_)
+    return false;
+  int32_t dev_features = debugd::DEV_FEATURES_DISABLED;
+  brillo::ErrorPtr error;
+  bool success = debugd_proxy_->QueryDevFeatures(&dev_features, &error);
+
+  // Some boards may not include debugd so it's expected that this may fail,
+  // in which case we default to disallowing custom update sources.
+  if (success && !(dev_features & debugd::DEV_FEATURES_DISABLED)) {
+    LOG(INFO) << "Debugd dev tools enabled; allowing any update source.";
+    return true;
+  }
+  LOG(INFO) << "Debugd dev tools disabled; disallowing custom update sources.";
+  return false;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/update_attempter.h b/update_attempter.h
new file mode 100644
index 0000000..cfe3e32
--- /dev/null
+++ b/update_attempter.h
@@ -0,0 +1,506 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_UPDATE_ATTEMPTER_H_
+#define UPDATE_ENGINE_UPDATE_ATTEMPTER_H_
+
+#include <time.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/time/time.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "debugd/dbus-proxies.h"
+#include "update_engine/chrome_browser_proxy_resolver.h"
+#include "update_engine/client_library/include/update_engine/update_status.h"
+#include "update_engine/common/action_processor.h"
+#include "update_engine/common/certificate_checker.h"
+#include "update_engine/libcros_proxy.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/proxy_resolver.h"
+#include "update_engine/system_state.h"
+#include "update_engine/update_manager/policy.h"
+#include "update_engine/update_manager/update_manager.h"
+
+class MetricsLibraryInterface;
+
+namespace policy {
+class PolicyProvider;
+}
+
+namespace chromeos_update_engine {
+
+class UpdateEngineAdaptor;
+
+class UpdateAttempter : public ActionProcessorDelegate,
+                        public DownloadActionDelegate,
+                        public CertificateChecker::Observer {
+ public:
+  using UpdateStatus = update_engine::UpdateStatus;
+  static const int kMaxDeltaUpdateFailures;
+
+  UpdateAttempter(SystemState* system_state,
+                  CertificateChecker* cert_checker,
+                  LibCrosProxy* libcros_proxy,
+                  org::chromium::debugdProxyInterface* debugd_proxy);
+  ~UpdateAttempter() override;
+
+  // Further initialization to be done post construction.
+  void Init();
+
+  // Initiates scheduling of update checks.
+  virtual void ScheduleUpdates();
+
+  // Checks for update and, if a newer version is available, attempts to update
+  // the system. Non-empty |in_app_version| or |in_update_url| prevents
+  // automatic detection of the parameter.  |target_channel| denotes a
+  // policy-mandated channel we are updating to, if not empty. If |obey_proxies|
+  // is true, the update will likely respect Chrome's proxy setting. For
+  // security reasons, we may still not honor them. |interactive| should be true
+  // if this was called from the user (ie dbus).
+  virtual void Update(const std::string& app_version,
+                      const std::string& omaha_url,
+                      const std::string& target_channel,
+                      const std::string& target_version_prefix,
+                      bool obey_proxies,
+                      bool interactive);
+
+  // ActionProcessorDelegate methods:
+  void ProcessingDone(const ActionProcessor* processor,
+                      ErrorCode code) override;
+  void ProcessingStopped(const ActionProcessor* processor) override;
+  void ActionCompleted(ActionProcessor* processor,
+                       AbstractAction* action,
+                       ErrorCode code) override;
+
+  // Resets the current state to UPDATE_STATUS_IDLE.
+  // Used by update_engine_client for restarting a new update without
+  // having to reboot once the previous update has reached
+  // UPDATE_STATUS_UPDATED_NEED_REBOOT state. This is used only
+  // for testing purposes.
+  virtual bool ResetStatus();
+
+  // Returns the current status in the out params. Returns true on success.
+  virtual bool GetStatus(int64_t* last_checked_time,
+                         double* progress,
+                         std::string* current_operation,
+                         std::string* new_version,
+                         int64_t* new_size);
+
+  // Runs chromeos-setgoodkernel, whose responsibility it is to mark the
+  // currently booted partition has high priority/permanent/etc. The execution
+  // is asynchronous. On completion, the action processor may be started
+  // depending on the |start_action_processor_| field. Note that every update
+  // attempt goes through this method.
+  void UpdateBootFlags();
+
+  // Called when the boot flags have been updated.
+  void CompleteUpdateBootFlags(bool success);
+
+  UpdateStatus status() const { return status_; }
+
+  int http_response_code() const { return http_response_code_; }
+  void set_http_response_code(int code) { http_response_code_ = code; }
+
+  void set_dbus_adaptor(UpdateEngineAdaptor* dbus_adaptor) {
+    dbus_adaptor_ = dbus_adaptor;
+  }
+
+  // This is the internal entry point for going through an
+  // update. If the current status is idle invokes Update.
+  // This is called by the DBus implementation.
+  virtual void CheckForUpdate(const std::string& app_version,
+                              const std::string& omaha_url,
+                              bool is_interactive);
+
+  // This is the internal entry point for going through a rollback. This will
+  // attempt to run the postinstall on the non-active partition and set it as
+  // the partition to boot from. If |powerwash| is True, perform a powerwash
+  // as part of rollback. Returns True on success.
+  bool Rollback(bool powerwash);
+
+  // This is the internal entry point for checking if we can rollback.
+  bool CanRollback() const;
+
+  // This is the internal entry point for getting a rollback partition name,
+  // if one exists. It returns the bootable rollback kernel device partition
+  // name or empty string if none is available.
+  BootControlInterface::Slot GetRollbackSlot() const;
+
+  // Initiates a reboot if the current state is
+  // UPDATED_NEED_REBOOT. Returns true on sucess, false otherwise.
+  bool RebootIfNeeded();
+
+  // DownloadActionDelegate methods:
+  void BytesReceived(uint64_t bytes_progressed,
+                     uint64_t bytes_received,
+                     uint64_t total) override;
+
+  // Returns that the update should be canceled when the download channel was
+  // changed.
+  bool ShouldCancel(ErrorCode* cancel_reason) override;
+
+  void DownloadComplete() override;
+
+  // Broadcasts the current status over D-Bus.
+  void BroadcastStatus();
+
+  // Returns the special flags to be added to ErrorCode values based on the
+  // parameters used in the current update attempt.
+  uint32_t GetErrorCodeFlags();
+
+  // Called at update_engine startup to do various house-keeping.
+  void UpdateEngineStarted();
+
+  // 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
+  // from the server asynchronously at its own frequency.
+  virtual void RefreshDevicePolicy();
+
+  // Stores in |out_boot_time| the boottime (CLOCK_BOOTTIME) recorded at the
+  // time of the last successful update in the current boot. Returns false if
+  // there wasn't a successful update in the current boot.
+  virtual bool GetBootTimeAtUpdate(base::Time *out_boot_time);
+
+  // Returns a version OS version that was being used before the last reboot,
+  // and if that reboot happended to be into an update (current version).
+  // This will return an empty string otherwise.
+  std::string const& GetPrevVersion() const { return prev_version_; }
+
+  // Returns the number of consecutive failed update checks.
+  virtual unsigned int consecutive_failed_update_checks() const {
+    return consecutive_failed_update_checks_;
+  }
+
+  // Returns the poll interval dictated by Omaha, if provided; zero otherwise.
+  virtual unsigned int server_dictated_poll_interval() const {
+    return server_dictated_poll_interval_;
+  }
+
+  // Sets a callback to be used when either a forced update request is received
+  // (first argument set to true) or cleared by an update attempt (first
+  // argument set to false). The callback further encodes whether the forced
+  // check is an interactive one (second argument set to true). Takes ownership
+  // of the callback object. A null value disables callback on these events.
+  // Note that only one callback can be set, so effectively at most one client
+  // can be notified.
+  virtual void set_forced_update_pending_callback(
+      base::Callback<void(bool, bool)>*  // NOLINT(readability/function)
+      callback) {
+    forced_update_pending_callback_.reset(callback);
+  }
+
+  // Returns true if we should allow updates from any source. In official builds
+  // we want to restrict updates to known safe sources, but under certain
+  // conditions it's useful to allow updating from anywhere (e.g. to allow
+  // 'cros flash' to function properly).
+  virtual bool IsAnyUpdateSourceAllowed();
+
+ private:
+  // Update server URL for automated lab test.
+  static const char* const kTestUpdateUrl;
+
+  // Friend declarations for testing purposes.
+  friend class UpdateAttempterUnderTest;
+  friend class UpdateAttempterTest;
+  FRIEND_TEST(UpdateAttempterTest, ActionCompletedDownloadTest);
+  FRIEND_TEST(UpdateAttempterTest, ActionCompletedErrorTest);
+  FRIEND_TEST(UpdateAttempterTest, ActionCompletedOmahaRequestTest);
+  FRIEND_TEST(UpdateAttempterTest, CreatePendingErrorEventTest);
+  FRIEND_TEST(UpdateAttempterTest, CreatePendingErrorEventResumedTest);
+  FRIEND_TEST(UpdateAttempterTest, DisableDeltaUpdateIfNeededTest);
+  FRIEND_TEST(UpdateAttempterTest, MarkDeltaUpdateFailureTest);
+  FRIEND_TEST(UpdateAttempterTest, PingOmahaTest);
+  FRIEND_TEST(UpdateAttempterTest, ScheduleErrorEventActionNoEventTest);
+  FRIEND_TEST(UpdateAttempterTest, ScheduleErrorEventActionTest);
+  FRIEND_TEST(UpdateAttempterTest, UpdateTest);
+  FRIEND_TEST(UpdateAttempterTest, ReportDailyMetrics);
+  FRIEND_TEST(UpdateAttempterTest, BootTimeInUpdateMarkerFile);
+
+  // CertificateChecker::Observer method.
+  // Report metrics about the certificate being checked.
+  void CertificateChecked(ServerToCheck server_to_check,
+                          CertificateCheckResult result) override;
+
+  // Checks if it's more than 24 hours since daily metrics were last
+  // reported and, if so, reports daily metrics. Returns |true| if
+  // metrics were reported, |false| otherwise.
+  bool CheckAndReportDailyMetrics();
+
+  // Calculates and reports the age of the currently running OS. This
+  // is defined as the age of the /etc/lsb-release file.
+  void ReportOSAge();
+
+  // Sets the status to the given status and notifies a status update over dbus.
+  void SetStatusAndNotify(UpdateStatus status);
+
+  // Sets up the download parameters after receiving the update check response.
+  void SetupDownload();
+
+  // Creates an error event object in |error_event_| to be included in an
+  // OmahaRequestAction once the current action processor is done.
+  void CreatePendingErrorEvent(AbstractAction* action, ErrorCode code);
+
+  // If there's a pending error event allocated in |error_event_|, schedules an
+  // OmahaRequestAction with that event in the current processor, clears the
+  // pending event, updates the status and returns true. Returns false
+  // otherwise.
+  bool ScheduleErrorEventAction();
+
+  // Sets the cpu shares to |shares| and updates |shares_| if the new
+  // |shares| is different than the current |shares_|, otherwise simply
+  // returns.
+  void SetCpuShares(utils::CpuShares shares);
+
+  // Sets the cpu shares to low and sets up timeout events to increase it.
+  void SetupCpuSharesManagement();
+
+  // Resets the cpu shares to normal and destroys any scheduled timeout
+  // sources.
+  void CleanupCpuSharesManagement();
+
+  // The cpu shares timeout source callback sets the current cpu shares to
+  // normal.
+  void ManageCpuSharesCallback();
+
+  // Schedules an event loop callback to start the action processor. This is
+  // scheduled asynchronously to unblock the event loop.
+  void ScheduleProcessingStart();
+
+  // Checks if a full update is needed and forces it by updating the Omaha
+  // request params.
+  void DisableDeltaUpdateIfNeeded();
+
+  // If this was a delta update attempt that failed, count it so that a full
+  // update can be tried when needed.
+  void MarkDeltaUpdateFailure();
+
+  ProxyResolver* GetProxyResolver() {
+    return obeying_proxies_ ?
+        reinterpret_cast<ProxyResolver*>(&chrome_proxy_resolver_) :
+        reinterpret_cast<ProxyResolver*>(&direct_proxy_resolver_);
+  }
+
+  // Sends a ping to Omaha.
+  // This is used after an update has been applied and we're waiting for the
+  // user to reboot.  This ping helps keep the number of actives count
+  // accurate in case a user takes a long time to reboot the device after an
+  // update has been applied.
+  void PingOmaha();
+
+  // 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 parametes.
+  bool CalculateUpdateParams(const std::string& app_version,
+                             const std::string& omaha_url,
+                             const std::string& target_channel,
+                             const std::string& target_version_prefix,
+                             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_.
+  void CalculateScatteringParams(bool is_interactive);
+
+  // Sets a random value for the waiting period to wait for before downloading
+  // an update, if one available. This value will be upperbounded by the
+  // scatter factor value specified from policy.
+  void GenerateNewWaitingPeriod();
+
+  // Helper method of Update() and Rollback() to construct the sequence of
+  // actions to be performed for the postinstall.
+  // |previous_action| is the previous action to get
+  // bonded with the install_plan that gets passed to postinstall.
+  void BuildPostInstallActions(InstallPlanAction* previous_action);
+
+  // Helper method of Update() to construct the sequence of actions to
+  // be performed for an update check. Please refer to
+  // Update() method for the meaning of the parameters.
+  void BuildUpdateActions(bool interactive);
+
+  // Decrements the count in the kUpdateCheckCountFilePath.
+  // Returns True if successfully decremented, false otherwise.
+  bool DecrementUpdateCheckCount();
+
+  // Starts p2p and performs housekeeping. Returns true only if p2p is
+  // running and housekeeping was done.
+  bool StartP2PAndPerformHousekeeping();
+
+  // Calculates whether peer-to-peer should be used. Sets the
+  // |use_p2p_to_download_| and |use_p2p_to_share_| parameters
+  // on the |omaha_request_params_| object.
+  void CalculateP2PParams(bool interactive);
+
+  // 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.
+  bool StartP2PAtStartup();
+
+  // Writes to the processing completed marker. Does nothing if
+  // |update_completed_marker_| is empty.
+  void WriteUpdateCompletedMarker();
+
+  // Sends a D-Bus message to the Chrome OS power manager asking it to reboot
+  // the system. Returns true on success.
+  bool RequestPowerManagerReboot();
+
+  // Reboots the system directly by calling /sbin/shutdown. Returns true on
+  // success.
+  bool RebootDirectly();
+
+  // Callback for the async UpdateCheckAllowed policy request. If |status| is
+  // |EvalStatus::kSucceeded|, either runs or suppresses periodic update checks,
+  // based on the content of |params|. Otherwise, retries the policy request.
+  void OnUpdateScheduled(
+      chromeos_update_manager::EvalStatus status,
+      const chromeos_update_manager::UpdateCheckParams& params);
+
+  // Updates the time an update was last attempted to the current time.
+  void UpdateLastCheckedTime();
+
+  // Returns whether an update is currently running or scheduled.
+  bool IsUpdateRunningOrScheduled();
+
+  // Last status notification timestamp used for throttling. Use monotonic
+  // TimeTicks to ensure that notifications are sent even if the system clock is
+  // set back in the middle of an update.
+  base::TimeTicks last_notify_time_;
+
+  std::vector<std::shared_ptr<AbstractAction>> actions_;
+  std::unique_ptr<ActionProcessor> 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_;
+
+  // If non-null, this UpdateAttempter will send status updates over this
+  // dbus service.
+  UpdateEngineAdaptor* dbus_adaptor_ = nullptr;
+
+  // Pointer to the OmahaResponseHandlerAction in the actions_ vector.
+  std::shared_ptr<OmahaResponseHandlerAction> response_handler_action_;
+
+  // Pointer to the DownloadAction in the actions_ vector.
+  std::shared_ptr<DownloadAction> download_action_;
+
+  // Pointer to the preferences store interface. This is just a cached
+  // copy of system_state->prefs() because it's used in many methods and
+  // is convenient this way.
+  PrefsInterface* prefs_ = nullptr;
+
+  // Pending error event, if any.
+  std::unique_ptr<OmahaEvent> error_event_;
+
+  // If we should request a reboot even tho we failed the update
+  bool fake_update_success_ = false;
+
+  // HTTP server response code from the last HTTP request action.
+  int http_response_code_ = 0;
+
+  // Current cpu shares.
+  utils::CpuShares shares_ = utils::kCpuSharesNormal;
+
+  // The cpu shares management timeout task id.
+  brillo::MessageLoop::TaskId manage_shares_id_{
+      brillo::MessageLoop::kTaskIdNull};
+
+  // For status:
+  UpdateStatus status_{UpdateStatus::IDLE};
+  double download_progress_ = 0.0;
+  int64_t last_checked_time_ = 0;
+  std::string prev_version_;
+  std::string new_version_ = "0.0.0.0";
+  int64_t new_payload_size_ = 0;
+
+  // Common parameters for all Omaha requests.
+  OmahaRequestParams* omaha_request_params_ = nullptr;
+
+  // Number of consecutive manual update checks we've had where we obeyed
+  // Chrome's proxy settings.
+  int proxy_manual_checks_ = 0;
+
+  // If true, this update cycle we are obeying proxies
+  bool obeying_proxies_ = true;
+
+  // Our two proxy resolvers
+  DirectProxyResolver direct_proxy_resolver_;
+  ChromeBrowserProxyResolver chrome_proxy_resolver_;
+
+  // Originally, both of these flags are false. Once UpdateBootFlags is called,
+  // |update_boot_flags_running_| is set to true. As soon as UpdateBootFlags
+  // completes its asynchronous run, |update_boot_flags_running_| is reset to
+  // false and |updated_boot_flags_| is set to true. From that point on there
+  // will be no more changes to these flags.
+  //
+  // True if UpdateBootFlags has completed.
+  bool updated_boot_flags_ = false;
+  // True if UpdateBootFlags is running.
+  bool update_boot_flags_running_ = false;
+
+  // True if the action processor needs to be started by the boot flag updater.
+  bool start_action_processor_ = false;
+
+  // Used for fetching information about the device policy.
+  std::unique_ptr<policy::PolicyProvider> policy_provider_;
+
+  // The current scatter factor as found in the policy setting.
+  base::TimeDelta scatter_factor_;
+
+  // The number of consecutive failed update checks. Needed for calculating the
+  // next update check interval.
+  unsigned int consecutive_failed_update_checks_ = 0;
+
+  // The poll interval (in seconds) that was dictated by Omaha, if any; zero
+  // otherwise. This is needed for calculating the update check interval.
+  unsigned int server_dictated_poll_interval_ = 0;
+
+  // Tracks whether we have scheduled update checks.
+  bool waiting_for_scheduled_check_ = false;
+
+  // A callback to use when a forced update request is either received (true) or
+  // cleared by an update attempt (false). The second argument indicates whether
+  // this is an interactive update, and its value is significant iff the first
+  // argument is true.
+  std::unique_ptr<base::Callback<void(bool, bool)>>
+      forced_update_pending_callback_;
+
+  // The |app_version| and |omaha_url| parameters received during the latest
+  // forced update request. They are retrieved for use once the update is
+  // actually scheduled.
+  std::string forced_app_version_;
+  std::string forced_omaha_url_;
+
+  org::chromium::debugdProxyInterface* debugd_proxy_;
+
+  DISALLOW_COPY_AND_ASSIGN(UpdateAttempter);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_UPDATE_ATTEMPTER_H_
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
new file mode 100644
index 0000000..747a974
--- /dev/null
+++ b/update_attempter_unittest.cc
@@ -0,0 +1,1010 @@
+//
+// 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/bind_lambda.h>
+#include <brillo/make_unique_ptr.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 <debugd/dbus-constants.h>
+#include <debugd/dbus-proxies.h>
+#include <debugd/dbus-proxy-mocks.h>
+#include <gtest/gtest.h>
+#include <policy/libpolicy.h>
+#include <policy/mock_device_policy.h>
+
+#include "libcros/dbus-proxies.h"
+#include "libcros/dbus-proxy-mocks.h"
+#include "update_engine/common/fake_clock.h"
+#include "update_engine/common/fake_prefs.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_action.h"
+#include "update_engine/mock_action_processor.h"
+#include "update_engine/mock_p2p_manager.h"
+#include "update_engine/mock_payload_state.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"
+
+using base::Time;
+using base::TimeDelta;
+using brillo::MessageLoop;
+using org::chromium::LibCrosServiceInterfaceProxyMock;
+using org::chromium::UpdateEngineLibcrosProxyResolvedInterfaceProxyMock;
+using std::string;
+using std::unique_ptr;
+using testing::DoAll;
+using testing::InSequence;
+using testing::Ne;
+using testing::NiceMock;
+using testing::Property;
+using testing::Return;
+using testing::ReturnPointee;
+using testing::SaveArg;
+using testing::SetArgumentPointee;
+using testing::_;
+using update_engine::UpdateStatus;
+
+namespace chromeos_update_engine {
+
+// 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(SystemState* system_state,
+                           LibCrosProxy* libcros_proxy,
+                           org::chromium::debugdProxyInterface* debugd_proxy)
+      : UpdateAttempter(system_state, nullptr, libcros_proxy, debugd_proxy) {}
+
+  // Wrap the update scheduling method, allowing us to opt out of scheduled
+  // updates for testing purposes.
+  void ScheduleUpdates() override {
+    schedule_updates_called_ = true;
+    if (do_schedule_updates_) {
+      UpdateAttempter::ScheduleUpdates();
+    } else {
+      LOG(INFO) << "[TEST] Update scheduling disabled.";
+    }
+  }
+  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()
+      : service_interface_mock_(new LibCrosServiceInterfaceProxyMock()),
+        ue_proxy_resolved_interface_mock_(
+            new NiceMock<UpdateEngineLibcrosProxyResolvedInterfaceProxyMock>()),
+        libcros_proxy_(
+            brillo::make_unique_ptr(service_interface_mock_),
+            brillo::make_unique_ptr(ue_proxy_resolved_interface_mock_)),
+        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_);
+    loop_.SetAsCurrent();
+
+    certificate_checker_.Init();
+
+    // Finish initializing the attempter.
+    attempter_.Init();
+  }
+
+  void SetUp() override {
+    CHECK(utils::MakeTempDirectory("UpdateAttempterTest-XXXXXX", &test_dir_));
+
+    EXPECT_EQ(nullptr, attempter_.dbus_adaptor_);
+    EXPECT_NE(nullptr, attempter_.system_state_);
+    EXPECT_EQ(0, attempter_.http_response_code_);
+    EXPECT_EQ(utils::kCpuSharesNormal, attempter_.shares_);
+    EXPECT_EQ(MessageLoop::kTaskIdNull, attempter_.manage_shares_id_);
+    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(0, 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_));
+  }
+
+  void TearDown() override {
+    base::DeleteFile(base::FilePath(test_dir_), true);
+  }
+
+ 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();
+
+  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_;
+  org::chromium::debugdProxyMock debugd_proxy_mock_;
+  LibCrosServiceInterfaceProxyMock* service_interface_mock_;
+  UpdateEngineLibcrosProxyResolvedInterfaceProxyMock*
+      ue_proxy_resolved_interface_mock_;
+  LibCrosProxy libcros_proxy_;
+  OpenSSLWrapper openssl_wrapper_;
+  CertificateChecker certificate_checker_;
+  UpdateAttempterUnderTest attempter_{&fake_system_state_,
+                                      &libcros_proxy_,
+                                      &debugd_proxy_mock_};
+
+  NiceMock<MockActionProcessor>* processor_;
+  NiceMock<MockPrefs>* prefs_;  // Shortcut to fake_system_state_->mock_prefs().
+  NiceMock<MockConnectionManager> mock_connection_manager;
+
+  string test_dir_;
+
+  bool actual_using_p2p_for_downloading_;
+  bool actual_using_p2p_for_sharing_;
+};
+
+void UpdateAttempterTest::ScheduleQuitMainLoop() {
+  loop_.PostTask(FROM_HERE, base::Bind([this] { this->loop_.BreakLoop(); }));
+}
+
+TEST_F(UpdateAttempterTest, ActionCompletedDownloadTest) {
+  unique_ptr<MockHttpFetcher> fetcher(new MockHttpFetcher("", 0, nullptr));
+  fetcher->FailTransfer(503);  // Sets the HTTP response code.
+  DownloadAction action(prefs_, nullptr, fetcher.release());
+  EXPECT_CALL(*prefs_, GetInt64(kPrefsDeltaUpdateFailures, _)).Times(0);
+  attempter_.ActionCompleted(nullptr, &action, ErrorCode::kSuccess);
+  EXPECT_EQ(503, attempter_.http_response_code());
+  EXPECT_EQ(UpdateStatus::FINALIZING, attempter_.status());
+  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, 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(234, 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);
+  UpdateAttempterUnderTest attempter(&fake_system_state_, nullptr,
+                                     &debugd_proxy_mock_);
+  attempter.Init();
+  EXPECT_EQ(UpdateStatus::UPDATED_NEED_REBOOT, attempter.status());
+}
+
+TEST_F(UpdateAttempterTest, GetErrorCodeForActionTest) {
+  extern ErrorCode GetErrorCodeForAction(AbstractAction* action,
+                                              ErrorCode code);
+  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(
+      fake_system_state_.boot_control(), VerifierMode::kVerifyTargetHash);
+  EXPECT_EQ(ErrorCode::kFilesystemVerifierError,
+            GetErrorCodeForAction(&filesystem_verifier_action,
+                                  ErrorCode::kError));
+  PostinstallRunnerAction postinstall_runner_action(
+      fake_system_state.fake_boot_control());
+  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(
+          SetArgumentPointee<1>(UpdateAttempter::kMaxDeltaUpdateFailures - 1),
+          Return(true)));
+  attempter_.DisableDeltaUpdateIfNeeded();
+  EXPECT_TRUE(attempter_.omaha_request_params_->delta_okay());
+  EXPECT_CALL(*prefs_, GetInt64(kPrefsDeltaUpdateFailures, _))
+      .WillOnce(DoAll(
+          SetArgumentPointee<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(SetArgumentPointee<1>(-1), Return(true)))
+      .WillOnce(DoAll(SetArgumentPointee<1>(1), Return(true)))
+      .WillOnce(DoAll(
+          SetArgumentPointee<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.payload_urls.push_back(url1);
+  response.payload_urls.push_back("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(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(),
+  FilesystemVerifierAction::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(),
+};
+
+}  // 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.
+  policy::MockDevicePolicy* device_policy = new policy::MockDevicePolicy();
+  attempter_.policy_provider_.reset(new policy::PolicyProvider(device_policy));
+  EXPECT_CALL(*device_policy, LoadPolicy())
+      .Times(testing::AtLeast(1)).WillRepeatedly(Return(true));
+
+  {
+    InSequence s;
+    for (size_t i = 0; i < arraysize(kUpdateActionTypes); ++i) {
+      EXPECT_CALL(*processor_,
+                  EnqueueAction(Property(&AbstractAction::Type,
+                                         kUpdateActionTypes[i])));
+    }
+    EXPECT_CALL(*processor_, StartProcessing());
+  }
+
+  attempter_.Update("", "", "", "", 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(arraysize(kUpdateActionTypes), attempter_.actions_.size());
+  for (size_t i = 0; i < arraysize(kUpdateActionTypes); ++i) {
+    EXPECT_EQ(kUpdateActionTypes[i], attempter_.actions_[i]->Type());
+  }
+  EXPECT_EQ(attempter_.response_handler_action_.get(),
+            attempter_.actions_[1].get());
+  DownloadAction* download_action =
+      dynamic_cast<DownloadAction*>(attempter_.actions_[4].get());
+  ASSERT_NE(nullptr, download_action);
+  EXPECT_EQ(&attempter_, download_action->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.
+  policy::MockDevicePolicy* device_policy = new policy::MockDevicePolicy();
+  attempter_.policy_provider_.reset(new policy::PolicyProvider(device_policy));
+
+  EXPECT_CALL(*device_policy, LoadPolicy()).WillRepeatedly(Return(true));
+  fake_system_state_.set_device_policy(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 (enterprise_rollback) {
+    // We return an empty owner as this is an enterprise.
+    EXPECT_CALL(*device_policy, GetOwner(_)).WillRepeatedly(
+        DoAll(SetArgumentPointee<0>(string("")),
+        Return(true)));
+  } else {
+    // We return a fake owner as this is an owned consumer device.
+    EXPECT_CALL(*device_policy, GetOwner(_)).WillRepeatedly(
+        DoAll(SetArgumentPointee<0>(string("fake.mail@fake.com")),
+        Return(true)));
+  }
+
+  if (is_rollback_allowed) {
+    InSequence s;
+    for (size_t i = 0; i < arraysize(kRollbackActionTypes); ++i) {
+      EXPECT_CALL(*processor_,
+                  EnqueueAction(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(arraysize(kRollbackActionTypes), attempter_.actions_.size());
+  for (size_t i = 0; i < arraysize(kRollbackActionTypes); ++i) {
+    EXPECT_EQ(kRollbackActionTypes[i], attempter_.actions_[i]->Type());
+  }
+  EXPECT_EQ(UpdateStatus::ATTEMPTING_ROLLBACK, attempter_.status());
+  InstallPlanAction* install_plan_action =
+        dynamic_cast<InstallPlanAction*>(attempter_.actions_[0].get());
+  InstallPlan* install_plan = install_plan_action->install_plan();
+  EXPECT_EQ(0, install_plan->partitions.size());
+  EXPECT_EQ(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(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) {
+  OmahaResponseHandlerAction *response_action =
+      new OmahaResponseHandlerAction(&fake_system_state_);
+  response_action->install_plan_.is_resume = true;
+  attempter_.response_handler_action_.reset(response_action);
+  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);
+  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);
+  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);
+  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);
+  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, true /* interactive */);
+  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;
+
+  policy::MockDevicePolicy* device_policy = new policy::MockDevicePolicy();
+  attempter_.policy_provider_.reset(new policy::PolicyProvider(device_policy));
+
+  EXPECT_CALL(*device_policy, LoadPolicy()).WillRepeatedly(Return(true));
+  fake_system_state_.set_device_policy(device_policy);
+
+  EXPECT_CALL(*device_policy, GetScatterFactorInSeconds(_))
+      .WillRepeatedly(DoAll(
+          SetArgumentPointee<0>(scatter_factor_in_seconds),
+          Return(true)));
+
+  attempter_.Update("", "", "", "", 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;
+
+  policy::MockDevicePolicy* device_policy = new policy::MockDevicePolicy();
+  attempter_.policy_provider_.reset(new policy::PolicyProvider(device_policy));
+
+  EXPECT_CALL(*device_policy, LoadPolicy()).WillRepeatedly(Return(true));
+  fake_system_state_.set_device_policy(device_policy);
+
+  EXPECT_CALL(*device_policy, GetScatterFactorInSeconds(_))
+      .WillRepeatedly(DoAll(
+          SetArgumentPointee<0>(scatter_factor_in_seconds),
+          Return(true)));
+
+  attempter_.Update("", "", "", "", 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);
+  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(kPrefsWallClockWaitPeriod, 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;
+
+  policy::MockDevicePolicy* device_policy = new policy::MockDevicePolicy();
+  attempter_.policy_provider_.reset(new policy::PolicyProvider(device_policy));
+
+  EXPECT_CALL(*device_policy, LoadPolicy()).WillRepeatedly(Return(true));
+  fake_system_state_.set_device_policy(device_policy);
+
+  EXPECT_CALL(*device_policy, GetScatterFactorInSeconds(_))
+      .WillRepeatedly(DoAll(
+          SetArgumentPointee<0>(scatter_factor_in_seconds),
+          Return(true)));
+
+  // Trigger an interactive check so we can test that scattering is disabled.
+  attempter_.Update("", "", "", "", false, 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(kPrefsWallClockWaitPeriod));
+  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();
+}
+
+// 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) {
+  UpdateAttempterUnderTest attempter{&fake_system_state_,
+                                     nullptr,  // libcros_proxy
+                                     &debugd_proxy_mock_};
+  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()->SetIsNormalBootMode(false);
+  EXPECT_CALL(debugd_proxy_mock_, QueryDevFeatures(_, _, _))
+      .WillRepeatedly(DoAll(SetArgumentPointee<0>(0), Return(true)));
+  EXPECT_TRUE(attempter_.IsAnyUpdateSourceAllowed());
+}
+
+TEST_F(UpdateAttempterTest, AnyUpdateSourceDisallowedOfficialNormal) {
+  fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
+  fake_system_state_.fake_hardware()->SetIsNormalBootMode(true);
+  // debugd should not be queried in this case.
+  EXPECT_CALL(debugd_proxy_mock_, QueryDevFeatures(_, _, _)).Times(0);
+  EXPECT_FALSE(attempter_.IsAnyUpdateSourceAllowed());
+}
+
+TEST_F(UpdateAttempterTest, AnyUpdateSourceDisallowedDebugdDisabled) {
+  using debugd::DEV_FEATURES_DISABLED;
+  fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
+  fake_system_state_.fake_hardware()->SetIsNormalBootMode(false);
+  EXPECT_CALL(debugd_proxy_mock_, QueryDevFeatures(_, _, _))
+      .WillRepeatedly(
+          DoAll(SetArgumentPointee<0>(DEV_FEATURES_DISABLED), Return(true)));
+  EXPECT_FALSE(attempter_.IsAnyUpdateSourceAllowed());
+}
+
+TEST_F(UpdateAttempterTest, AnyUpdateSourceDisallowedDebugdFailure) {
+  fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
+  fake_system_state_.fake_hardware()->SetIsNormalBootMode(false);
+  EXPECT_CALL(debugd_proxy_mock_, QueryDevFeatures(_, _, _))
+      .WillRepeatedly(Return(false));
+  EXPECT_FALSE(attempter_.IsAnyUpdateSourceAllowed());
+}
+
+TEST_F(UpdateAttempterTest, CheckForUpdateAUTest) {
+  fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
+  fake_system_state_.fake_hardware()->SetIsNormalBootMode(true);
+  attempter_.CheckForUpdate("", "autest", true);
+  EXPECT_EQ(constants::kOmahaDefaultAUTestURL, attempter_.forced_omaha_url());
+}
+
+TEST_F(UpdateAttempterTest, CheckForUpdateScheduledAUTest) {
+  fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
+  fake_system_state_.fake_hardware()->SetIsNormalBootMode(true);
+  attempter_.CheckForUpdate("", "autest-scheduled", true);
+  EXPECT_EQ(constants::kOmahaDefaultAUTestURL, attempter_.forced_omaha_url());
+}
+
+}  // namespace chromeos_update_engine
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
new file mode 100644
index 0000000..449e669
--- /dev/null
+++ b/update_engine.conf
@@ -0,0 +1,2 @@
+PAYLOAD_MAJOR_VERSION=2
+PAYLOAD_MINOR_VERSION=3
diff --git a/update_engine.gyp b/update_engine.gyp
new file mode 100644
index 0000000..2fa6ae8
--- /dev/null
+++ b/update_engine.gyp
@@ -0,0 +1,508 @@
+#
+# 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.
+#
+{
+  'target_defaults': {
+    'variables': {
+      'deps': [
+        'libbrillo-<(libbase_ver)',
+        'libchrome-<(libbase_ver)',
+      ],
+      # 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_dbus%': '1',
+      'USE_hwid_override%': '0',
+      'USE_mtd%': '0',
+      'USE_power_management%': '0',
+    },
+    'cflags': [
+      '-g',
+      '-ffunction-sections',
+      '-Wall',
+      '-Wextra',
+      '-Werror',
+      '-Wno-unused-parameter',
+    ],
+    'cflags_cc': [
+      '-fno-strict-aliasing',
+      '-Wnon-virtual-dtor',
+    ],
+    'ldflags': [
+      '-Wl,--gc-sections',
+    ],
+    'defines': [
+      '_FILE_OFFSET_BITS=64',
+      '_POSIX_C_SOURCE=199309L',
+      'USE_DBUS=<(USE_dbus)',
+      'USE_HWID_OVERRIDE=<(USE_hwid_override)',
+      'USE_MTD=<(USE_mtd)',
+      'USE_POWER_MANAGEMENT=<(USE_power_management)',
+    ],
+    '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',
+        'exported_deps': [
+          'protobuf-lite',
+        ],
+        'deps': ['<@(exported_deps)'],
+      },
+      'all_dependent_settings': {
+        'variables': {
+          'deps': [
+            '<@(exported_deps)',
+          ],
+        },
+      },
+      '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-other-dbus-proxies',
+      'type': 'none',
+      'actions': [
+        {
+          'action_name': 'update_engine-dbus-libcros-client',
+          'variables': {
+            'mock_output_file': 'include/libcros/dbus-proxy-mocks.h',
+            'proxy_output_file': 'include/libcros/dbus-proxies.h'
+          },
+          'sources': [
+            'dbus_bindings/org.chromium.LibCrosService.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',
+          'libcurl',
+          'libssl',
+          'xz-embedded',
+        ],
+        '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/certificate_checker.cc',
+        'common/clock.cc',
+        'common/constants.cc',
+        'common/hash_calculator.cc',
+        'common/http_common.cc',
+        'common/http_fetcher.cc',
+        'common/hwid_override.cc',
+        'common/libcurl_http_fetcher.cc',
+        'common/multi_range_http_fetcher.cc',
+        'common/platform_constants_chromeos.cc',
+        'common/prefs.cc',
+        'common/subprocess.cc',
+        'common/terminator.cc',
+        'common/utils.cc',
+        'payload_consumer/bzip_extent_writer.cc',
+        'payload_consumer/delta_performer.cc',
+        'payload_consumer/download_action.cc',
+        'payload_consumer/extent_writer.cc',
+        'payload_consumer/file_descriptor.cc',
+        'payload_consumer/file_writer.cc',
+        'payload_consumer/filesystem_verifier_action.cc',
+        'payload_consumer/install_plan.cc',
+        'payload_consumer/payload_constants.cc',
+        'payload_consumer/payload_verifier.cc',
+        'payload_consumer/postinstall_runner_action.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',
+        'update_engine-other-dbus-proxies',
+      ],
+      'variables': {
+        'exported_deps': [
+          'dbus-1',
+          'libdebugd-client',
+          'libsession_manager-client',
+          'libmetrics-<(libbase_ver)',
+          'libpower_manager-client',
+          'libupdate_engine-client',
+          'libshill-client',
+          'expat',
+        ],
+        'deps': ['<@(exported_deps)'],
+      },
+      'all_dependent_settings': {
+        'variables': {
+          'deps': [
+            '<@(exported_deps)',
+          ],
+        },
+      },
+      'link_settings': {
+        'variables': {
+          'deps': [
+            '<@(exported_deps)',
+          ],
+        },
+        'libraries': [
+          '-lbz2',
+          '-lpolicy-<(libbase_ver)',
+          '-lrootdev',
+          '-lrt',
+          '-lvboot_host',
+        ],
+      },
+      'sources': [
+        'boot_control_chromeos.cc',
+        'chrome_browser_proxy_resolver.cc',
+        'connection_manager.cc',
+        'daemon.cc',
+        'dbus_service.cc',
+        'hardware_chromeos.cc',
+        'image_properties_chromeos.cc',
+        'libcros_proxy.cc',
+        'metrics.cc',
+        'metrics_utils.cc',
+        'omaha_request_action.cc',
+        'omaha_request_params.cc',
+        'omaha_response_handler_action.cc',
+        'p2p_manager.cc',
+        'payload_state.cc',
+        'proxy_resolver.cc',
+        'real_system_state.cc',
+        'shill_proxy.cc',
+        'update_attempter.cc',
+        'update_manager/boxed_value.cc',
+        'update_manager/chromeos_policy.cc',
+        'update_manager/default_policy.cc',
+        'update_manager/evaluation_context.cc',
+        'update_manager/policy.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/state_factory.cc',
+        'update_manager/update_manager.cc',
+        'update_status_utils.cc',
+      ],
+    },
+    # update_engine daemon.
+    {
+      'target_name': 'update_engine',
+      'type': 'executable',
+      'dependencies': [
+        'libupdate_engine',
+      ],
+      'sources': [
+        'main.cc',
+      ],
+    },
+    # update_engine console client.
+    {
+      'target_name': 'update_engine_client',
+      'type': 'executable',
+      'variables': {
+        'exported_deps': [
+          'libupdate_engine-client',
+        ],
+        'deps': ['<@(exported_deps)'],
+      },
+      'sources': [
+        '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',
+        ],
+        'deps': ['<@(exported_deps)'],
+      },
+      'all_dependent_settings': {
+        'variables': {
+          'deps': [
+            '<@(exported_deps)',
+          ],
+        },
+      },
+      'link_settings': {
+        'variables': {
+          'deps': [
+            '<@(exported_deps)',
+          ],
+        },
+      },
+      'sources': [
+        'payload_generator/ab_generator.cc',
+        'payload_generator/annotated_operation.cc',
+        'payload_generator/blob_file_writer.cc',
+        'payload_generator/block_mapping.cc',
+        'payload_generator/bzip.cc',
+        'payload_generator/cycle_breaker.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/payload_file.cc',
+        'payload_generator/payload_generation_config.cc',
+        'payload_generator/payload_signer.cc',
+        'payload_generator/raw_filesystem.cc',
+        'payload_generator/tarjan.cc',
+        'payload_generator/topological_sort.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',
+      ],
+    },
+  ],
+  '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',
+          ],
+          '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',
+          'dependencies': ['libupdate_engine'],
+          'sources': [
+            'test_http_server.cc',
+          ],
+        },
+        # Main unittest file.
+        {
+          'target_name': 'update_engine_unittests',
+          'type': 'executable',
+          'includes': ['../../../platform2/common-mk/common_test.gypi'],
+          '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',
+          ],
+          'includes': ['../../../platform2/common-mk/common_test.gypi'],
+          'sources': [
+            'chrome_browser_proxy_resolver_unittest.cc',
+            'common/action_pipe_unittest.cc',
+            'common/action_processor_unittest.cc',
+            'common/action_unittest.cc',
+            'common/boot_control_chromeos_unittest.cc',
+            'common/certificate_checker_unittest.cc',
+            'common/fake_prefs.cc',
+            'common/hash_calculator_unittest.cc',
+            'common/http_fetcher_unittest.cc',
+            'common/hwid_override_unittest.cc',
+            'common/mock_http_fetcher.cc',
+            'common/prefs_unittest.cc',
+            'common/subprocess_unittest.cc',
+            'common/terminator_unittest.cc',
+            'common/test_utils.cc',
+            'common/utils_unittest.cc',
+            'connection_manager_unittest.cc',
+            'dbus_service_unittest.cc',
+            'fake_shill_proxy.cc',
+            'fake_system_state.cc',
+            'metrics_utils_unittest.cc',
+            'omaha_request_action_unittest.cc',
+            'omaha_request_params_unittest.cc',
+            'omaha_response_handler_action_unittest.cc',
+            'p2p_manager_unittest.cc',
+            'payload_consumer/bzip_extent_writer_unittest.cc',
+            'payload_consumer/delta_performer_integration_test.cc',
+            'payload_consumer/delta_performer_unittest.cc',
+            'payload_consumer/download_action_unittest.cc',
+            'payload_consumer/extent_writer_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/cycle_breaker_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/fake_filesystem.cc',
+            'payload_generator/full_update_generator_unittest.cc',
+            'payload_generator/graph_utils_unittest.cc',
+            'payload_generator/inplace_generator_unittest.cc',
+            'payload_generator/payload_file_unittest.cc',
+            'payload_generator/payload_generation_config_unittest.cc',
+            'payload_generator/payload_signer_unittest.cc',
+            'payload_generator/tarjan_unittest.cc',
+            'payload_generator/topological_sort_unittest.cc',
+            'payload_generator/zip_unittest.cc',
+            'payload_state_unittest.cc',
+            'update_attempter_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_config_provider_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/umtest_utils.cc',
+            'update_manager/update_manager_unittest.cc',
+            'update_manager/variable_unittest.cc',
+            # Main entry point for runnning tests.
+            'testrunner.cc',
+          ],
+        },
+      ],
+    }],
+  ],
+}
diff --git a/update_engine.rc b/update_engine.rc
new file mode 100644
index 0000000..69fc7af
--- /dev/null
+++ b/update_engine.rc
@@ -0,0 +1,4 @@
+service update_engine /system/bin/update_engine --logtostderr --foreground
+    class late_start
+    user root
+    group root system dbus inet
diff --git a/update_engine_client.cc b/update_engine_client.cc
new file mode 100644
index 0000000..b59f22f
--- /dev/null
+++ b/update_engine_client.cc
@@ -0,0 +1,653 @@
+//
+// 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 <inttypes.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <base/bind.h>
+#include <base/command_line.h>
+#include <base/logging.h>
+#include <base/macros.h>
+#include <brillo/daemons/dbus_daemon.h>
+#include <brillo/flag_helper.h>
+#include <dbus/bus.h>
+#include <update_engine/dbus-constants.h>
+#include <update_engine/dbus-proxies.h>
+
+using std::string;
+using update_engine::kAttemptUpdateFlagNonInteractive;
+using update_engine::kUpdateEngineServiceName;
+
+namespace {
+
+// Constant to signal that we need to continue running the daemon after
+// initialization.
+const int kContinueRunning = -1;
+
+class UpdateEngineClient : public brillo::DBusDaemon {
+ public:
+  UpdateEngineClient(int argc, char** argv) : argc_(argc), argv_(argv) {}
+  ~UpdateEngineClient() override = default;
+
+ protected:
+  int OnInit() override {
+    int ret = DBusDaemon::OnInit();
+    if (ret != EX_OK)
+      return ret;
+    if (!InitProxy())
+      return 1;
+    // Wait for the UpdateEngine to be available or timeout.
+    proxy_->GetObjectProxy()->WaitForServiceToBeAvailable(
+        base::Bind(&UpdateEngineClient::OnServiceAvailable,
+                   base::Unretained(this)));
+    base::MessageLoop::current()->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&UpdateEngineClient::OnServiceAvailableTimeout,
+                   base::Unretained(this)),
+        base::TimeDelta::FromSeconds(10));
+    return EX_OK;
+  }
+
+ private:
+  bool InitProxy();
+
+  // Callback called when the UpdateEngine service becomes available.
+  void OnServiceAvailable(bool service_is_available);
+
+  // Callback called when the UpdateEngine service doesn't become available
+  // after a timeout.
+  void OnServiceAvailableTimeout();
+
+
+  // Callback called when a StatusUpdate signal is received.
+  void OnStatusUpdateSignal(int64_t last_checked_time,
+                            double progress,
+                            const string& current_operation,
+                            const string& new_version,
+                            int64_t new_size);
+  // Callback called when the OnStatusUpdateSignal() handler is registered.
+  void OnStatusUpdateSignalRegistration(const string& interface,
+                                        const string& signal_name,
+                                        bool success);
+
+  // Registers a callback that prints on stderr the received StatusUpdate
+  // signals.
+  // The daemon should continue running for this to work.
+  void WatchForUpdates();
+
+  void ResetStatus();
+
+  // Show the status of the update engine in stdout.
+  // Blocking call. Exits the program with error 1 in case of an error.
+  void ShowStatus();
+
+  // Return the current operation status, such as UPDATE_STATUS_IDLE.
+  // Blocking call. Exits the program with error 1 in case of an error.
+  string GetCurrentOperation();
+
+  void Rollback(bool rollback);
+  string GetRollbackPartition();
+  void CheckForUpdates(const string& app_version,
+                       const string& omaha_url,
+                       bool interactive);
+
+  // Reboot the device if a reboot is needed.
+  // Blocking call. Ignores failures.
+  void RebootIfNeeded();
+
+  // Getter and setter for the target channel. If |get_current_channel| is true,
+  // the current channel instead of the target channel will be returned.
+  // Blocking call. Exits the program with error 1 in case of an error.
+  void SetTargetChannel(const string& target_channel, bool allow_powerwash);
+  string GetChannel(bool get_current_channel);
+
+  // Getter and setter for the updates over cellular connections.
+  // Blocking call. Exits the program with error 1 in case of an error.
+  void SetUpdateOverCellularPermission(bool allowed);
+  bool GetUpdateOverCellularPermission();
+
+  // Getter and setter for the updates from P2P permission.
+  // Blocking call. Exits the program with error 1 in case of an error.
+  void SetP2PUpdatePermission(bool enabled);
+  bool GetP2PUpdatePermission();
+
+  // This is similar to watching for updates but rather than registering
+  // a signal watch, actively poll the daemon just in case it stops
+  // sending notifications.
+  void WaitForUpdateComplete();
+  void OnUpdateCompleteCheck(int64_t last_checked_time,
+                             double progress,
+                             const string& current_operation,
+                             const string& new_version,
+                             int64_t new_size);
+
+  // Blocking call. Exits the program with error 1 in case of an error.
+  void ShowPrevVersion();
+
+  // Returns whether the current status is such that a reboot is needed.
+  // Blocking call. Exits the program with error 1 in case of an error.
+  bool GetIsRebootNeeded();
+
+  // Blocks until a reboot is needed. If the reboot is needed, exits the program
+  // with 0. Otherwise it exits the program with 1 if an error occurs before
+  // the reboot is needed.
+  void WaitForRebootNeeded();
+  void OnRebootNeededCheck(int64_t last_checked_time,
+                           double progress,
+                           const string& current_operation,
+                           const string& new_version,
+                           int64_t new_size);
+  // Callback called when the OnRebootNeededCheck() handler is registered. This
+  // is useful to check at this point if the reboot is needed, without loosing
+  // any StatusUpdate signals and avoiding the race condition.
+  void OnRebootNeededCheckRegistration(const string& interface,
+                                       const string& signal_name,
+                                       bool success);
+
+  // Main method that parses and triggers all the actions based on the passed
+  // flags.
+  int ProcessFlags();
+
+  // DBus Proxy to the update_engine daemon object used for all the calls.
+  std::unique_ptr<org::chromium::UpdateEngineInterfaceProxy> proxy_;
+
+  // Copy of argc and argv passed to main().
+  int argc_;
+  char** argv_;
+
+  // Tell whether the UpdateEngine service is available after startup.
+  bool service_is_available_{false};
+
+  DISALLOW_COPY_AND_ASSIGN(UpdateEngineClient);
+};
+
+bool UpdateEngineClient::InitProxy() {
+  proxy_.reset(new org::chromium::UpdateEngineInterfaceProxy(bus_));
+
+  if (!proxy_->GetObjectProxy()) {
+    LOG(ERROR) << "Error getting dbus proxy for " << kUpdateEngineServiceName;
+    return false;
+  }
+  return true;
+}
+
+void UpdateEngineClient::OnServiceAvailable(bool service_is_available) {
+  service_is_available_ = service_is_available;
+  if (!service_is_available) {
+    LOG(ERROR) << "UpdateEngineService not available.";
+    QuitWithExitCode(-1);
+  }
+  int ret = ProcessFlags();
+  if (ret != kContinueRunning)
+    QuitWithExitCode(ret);
+}
+
+void UpdateEngineClient::OnServiceAvailableTimeout() {
+  if (!service_is_available_) {
+    LOG(ERROR) << "Waiting for UpdateEngineService timeout. Is update_engine "
+                  "daemon running?";
+    QuitWithExitCode(-1);
+  }
+}
+
+void UpdateEngineClient::OnStatusUpdateSignal(
+    int64_t last_checked_time,
+    double progress,
+    const string& 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: " << current_operation;
+  LOG(INFO) << "  new_version: " << new_version;
+  LOG(INFO) << "  new_size: " << new_size;
+}
+
+void UpdateEngineClient::OnStatusUpdateSignalRegistration(
+    const string& interface,
+    const string& signal_name,
+    bool success) {
+  VLOG(1) << "OnStatusUpdateSignalRegistration(" << interface << ", "
+          << signal_name << ", " << success << ");";
+  if (!success) {
+    LOG(ERROR) << "Couldn't connect to the " << signal_name << " signal.";
+    exit(1);
+  }
+}
+
+void UpdateEngineClient::WatchForUpdates() {
+  proxy_->RegisterStatusUpdateSignalHandler(
+      base::Bind(&UpdateEngineClient::OnStatusUpdateSignal,
+                 base::Unretained(this)),
+      base::Bind(&UpdateEngineClient::OnStatusUpdateSignalRegistration,
+                 base::Unretained(this)));
+}
+
+void UpdateEngineClient::ResetStatus() {
+  bool ret = proxy_->ResetStatus(nullptr);
+  CHECK(ret) << "ResetStatus() failed.";
+}
+
+void UpdateEngineClient::ShowStatus() {
+  int64_t last_checked_time = 0;
+  double progress = 0.0;
+  string current_op;
+  string new_version;
+  int64_t new_size = 0;
+
+  bool ret = proxy_->GetStatus(
+      &last_checked_time, &progress, &current_op, &new_version, &new_size,
+      nullptr);
+  CHECK(ret) << "GetStatus() failed";
+  printf("LAST_CHECKED_TIME=%" PRIi64 "\nPROGRESS=%f\nCURRENT_OP=%s\n"
+         "NEW_VERSION=%s\nNEW_SIZE=%" PRIi64 "\n",
+         last_checked_time,
+         progress,
+         current_op.c_str(),
+         new_version.c_str(),
+         new_size);
+}
+
+string UpdateEngineClient::GetCurrentOperation() {
+  int64_t last_checked_time = 0;
+  double progress = 0.0;
+  string current_op;
+  string new_version;
+  int64_t new_size = 0;
+
+  bool ret = proxy_->GetStatus(
+      &last_checked_time, &progress, &current_op, &new_version, &new_size,
+      nullptr);
+  CHECK(ret) << "GetStatus() failed";
+  return current_op;
+}
+
+void UpdateEngineClient::Rollback(bool rollback) {
+  bool ret = proxy_->AttemptRollback(rollback, nullptr);
+  CHECK(ret) << "Rollback request failed.";
+}
+
+string UpdateEngineClient::GetRollbackPartition() {
+  string rollback_partition;
+  bool ret = proxy_->GetRollbackPartition(&rollback_partition, nullptr);
+  CHECK(ret) << "Error while querying rollback partition availabilty.";
+  return rollback_partition;
+}
+
+void UpdateEngineClient::CheckForUpdates(const string& app_version,
+                                         const string& omaha_url,
+                                         bool interactive) {
+  int32_t flags = interactive ? 0 : kAttemptUpdateFlagNonInteractive;
+  bool ret = proxy_->AttemptUpdateWithFlags(app_version, omaha_url, flags,
+                                            nullptr);
+  CHECK(ret) << "Error checking for update.";
+}
+
+void UpdateEngineClient::RebootIfNeeded() {
+  bool ret = proxy_->RebootIfNeeded(nullptr);
+  if (!ret) {
+    // 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.";
+  }
+}
+
+void UpdateEngineClient::SetTargetChannel(const string& target_channel,
+                                          bool allow_powerwash) {
+  bool ret = proxy_->SetChannel(target_channel, allow_powerwash, nullptr);
+  CHECK(ret) << "Error setting the channel.";
+  LOG(INFO) << "Channel permanently set to: " << target_channel;
+}
+
+string UpdateEngineClient::GetChannel(bool get_current_channel) {
+  string channel;
+  bool ret = proxy_->GetChannel(get_current_channel, &channel, nullptr);
+  CHECK(ret) << "Error getting the channel.";
+  return channel;
+}
+
+void UpdateEngineClient::SetUpdateOverCellularPermission(bool allowed) {
+  bool ret = proxy_->SetUpdateOverCellularPermission(allowed, nullptr);
+  CHECK(ret) << "Error setting the update over cellular setting.";
+}
+
+bool UpdateEngineClient::GetUpdateOverCellularPermission() {
+  bool allowed;
+  bool ret = proxy_->GetUpdateOverCellularPermission(&allowed, nullptr);
+  CHECK(ret) << "Error getting the update over cellular setting.";
+  return allowed;
+}
+
+void UpdateEngineClient::SetP2PUpdatePermission(bool enabled) {
+  bool ret = proxy_->SetP2PUpdatePermission(enabled, nullptr);
+  CHECK(ret) << "Error setting the peer-to-peer update setting.";
+}
+
+bool UpdateEngineClient::GetP2PUpdatePermission() {
+  bool enabled;
+  bool ret = proxy_->GetP2PUpdatePermission(&enabled, nullptr);
+  CHECK(ret) << "Error getting the peer-to-peer update setting.";
+  return enabled;
+}
+
+void UpdateEngineClient::OnUpdateCompleteCheck(
+    int64_t /* last_checked_time */,
+    double /* progress */,
+    const string& current_operation,
+    const string& /* new_version */,
+    int64_t /* new_size */) {
+  if (current_operation == update_engine::kUpdateStatusIdle) {
+    LOG(ERROR) << "Update failed, current operations is " << current_operation;
+    exit(1);
+  }
+  if (current_operation == update_engine::kUpdateStatusUpdatedNeedReboot) {
+    LOG(INFO) << "Update succeeded -- reboot needed.";
+    exit(0);
+  }
+}
+
+void UpdateEngineClient::WaitForUpdateComplete() {
+  proxy_->RegisterStatusUpdateSignalHandler(
+      base::Bind(&UpdateEngineClient::OnUpdateCompleteCheck,
+                 base::Unretained(this)),
+      base::Bind(&UpdateEngineClient::OnStatusUpdateSignalRegistration,
+                 base::Unretained(this)));
+}
+
+void UpdateEngineClient::ShowPrevVersion() {
+  string prev_version = nullptr;
+
+  bool ret = proxy_->GetPrevVersion(&prev_version, nullptr);;
+  if (!ret) {
+    LOG(ERROR) << "Error getting previous version.";
+  } else {
+    LOG(INFO) << "Previous version = " << prev_version;
+  }
+}
+
+bool UpdateEngineClient::GetIsRebootNeeded() {
+  return GetCurrentOperation() == update_engine::kUpdateStatusUpdatedNeedReboot;
+}
+
+void UpdateEngineClient::OnRebootNeededCheck(
+    int64_t /* last_checked_time */,
+    double /* progress */,
+    const string& current_operation,
+    const string& /* new_version */,
+    int64_t /* new_size */) {
+  if (current_operation == update_engine::kUpdateStatusUpdatedNeedReboot) {
+    LOG(INFO) << "Reboot needed.";
+    exit(0);
+  }
+}
+
+void UpdateEngineClient::OnRebootNeededCheckRegistration(
+    const string& interface,
+    const string& signal_name,
+    bool success) {
+  if (GetIsRebootNeeded())
+    exit(0);
+  if (!success) {
+    LOG(ERROR) << "Couldn't connect to the " << signal_name << " signal.";
+    exit(1);
+  }
+}
+
+// Blocks until a reboot is needed. Returns true if waiting succeeded,
+// false if an error occurred.
+void UpdateEngineClient::WaitForRebootNeeded() {
+  proxy_->RegisterStatusUpdateSignalHandler(
+      base::Bind(&UpdateEngineClient::OnUpdateCompleteCheck,
+                 base::Unretained(this)),
+      base::Bind(&UpdateEngineClient::OnStatusUpdateSignalRegistration,
+                 base::Unretained(this)));
+  if (GetIsRebootNeeded())
+    exit(0);
+}
+
+int UpdateEngineClient::ProcessFlags() {
+  DEFINE_string(app_version, "", "Force the current app version.");
+  DEFINE_string(channel, "",
+                "Set the target channel. The device will be powerwashed if the "
+                "target channel is more stable than the current channel unless "
+                "--nopowerwash is specified.");
+  DEFINE_bool(check_for_update, false, "Initiate check for updates.");
+  DEFINE_bool(follow, false, "Wait for any update operations to complete."
+              "Exit status is 0 if the update succeeded, and 1 otherwise.");
+  DEFINE_bool(interactive, true, "Mark the update request as interactive.");
+  DEFINE_string(omaha_url, "", "The URL of the Omaha update server.");
+  DEFINE_string(p2p_update, "",
+                "Enables (\"yes\") or disables (\"no\") the peer-to-peer update"
+                " sharing.");
+  DEFINE_bool(powerwash, true, "When performing rollback or channel change, "
+              "do a powerwash or allow it respectively.");
+  DEFINE_bool(reboot, false, "Initiate a reboot if needed.");
+  DEFINE_bool(is_reboot_needed, false, "Exit status 0 if reboot is needed, "
+              "2 if reboot is not needed or 1 if an error occurred.");
+  DEFINE_bool(block_until_reboot_is_needed, false, "Blocks until reboot is "
+              "needed. Returns non-zero exit status if an error occurred.");
+  DEFINE_bool(reset_status, false, "Sets the status in update_engine to idle.");
+  DEFINE_bool(rollback, false,
+              "Perform a rollback to the previous partition. The device will "
+              "be powerwashed unless --nopowerwash is specified.");
+  DEFINE_bool(can_rollback, false, "Shows whether rollback partition "
+              "is available.");
+  DEFINE_bool(show_channel, false, "Show the current and target channels.");
+  DEFINE_bool(show_p2p_update, false,
+              "Show the current setting for peer-to-peer update sharing.");
+  DEFINE_bool(show_update_over_cellular, false,
+              "Show the current setting for updates over cellular networks.");
+  DEFINE_bool(status, false, "Print the status to stdout.");
+  DEFINE_bool(update, false, "Forces an update and waits for it to complete. "
+              "Implies --follow.");
+  DEFINE_string(update_over_cellular, "",
+                "Enables (\"yes\") or disables (\"no\") the updates over "
+                "cellular networks.");
+  DEFINE_bool(watch_for_updates, false,
+              "Listen for status updates and print them to the screen.");
+  DEFINE_bool(prev_version, false,
+              "Show the previous OS version used before the update reboot.");
+
+  // Boilerplate init commands.
+  base::CommandLine::Init(argc_, argv_);
+  brillo::FlagHelper::Init(argc_, argv_, "Chromium OS Update Engine Client");
+
+  // Ensure there are no positional arguments.
+  const std::vector<string> positional_args =
+      base::CommandLine::ForCurrentProcess()->GetArgs();
+  if (!positional_args.empty()) {
+    LOG(ERROR) << "Found a positional argument '" << positional_args.front()
+               << "'. If you want to pass a value to a flag, pass it as "
+                  "--flag=value.";
+    return 1;
+  }
+
+  // Update the status if requested.
+  if (FLAGS_reset_status) {
+    LOG(INFO) << "Setting Update Engine status to idle ...";
+    ResetStatus();
+    LOG(INFO) << "ResetStatus succeeded; to undo partition table changes run:\n"
+                 "(D=$(rootdev -d) P=$(rootdev -s); cgpt p -i$(($(echo ${P#$D} "
+                 "| sed 's/^[^0-9]*//')-1)) $D;)";
+  }
+
+  // Changes the current update over cellular network setting.
+  if (!FLAGS_update_over_cellular.empty()) {
+    bool allowed = FLAGS_update_over_cellular == "yes";
+    if (!allowed && FLAGS_update_over_cellular != "no") {
+      LOG(ERROR) << "Unknown option: \"" << FLAGS_update_over_cellular
+                 << "\". Please specify \"yes\" or \"no\".";
+    } else {
+      SetUpdateOverCellularPermission(allowed);
+    }
+  }
+
+  // Show the current update over cellular network setting.
+  if (FLAGS_show_update_over_cellular) {
+    bool allowed = GetUpdateOverCellularPermission();
+    LOG(INFO) << "Current update over cellular network setting: "
+              << (allowed ? "ENABLED" : "DISABLED");
+  }
+
+  if (!FLAGS_powerwash && !FLAGS_rollback && FLAGS_channel.empty()) {
+    LOG(ERROR) << "powerwash flag only makes sense rollback or channel change";
+    return 1;
+  }
+
+  // Change the P2P enabled setting.
+  if (!FLAGS_p2p_update.empty()) {
+    bool enabled = FLAGS_p2p_update == "yes";
+    if (!enabled && FLAGS_p2p_update != "no") {
+      LOG(ERROR) << "Unknown option: \"" << FLAGS_p2p_update
+                 << "\". Please specify \"yes\" or \"no\".";
+    } else {
+      SetP2PUpdatePermission(enabled);
+    }
+  }
+
+  // Show the rollback availability.
+  if (FLAGS_can_rollback) {
+    string rollback_partition = GetRollbackPartition();
+    bool can_rollback = true;
+    if (rollback_partition.empty()) {
+      rollback_partition = "UNAVAILABLE";
+      can_rollback = false;
+    } else {
+      rollback_partition = "AVAILABLE: " + rollback_partition;
+    }
+
+    LOG(INFO) << "Rollback partition: " << rollback_partition;
+    if (!can_rollback) {
+      return 1;
+    }
+  }
+
+  // Show the current P2P enabled setting.
+  if (FLAGS_show_p2p_update) {
+    bool enabled = GetP2PUpdatePermission();
+    LOG(INFO) << "Current update using P2P setting: "
+              << (enabled ? "ENABLED" : "DISABLED");
+  }
+
+  // First, update the target channel if requested.
+  if (!FLAGS_channel.empty())
+    SetTargetChannel(FLAGS_channel, FLAGS_powerwash);
+
+  // Show the current and target channels if requested.
+  if (FLAGS_show_channel) {
+    string current_channel = GetChannel(true);
+    LOG(INFO) << "Current Channel: " << current_channel;
+
+    string target_channel = GetChannel(false);
+    if (!target_channel.empty())
+      LOG(INFO) << "Target Channel (pending update): " << target_channel;
+  }
+
+  bool do_update_request = FLAGS_check_for_update | FLAGS_update |
+      !FLAGS_app_version.empty() | !FLAGS_omaha_url.empty();
+  if (FLAGS_update)
+    FLAGS_follow = true;
+
+  if (do_update_request && FLAGS_rollback) {
+    LOG(ERROR) << "Incompatible flags specified with rollback."
+               << "Rollback should not include update-related flags.";
+    return 1;
+  }
+
+  if (FLAGS_rollback) {
+    LOG(INFO) << "Requesting rollback.";
+    Rollback(FLAGS_powerwash);
+  }
+
+  // Initiate an update check, if necessary.
+  if (do_update_request) {
+    LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored.";
+    string app_version = FLAGS_app_version;
+    if (FLAGS_update && app_version.empty()) {
+      app_version = "ForcedUpdate";
+      LOG(INFO) << "Forcing an update by setting app_version to ForcedUpdate.";
+    }
+    LOG(INFO) << "Initiating update check and install.";
+    CheckForUpdates(app_version, FLAGS_omaha_url, FLAGS_interactive);
+  }
+
+  // These final options are all mutually exclusive with one another.
+  if (FLAGS_follow + FLAGS_watch_for_updates + FLAGS_reboot +
+      FLAGS_status + FLAGS_is_reboot_needed +
+      FLAGS_block_until_reboot_is_needed > 1) {
+    LOG(ERROR) << "Multiple exclusive options selected. "
+               << "Select only one of --follow, --watch_for_updates, --reboot, "
+               << "--is_reboot_needed, --block_until_reboot_is_needed, "
+               << "or --status.";
+    return 1;
+  }
+
+  if (FLAGS_status) {
+    LOG(INFO) << "Querying Update Engine status...";
+    ShowStatus();
+    return 0;
+  }
+
+  if (FLAGS_follow) {
+    LOG(INFO) << "Waiting for update to complete.";
+    WaitForUpdateComplete();
+    return kContinueRunning;
+  }
+
+  if (FLAGS_watch_for_updates) {
+    LOG(INFO) << "Watching for status updates.";
+    WatchForUpdates();
+    return kContinueRunning;
+  }
+
+  if (FLAGS_reboot) {
+    LOG(INFO) << "Requesting a reboot...";
+    RebootIfNeeded();
+    return 0;
+  }
+
+  if (FLAGS_prev_version) {
+    ShowPrevVersion();
+  }
+
+  if (FLAGS_is_reboot_needed) {
+    // In case of error GetIsRebootNeeded() will exit with 1.
+    if (GetIsRebootNeeded()) {
+      return 0;
+    } else {
+      return 2;
+    }
+  }
+
+  if (FLAGS_block_until_reboot_is_needed) {
+    WaitForRebootNeeded();
+    return kContinueRunning;
+  }
+
+  return 0;
+}
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  UpdateEngineClient client(argc, argv);
+  return client.Run();
+}
diff --git a/update_manager/boxed_value.cc b/update_manager/boxed_value.cc
new file mode 100644
index 0000000..a4aeede
--- /dev/null
+++ b/update_manager/boxed_value.cc
@@ -0,0 +1,194 @@
+//
+// 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/update_manager/boxed_value.h"
+
+#include <stdint.h>
+
+#include <set>
+#include <string>
+
+#include <base/strings/string_number_conversions.h>
+#include <base/time/time.h>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/update_manager/shill_provider.h"
+#include "update_engine/update_manager/updater_provider.h"
+
+using std::set;
+using std::string;
+
+namespace chromeos_update_manager {
+
+// Template instantiation for common types; used in BoxedValue::ToString().
+// Keep in sync with boxed_value_unitttest.cc.
+
+template<>
+string BoxedValue::ValuePrinter<string>(const void* value) {
+  const string* val = reinterpret_cast<const string*>(value);
+  return *val;
+}
+
+template<>
+string BoxedValue::ValuePrinter<int>(const void* value) {
+  const int* val = reinterpret_cast<const int*>(value);
+  return base::IntToString(*val);
+}
+
+template<>
+string BoxedValue::ValuePrinter<unsigned int>(const void* value) {
+  const unsigned int* val = reinterpret_cast<const unsigned int*>(value);
+  return base::UintToString(*val);
+}
+
+template<>
+string BoxedValue::ValuePrinter<int64_t>(const void* value) {
+  const int64_t* val = reinterpret_cast<const int64_t*>(value);
+  return base::Int64ToString(*val);
+}
+
+template<>
+string BoxedValue::ValuePrinter<uint64_t>(const void* value) {
+  const uint64_t* val =
+    reinterpret_cast<const uint64_t*>(value);
+  return base::Uint64ToString(static_cast<uint64_t>(*val));
+}
+
+template<>
+string BoxedValue::ValuePrinter<bool>(const void* value) {
+  const bool* val = reinterpret_cast<const bool*>(value);
+  return *val ? "true" : "false";
+}
+
+template<>
+string BoxedValue::ValuePrinter<double>(const void* value) {
+  const double* val = reinterpret_cast<const double*>(value);
+  return base::DoubleToString(*val);
+}
+
+template<>
+string BoxedValue::ValuePrinter<base::Time>(const void* value) {
+  const base::Time* val = reinterpret_cast<const base::Time*>(value);
+  return chromeos_update_engine::utils::ToString(*val);
+}
+
+template<>
+string BoxedValue::ValuePrinter<base::TimeDelta>(const void* value) {
+  const base::TimeDelta* val = reinterpret_cast<const base::TimeDelta*>(value);
+  return chromeos_update_engine::utils::FormatTimeDelta(*val);
+}
+
+static string ConnectionTypeToString(ConnectionType type) {
+  switch (type) {
+    case ConnectionType::kEthernet:
+      return "Ethernet";
+    case ConnectionType::kWifi:
+      return "Wifi";
+    case ConnectionType::kWimax:
+      return "Wimax";
+    case ConnectionType::kBluetooth:
+      return "Bluetooth";
+    case ConnectionType::kCellular:
+      return "Cellular";
+    case ConnectionType::kUnknown:
+      return "Unknown";
+  }
+  NOTREACHED();
+  return "Unknown";
+}
+
+template<>
+string BoxedValue::ValuePrinter<ConnectionType>(const void* value) {
+  const ConnectionType* val = reinterpret_cast<const ConnectionType*>(value);
+  return ConnectionTypeToString(*val);
+}
+
+template<>
+string BoxedValue::ValuePrinter<set<ConnectionType>>(const void* value) {
+  string ret = "";
+  const set<ConnectionType>* val =
+      reinterpret_cast<const set<ConnectionType>*>(value);
+  for (auto& it : *val) {
+    ConnectionType type = it;
+    if (ret.size() > 0)
+      ret += ",";
+    ret += ConnectionTypeToString(type);
+  }
+  return ret;
+}
+
+template<>
+string BoxedValue::ValuePrinter<ConnectionTethering>(const void* value) {
+  const ConnectionTethering* val =
+      reinterpret_cast<const ConnectionTethering*>(value);
+  switch (*val) {
+    case ConnectionTethering::kNotDetected:
+      return "Not Detected";
+    case ConnectionTethering::kSuspected:
+      return "Suspected";
+    case ConnectionTethering::kConfirmed:
+      return "Confirmed";
+    case ConnectionTethering::kUnknown:
+      return "Unknown";
+  }
+  NOTREACHED();
+  return "Unknown";
+}
+
+template<>
+string BoxedValue::ValuePrinter<Stage>(const void* value) {
+  const Stage* val = reinterpret_cast<const Stage*>(value);
+  switch (*val) {
+    case Stage::kIdle:
+      return "Idle";
+    case Stage::kCheckingForUpdate:
+      return "Checking For Update";
+    case Stage::kUpdateAvailable:
+      return "Update Available";
+    case Stage::kDownloading:
+      return "Downloading";
+    case Stage::kVerifying:
+      return "Verifying";
+    case Stage::kFinalizing:
+      return "Finalizing";
+    case Stage::kUpdatedNeedReboot:
+      return "Updated, Need Reboot";
+    case Stage::kReportingErrorEvent:
+      return "Reporting Error Event";
+    case Stage::kAttemptingRollback:
+      return "Attempting Rollback";
+  }
+  NOTREACHED();
+  return "Unknown";
+}
+
+template<>
+string BoxedValue::ValuePrinter<UpdateRequestStatus>(const void* value) {
+  const UpdateRequestStatus* val =
+      reinterpret_cast<const UpdateRequestStatus*>(value);
+  switch (*val) {
+    case UpdateRequestStatus::kNone:
+      return "None";
+    case UpdateRequestStatus::kInteractive:
+      return "Interactive";
+    case UpdateRequestStatus::kPeriodic:
+      return "Periodic";
+  }
+  NOTREACHED();
+  return "Unknown";
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/boxed_value.h b/update_manager/boxed_value.h
new file mode 100644
index 0000000..5f41835
--- /dev/null
+++ b/update_manager/boxed_value.h
@@ -0,0 +1,124 @@
+//
+// 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_UPDATE_MANAGER_BOXED_VALUE_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_BOXED_VALUE_H_
+
+#include <memory>
+#include <string>
+
+#include <base/macros.h>
+
+namespace chromeos_update_manager {
+
+// BoxedValue is a class to hold pointers of a given type that deletes them when
+// the instance goes out of scope, as std::unique_ptr<T> does. The main
+// difference with it is that the type T is not part of the class, i.e., this
+// isn't a parametric class. The class has a parametric constructor that accepts
+// a const T* which will define the type of the object passed on delete.
+//
+// It is safe to use this class in linked containers such as std::list and
+// std::map but the object can't be copied. This means that you need to
+// construct the BoxedValue in place using a container method like emplace()
+// or move it with std::move().
+//
+//   list<BoxedValue> lst;
+//   lst.emplace_back(new const int(42));
+//   lst.emplace_back(new const string("Hello world!"));
+//
+//   map<int, BoxedValue> m;
+//   m.emplace(123, std::move(BoxedValue(new const string("Hola mundo!"))));
+//
+//   auto it = m.find(42);
+//   if (it != m.end())
+//     cout << "m[42] points to " << it->second.value() << endl;
+//   cout << "m[33] points to " << m[33].value() << endl;
+//
+// Since copy and assign are not allowed, you can't create a copy of the
+// BoxedValue which means that you can only use a reference to it.
+//
+
+class BoxedValue {
+ public:
+  // Creates an empty BoxedValue. Since the pointer can't be assigned from other
+  // BoxedValues or pointers, this is only useful in places where a default
+  // constructor is required, such as std::map::operator[].
+  BoxedValue() : value_(nullptr), deleter_(nullptr), printer_(nullptr) {}
+
+  // Creates a BoxedValue for the passed pointer |value|. The BoxedValue keeps
+  // the ownership of this pointer and can't be released.
+  template<typename T>
+  explicit BoxedValue(const T* value)
+    : value_(static_cast<const void*>(value)), deleter_(ValueDeleter<T>),
+      printer_(ValuePrinter<T>) {}
+
+  // The move constructor takes ownership of the pointer since the semantics of
+  // it allows to render the passed BoxedValue undefined. You need to use the
+  // move constructor explicitly preventing it from accidental references,
+  // like in:
+  //   BoxedValue new_box(std::move(other_box));
+  BoxedValue(BoxedValue&& other)  // NOLINT(build/c++11)
+      : value_(other.value_), deleter_(other.deleter_),
+        printer_(other.printer_) {
+    other.value_ = nullptr;
+    other.deleter_ = nullptr;
+    other.printer_ = nullptr;
+  }
+
+  // Deletes the |value| passed on construction using the delete for the passed
+  // type.
+  ~BoxedValue() {
+    if (deleter_)
+      deleter_(value_);
+  }
+
+  const void* value() const { return value_; }
+
+  std::string ToString() const {
+    if (!printer_)
+      return "(no printer)";
+    if (!value_)
+      return "(no value)";
+    return printer_(value_);
+  }
+
+  // Static method to call the destructor of the right type.
+  template<typename T>
+  static void ValueDeleter(const void* value) {
+    delete reinterpret_cast<const T*>(value);
+  }
+
+  // Static method to print a type. See boxed_value.cc for common
+  // instantiations.
+  template<typename T>
+  static std::string ValuePrinter(const void* value);
+
+ private:
+  // A pointer to the cached value.
+  const void* value_;
+
+  // A function that calls delete for the right type of value_.
+  void (*deleter_)(const void*);
+
+  // A function that converts value_ to a string.
+  std::string (*printer_)(const void*);
+
+  DISALLOW_COPY_AND_ASSIGN(BoxedValue);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_BOXED_VALUE_H_
diff --git a/update_manager/boxed_value_unittest.cc b/update_manager/boxed_value_unittest.cc
new file mode 100644
index 0000000..47bfd8f
--- /dev/null
+++ b/update_manager/boxed_value_unittest.cc
@@ -0,0 +1,232 @@
+//
+// 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/update_manager/boxed_value.h"
+
+#include <gtest/gtest.h>
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+
+#include <base/strings/stringprintf.h>
+#include <base/time/time.h>
+
+#include "update_engine/update_manager/shill_provider.h"
+#include "update_engine/update_manager/umtest_utils.h"
+#include "update_engine/update_manager/updater_provider.h"
+
+using base::Time;
+using base::TimeDelta;
+using std::list;
+using std::map;
+using std::set;
+using std::string;
+
+namespace chromeos_update_manager {
+
+// The DeleterMarker flags a bool variable when the class is destroyed.
+class DeleterMarker {
+ public:
+  explicit DeleterMarker(bool* marker) : marker_(marker) { *marker_ = false; }
+
+  ~DeleterMarker() { *marker_ = true; }
+
+ private:
+  friend string BoxedValue::ValuePrinter<DeleterMarker>(const void *);
+
+  // Pointer to the bool marker.
+  bool* marker_;
+};
+
+template<>
+string BoxedValue::ValuePrinter<DeleterMarker>(const void *value) {
+  const DeleterMarker* val = reinterpret_cast<const DeleterMarker*>(value);
+  return base::StringPrintf("DeleterMarker:%s",
+                            *val->marker_ ? "true" : "false");
+}
+
+TEST(UmBoxedValueTest, Deleted) {
+  bool marker = true;
+  const DeleterMarker* deleter_marker = new DeleterMarker(&marker);
+
+  EXPECT_FALSE(marker);
+  BoxedValue* box = new BoxedValue(deleter_marker);
+  EXPECT_FALSE(marker);
+  delete box;
+  EXPECT_TRUE(marker);
+}
+
+TEST(UmBoxedValueTest, MoveConstructor) {
+  bool marker = true;
+  const DeleterMarker* deleter_marker = new DeleterMarker(&marker);
+
+  BoxedValue* box = new BoxedValue(deleter_marker);
+  BoxedValue* new_box = new BoxedValue(std::move(*box));
+  // box is now undefined but valid.
+  delete box;
+  EXPECT_FALSE(marker);
+  // The deleter_marker gets deleted at this point.
+  delete new_box;
+  EXPECT_TRUE(marker);
+}
+
+TEST(UmBoxedValueTest, MixedList) {
+  list<BoxedValue> lst;
+  // This is mostly a compile test.
+  lst.emplace_back(new const int{42});
+  lst.emplace_back(new const string("Hello world!"));
+  bool marker;
+  lst.emplace_back(new const DeleterMarker(&marker));
+  EXPECT_FALSE(marker);
+  lst.clear();
+  EXPECT_TRUE(marker);
+}
+
+TEST(UmBoxedValueTest, MixedMap) {
+  map<int, BoxedValue> m;
+  m.emplace(42, BoxedValue(new const string("Hola mundo!")));
+
+  auto it = m.find(42);
+  ASSERT_NE(it, m.end());
+  EXPECT_NE(nullptr, it->second.value());
+  EXPECT_EQ(nullptr, m[33].value());
+}
+
+TEST(UmBoxedValueTest, StringToString) {
+  EXPECT_EQ("Hej Verden!",
+            BoxedValue(new string("Hej Verden!")).ToString());
+}
+
+TEST(UmBoxedValueTest, IntToString) {
+  EXPECT_EQ("42", BoxedValue(new int(42)).ToString());
+}
+
+TEST(UmBoxedValueTest, Int64ToString) {
+  // -123456789012345 doesn't fit in 32-bit integers.
+  EXPECT_EQ("-123456789012345", BoxedValue(
+      new int64_t(-123456789012345LL)).ToString());
+}
+
+TEST(UmBoxedValueTest, UnsignedIntToString) {
+  // 4294967295 is the biggest possible 32-bit unsigned integer.
+  EXPECT_EQ("4294967295",
+            BoxedValue(new unsigned int(4294967295U)).ToString());  // NOLINT
+}
+
+TEST(UmBoxedValueTest, UnsignedInt64ToString) {
+  // 18446744073709551615 is the biggest possible 64-bit unsigned integer.
+  EXPECT_EQ("18446744073709551615", BoxedValue(
+      new uint64_t(18446744073709551615ULL)).ToString());
+}
+
+TEST(UmBoxedValueTest, BoolToString) {
+  EXPECT_EQ("false", BoxedValue(new bool(false)).ToString());
+  EXPECT_EQ("true", BoxedValue(new bool(true)).ToString());
+}
+
+TEST(UmBoxedValueTest, DoubleToString) {
+  EXPECT_EQ("1.501", BoxedValue(new double(1.501)).ToString());
+}
+
+TEST(UmBoxedValueTest, TimeToString) {
+  // Tue Apr 29 22:30:55 UTC 2014 is 1398810655 seconds since the Unix Epoch.
+  EXPECT_EQ("4/29/2014 22:30:55 GMT",
+            BoxedValue(new Time(Time::FromTimeT(1398810655))).ToString());
+}
+
+TEST(UmBoxedValueTest, TimeDeltaToString) {
+  // 12345 seconds is 3 hours, 25 minutes and 45 seconds.
+  EXPECT_EQ("3h25m45s",
+            BoxedValue(new TimeDelta(TimeDelta::FromSeconds(12345)))
+            .ToString());
+}
+
+TEST(UmBoxedValueTest, ConnectionTypeToString) {
+  EXPECT_EQ("Ethernet",
+            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());
+  EXPECT_EQ("Unknown",
+            BoxedValue(new ConnectionType(ConnectionType::kUnknown))
+            .ToString());
+}
+
+TEST(UmBoxedValueTest, ConnectionTetheringToString) {
+  EXPECT_EQ("Not Detected",
+            BoxedValue(new ConnectionTethering(
+                ConnectionTethering::kNotDetected)).ToString());
+  EXPECT_EQ("Suspected",
+            BoxedValue(new ConnectionTethering(ConnectionTethering::kSuspected))
+            .ToString());
+  EXPECT_EQ("Confirmed",
+            BoxedValue(new ConnectionTethering(ConnectionTethering::kConfirmed))
+            .ToString());
+  EXPECT_EQ("Unknown",
+            BoxedValue(new ConnectionTethering(ConnectionTethering::kUnknown))
+            .ToString());
+}
+
+TEST(UmBoxedValueTest, SetConnectionTypeToString) {
+  set<ConnectionType>* set1 = new set<ConnectionType>;
+  set1->insert(ConnectionType::kWimax);
+  set1->insert(ConnectionType::kEthernet);
+  EXPECT_EQ("Ethernet,Wimax", BoxedValue(set1).ToString());
+
+  set<ConnectionType>* set2 = new set<ConnectionType>;
+  set2->insert(ConnectionType::kWifi);
+  EXPECT_EQ("Wifi", BoxedValue(set2).ToString());
+}
+
+TEST(UmBoxedValueTest, StageToString) {
+  EXPECT_EQ("Idle",
+            BoxedValue(new Stage(Stage::kIdle)).ToString());
+  EXPECT_EQ("Checking For Update",
+            BoxedValue(new Stage(Stage::kCheckingForUpdate)).ToString());
+  EXPECT_EQ("Update Available",
+            BoxedValue(new Stage(Stage::kUpdateAvailable)).ToString());
+  EXPECT_EQ("Downloading",
+            BoxedValue(new Stage(Stage::kDownloading)).ToString());
+  EXPECT_EQ("Verifying",
+            BoxedValue(new Stage(Stage::kVerifying)).ToString());
+  EXPECT_EQ("Finalizing",
+            BoxedValue(new Stage(Stage::kFinalizing)).ToString());
+  EXPECT_EQ("Updated, Need Reboot",
+            BoxedValue(new Stage(Stage::kUpdatedNeedReboot)).ToString());
+  EXPECT_EQ("Reporting Error Event",
+            BoxedValue(new Stage(Stage::kReportingErrorEvent)).ToString());
+  EXPECT_EQ("Attempting Rollback",
+            BoxedValue(new Stage(Stage::kAttemptingRollback)).ToString());
+}
+
+TEST(UmBoxedValueTest, DeleterMarkerToString) {
+  bool marker = false;
+  BoxedValue value = BoxedValue(new DeleterMarker(&marker));
+  EXPECT_EQ("DeleterMarker:false", value.ToString());
+  marker = true;
+  EXPECT_EQ("DeleterMarker:true", value.ToString());
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/chromeos_policy.cc b/update_manager/chromeos_policy.cc
new file mode 100644
index 0000000..6ba6f82
--- /dev/null
+++ b/update_manager/chromeos_policy.cc
@@ -0,0 +1,942 @@
+//
+// 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/update_manager/chromeos_policy.h"
+
+#include <algorithm>
+#include <set>
+#include <string>
+
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <base/time/time.h>
+
+#include "update_engine/common/error_code.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/update_manager/device_policy_provider.h"
+#include "update_engine/update_manager/policy_utils.h"
+#include "update_engine/update_manager/shill_provider.h"
+
+using base::Time;
+using base::TimeDelta;
+using chromeos_update_engine::ErrorCode;
+using std::get;
+using std::max;
+using std::min;
+using std::set;
+using std::string;
+
+namespace {
+
+// Examines |err_code| and decides whether the URL index needs to be advanced,
+// the error count for the URL incremented, or none of the above. In the first
+// case, returns true; in the second case, increments |*url_num_error_p| and
+// returns false; otherwise just returns false.
+//
+// TODO(garnold) Adapted from PayloadState::UpdateFailed() (to be retired).
+bool HandleErrorCode(ErrorCode err_code, int* url_num_error_p) {
+  err_code = chromeos_update_engine::utils::GetBaseErrorCode(err_code);
+  switch (err_code) {
+    // Errors which are good indicators of a problem with a particular URL or
+    // the protocol used in the URL or entities in the communication channel
+    // (e.g. proxies). We should try the next available URL in the next update
+    // check to quickly recover from these errors.
+    case ErrorCode::kPayloadHashMismatchError:
+    case ErrorCode::kPayloadSizeMismatchError:
+    case ErrorCode::kDownloadPayloadVerificationError:
+    case ErrorCode::kDownloadPayloadPubKeyVerificationError:
+    case ErrorCode::kSignedDeltaPayloadExpectedError:
+    case ErrorCode::kDownloadInvalidMetadataMagicString:
+    case ErrorCode::kDownloadSignatureMissingInManifest:
+    case ErrorCode::kDownloadManifestParseError:
+    case ErrorCode::kDownloadMetadataSignatureError:
+    case ErrorCode::kDownloadMetadataSignatureVerificationError:
+    case ErrorCode::kDownloadMetadataSignatureMismatch:
+    case ErrorCode::kDownloadOperationHashVerificationError:
+    case ErrorCode::kDownloadOperationExecutionError:
+    case ErrorCode::kDownloadOperationHashMismatch:
+    case ErrorCode::kDownloadInvalidMetadataSize:
+    case ErrorCode::kDownloadInvalidMetadataSignature:
+    case ErrorCode::kDownloadOperationHashMissingError:
+    case ErrorCode::kDownloadMetadataSignatureMissingError:
+    case ErrorCode::kPayloadMismatchedType:
+    case ErrorCode::kUnsupportedMajorPayloadVersion:
+    case ErrorCode::kUnsupportedMinorPayloadVersion:
+      LOG(INFO) << "Advancing download URL due to error "
+                << chromeos_update_engine::utils::CodeToString(err_code)
+                << " (" << static_cast<int>(err_code) << ")";
+      return true;
+
+    // Errors which seem to be just transient network/communication related
+    // failures and do not indicate any inherent problem with the URL itself.
+    // So, we should keep the current URL but just increment the
+    // failure count to give it more chances. This way, while we maximize our
+    // chances of downloading from the URLs that appear earlier in the response
+    // (because download from a local server URL that appears earlier in a
+    // response is preferable than downloading from the next URL which could be
+    // an Internet URL and thus could be more expensive).
+    case ErrorCode::kError:
+    case ErrorCode::kDownloadTransferError:
+    case ErrorCode::kDownloadWriteError:
+    case ErrorCode::kDownloadStateInitializationError:
+    case ErrorCode::kOmahaErrorInHTTPResponse:  // Aggregate for HTTP errors.
+      LOG(INFO) << "Incrementing URL failure count due to error "
+                << chromeos_update_engine::utils::CodeToString(err_code)
+                << " (" << static_cast<int>(err_code) << ")";
+      *url_num_error_p += 1;
+      return false;
+
+    // Errors which are not specific to a URL and hence shouldn't result in
+    // the URL being penalized. This can happen in two cases:
+    // 1. We haven't started downloading anything: These errors don't cost us
+    // anything in terms of actual payload bytes, so we should just do the
+    // regular retries at the next update check.
+    // 2. We have successfully downloaded the payload: In this case, the
+    // payload attempt number would have been incremented and would take care
+    // of the back-off at the next update check.
+    // In either case, there's no need to update URL index or failure count.
+    case ErrorCode::kOmahaRequestError:
+    case ErrorCode::kOmahaResponseHandlerError:
+    case ErrorCode::kPostinstallRunnerError:
+    case ErrorCode::kFilesystemCopierError:
+    case ErrorCode::kInstallDeviceOpenError:
+    case ErrorCode::kKernelDeviceOpenError:
+    case ErrorCode::kDownloadNewPartitionInfoError:
+    case ErrorCode::kNewRootfsVerificationError:
+    case ErrorCode::kNewKernelVerificationError:
+    case ErrorCode::kPostinstallBootedFromFirmwareB:
+    case ErrorCode::kPostinstallFirmwareRONotUpdatable:
+    case ErrorCode::kOmahaRequestEmptyResponseError:
+    case ErrorCode::kOmahaRequestXMLParseError:
+    case ErrorCode::kOmahaResponseInvalid:
+    case ErrorCode::kOmahaUpdateIgnoredPerPolicy:
+    case ErrorCode::kOmahaUpdateDeferredPerPolicy:
+    case ErrorCode::kOmahaUpdateDeferredForBackoff:
+    case ErrorCode::kPostinstallPowerwashError:
+    case ErrorCode::kUpdateCanceledByChannelChange:
+    case ErrorCode::kOmahaRequestXMLHasEntityDecl:
+    case ErrorCode::kFilesystemVerifierError:
+      LOG(INFO) << "Not changing URL index or failure count due to error "
+                << chromeos_update_engine::utils::CodeToString(err_code)
+                << " (" << static_cast<int>(err_code) << ")";
+      return false;
+
+    case ErrorCode::kSuccess:                            // success code
+    case ErrorCode::kUmaReportedMax:                     // not an error code
+    case ErrorCode::kOmahaRequestHTTPResponseBase:       // aggregated already
+    case ErrorCode::kDevModeFlag:                       // not an error code
+    case ErrorCode::kResumedFlag:                        // not an error code
+    case ErrorCode::kTestImageFlag:                      // not an error code
+    case ErrorCode::kTestOmahaUrlFlag:                   // not an error code
+    case ErrorCode::kSpecialFlags:                       // not an error code
+      // These shouldn't happen. Enumerating these  explicitly here so that we
+      // can let the compiler warn about new error codes that are added to
+      // action_processor.h but not added here.
+      LOG(WARNING) << "Unexpected error "
+                   << chromeos_update_engine::utils::CodeToString(err_code)
+                   << " (" << static_cast<int>(err_code) << ")";
+    // Note: Not adding a default here so as to let the compiler warn us of
+    // any new enums that were added in the .h but not listed in this switch.
+  }
+  return false;
+}
+
+// Checks whether |url| can be used under given download restrictions.
+bool IsUrlUsable(const string& url, bool http_allowed) {
+  return http_allowed || !base::StartsWithASCII(url, "http://", false);
+}
+
+}  // namespace
+
+namespace chromeos_update_manager {
+
+const int ChromeOSPolicy::kTimeoutInitialInterval =  7 * 60;
+
+// TODO(deymo): Split the update_manager policies for Brillo and ChromeOS and
+// make the update check periodic interval configurable.
+#ifdef __ANDROID__
+const int ChromeOSPolicy::kTimeoutPeriodicInterval = 5 * 60 * 60;
+const int ChromeOSPolicy::kTimeoutMaxBackoffInterval = 26 * 60 * 60;
+#else
+const int ChromeOSPolicy::kTimeoutPeriodicInterval = 45 * 60;
+const int ChromeOSPolicy::kTimeoutMaxBackoffInterval = 4 * 60 * 60;
+#endif  // __ANDROID__
+
+const int ChromeOSPolicy::kTimeoutRegularFuzz = 10 * 60;
+const int ChromeOSPolicy::kAttemptBackoffMaxIntervalInDays = 16;
+const int ChromeOSPolicy::kAttemptBackoffFuzzInHours = 12;
+const int ChromeOSPolicy::kMaxP2PAttempts = 10;
+const int ChromeOSPolicy::kMaxP2PAttemptsPeriodInSeconds = 5 * 24 * 60 * 60;
+
+EvalStatus ChromeOSPolicy::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->is_interactive = false;
+
+  DevicePolicyProvider* const dp_provider = state->device_policy_provider();
+  UpdaterProvider* const updater_provider = state->updater_provider();
+  SystemProvider* const system_provider = state->system_provider();
+
+  // Do not perform any updates if booted from removable device. This decision
+  // is final.
+  const unsigned int* num_slots_p = ec->GetValue(
+      system_provider->var_num_slots());
+  if (!num_slots_p || *num_slots_p < 2) {
+    LOG(INFO) << "Not enough slots for A/B updates, disabling update checks.";
+    result->updates_enabled = false;
+    return EvalStatus::kSucceeded;
+  }
+
+  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) {
+    // Check whether updates are disabled by policy.
+    const bool* update_disabled_p = ec->GetValue(
+        dp_provider->var_update_disabled());
+    if (update_disabled_p && *update_disabled_p) {
+      LOG(INFO) << "Updates disabled by policy, blocking update checks.";
+      return EvalStatus::kAskMeAgainLater;
+    }
+
+    // Determine whether a target version prefix is dictated by policy.
+    const string* target_version_prefix_p = ec->GetValue(
+        dp_provider->var_target_version_prefix());
+    if (target_version_prefix_p)
+      result->target_version_prefix = *target_version_prefix_p;
+
+    // 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)
+        result->target_channel = *release_channel_p;
+    }
+  }
+
+  // First, check to see if an interactive update was requested.
+  const UpdateRequestStatus* forced_update_requested_p = ec->GetValue(
+      updater_provider->var_forced_update_requested());
+  if (forced_update_requested_p &&
+      *forced_update_requested_p != UpdateRequestStatus::kNone) {
+    result->is_interactive =
+        (*forced_update_requested_p == UpdateRequestStatus::kInteractive);
+    LOG(INFO) << "Forced update signaled ("
+              << (result->is_interactive ?  "interactive" : "periodic")
+              << "), allowing update check.";
+    return EvalStatus::kSucceeded;
+  }
+
+  // The logic thereafter applies to periodic updates. Bear in mind that we
+  // should not return a final "no" if any of these criteria are not satisfied,
+  // because the system may still update due to an interactive update request.
+
+  // Unofficial builds should not perform periodic update checks.
+  const bool* is_official_build_p = ec->GetValue(
+      system_provider->var_is_official_build());
+  if (is_official_build_p && !(*is_official_build_p)) {
+    LOG(INFO) << "Unofficial build, blocking periodic update checks.";
+    return EvalStatus::kAskMeAgainLater;
+  }
+
+  // If OOBE is enabled, wait until it is completed.
+  const bool* is_oobe_enabled_p = ec->GetValue(
+      state->config_provider()->var_is_oobe_enabled());
+  if (is_oobe_enabled_p && *is_oobe_enabled_p) {
+    const bool* is_oobe_complete_p = ec->GetValue(
+        system_provider->var_is_oobe_complete());
+    if (is_oobe_complete_p && !(*is_oobe_complete_p)) {
+      LOG(INFO) << "OOBE not completed, blocking update checks.";
+      return EvalStatus::kAskMeAgainLater;
+    }
+  }
+
+  // Ensure that periodic update checks are timed properly.
+  Time next_update_check;
+  if (NextUpdateCheckTime(ec, state, error, &next_update_check) !=
+      EvalStatus::kSucceeded) {
+    return EvalStatus::kFailed;
+  }
+  if (!ec->IsWallclockTimeGreaterThan(next_update_check)) {
+    LOG(INFO) << "Periodic check interval not satisfied, blocking until "
+              << chromeos_update_engine::utils::ToString(next_update_check);
+    return EvalStatus::kAskMeAgainLater;
+  }
+
+  // It is time to check for an update.
+  LOG(INFO) << "Allowing update check.";
+  return EvalStatus::kSucceeded;
+}
+
+EvalStatus ChromeOSPolicy::UpdateCanStart(
+    EvaluationContext* ec,
+    State* state,
+    string* error,
+    UpdateDownloadParams* result,
+    const UpdateState update_state) const {
+  // Set the default return values. Note that we set persisted values (backoff,
+  // scattering) to the same values presented in the update state. The reason is
+  // that preemptive returns, such as the case where an update check is due,
+  // should not clear off the said values; rather, it is the deliberate
+  // inference of new values that should cause them to be reset.
+  result->update_can_start = false;
+  result->cannot_start_reason = UpdateCannotStartReason::kUndefined;
+  result->download_url_idx = -1;
+  result->download_url_allowed = true;
+  result->download_url_num_errors = 0;
+  result->p2p_downloading_allowed = false;
+  result->p2p_sharing_allowed = false;
+  result->do_increment_failures = false;
+  result->backoff_expiry = update_state.backoff_expiry;
+  result->scatter_wait_period = update_state.scatter_wait_period;
+  result->scatter_check_threshold = update_state.scatter_check_threshold;
+
+  // Make sure that we're not due for an update check.
+  UpdateCheckParams check_result;
+  EvalStatus check_status = UpdateCheckAllowed(ec, state, error, &check_result);
+  if (check_status == EvalStatus::kFailed)
+    return EvalStatus::kFailed;
+  bool is_check_due = (check_status == EvalStatus::kSucceeded &&
+                       check_result.updates_enabled == true);
+
+  // Check whether backoff applies, and if not then which URL can be used for
+  // downloading. These require scanning the download error log, and so they are
+  // done together.
+  UpdateBackoffAndDownloadUrlResult backoff_url_result;
+  EvalStatus backoff_url_status = UpdateBackoffAndDownloadUrl(
+      ec, state, error, &backoff_url_result, update_state);
+  if (backoff_url_status == EvalStatus::kFailed)
+    return EvalStatus::kFailed;
+  result->download_url_idx = backoff_url_result.url_idx;
+  result->download_url_num_errors = backoff_url_result.url_num_errors;
+  result->do_increment_failures = backoff_url_result.do_increment_failures;
+  result->backoff_expiry = backoff_url_result.backoff_expiry;
+  bool is_backoff_active =
+      (backoff_url_status == EvalStatus::kAskMeAgainLater) ||
+      !backoff_url_result.backoff_expiry.is_null();
+
+  DevicePolicyProvider* const dp_provider = state->device_policy_provider();
+  bool is_scattering_active = false;
+  EvalStatus scattering_status = EvalStatus::kSucceeded;
+
+  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) {
+    // Check whether scattering applies to this update attempt. We should not be
+    // scattering if this is an interactive update check, or if OOBE is enabled
+    // but not completed.
+    //
+    // Note: current code further suppresses scattering if a "deadline"
+    // attribute is found in the Omaha response. However, it appears that the
+    // presence of this attribute is merely indicative of an OOBE update, during
+    // which we suppress scattering anyway.
+    bool is_scattering_applicable = false;
+    result->scatter_wait_period = kZeroInterval;
+    result->scatter_check_threshold = 0;
+    if (!update_state.is_interactive) {
+      const bool* is_oobe_enabled_p = ec->GetValue(
+          state->config_provider()->var_is_oobe_enabled());
+      if (is_oobe_enabled_p && !(*is_oobe_enabled_p)) {
+        is_scattering_applicable = true;
+      } else {
+        const bool* is_oobe_complete_p = ec->GetValue(
+            state->system_provider()->var_is_oobe_complete());
+        is_scattering_applicable = (is_oobe_complete_p && *is_oobe_complete_p);
+      }
+    }
+
+    // Compute scattering values.
+    if (is_scattering_applicable) {
+      UpdateScatteringResult scatter_result;
+      scattering_status = UpdateScattering(ec, state, error, &scatter_result,
+                                           update_state);
+      if (scattering_status == EvalStatus::kFailed) {
+        return EvalStatus::kFailed;
+      } else {
+        result->scatter_wait_period = scatter_result.wait_period;
+        result->scatter_check_threshold = scatter_result.check_threshold;
+        if (scattering_status == EvalStatus::kAskMeAgainLater ||
+            scatter_result.is_scattering)
+          is_scattering_active = true;
+      }
+    }
+  }
+
+  // Find out whether P2P is globally enabled.
+  bool p2p_enabled;
+  EvalStatus p2p_enabled_status = P2PEnabled(ec, state, error, &p2p_enabled);
+  if (p2p_enabled_status != EvalStatus::kSucceeded)
+    return EvalStatus::kFailed;
+
+  // Is P2P is enabled, consider allowing it for downloading and/or sharing.
+  if (p2p_enabled) {
+    // Sharing via P2P is allowed if not disabled by Omaha.
+    if (update_state.p2p_sharing_disabled) {
+      LOG(INFO) << "Blocked P2P sharing because it is disabled by Omaha.";
+    } else {
+      result->p2p_sharing_allowed = true;
+    }
+
+    // Downloading via P2P is allowed if not disabled by Omaha, an update is not
+    // interactive, and other limits haven't been reached.
+    if (update_state.p2p_downloading_disabled) {
+      LOG(INFO) << "Blocked P2P downloading because it is disabled by Omaha.";
+    } else if (update_state.is_interactive) {
+      LOG(INFO) << "Blocked P2P downloading because update is interactive.";
+    } else if (update_state.p2p_num_attempts >= kMaxP2PAttempts) {
+      LOG(INFO) << "Blocked P2P downloading as it was attempted too many "
+                   "times.";
+    } else if (!update_state.p2p_first_attempted.is_null() &&
+               ec->IsWallclockTimeGreaterThan(
+                   update_state.p2p_first_attempted +
+                   TimeDelta::FromSeconds(kMaxP2PAttemptsPeriodInSeconds))) {
+      LOG(INFO) << "Blocked P2P downloading as its usage timespan exceeds "
+                   "limit.";
+    } else {
+      // P2P download is allowed; if backoff or scattering are active, be sure
+      // to suppress them, yet prevent any download URL from being used.
+      result->p2p_downloading_allowed = true;
+      if (is_backoff_active || is_scattering_active) {
+        is_backoff_active = is_scattering_active = false;
+        result->download_url_allowed = false;
+      }
+    }
+  }
+
+  // Check for various deterrents.
+  if (is_check_due) {
+    result->cannot_start_reason = UpdateCannotStartReason::kCheckDue;
+    return EvalStatus::kSucceeded;
+  }
+  if (is_backoff_active) {
+    result->cannot_start_reason = UpdateCannotStartReason::kBackoff;
+    return backoff_url_status;
+  }
+  if (is_scattering_active) {
+    result->cannot_start_reason = UpdateCannotStartReason::kScattering;
+    return scattering_status;
+  }
+  if (result->download_url_idx < 0 && !result->p2p_downloading_allowed) {
+    result->cannot_start_reason = UpdateCannotStartReason::kCannotDownload;
+    return EvalStatus::kSucceeded;
+  }
+
+  // Update is good to go.
+  result->update_can_start = true;
+  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,
+                                      bool* result) const {
+  bool enabled = false;
+
+  // Determine whether use of P2P is allowed by policy. Even if P2P is not
+  // explicitly allowed, we allow it if the device is enterprise enrolled (that
+  // is, missing or empty owner string).
+  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 bool* policy_au_p2p_enabled_p = ec->GetValue(
+        dp_provider->var_au_p2p_enabled());
+    if (policy_au_p2p_enabled_p) {
+      enabled = *policy_au_p2p_enabled_p;
+    } else {
+      const string* policy_owner_p = ec->GetValue(dp_provider->var_owner());
+      if (!policy_owner_p || policy_owner_p->empty())
+        enabled = true;
+    }
+  }
+
+  // Enable P2P, if so mandated by the updater configuration. This is additive
+  // to whether or not P2P is enabled by device policy.
+  if (!enabled) {
+    const bool* updater_p2p_enabled_p = ec->GetValue(
+        state->updater_provider()->var_p2p_enabled());
+    enabled = updater_p2p_enabled_p && *updater_p2p_enabled_p;
+  }
+
+  *result = enabled;
+  return EvalStatus::kSucceeded;
+}
+
+EvalStatus ChromeOSPolicy::P2PEnabledChanged(EvaluationContext* ec,
+                                             State* state,
+                                             string* error,
+                                             bool* result,
+                                             bool prev_result) const {
+  EvalStatus status = P2PEnabled(ec, state, error, result);
+  if (status == EvalStatus::kSucceeded && *result == prev_result)
+    return EvalStatus::kAskMeAgainLater;
+  return status;
+}
+
+EvalStatus ChromeOSPolicy::NextUpdateCheckTime(EvaluationContext* ec,
+                                               State* state, string* error,
+                                               Time* next_update_check) const {
+  UpdaterProvider* const updater_provider = state->updater_provider();
+
+  // Don't check for updates too often. We limit the update checks to once every
+  // some interval. The interval is kTimeoutInitialInterval the first time and
+  // kTimeoutPeriodicInterval for the subsequent update checks. If the update
+  // check fails, we increase the interval between the update checks
+  // exponentially until kTimeoutMaxBackoffInterval. Finally, to avoid having
+  // many chromebooks running update checks at the exact same time, we add some
+  // fuzz to the interval.
+  const Time* updater_started_time =
+      ec->GetValue(updater_provider->var_updater_started_time());
+  POLICY_CHECK_VALUE_AND_FAIL(updater_started_time, error);
+
+  const Time* last_checked_time =
+      ec->GetValue(updater_provider->var_last_checked_time());
+
+  const uint64_t* seed = ec->GetValue(state->random_provider()->var_seed());
+  POLICY_CHECK_VALUE_AND_FAIL(seed, error);
+
+  PRNG prng(*seed);
+
+  // If this is the first attempt, compute and return an initial value.
+  if (!last_checked_time || *last_checked_time < *updater_started_time) {
+    *next_update_check = *updater_started_time + FuzzedInterval(
+        &prng, kTimeoutInitialInterval, kTimeoutRegularFuzz);
+    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 = ec->GetValue(
+      updater_provider->var_server_dictated_poll_interval());
+  POLICY_CHECK_VALUE_AND_FAIL(server_dictated_poll_interval, error);
+
+  int interval = *server_dictated_poll_interval;
+  int fuzz = 0;
+
+  // If no poll interval was dictated by server compute a back-off period,
+  // starting from a predetermined base periodic interval and increasing
+  // exponentially by the number of consecutive failed attempts.
+  if (interval == 0) {
+    const unsigned int* consecutive_failed_update_checks = ec->GetValue(
+        updater_provider->var_consecutive_failed_update_checks());
+    POLICY_CHECK_VALUE_AND_FAIL(consecutive_failed_update_checks, error);
+
+    interval = kTimeoutPeriodicInterval;
+    unsigned int num_failures = *consecutive_failed_update_checks;
+    while (interval < kTimeoutMaxBackoffInterval && num_failures) {
+      interval *= 2;
+      num_failures--;
+    }
+  }
+
+  // We cannot back off longer than the predetermined maximum interval.
+  if (interval > kTimeoutMaxBackoffInterval)
+    interval = kTimeoutMaxBackoffInterval;
+
+  // We cannot back off shorter than the predetermined periodic interval. Also,
+  // in this case set the fuzz to a predetermined regular value.
+  if (interval <= kTimeoutPeriodicInterval) {
+    interval = kTimeoutPeriodicInterval;
+    fuzz = kTimeoutRegularFuzz;
+  }
+
+  // If not otherwise determined, defer to a fuzz of +/-(interval / 2).
+  if (fuzz == 0)
+    fuzz = interval;
+
+  *next_update_check = *last_checked_time + FuzzedInterval(
+      &prng, interval, fuzz);
+  return EvalStatus::kSucceeded;
+}
+
+TimeDelta ChromeOSPolicy::FuzzedInterval(PRNG* prng, int interval, int fuzz) {
+  DCHECK_GE(interval, 0);
+  DCHECK_GE(fuzz, 0);
+  int half_fuzz = fuzz / 2;
+  // This guarantees the output interval is non negative.
+  int interval_min = max(interval - half_fuzz, 0);
+  int interval_max = interval + half_fuzz;
+  return TimeDelta::FromSeconds(prng->RandMinMax(interval_min, interval_max));
+}
+
+EvalStatus ChromeOSPolicy::UpdateBackoffAndDownloadUrl(
+    EvaluationContext* ec, State* state, string* error,
+    UpdateBackoffAndDownloadUrlResult* result,
+    const UpdateState& update_state) const {
+  // Sanity checks.
+  DCHECK_GE(update_state.download_errors_max, 0);
+
+  // Set default result values.
+  result->do_increment_failures = false;
+  result->backoff_expiry = update_state.backoff_expiry;
+  result->url_idx = -1;
+  result->url_num_errors = 0;
+
+  const bool* is_official_build_p = ec->GetValue(
+      state->system_provider()->var_is_official_build());
+  bool is_official_build = (is_official_build_p ? *is_official_build_p : true);
+
+  // Check whether backoff is enabled.
+  bool may_backoff = false;
+  if (update_state.is_backoff_disabled) {
+    LOG(INFO) << "Backoff disabled by Omaha.";
+  } else if (update_state.is_interactive) {
+    LOG(INFO) << "No backoff for interactive updates.";
+  } else if (update_state.is_delta_payload) {
+    LOG(INFO) << "No backoff for delta payloads.";
+  } else if (!is_official_build) {
+    LOG(INFO) << "No backoff for unofficial builds.";
+  } else {
+    may_backoff = true;
+  }
+
+  // If previous backoff still in effect, block.
+  if (may_backoff && !update_state.backoff_expiry.is_null() &&
+      !ec->IsWallclockTimeGreaterThan(update_state.backoff_expiry)) {
+    LOG(INFO) << "Previous backoff has not expired, waiting.";
+    return EvalStatus::kAskMeAgainLater;
+  }
+
+  // Determine whether HTTP downloads are forbidden by policy. This only
+  // applies to official system builds; otherwise, HTTP is always enabled.
+  bool http_allowed = true;
+  if (is_official_build) {
+    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 bool* policy_http_downloads_enabled_p = ec->GetValue(
+          dp_provider->var_http_downloads_enabled());
+      http_allowed = (!policy_http_downloads_enabled_p ||
+                      *policy_http_downloads_enabled_p);
+    }
+  }
+
+  int url_idx = update_state.last_download_url_idx;
+  if (url_idx < 0)
+    url_idx = -1;
+  bool do_advance_url = false;
+  bool is_failure_occurred = false;
+  Time err_time;
+
+  // Scan the relevant part of the download error log, tracking which URLs are
+  // being used, and accounting the number of errors for each URL. Note that
+  // this process may not traverse all errors provided, as it may decide to bail
+  // out midway depending on the particular errors exhibited, the number of
+  // failures allowed, etc. When this ends, |url_idx| will point to the last URL
+  // used (-1 if starting fresh), |do_advance_url| will determine whether the
+  // URL needs to be advanced, and |err_time| the point in time when the last
+  // reported error occurred.  Additionally, if the error log indicates that an
+  // update attempt has failed (abnormal), then |is_failure_occurred| will be
+  // set to true.
+  const int num_urls = update_state.download_urls.size();
+  int prev_url_idx = -1;
+  int url_num_errors = update_state.last_download_url_num_errors;
+  Time prev_err_time;
+  bool is_first = true;
+  for (const auto& err_tuple : update_state.download_errors) {
+    // 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
+                   << ") not as expected (" << url_idx << ")";
+    }
+    is_first = false;
+    url_idx = used_url_idx;
+    if (url_idx < 0 || url_idx >= num_urls) {
+      LOG(ERROR) << "Download error log contains an invalid URL index ("
+                 << url_idx << ")";
+      return EvalStatus::kFailed;
+    }
+    err_time = get<2>(err_tuple);
+    if (!(prev_err_time.is_null() || err_time >= prev_err_time)) {
+      // TODO(garnold) Monotonicity cannot really be assumed when dealing with
+      // wallclock-based timestamps. However, we're making a simplifying
+      // assumption so as to keep the policy implementation straightforward, for
+      // now. In general, we should convert all timestamp handling in the
+      // UpdateManager to use monotonic time (instead of wallclock), including
+      // the computation of various expiration times (backoff, scattering, etc).
+      // The client will do whatever conversions necessary when
+      // persisting/retrieving these values across reboots. See chromium:408794.
+      LOG(ERROR) << "Download error timestamps not monotonically increasing.";
+      return EvalStatus::kFailed;
+    }
+    prev_err_time = err_time;
+
+    // Ignore errors that happened before the last known failed attempt.
+    if (!update_state.failures_last_updated.is_null() &&
+        err_time <= update_state.failures_last_updated)
+      continue;
+
+    if (prev_url_idx >= 0) {
+      if (url_idx < prev_url_idx) {
+        LOG(ERROR) << "The URLs in the download error log have wrapped around ("
+                   << prev_url_idx << "->" << url_idx
+                   << "). This should not have happened and means that there's "
+                      "a bug. To be conservative, we record a failed attempt "
+                      "(invalidating the rest of the error log) and resume "
+                      "download from the first usable URL.";
+        url_idx = -1;
+        is_failure_occurred = true;
+        break;
+      }
+
+      if (url_idx > prev_url_idx) {
+        url_num_errors = 0;
+        do_advance_url = false;
+      }
+    }
+
+    if (HandleErrorCode(get<1>(err_tuple), &url_num_errors) ||
+        url_num_errors > update_state.download_errors_max)
+      do_advance_url = true;
+
+    prev_url_idx = url_idx;
+  }
+
+  // If required, advance to the next usable URL. If the URLs wraparound, we
+  // mark an update attempt failure. Also be sure to set the download error
+  // count to zero.
+  if (url_idx < 0 || do_advance_url) {
+    url_num_errors = 0;
+    int start_url_idx = -1;
+    do {
+      if (++url_idx == num_urls) {
+        url_idx = 0;
+        // We only mark failure if an actual advancing of a URL was required.
+        if (do_advance_url)
+          is_failure_occurred = true;
+      }
+
+      if (start_url_idx < 0)
+        start_url_idx = url_idx;
+      else if (url_idx == start_url_idx)
+        url_idx = -1;  // No usable URL.
+    } while (url_idx >= 0 &&
+             !IsUrlUsable(update_state.download_urls[url_idx], http_allowed));
+  }
+
+  // If we have a download URL but a failure was observed, compute a new backoff
+  // expiry (if allowed). The backoff period is generally 2 ^ (num_failures - 1)
+  // days, bounded by the size of int and kAttemptBackoffMaxIntervalInDays, and
+  // fuzzed by kAttemptBackoffFuzzInHours hours. Backoff expiry is computed from
+  // the latest recorded time of error.
+  Time backoff_expiry;
+  if (url_idx >= 0 && is_failure_occurred && may_backoff) {
+    CHECK(!err_time.is_null())
+        << "We must have an error timestamp if a failure occurred!";
+    const uint64_t* seed = ec->GetValue(state->random_provider()->var_seed());
+    POLICY_CHECK_VALUE_AND_FAIL(seed, error);
+    PRNG prng(*seed);
+    int exp = min(update_state.num_failures,
+                       static_cast<int>(sizeof(int)) * 8 - 2);
+    TimeDelta backoff_interval = TimeDelta::FromDays(
+        min(1 << exp, kAttemptBackoffMaxIntervalInDays));
+    TimeDelta backoff_fuzz = TimeDelta::FromHours(kAttemptBackoffFuzzInHours);
+    TimeDelta wait_period = FuzzedInterval(&prng, backoff_interval.InSeconds(),
+                                           backoff_fuzz.InSeconds());
+    backoff_expiry = err_time + wait_period;
+
+    // If the newly computed backoff already expired, nullify it.
+    if (ec->IsWallclockTimeGreaterThan(backoff_expiry))
+      backoff_expiry = Time();
+  }
+
+  result->do_increment_failures = is_failure_occurred;
+  result->backoff_expiry = backoff_expiry;
+  result->url_idx = url_idx;
+  result->url_num_errors = url_num_errors;
+  return EvalStatus::kSucceeded;
+}
+
+EvalStatus ChromeOSPolicy::UpdateScattering(
+    EvaluationContext* ec,
+    State* state,
+    string* error,
+    UpdateScatteringResult* result,
+    const UpdateState& update_state) const {
+  // Preconditions. These stem from the postconditions and usage contract.
+  DCHECK(update_state.scatter_wait_period >= kZeroInterval);
+  DCHECK_GE(update_state.scatter_check_threshold, 0);
+
+  // Set default result values.
+  result->is_scattering = false;
+  result->wait_period = kZeroInterval;
+  result->check_threshold = 0;
+
+  DevicePolicyProvider* const dp_provider = state->device_policy_provider();
+
+  // Ensure that a device policy is loaded.
+  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))
+    return EvalStatus::kSucceeded;
+
+  // Is scattering enabled by policy?
+  const TimeDelta* scatter_factor_p = ec->GetValue(
+      dp_provider->var_scatter_factor());
+  if (!scatter_factor_p || *scatter_factor_p == kZeroInterval)
+    return EvalStatus::kSucceeded;
+
+  // Obtain a pseudo-random number generator.
+  const uint64_t* seed = ec->GetValue(state->random_provider()->var_seed());
+  POLICY_CHECK_VALUE_AND_FAIL(seed, error);
+  PRNG prng(*seed);
+
+  // Step 1: Maintain the scattering wait period.
+  //
+  // If no wait period was previously determined, or it no longer fits in the
+  // scatter factor, then generate a new one. Otherwise, keep the one we have.
+  TimeDelta wait_period = update_state.scatter_wait_period;
+  if (wait_period == kZeroInterval || wait_period > *scatter_factor_p) {
+    wait_period = TimeDelta::FromSeconds(
+        prng.RandMinMax(1, scatter_factor_p->InSeconds()));
+  }
+
+  // If we surpassed the wait period or the max scatter period associated with
+  // the update, then no wait is needed.
+  Time wait_expires = (update_state.first_seen +
+                       min(wait_period, update_state.scatter_wait_period_max));
+  if (ec->IsWallclockTimeGreaterThan(wait_expires))
+    wait_period = kZeroInterval;
+
+  // Step 2: Maintain the update check threshold count.
+  //
+  // If an update check threshold is not specified then generate a new
+  // one.
+  int check_threshold = update_state.scatter_check_threshold;
+  if (check_threshold == 0) {
+    check_threshold = prng.RandMinMax(
+        update_state.scatter_check_threshold_min,
+        update_state.scatter_check_threshold_max);
+  }
+
+  // If the update check threshold is not within allowed range then nullify it.
+  // TODO(garnold) This is compliant with current logic found in
+  // OmahaRequestAction::IsUpdateCheckCountBasedWaitingSatisfied(). We may want
+  // to change it so that it behaves similarly to the wait period case, namely
+  // if the current value exceeds the maximum, we set a new one within range.
+  if (check_threshold > update_state.scatter_check_threshold_max)
+    check_threshold = 0;
+
+  // If the update check threshold is non-zero and satisfied, then nullify it.
+  if (check_threshold > 0 && update_state.num_checks >= check_threshold)
+    check_threshold = 0;
+
+  bool is_scattering = (wait_period != kZeroInterval || check_threshold);
+  EvalStatus ret = EvalStatus::kSucceeded;
+  if (is_scattering && wait_period == update_state.scatter_wait_period &&
+      check_threshold == update_state.scatter_check_threshold)
+    ret = EvalStatus::kAskMeAgainLater;
+  result->is_scattering = is_scattering;
+  result->wait_period = wait_period;
+  result->check_threshold = check_threshold;
+  return ret;
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/chromeos_policy.h b/update_manager/chromeos_policy.h
new file mode 100644
index 0000000..b4370c4
--- /dev/null
+++ b/update_manager/chromeos_policy.h
@@ -0,0 +1,203 @@
+//
+// 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_UPDATE_MANAGER_CHROMEOS_POLICY_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_CHROMEOS_POLICY_H_
+
+#include <string>
+
+#include <base/time/time.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "update_engine/update_manager/policy.h"
+#include "update_engine/update_manager/prng.h"
+
+namespace chromeos_update_manager {
+
+// Output information from UpdateBackoffAndDownloadUrl.
+struct UpdateBackoffAndDownloadUrlResult {
+  // Whether the failed attempt count (maintained by the caller) needs to be
+  // incremented.
+  bool do_increment_failures;
+  // The current backoff expiry. Null if backoff is not in effect.
+  base::Time backoff_expiry;
+  // The new URL index to use and number of download errors associated with it.
+  // Significant iff |do_increment_failures| is false and |backoff_expiry| is
+  // null. Negative value means no usable URL was found.
+  int url_idx;
+  int url_num_errors;
+};
+
+// Parameters for update scattering, as returned by UpdateScattering.
+struct UpdateScatteringResult {
+  bool is_scattering;
+  base::TimeDelta wait_period;
+  int check_threshold;
+};
+
+// ChromeOSPolicy implements the policy-related logic used in ChromeOS.
+class ChromeOSPolicy : public Policy {
+ public:
+  ChromeOSPolicy() {}
+  ~ChromeOSPolicy() override {}
+
+  // Policy overrides.
+  EvalStatus UpdateCheckAllowed(
+      EvaluationContext* ec, State* state, std::string* error,
+      UpdateCheckParams* result) const override;
+
+  EvalStatus UpdateCanStart(
+      EvaluationContext* ec,
+      State* state,
+      std::string* error,
+      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,
+      bool* result) const override;
+
+  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 "ChromeOSPolicy"; }
+
+ private:
+  friend class UmChromeOSPolicyTest;
+  FRIEND_TEST(UmChromeOSPolicyTest,
+              FirstCheckIsAtMostInitialIntervalAfterStart);
+  FRIEND_TEST(UmChromeOSPolicyTest, RecurringCheckBaseIntervalAndFuzz);
+  FRIEND_TEST(UmChromeOSPolicyTest, RecurringCheckBackoffIntervalAndFuzz);
+  FRIEND_TEST(UmChromeOSPolicyTest, RecurringCheckServerDictatedPollInterval);
+  FRIEND_TEST(UmChromeOSPolicyTest, ExponentialBackoffIsCapped);
+  FRIEND_TEST(UmChromeOSPolicyTest, UpdateCheckAllowedWaitsForTheTimeout);
+  FRIEND_TEST(UmChromeOSPolicyTest, UpdateCheckAllowedWaitsForOOBE);
+  FRIEND_TEST(UmChromeOSPolicyTest,
+              UpdateCanStartNotAllowedScatteringNewWaitPeriodApplies);
+  FRIEND_TEST(UmChromeOSPolicyTest,
+              UpdateCanStartNotAllowedScatteringPrevWaitPeriodStillApplies);
+  FRIEND_TEST(UmChromeOSPolicyTest,
+              UpdateCanStartNotAllowedScatteringNewCountThresholdApplies);
+  FRIEND_TEST(UmChromeOSPolicyTest,
+              UpdateCanStartNotAllowedScatteringPrevCountThresholdStillApplies);
+  FRIEND_TEST(UmChromeOSPolicyTest, UpdateCanStartAllowedScatteringSatisfied);
+  FRIEND_TEST(UmChromeOSPolicyTest,
+              UpdateCanStartAllowedInteractivePreventsScattering);
+  FRIEND_TEST(UmChromeOSPolicyTest,
+              UpdateCanStartAllowedP2PDownloadingBlockedDueToNumAttempts);
+  FRIEND_TEST(UmChromeOSPolicyTest,
+              UpdateCanStartAllowedP2PDownloadingBlockedDueToAttemptsPeriod);
+
+  // Auxiliary constant (zero by default).
+  const base::TimeDelta kZeroInterval;
+
+  // Default update check timeout interval/fuzz values used to compute the
+  // NextUpdateCheckTime(), in seconds. Actual fuzz is within +/- half of the
+  // indicated value.
+  static const int kTimeoutInitialInterval;
+  static const int kTimeoutPeriodicInterval;
+  static const int kTimeoutMaxBackoffInterval;
+  static const int kTimeoutRegularFuzz;
+
+  // Maximum update attempt backoff interval and fuzz.
+  static const int kAttemptBackoffMaxIntervalInDays;
+  static const int kAttemptBackoffFuzzInHours;
+
+  // Maximum number of times we'll allow using P2P for the same update payload.
+  static const int kMaxP2PAttempts;
+  // Maximum period of time allowed for download a payload via P2P, in seconds.
+  static const int kMaxP2PAttemptsPeriodInSeconds;
+
+  // A private policy implementation returning the wallclock timestamp when
+  // the next update check should happen.
+  // TODO(garnold) We should probably change that to infer a monotonic
+  // timestamp, which will make the update check intervals more resilient to
+  // clock skews. Might require switching some of the variables exported by the
+  // UpdaterProvider to report monotonic time, as well.
+  EvalStatus NextUpdateCheckTime(EvaluationContext* ec, State* state,
+                                 std::string* error,
+                                 base::Time* next_update_check) const;
+
+  // Returns a TimeDelta based on the provided |interval| seconds +/- half
+  // |fuzz| seconds. The return value is guaranteed to be a non-negative
+  // TimeDelta.
+  static base::TimeDelta FuzzedInterval(PRNG* prng, int interval, int fuzz);
+
+  // A private policy for determining backoff and the download URL to use.
+  // Within |update_state|, |backoff_expiry| and |is_backoff_disabled| are used
+  // for determining whether backoff is still in effect; if not,
+  // |download_errors| is scanned past |failures_last_updated|, and a new
+  // download URL from |download_urls| is found and written to |result->url_idx|
+  // (-1 means no usable URL exists); |download_errors_max| determines the
+  // maximum number of attempts per URL, according to the Omaha response. If an
+  // update failure is identified then |result->do_increment_failures| is set to
+  // true; if backoff is enabled, a new backoff period is computed (from the
+  // time of failure) based on |num_failures|. Otherwise, backoff expiry is
+  // nullified, indicating that no backoff is in effect.
+  //
+  // If backing off but the previous backoff expiry is unchanged, returns
+  // |EvalStatus::kAskMeAgainLater|. Otherwise:
+  //
+  // * If backing off with a new expiry time, then |result->backoff_expiry| is
+  //   set to this time.
+  //
+  // * Else, |result->backoff_expiry| is set to null, indicating that no backoff
+  //   is in effect.
+  //
+  // In any of these cases, returns |EvalStatus::kSucceeded|. If an error
+  // occurred, returns |EvalStatus::kFailed|.
+  EvalStatus UpdateBackoffAndDownloadUrl(
+      EvaluationContext* ec, State* state, std::string* error,
+      UpdateBackoffAndDownloadUrlResult* result,
+      const UpdateState& update_state) const;
+
+  // A private policy for checking whether scattering is due. Writes in |result|
+  // the decision as to whether or not to scatter; a wallclock-based scatter
+  // wait period, which ranges from zero (do not wait) and no greater than the
+  // current scatter factor provided by the device policy (if available) or the
+  // maximum wait period determined by Omaha; and an update check-based
+  // threshold between zero (no threshold) and the maximum number determined by
+  // the update engine. Within |update_state|, |scatter_wait_period| should
+  // contain the last scattering period returned by this function, or zero if no
+  // wait period is known; |scatter_check_threshold| is the last update check
+  // threshold, or zero if no such threshold is known. If not scattering, or if
+  // any of the scattering values has changed, returns |EvalStatus::kSucceeded|;
+  // otherwise, |EvalStatus::kAskMeAgainLater|.
+  EvalStatus UpdateScattering(EvaluationContext* ec, State* state,
+                              std::string* error,
+                              UpdateScatteringResult* result,
+                              const UpdateState& update_state) const;
+
+  DISALLOW_COPY_AND_ASSIGN(ChromeOSPolicy);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_CHROMEOS_POLICY_H_
diff --git a/update_manager/chromeos_policy_unittest.cc b/update_manager/chromeos_policy_unittest.cc
new file mode 100644
index 0000000..a78257d
--- /dev/null
+++ b/update_manager/chromeos_policy_unittest.cc
@@ -0,0 +1,1618 @@
+//
+// 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/update_manager/chromeos_policy.h"
+
+#include <set>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include <base/time/time.h>
+#include <brillo/message_loops/fake_message_loop.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/fake_clock.h"
+#include "update_engine/update_manager/evaluation_context.h"
+#include "update_engine/update_manager/fake_state.h"
+#include "update_engine/update_manager/umtest_utils.h"
+
+using base::Time;
+using base::TimeDelta;
+using chromeos_update_engine::ErrorCode;
+using chromeos_update_engine::FakeClock;
+using std::set;
+using std::string;
+using std::tuple;
+using std::vector;
+
+namespace chromeos_update_manager {
+
+class UmChromeOSPolicyTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    loop_.SetAsCurrent();
+    SetUpDefaultClock();
+    eval_ctx_ = new EvaluationContext(&fake_clock_, TimeDelta::FromSeconds(5));
+    SetUpDefaultState();
+    SetUpDefaultDevicePolicy();
+  }
+
+  void TearDown() override {
+    EXPECT_FALSE(loop_.PendingTasks());
+  }
+
+  // Sets the clock to fixed values.
+  void SetUpDefaultClock() {
+    fake_clock_.SetMonotonicTime(Time::FromInternalValue(12345678L));
+    fake_clock_.SetWallclockTime(Time::FromInternalValue(12345678901234L));
+  }
+
+  void SetUpDefaultState() {
+    fake_state_.updater_provider()->var_updater_started_time()->reset(
+        new Time(fake_clock_.GetWallclockTime()));
+    fake_state_.updater_provider()->var_last_checked_time()->reset(
+        new Time(fake_clock_.GetWallclockTime()));
+    fake_state_.updater_provider()->var_consecutive_failed_update_checks()->
+        reset(new unsigned int{0});
+    fake_state_.updater_provider()->var_server_dictated_poll_interval()->
+        reset(new unsigned int{0});
+    fake_state_.updater_provider()->var_forced_update_requested()->
+        reset(new UpdateRequestStatus{UpdateRequestStatus::kNone});
+
+    fake_state_.random_provider()->var_seed()->reset(
+        new uint64_t(4));  // chosen by fair dice roll.
+                           // guaranteed to be random.
+
+    // No device policy loaded by default.
+    fake_state_.device_policy_provider()->var_device_policy_is_loaded()->reset(
+        new bool(false));
+
+    // OOBE is enabled by default.
+    fake_state_.config_provider()->var_is_oobe_enabled()->reset(
+        new bool(true));
+
+    // For the purpose of the tests, this is an official build and OOBE was
+    // completed.
+    fake_state_.system_provider()->var_is_official_build()->reset(
+        new bool(true));
+    fake_state_.system_provider()->var_is_oobe_complete()->reset(
+        new bool(true));
+    fake_state_.system_provider()->var_num_slots()->reset(new unsigned int(2));
+
+    // Connection is wifi, untethered.
+    fake_state_.shill_provider()->var_conn_type()->
+        reset(new ConnectionType(ConnectionType::kWifi));
+    fake_state_.shill_provider()->var_conn_tethering()->
+        reset(new ConnectionTethering(ConnectionTethering::kNotDetected));
+  }
+
+  // Sets up a default device policy that does not impose any restrictions
+  // (HTTP) nor enables any features (P2P).
+  void SetUpDefaultDevicePolicy() {
+    fake_state_.device_policy_provider()->var_device_policy_is_loaded()->reset(
+        new bool(true));
+    fake_state_.device_policy_provider()->var_update_disabled()->reset(
+        new bool(false));
+    fake_state_.device_policy_provider()->
+        var_allowed_connection_types_for_update()->reset(nullptr);
+    fake_state_.device_policy_provider()->var_scatter_factor()->reset(
+        new TimeDelta());
+    fake_state_.device_policy_provider()->var_http_downloads_enabled()->reset(
+        new bool(true));
+    fake_state_.device_policy_provider()->var_au_p2p_enabled()->reset(
+        new bool(false));
+    fake_state_.device_policy_provider()->var_release_channel_delegated()->
+        reset(new bool(true));
+  }
+
+  // Configures the UpdateCheckAllowed policy to return a desired value by
+  // faking the current wall clock time as needed. Restores the default state.
+  // This is used when testing policies that depend on this one.
+  void SetUpdateCheckAllowed(bool allow_check) {
+    Time next_update_check;
+    ExpectPolicyStatus(EvalStatus::kSucceeded,
+                       &ChromeOSPolicy::NextUpdateCheckTime,
+                       &next_update_check);
+    SetUpDefaultState();
+    SetUpDefaultDevicePolicy();
+    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);
+  }
+
+  // Returns a default UpdateState structure:
+  UpdateState GetDefaultUpdateState(TimeDelta 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
+    // first time (|first_seen_period| ago). Clearly, there were no failed
+    // attempts so far.
+    update_state.is_interactive = false;
+    update_state.is_delta_payload = false;
+    update_state.first_seen = first_seen_time;
+    update_state.num_checks = 1;
+    update_state.num_failures = 0;
+    update_state.failures_last_updated = Time();  // Needs to be zero.
+    // There's a single HTTP download URL with a maximum of 10 retries.
+    update_state.download_urls = vector<string>{"http://fake/url/"};
+    update_state.download_errors_max = 10;
+    // Download was never attempted.
+    update_state.last_download_url_idx = -1;
+    update_state.last_download_url_num_errors = 0;
+    // There were no download errors.
+    update_state.download_errors = vector<tuple<int, ErrorCode, Time>>();
+    // P2P is not disabled by Omaha.
+    update_state.p2p_downloading_disabled = false;
+    update_state.p2p_sharing_disabled = false;
+    // P2P was not attempted.
+    update_state.p2p_num_attempts = 0;
+    update_state.p2p_first_attempted = Time();
+    // No active backoff period, backoff is not disabled by Omaha.
+    update_state.backoff_expiry = Time();
+    update_state.is_backoff_disabled = false;
+    // There is no active scattering wait period (max 7 days allowed) nor check
+    // threshold (none allowed).
+    update_state.scatter_wait_period = TimeDelta();
+    update_state.scatter_check_threshold = 0;
+    update_state.scatter_wait_period_max = TimeDelta::FromDays(7);
+    update_state.scatter_check_threshold_min = 0;
+    update_state.scatter_check_threshold_max = 0;
+
+    return update_state;
+  }
+
+  // Runs the passed |policy_method| policy and expects it to return the
+  // |expected| return value.
+  template<typename T, typename R, typename... Args>
+  void ExpectPolicyStatus(
+      EvalStatus expected,
+      T policy_method,
+      R* result, Args... args) {
+    string error = "<None>";
+    eval_ctx_->ResetEvaluation();
+    EXPECT_EQ(expected,
+              (policy_.*policy_method)(eval_ctx_.get(), &fake_state_, &error,
+                                       result, args...))
+        << "Returned error: " << error
+        << "\nEvaluation context: " << eval_ctx_->DumpContext();
+  }
+
+  brillo::FakeMessageLoop loop_{nullptr};
+  FakeClock fake_clock_;
+  FakeState fake_state_;
+  scoped_refptr<EvaluationContext> eval_ctx_;
+  ChromeOSPolicy policy_;  // ChromeOSPolicy under test.
+};
+
+TEST_F(UmChromeOSPolicyTest, FirstCheckIsAtMostInitialIntervalAfterStart) {
+  Time next_update_check;
+
+  // 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)));
+
+  ExpectPolicyStatus(EvalStatus::kSucceeded,
+                     &ChromeOSPolicy::NextUpdateCheckTime, &next_update_check);
+
+  EXPECT_LE(fake_clock_.GetWallclockTime(), next_update_check);
+  EXPECT_GE(
+      fake_clock_.GetWallclockTime() + TimeDelta::FromSeconds(
+          ChromeOSPolicy::kTimeoutInitialInterval +
+          ChromeOSPolicy::kTimeoutRegularFuzz / 2),
+      next_update_check);
+}
+
+TEST_F(UmChromeOSPolicyTest, RecurringCheckBaseIntervalAndFuzz) {
+  // Ensure that we're using the correct interval (kPeriodicInterval) and fuzz
+  // (kTimeoutRegularFuzz) as base values for period updates.
+  Time next_update_check;
+
+  ExpectPolicyStatus(EvalStatus::kSucceeded,
+                     &ChromeOSPolicy::NextUpdateCheckTime, &next_update_check);
+
+  EXPECT_LE(
+      fake_clock_.GetWallclockTime() + TimeDelta::FromSeconds(
+          ChromeOSPolicy::kTimeoutPeriodicInterval -
+          ChromeOSPolicy::kTimeoutRegularFuzz / 2),
+      next_update_check);
+  EXPECT_GE(
+      fake_clock_.GetWallclockTime() + TimeDelta::FromSeconds(
+          ChromeOSPolicy::kTimeoutPeriodicInterval +
+          ChromeOSPolicy::kTimeoutRegularFuzz / 2),
+      next_update_check);
+}
+
+TEST_F(UmChromeOSPolicyTest, RecurringCheckBackoffIntervalAndFuzz) {
+  // Ensure that we're properly backing off and fuzzing in the presence of
+  // failed updates attempts.
+  Time next_update_check;
+
+  fake_state_.updater_provider()->var_consecutive_failed_update_checks()->
+      reset(new unsigned int{2});
+
+  ExpectPolicyStatus(EvalStatus::kSucceeded,
+                     &ChromeOSPolicy::NextUpdateCheckTime, &next_update_check);
+
+  int expected_interval = ChromeOSPolicy::kTimeoutPeriodicInterval * 4;
+  EXPECT_LE(
+      fake_clock_.GetWallclockTime() + TimeDelta::FromSeconds(
+          expected_interval - expected_interval / 2),
+      next_update_check);
+  EXPECT_GE(
+      fake_clock_.GetWallclockTime() + TimeDelta::FromSeconds(
+          expected_interval + expected_interval / 2),
+      next_update_check);
+}
+
+TEST_F(UmChromeOSPolicyTest, RecurringCheckServerDictatedPollInterval) {
+  // Policy honors the server provided check poll interval.
+  Time next_update_check;
+
+  const unsigned int kInterval = ChromeOSPolicy::kTimeoutPeriodicInterval * 4;
+  fake_state_.updater_provider()->var_server_dictated_poll_interval()->
+      reset(new unsigned int{kInterval});
+  // We should not be backing off in this case.
+  fake_state_.updater_provider()->var_consecutive_failed_update_checks()->
+      reset(new unsigned int{2});
+
+  ExpectPolicyStatus(EvalStatus::kSucceeded,
+                     &ChromeOSPolicy::NextUpdateCheckTime, &next_update_check);
+
+  EXPECT_LE(
+      fake_clock_.GetWallclockTime() + TimeDelta::FromSeconds(
+          kInterval - kInterval / 2),
+      next_update_check);
+  EXPECT_GE(
+      fake_clock_.GetWallclockTime() + TimeDelta::FromSeconds(
+          kInterval + kInterval / 2),
+      next_update_check);
+}
+
+TEST_F(UmChromeOSPolicyTest, ExponentialBackoffIsCapped) {
+  Time next_update_check;
+
+  fake_state_.updater_provider()->var_consecutive_failed_update_checks()->
+      reset(new unsigned int{100});
+
+  ExpectPolicyStatus(EvalStatus::kSucceeded,
+                     &ChromeOSPolicy::NextUpdateCheckTime, &next_update_check);
+
+  EXPECT_LE(
+      fake_clock_.GetWallclockTime() + TimeDelta::FromSeconds(
+          ChromeOSPolicy::kTimeoutMaxBackoffInterval -
+          ChromeOSPolicy::kTimeoutMaxBackoffInterval / 2),
+      next_update_check);
+  EXPECT_GE(
+      fake_clock_.GetWallclockTime() + TimeDelta::FromSeconds(
+          ChromeOSPolicy::kTimeoutMaxBackoffInterval +
+          ChromeOSPolicy::kTimeoutMaxBackoffInterval /2),
+      next_update_check);
+}
+
+TEST_F(UmChromeOSPolicyTest, 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);
+
+  fake_state_.updater_provider()->var_last_checked_time()->reset(
+      new Time(last_checked_time));
+  ExpectPolicyStatus(EvalStatus::kSucceeded,
+                     &ChromeOSPolicy::NextUpdateCheckTime, &next_update_check);
+
+  UpdateCheckParams result;
+
+  // 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));
+  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.is_interactive);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedWaitsForOOBE) {
+  // Update checks are deferred until OOBE is completed.
+
+  // 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_state_.updater_provider()->var_last_checked_time()->reset(
+      new Time(last_checked_time));
+  ExpectPolicyStatus(EvalStatus::kSucceeded,
+                     &ChromeOSPolicy::NextUpdateCheckTime, &next_update_check);
+
+  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));
+  fake_state_.system_provider()->var_is_oobe_complete()->reset(
+      new bool(false));
+
+  UpdateCheckParams result;
+  ExpectPolicyStatus(EvalStatus::kAskMeAgainLater,
+                     &Policy::UpdateCheckAllowed, &result);
+
+  // Now check that it is allowed if OOBE is completed.
+  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.is_interactive);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedWithAttributes) {
+  // Update check is allowed, response includes attributes for use in the
+  // request.
+  SetUpdateCheckAllowed(true);
+
+  // Override specific device policy attributes.
+  fake_state_.device_policy_provider()->var_target_version_prefix()->
+      reset(new string("1.2"));
+  fake_state_.device_policy_provider()->var_release_channel_delegated()->
+      reset(new bool(false));
+  fake_state_.device_policy_provider()->var_release_channel()->
+      reset(new string("foo-channel"));
+
+  UpdateCheckParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded,
+                     &Policy::UpdateCheckAllowed, &result);
+  EXPECT_TRUE(result.updates_enabled);
+  EXPECT_EQ("1.2", result.target_version_prefix);
+  EXPECT_EQ("foo-channel", result.target_channel);
+  EXPECT_FALSE(result.is_interactive);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+       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(UmChromeOSPolicyTest,
+       UpdateCheckAllowedUpdatesDisabledForRemovableBootDevice) {
+  // UpdateCheckAllowed should return false (kSucceeded) if the image booted
+  // from a removable device.
+
+  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(UmChromeOSPolicyTest, UpdateCheckAllowedUpdatesDisabledByPolicy) {
+  // UpdateCheckAllowed should return kAskMeAgainLater because a device policy
+  // is loaded and prohibits updates.
+
+  SetUpdateCheckAllowed(false);
+  fake_state_.device_policy_provider()->var_update_disabled()->reset(
+      new bool(true));
+
+  UpdateCheckParams result;
+  ExpectPolicyStatus(EvalStatus::kAskMeAgainLater,
+                     &Policy::UpdateCheckAllowed, &result);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+       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.is_interactive);
+}
+
+TEST_F(UmChromeOSPolicyTest, 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.is_interactive);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartFailsCheckAllowedError) {
+  // The UpdateCanStart policy fails, not being able to query
+  // UpdateCheckAllowed.
+
+  // Configure the UpdateCheckAllowed policy to fail.
+  fake_state_.updater_provider()->var_updater_started_time()->reset(nullptr);
+
+  // Check that the UpdateCanStart fails.
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kFailed,
+                     &Policy::UpdateCanStart, &result, update_state);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartNotAllowedCheckDue) {
+  // The UpdateCanStart policy returns false because we are due for another
+  // update check. Ensure that download related values are still returned.
+
+  SetUpdateCheckAllowed(true);
+
+  // Check that the UpdateCanStart returns false.
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded,
+                     &Policy::UpdateCanStart, &result, update_state);
+  EXPECT_FALSE(result.update_can_start);
+  EXPECT_EQ(UpdateCannotStartReason::kCheckDue, result.cannot_start_reason);
+  EXPECT_EQ(0, result.download_url_idx);
+  EXPECT_EQ(0, result.download_url_num_errors);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedNoDevicePolicy) {
+  // The UpdateCanStart policy returns true; no device policy is loaded.
+
+  SetUpdateCheckAllowed(false);
+  fake_state_.device_policy_provider()->var_device_policy_is_loaded()->reset(
+      new bool(false));
+
+  // Check that the UpdateCanStart returns true with no further attributes.
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded,
+                     &Policy::UpdateCanStart, &result, update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_FALSE(result.p2p_downloading_allowed);
+  EXPECT_FALSE(result.p2p_sharing_allowed);
+  EXPECT_EQ(0, result.download_url_idx);
+  EXPECT_TRUE(result.download_url_allowed);
+  EXPECT_EQ(0, result.download_url_num_errors);
+  EXPECT_FALSE(result.do_increment_failures);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedBlankPolicy) {
+  // The UpdateCanStart policy returns true; device policy is loaded but imposes
+  // no restrictions on updating.
+
+  SetUpdateCheckAllowed(false);
+
+  // Check that the UpdateCanStart returns true.
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded,
+                     &Policy::UpdateCanStart, &result, update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_FALSE(result.p2p_downloading_allowed);
+  EXPECT_FALSE(result.p2p_sharing_allowed);
+  EXPECT_EQ(0, result.download_url_idx);
+  EXPECT_TRUE(result.download_url_allowed);
+  EXPECT_EQ(0, result.download_url_num_errors);
+  EXPECT_FALSE(result.do_increment_failures);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+       UpdateCanStartNotAllowedBackoffNewWaitPeriodApplies) {
+  // The UpdateCanStart policy returns false; failures are reported and a new
+  // backoff period is enacted.
+
+  SetUpdateCheckAllowed(false);
+
+  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(
+      0, ErrorCode::kDownloadTransferError,
+      curr_time - TimeDelta::FromSeconds(8));
+  update_state.download_errors.emplace_back(
+      0, ErrorCode::kDownloadTransferError,
+      curr_time - TimeDelta::FromSeconds(2));
+
+  // Check that UpdateCanStart returns false and a new backoff expiry is
+  // generated.
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_FALSE(result.update_can_start);
+  EXPECT_EQ(UpdateCannotStartReason::kBackoff, result.cannot_start_reason);
+  EXPECT_TRUE(result.do_increment_failures);
+  EXPECT_LT(curr_time, result.backoff_expiry);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+       UpdateCanStartNotAllowedBackoffPrevWaitPeriodStillApplies) {
+  // The UpdateCanStart policy returns false; a previously enacted backoff
+  // period still applies.
+
+  SetUpdateCheckAllowed(false);
+
+  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(
+      0, ErrorCode::kDownloadTransferError,
+      curr_time - TimeDelta::FromSeconds(8));
+  update_state.download_errors.emplace_back(
+      0, ErrorCode::kDownloadTransferError,
+      curr_time - TimeDelta::FromSeconds(2));
+  update_state.failures_last_updated = curr_time;
+  update_state.backoff_expiry = curr_time + TimeDelta::FromMinutes(3);
+
+  // Check that UpdateCanStart returns false and a new backoff expiry is
+  // generated.
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kAskMeAgainLater, &Policy::UpdateCanStart,
+                     &result, update_state);
+  EXPECT_FALSE(result.update_can_start);
+  EXPECT_EQ(UpdateCannotStartReason::kBackoff, result.cannot_start_reason);
+  EXPECT_FALSE(result.do_increment_failures);
+  EXPECT_LT(curr_time, result.backoff_expiry);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedBackoffSatisfied) {
+  // The UpdateCanStart policy returns true; a previously enacted backoff period
+  // has elapsed, we're good to go.
+
+  SetUpdateCheckAllowed(false);
+
+  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(
+      0, ErrorCode::kDownloadTransferError,
+      curr_time - TimeDelta::FromSeconds(8));
+  update_state.download_errors.emplace_back(
+      0, ErrorCode::kDownloadTransferError,
+      curr_time - TimeDelta::FromSeconds(2));
+  update_state.failures_last_updated = curr_time - TimeDelta::FromSeconds(1);
+  update_state.backoff_expiry = curr_time - TimeDelta::FromSeconds(1);
+
+  // Check that UpdateCanStart returns false and a new backoff expiry is
+  // generated.
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart,
+                     &result, update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_EQ(UpdateCannotStartReason::kUndefined, result.cannot_start_reason);
+  EXPECT_EQ(0, result.download_url_idx);
+  EXPECT_TRUE(result.download_url_allowed);
+  EXPECT_EQ(0, result.download_url_num_errors);
+  EXPECT_FALSE(result.do_increment_failures);
+  EXPECT_EQ(Time(), result.backoff_expiry);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedBackoffDisabled) {
+  // The UpdateCanStart policy returns false; failures are reported but backoff
+  // is disabled.
+
+  SetUpdateCheckAllowed(false);
+
+  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(
+      0, ErrorCode::kDownloadTransferError,
+      curr_time - TimeDelta::FromSeconds(8));
+  update_state.download_errors.emplace_back(
+      0, ErrorCode::kDownloadTransferError,
+      curr_time - TimeDelta::FromSeconds(2));
+  update_state.is_backoff_disabled = true;
+
+  // Check that UpdateCanStart returns false and a new backoff expiry is
+  // generated.
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_EQ(UpdateCannotStartReason::kUndefined, result.cannot_start_reason);
+  EXPECT_EQ(0, result.download_url_idx);
+  EXPECT_TRUE(result.download_url_allowed);
+  EXPECT_EQ(0, result.download_url_num_errors);
+  EXPECT_TRUE(result.do_increment_failures);
+  EXPECT_EQ(Time(), result.backoff_expiry);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedNoBackoffInteractive) {
+  // The UpdateCanStart policy returns false; failures are reported but this is
+  // an interactive update check.
+
+  SetUpdateCheckAllowed(false);
+
+  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(
+      0, ErrorCode::kDownloadTransferError,
+      curr_time - TimeDelta::FromSeconds(8));
+  update_state.download_errors.emplace_back(
+      0, ErrorCode::kDownloadTransferError,
+      curr_time - TimeDelta::FromSeconds(2));
+  update_state.is_interactive = true;
+
+  // Check that UpdateCanStart returns false and a new backoff expiry is
+  // generated.
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_EQ(UpdateCannotStartReason::kUndefined, result.cannot_start_reason);
+  EXPECT_EQ(0, result.download_url_idx);
+  EXPECT_TRUE(result.download_url_allowed);
+  EXPECT_EQ(0, result.download_url_num_errors);
+  EXPECT_TRUE(result.do_increment_failures);
+  EXPECT_EQ(Time(), result.backoff_expiry);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedNoBackoffDelta) {
+  // The UpdateCanStart policy returns false; failures are reported but this is
+  // a delta payload.
+
+  SetUpdateCheckAllowed(false);
+
+  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(
+      0, ErrorCode::kDownloadTransferError,
+      curr_time - TimeDelta::FromSeconds(8));
+  update_state.download_errors.emplace_back(
+      0, ErrorCode::kDownloadTransferError,
+      curr_time - TimeDelta::FromSeconds(2));
+  update_state.is_delta_payload = true;
+
+  // Check that UpdateCanStart returns false and a new backoff expiry is
+  // generated.
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_EQ(UpdateCannotStartReason::kUndefined, result.cannot_start_reason);
+  EXPECT_EQ(0, result.download_url_idx);
+  EXPECT_TRUE(result.download_url_allowed);
+  EXPECT_EQ(0, result.download_url_num_errors);
+  EXPECT_TRUE(result.do_increment_failures);
+  EXPECT_EQ(Time(), result.backoff_expiry);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedNoBackoffUnofficialBuild) {
+  // The UpdateCanStart policy returns false; failures are reported but this is
+  // an unofficial build.
+
+  SetUpdateCheckAllowed(false);
+
+  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(
+      0, ErrorCode::kDownloadTransferError,
+      curr_time - TimeDelta::FromSeconds(8));
+  update_state.download_errors.emplace_back(
+      0, ErrorCode::kDownloadTransferError,
+      curr_time - TimeDelta::FromSeconds(2));
+
+  fake_state_.system_provider()->var_is_official_build()->
+      reset(new bool(false));
+
+  // Check that UpdateCanStart returns false and a new backoff expiry is
+  // generated.
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_EQ(UpdateCannotStartReason::kUndefined, result.cannot_start_reason);
+  EXPECT_EQ(0, result.download_url_idx);
+  EXPECT_TRUE(result.download_url_allowed);
+  EXPECT_EQ(0, result.download_url_num_errors);
+  EXPECT_TRUE(result.do_increment_failures);
+  EXPECT_EQ(Time(), result.backoff_expiry);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartFailsScatteringFailed) {
+  // The UpdateCanStart policy fails because the UpdateScattering policy it
+  // depends on fails (unset variable).
+
+  SetUpdateCheckAllowed(false);
+
+  // Override the default seed variable with a null value so that the policy
+  // request would fail.
+  // TODO(garnold) This failure may or may not fail a number
+  // sub-policies/decisions, like scattering and backoff. We'll need a more
+  // deliberate setup to ensure that we're failing what we want to be failing.
+  fake_state_.random_provider()->var_seed()->reset(nullptr);
+
+  // Check that the UpdateCanStart fails.
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(1));
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kFailed,
+                     &Policy::UpdateCanStart, &result, update_state);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+       UpdateCanStartNotAllowedScatteringNewWaitPeriodApplies) {
+  // The UpdateCanStart policy returns false; device policy is loaded and
+  // scattering applies due to an unsatisfied wait period, which was newly
+  // generated.
+
+  SetUpdateCheckAllowed(false);
+  fake_state_.device_policy_provider()->var_scatter_factor()->reset(
+      new TimeDelta(TimeDelta::FromMinutes(2)));
+
+
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(1));
+
+  // Check that the UpdateCanStart returns false and a new wait period
+  // generated.
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_FALSE(result.update_can_start);
+  EXPECT_EQ(UpdateCannotStartReason::kScattering, result.cannot_start_reason);
+  EXPECT_LT(TimeDelta(), result.scatter_wait_period);
+  EXPECT_EQ(0, result.scatter_check_threshold);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+       UpdateCanStartNotAllowedScatteringPrevWaitPeriodStillApplies) {
+  // The UpdateCanStart policy returns false w/ kAskMeAgainLater; device policy
+  // is loaded and a previously generated scattering period still applies, none
+  // of the scattering values has changed.
+
+  SetUpdateCheckAllowed(false);
+  fake_state_.device_policy_provider()->var_scatter_factor()->reset(
+      new TimeDelta(TimeDelta::FromMinutes(2)));
+
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(1));
+  update_state.scatter_wait_period = TimeDelta::FromSeconds(35);
+
+  // Check that the UpdateCanStart returns false and a new wait period
+  // generated.
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kAskMeAgainLater, &Policy::UpdateCanStart,
+                     &result, update_state);
+  EXPECT_FALSE(result.update_can_start);
+  EXPECT_EQ(UpdateCannotStartReason::kScattering, result.cannot_start_reason);
+  EXPECT_EQ(TimeDelta::FromSeconds(35), result.scatter_wait_period);
+  EXPECT_EQ(0, result.scatter_check_threshold);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+       UpdateCanStartNotAllowedScatteringNewCountThresholdApplies) {
+  // The UpdateCanStart policy returns false; device policy is loaded and
+  // scattering applies due to an unsatisfied update check count threshold.
+  //
+  // This ensures a non-zero check threshold, which may or may not be combined
+  // with a non-zero wait period (for which we cannot reliably control).
+
+  SetUpdateCheckAllowed(false);
+  fake_state_.device_policy_provider()->var_scatter_factor()->reset(
+      new TimeDelta(TimeDelta::FromSeconds(1)));
+
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(1));
+  update_state.scatter_check_threshold_min = 2;
+  update_state.scatter_check_threshold_max = 5;
+
+  // Check that the UpdateCanStart returns false.
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_FALSE(result.update_can_start);
+  EXPECT_EQ(UpdateCannotStartReason::kScattering, result.cannot_start_reason);
+  EXPECT_LE(2, result.scatter_check_threshold);
+  EXPECT_GE(5, result.scatter_check_threshold);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+       UpdateCanStartNotAllowedScatteringPrevCountThresholdStillApplies) {
+  // The UpdateCanStart policy returns false; device policy is loaded and
+  // scattering due to a previously generated count threshold still applies.
+
+  SetUpdateCheckAllowed(false);
+  fake_state_.device_policy_provider()->var_scatter_factor()->reset(
+      new TimeDelta(TimeDelta::FromSeconds(1)));
+
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(1));
+  update_state.scatter_check_threshold = 3;
+  update_state.scatter_check_threshold_min = 2;
+  update_state.scatter_check_threshold_max = 5;
+
+  // Check that the UpdateCanStart returns false.
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_FALSE(result.update_can_start);
+  EXPECT_EQ(UpdateCannotStartReason::kScattering, result.cannot_start_reason);
+  EXPECT_EQ(3, result.scatter_check_threshold);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedScatteringSatisfied) {
+  // The UpdateCanStart policy returns true; device policy is loaded and
+  // scattering is enabled, but both wait period and check threshold are
+  // satisfied.
+
+  SetUpdateCheckAllowed(false);
+  fake_state_.device_policy_provider()->var_scatter_factor()->reset(
+      new TimeDelta(TimeDelta::FromSeconds(120)));
+
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(75));
+  update_state.num_checks = 4;
+  update_state.scatter_wait_period = TimeDelta::FromSeconds(60);
+  update_state.scatter_check_threshold = 3;
+  update_state.scatter_check_threshold_min = 2;
+  update_state.scatter_check_threshold_max = 5;
+
+  // Check that the UpdateCanStart returns true.
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_EQ(TimeDelta(), result.scatter_wait_period);
+  EXPECT_EQ(0, result.scatter_check_threshold);
+  EXPECT_EQ(0, result.download_url_idx);
+  EXPECT_TRUE(result.download_url_allowed);
+  EXPECT_EQ(0, result.download_url_num_errors);
+  EXPECT_FALSE(result.do_increment_failures);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+       UpdateCanStartAllowedInteractivePreventsScattering) {
+  // The UpdateCanStart policy returns true; device policy is loaded and
+  // scattering would have applied, except that the update check is interactive
+  // and so it is suppressed.
+
+  SetUpdateCheckAllowed(false);
+  fake_state_.device_policy_provider()->var_scatter_factor()->reset(
+      new TimeDelta(TimeDelta::FromSeconds(1)));
+
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(1));
+  update_state.is_interactive = true;
+  update_state.scatter_check_threshold = 0;
+  update_state.scatter_check_threshold_min = 2;
+  update_state.scatter_check_threshold_max = 5;
+
+  // Check that the UpdateCanStart returns true.
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_EQ(TimeDelta(), result.scatter_wait_period);
+  EXPECT_EQ(0, result.scatter_check_threshold);
+  EXPECT_EQ(0, result.download_url_idx);
+  EXPECT_TRUE(result.download_url_allowed);
+  EXPECT_EQ(0, result.download_url_num_errors);
+  EXPECT_FALSE(result.do_increment_failures);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+       UpdateCanStartAllowedOobePreventsScattering) {
+  // The UpdateCanStart policy returns true; device policy is loaded and
+  // scattering would have applied, except that OOBE was not completed and so it
+  // is suppressed.
+
+  SetUpdateCheckAllowed(false);
+  fake_state_.device_policy_provider()->var_scatter_factor()->reset(
+      new TimeDelta(TimeDelta::FromSeconds(1)));
+  fake_state_.system_provider()->var_is_oobe_complete()->reset(new bool(false));
+
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(1));
+  update_state.is_interactive = true;
+  update_state.scatter_check_threshold = 0;
+  update_state.scatter_check_threshold_min = 2;
+  update_state.scatter_check_threshold_max = 5;
+
+  // Check that the UpdateCanStart returns true.
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_EQ(TimeDelta(), result.scatter_wait_period);
+  EXPECT_EQ(0, result.scatter_check_threshold);
+  EXPECT_EQ(0, result.download_url_idx);
+  EXPECT_TRUE(result.download_url_allowed);
+  EXPECT_EQ(0, result.download_url_num_errors);
+  EXPECT_FALSE(result.do_increment_failures);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedWithAttributes) {
+  // The UpdateCanStart policy returns true; device policy permits both HTTP and
+  // P2P updates, as well as a non-empty target channel string.
+
+  SetUpdateCheckAllowed(false);
+
+  // Override specific device policy attributes.
+  fake_state_.device_policy_provider()->var_http_downloads_enabled()->reset(
+      new bool(true));
+  fake_state_.device_policy_provider()->var_au_p2p_enabled()->reset(
+      new bool(true));
+
+  // Check that the UpdateCanStart returns true.
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_TRUE(result.p2p_downloading_allowed);
+  EXPECT_TRUE(result.p2p_sharing_allowed);
+  EXPECT_EQ(0, result.download_url_idx);
+  EXPECT_TRUE(result.download_url_allowed);
+  EXPECT_EQ(0, result.download_url_num_errors);
+  EXPECT_FALSE(result.do_increment_failures);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedWithP2PFromUpdater) {
+  // The UpdateCanStart policy returns true; device policy forbids both HTTP and
+  // P2P updates, but the updater is configured to allow P2P and overrules the
+  // setting.
+
+  SetUpdateCheckAllowed(false);
+
+  // Override specific device policy attributes.
+  fake_state_.updater_provider()->var_p2p_enabled()->reset(new bool(true));
+
+  // Check that the UpdateCanStart returns true.
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_TRUE(result.p2p_downloading_allowed);
+  EXPECT_TRUE(result.p2p_sharing_allowed);
+  EXPECT_EQ(0, result.download_url_idx);
+  EXPECT_TRUE(result.download_url_allowed);
+  EXPECT_EQ(0, result.download_url_num_errors);
+  EXPECT_FALSE(result.do_increment_failures);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+       UpdateCanStartAllowedP2PDownloadingBlockedDueToOmaha) {
+  // The UpdateCanStart policy returns true; device policy permits HTTP, but
+  // policy blocks P2P downloading because Omaha forbids it.  P2P sharing is
+  // still permitted.
+
+  SetUpdateCheckAllowed(false);
+
+  // Override specific device policy attributes.
+  fake_state_.device_policy_provider()->var_http_downloads_enabled()->reset(
+      new bool(true));
+  fake_state_.device_policy_provider()->var_au_p2p_enabled()->reset(
+      new bool(true));
+
+  // Check that the UpdateCanStart returns true.
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+  update_state.p2p_downloading_disabled = true;
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_FALSE(result.p2p_downloading_allowed);
+  EXPECT_TRUE(result.p2p_sharing_allowed);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+       UpdateCanStartAllowedP2PSharingBlockedDueToOmaha) {
+  // The UpdateCanStart policy returns true; device policy permits HTTP, but
+  // policy blocks P2P sharing because Omaha forbids it.  P2P downloading is
+  // still permitted.
+
+  SetUpdateCheckAllowed(false);
+
+  // Override specific device policy attributes.
+  fake_state_.device_policy_provider()->var_http_downloads_enabled()->reset(
+      new bool(true));
+  fake_state_.device_policy_provider()->var_au_p2p_enabled()->reset(
+      new bool(true));
+
+  // Check that the UpdateCanStart returns true.
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+  update_state.p2p_sharing_disabled = true;
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_TRUE(result.p2p_downloading_allowed);
+  EXPECT_FALSE(result.p2p_sharing_allowed);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+       UpdateCanStartAllowedP2PDownloadingBlockedDueToNumAttempts) {
+  // The UpdateCanStart policy returns true; device policy permits HTTP but
+  // blocks P2P download, because the max number of P2P downloads have been
+  // attempted. P2P sharing is still permitted.
+
+  SetUpdateCheckAllowed(false);
+
+  // Override specific device policy attributes.
+  fake_state_.device_policy_provider()->var_http_downloads_enabled()->reset(
+      new bool(true));
+  fake_state_.device_policy_provider()->var_au_p2p_enabled()->reset(
+      new bool(true));
+
+  // Check that the UpdateCanStart returns true.
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+  update_state.p2p_num_attempts = ChromeOSPolicy::kMaxP2PAttempts;
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_FALSE(result.p2p_downloading_allowed);
+  EXPECT_TRUE(result.p2p_sharing_allowed);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+       UpdateCanStartAllowedP2PDownloadingBlockedDueToAttemptsPeriod) {
+  // The UpdateCanStart policy returns true; device policy permits HTTP but
+  // blocks P2P download, because the max period for attempt to download via P2P
+  // has elapsed. P2P sharing is still permitted.
+
+  SetUpdateCheckAllowed(false);
+
+  // Override specific device policy attributes.
+  fake_state_.device_policy_provider()->var_http_downloads_enabled()->reset(
+      new bool(true));
+  fake_state_.device_policy_provider()->var_au_p2p_enabled()->reset(
+      new bool(true));
+
+  // Check that the UpdateCanStart returns true.
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+  update_state.p2p_num_attempts = 1;
+  update_state.p2p_first_attempted =
+      fake_clock_.GetWallclockTime() -
+      TimeDelta::FromSeconds(
+          ChromeOSPolicy::kMaxP2PAttemptsPeriodInSeconds + 1);
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_FALSE(result.p2p_downloading_allowed);
+  EXPECT_TRUE(result.p2p_sharing_allowed);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+       UpdateCanStartAllowedWithHttpUrlForUnofficialBuild) {
+  // The UpdateCanStart policy returns true; device policy forbids both HTTP and
+  // P2P updates, but marking this an unofficial build overrules the HTTP
+  // setting.
+
+  SetUpdateCheckAllowed(false);
+
+  // Override specific device policy attributes.
+  fake_state_.device_policy_provider()->var_http_downloads_enabled()->reset(
+      new bool(false));
+  fake_state_.system_provider()->var_is_official_build()->
+      reset(new bool(false));
+
+  // Check that the UpdateCanStart returns true.
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_EQ(0, result.download_url_idx);
+  EXPECT_TRUE(result.download_url_allowed);
+  EXPECT_EQ(0, result.download_url_num_errors);
+  EXPECT_FALSE(result.do_increment_failures);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedWithHttpsUrl) {
+  // The UpdateCanStart policy returns true; device policy forbids both HTTP and
+  // P2P updates, but an HTTPS URL is provided and selected for download.
+
+  SetUpdateCheckAllowed(false);
+
+  // Override specific device policy attributes.
+  fake_state_.device_policy_provider()->var_http_downloads_enabled()->reset(
+      new bool(false));
+
+  // Add an HTTPS URL.
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+  update_state.download_urls.emplace_back("https://secure/url/");
+
+  // Check that the UpdateCanStart returns true.
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_EQ(1, result.download_url_idx);
+  EXPECT_TRUE(result.download_url_allowed);
+  EXPECT_EQ(0, result.download_url_num_errors);
+  EXPECT_FALSE(result.do_increment_failures);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedMaxErrorsNotExceeded) {
+  // The UpdateCanStart policy returns true; the first URL has download errors
+  // but does not exceed the maximum allowed number of failures, so it is stilli
+  // usable.
+
+  SetUpdateCheckAllowed(false);
+
+  // Add a second URL; update with this URL attempted and failed enough times to
+  // disqualify the current (first) URL.
+  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);
+  for (int i = 0; i < 5; i++) {
+    update_state.download_errors.emplace_back(
+        0, ErrorCode::kDownloadTransferError, t);
+    t += TimeDelta::FromSeconds(1);
+  }
+
+  // Check that the UpdateCanStart returns true.
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_EQ(0, result.download_url_idx);
+  EXPECT_TRUE(result.download_url_allowed);
+  EXPECT_EQ(5, result.download_url_num_errors);
+  EXPECT_FALSE(result.do_increment_failures);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedWithSecondUrlMaxExceeded) {
+  // The UpdateCanStart policy returns true; the first URL exceeded the maximum
+  // allowed number of failures, but a second URL is available.
+
+  SetUpdateCheckAllowed(false);
+
+  // Add a second URL; update with this URL attempted and failed enough times to
+  // disqualify the current (first) URL.
+  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);
+  for (int i = 0; i < 11; i++) {
+    update_state.download_errors.emplace_back(
+        0, ErrorCode::kDownloadTransferError, t);
+    t += TimeDelta::FromSeconds(1);
+  }
+
+  // Check that the UpdateCanStart returns true.
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_EQ(1, result.download_url_idx);
+  EXPECT_TRUE(result.download_url_allowed);
+  EXPECT_EQ(0, result.download_url_num_errors);
+  EXPECT_FALSE(result.do_increment_failures);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedWithSecondUrlHardError) {
+  // The UpdateCanStart policy returns true; the first URL fails with a hard
+  // error, but a second URL is available.
+
+  SetUpdateCheckAllowed(false);
+
+  // Add a second URL; update with this URL attempted and failed in a way that
+  // causes it to switch directly to the next URL.
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+  update_state.num_checks = 10;
+  update_state.download_urls.emplace_back("http://another/fake/url/");
+  update_state.download_errors.emplace_back(
+      0, ErrorCode::kPayloadHashMismatchError,
+      fake_clock_.GetWallclockTime() - TimeDelta::FromSeconds(1));
+
+  // Check that the UpdateCanStart returns true.
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_EQ(1, result.download_url_idx);
+  EXPECT_TRUE(result.download_url_allowed);
+  EXPECT_EQ(0, result.download_url_num_errors);
+  EXPECT_FALSE(result.do_increment_failures);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedUrlWrapsAround) {
+  // The UpdateCanStart policy returns true; URL search properly wraps around
+  // the last one on the list.
+
+  SetUpdateCheckAllowed(false);
+
+  // Add a second URL; update with this URL attempted and failed in a way that
+  // causes it to switch directly to the next URL. We must disable backoff in
+  // order for it not to interfere.
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+  update_state.num_checks = 1;
+  update_state.is_backoff_disabled = true;
+  update_state.download_urls.emplace_back("http://another/fake/url/");
+  update_state.download_errors.emplace_back(
+      1, ErrorCode::kPayloadHashMismatchError,
+      fake_clock_.GetWallclockTime() - TimeDelta::FromSeconds(1));
+
+  // Check that the UpdateCanStart returns true.
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_EQ(0, result.download_url_idx);
+  EXPECT_TRUE(result.download_url_allowed);
+  EXPECT_EQ(0, result.download_url_num_errors);
+  EXPECT_TRUE(result.do_increment_failures);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartNotAllowedNoUsableUrls) {
+  // The UpdateCanStart policy returns false; there's a single HTTP URL but its
+  // use is forbidden by policy.
+  //
+  // Note: In the case where no usable URLs are found, the policy should not
+  // increment the number of failed attempts! Doing so would result in a
+  // non-idempotent semantics, and does not fall within the intended purpose of
+  // the backoff mechanism anyway.
+
+  SetUpdateCheckAllowed(false);
+
+  // Override specific device policy attributes.
+  fake_state_.device_policy_provider()->var_http_downloads_enabled()->reset(
+      new bool(false));
+
+  // Check that the UpdateCanStart returns false.
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_FALSE(result.update_can_start);
+  EXPECT_EQ(UpdateCannotStartReason::kCannotDownload,
+            result.cannot_start_reason);
+  EXPECT_FALSE(result.do_increment_failures);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedNoUsableUrlsButP2PEnabled) {
+  // The UpdateCanStart policy returns true; there's a single HTTP URL but its
+  // use is forbidden by policy, however P2P is enabled. The result indicates
+  // that no URL can be used.
+  //
+  // Note: The number of failed attempts should not increase in this case (see
+  // above test).
+
+  SetUpdateCheckAllowed(false);
+
+  // Override specific device policy attributes.
+  fake_state_.device_policy_provider()->var_au_p2p_enabled()->reset(
+      new bool(true));
+  fake_state_.device_policy_provider()->var_http_downloads_enabled()->reset(
+      new bool(false));
+
+  // Check that the UpdateCanStart returns true.
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_TRUE(result.p2p_downloading_allowed);
+  EXPECT_TRUE(result.p2p_sharing_allowed);
+  EXPECT_GT(0, result.download_url_idx);
+  EXPECT_TRUE(result.download_url_allowed);
+  EXPECT_EQ(0, result.download_url_num_errors);
+  EXPECT_FALSE(result.do_increment_failures);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+       UpdateCanStartAllowedNoUsableUrlsButEnterpriseEnrolled) {
+  // The UpdateCanStart policy returns true; there's a single HTTP URL but its
+  // use is forbidden by policy, and P2P is unset on the policy, however the
+  // device is enterprise-enrolled so P2P is allowed. The result indicates that
+  // no URL can be used.
+  //
+  // Note: The number of failed attempts should not increase in this case (see
+  // above test).
+
+  SetUpdateCheckAllowed(false);
+
+  // Override specific device policy attributes.
+  fake_state_.device_policy_provider()->var_au_p2p_enabled()->reset(nullptr);
+  fake_state_.device_policy_provider()->var_owner()->reset(nullptr);
+  fake_state_.device_policy_provider()->var_http_downloads_enabled()->reset(
+      new bool(false));
+
+  // Check that the UpdateCanStart returns true.
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_TRUE(result.p2p_downloading_allowed);
+  EXPECT_TRUE(result.p2p_sharing_allowed);
+  EXPECT_GT(0, result.download_url_idx);
+  EXPECT_TRUE(result.download_url_allowed);
+  EXPECT_EQ(0, result.download_url_num_errors);
+  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
+  // are download URL values, albeit the latter are not allowed to be used.
+
+  SetUpdateCheckAllowed(false);
+  fake_state_.device_policy_provider()->var_scatter_factor()->reset(
+      new TimeDelta(TimeDelta::FromMinutes(2)));
+  fake_state_.updater_provider()->var_p2p_enabled()->reset(new bool(true));
+
+  UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(1));
+  update_state.scatter_wait_period = TimeDelta::FromSeconds(35);
+
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart,
+                     &result, update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_EQ(0, result.download_url_idx);
+  EXPECT_FALSE(result.download_url_allowed);
+  EXPECT_EQ(0, result.download_url_num_errors);
+  EXPECT_TRUE(result.p2p_downloading_allowed);
+  EXPECT_TRUE(result.p2p_sharing_allowed);
+  EXPECT_FALSE(result.do_increment_failures);
+  EXPECT_EQ(TimeDelta::FromSeconds(35), result.scatter_wait_period);
+  EXPECT_EQ(0, result.scatter_check_threshold);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+       UpdateCanStartAllowedBackoffSupressedDueToP2P) {
+  // The UpdateCanStart policy returns true; backoff should have applied, but
+  // P2P download is allowed. Backoff values are nonetheless returned, and so
+  // are download URL values, albeit the latter are not allowed to be used.
+
+  SetUpdateCheckAllowed(false);
+
+  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(
+      0, ErrorCode::kDownloadTransferError,
+      curr_time - TimeDelta::FromSeconds(8));
+  update_state.download_errors.emplace_back(
+      0, ErrorCode::kDownloadTransferError,
+      curr_time - TimeDelta::FromSeconds(2));
+  fake_state_.updater_provider()->var_p2p_enabled()->reset(new bool(true));
+
+  UpdateDownloadParams result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+                     update_state);
+  EXPECT_TRUE(result.update_can_start);
+  EXPECT_EQ(0, result.download_url_idx);
+  EXPECT_FALSE(result.download_url_allowed);
+  EXPECT_EQ(0, result.download_url_num_errors);
+  EXPECT_TRUE(result.p2p_downloading_allowed);
+  EXPECT_TRUE(result.p2p_sharing_allowed);
+  EXPECT_TRUE(result.do_increment_failures);
+  EXPECT_LT(curr_time, result.backoff_expiry);
+}
+
+TEST_F(UmChromeOSPolicyTest, P2PEnabledNotAllowed) {
+  bool result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::P2PEnabled, &result);
+  EXPECT_FALSE(result);
+}
+
+TEST_F(UmChromeOSPolicyTest, P2PEnabledAllowedByDevicePolicy) {
+  fake_state_.device_policy_provider()->var_au_p2p_enabled()->reset(
+      new bool(true));
+
+  bool result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::P2PEnabled, &result);
+  EXPECT_TRUE(result);
+}
+
+TEST_F(UmChromeOSPolicyTest, P2PEnabledAllowedByUpdater) {
+  fake_state_.updater_provider()->var_p2p_enabled()->reset(new bool(true));
+
+  bool result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::P2PEnabled, &result);
+  EXPECT_TRUE(result);
+}
+
+TEST_F(UmChromeOSPolicyTest, P2PEnabledAllowedDeviceEnterpriseEnrolled) {
+  fake_state_.device_policy_provider()->var_au_p2p_enabled()->reset(nullptr);
+  fake_state_.device_policy_provider()->var_owner()->reset(nullptr);
+
+  bool result;
+  ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::P2PEnabled, &result);
+  EXPECT_TRUE(result);
+}
+
+TEST_F(UmChromeOSPolicyTest, P2PEnabledChangedBlocks) {
+  bool result;
+  ExpectPolicyStatus(EvalStatus::kAskMeAgainLater, &Policy::P2PEnabledChanged,
+                     &result, false);
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/config_provider.h b/update_manager/config_provider.h
new file mode 100644
index 0000000..36d57a7
--- /dev/null
+++ b/update_manager/config_provider.h
@@ -0,0 +1,43 @@
+//
+// 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_UPDATE_MANAGER_CONFIG_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_CONFIG_PROVIDER_H_
+
+#include "update_engine/update_manager/provider.h"
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+// Provider for const system configurations. This provider reads the
+// configuration from a file on /etc.
+class ConfigProvider : public Provider {
+ public:
+  // Returns a variable stating whether the out of the box experience (OOBE) is
+  // enabled on this device. A value of false means that the device doesn't have
+  // an OOBE workflow.
+  virtual Variable<bool>* var_is_oobe_enabled() = 0;
+
+ protected:
+  ConfigProvider() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ConfigProvider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_CONFIG_PROVIDER_H_
diff --git a/update_manager/default_policy.cc b/update_manager/default_policy.cc
new file mode 100644
index 0000000..9a5ce7e
--- /dev/null
+++ b/update_manager/default_policy.cc
@@ -0,0 +1,107 @@
+//
+// 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/update_manager/default_policy.h"
+
+namespace {
+
+// A fixed minimum interval between consecutive allowed update checks. This
+// needs to be long enough to prevent busywork and/or DDoS attacks on Omaha, but
+// at the same time short enough to allow the machine to update itself
+// reasonably soon.
+const int kCheckIntervalInSeconds = 15 * 60;
+
+}  // namespace
+
+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->target_version_prefix.clear();
+  result->is_interactive = false;
+
+  // Ensure that the minimum interval is set. If there's no clock, this defaults
+  // to always allowing the update.
+  if (!aux_state_->IsLastCheckAllowedTimeSet() ||
+      ec->IsMonotonicTimeGreaterThan(
+          aux_state_->last_check_allowed_time() +
+          base::TimeDelta::FromSeconds(kCheckIntervalInSeconds))) {
+    if (clock_)
+      aux_state_->set_last_check_allowed_time(clock_->GetMonotonicTime());
+    return EvalStatus::kSucceeded;
+  }
+
+  return EvalStatus::kAskMeAgainLater;
+}
+
+EvalStatus DefaultPolicy::UpdateCanStart(
+    EvaluationContext* ec,
+    State* state,
+    std::string* error,
+    UpdateDownloadParams* result,
+    const UpdateState update_state) const {
+  result->update_can_start = true;
+  result->cannot_start_reason = UpdateCannotStartReason::kUndefined;
+  result->download_url_idx = 0;
+  result->download_url_allowed = true;
+  result->download_url_num_errors = 0;
+  result->p2p_downloading_allowed = false;
+  result->p2p_sharing_allowed = false;
+  result->do_increment_failures = false;
+  result->backoff_expiry = base::Time();
+  result->scatter_wait_period = base::TimeDelta();
+  result->scatter_check_threshold = 0;
+  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,
+    bool* result) const {
+  *result = false;
+  return EvalStatus::kSucceeded;
+}
+
+EvalStatus DefaultPolicy::P2PEnabledChanged(
+    EvaluationContext* ec,
+    State* state,
+    std::string* error,
+    bool* result,
+    bool prev_result) const {
+  // This policy will always prohibit P2P, so this is signaling to the caller
+  // that the decision is final (because the current value is the same as the
+  // previous one) and there's no need to issue another call.
+  *result = false;
+  return EvalStatus::kSucceeded;
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/default_policy.h b/update_manager/default_policy.h
new file mode 100644
index 0000000..3f41178
--- /dev/null
+++ b/update_manager/default_policy.h
@@ -0,0 +1,105 @@
+//
+// 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_UPDATE_MANAGER_DEFAULT_POLICY_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_DEFAULT_POLICY_H_
+
+#include <memory>
+#include <string>
+
+#include <base/time/time.h>
+
+#include "update_engine/common/clock_interface.h"
+#include "update_engine/update_manager/policy.h"
+
+namespace chromeos_update_manager {
+
+// Auxiliary state class for DefaultPolicy evaluations.
+//
+// IMPORTANT: The use of a state object in policies is generally forbidden, as
+// it was a design decision to keep policy calls side-effect free. We make an
+// exception here to ensure that DefaultPolicy indeed serves as a safe (and
+// secure) fallback option. This practice should be avoided when imlpementing
+// other policies.
+class DefaultPolicyState {
+ public:
+  DefaultPolicyState() {}
+
+  bool IsLastCheckAllowedTimeSet() const {
+    return last_check_allowed_time_ != base::Time::Max();
+  }
+
+  // Sets/returns the point time on the monotonic time scale when the latest
+  // check allowed was recorded.
+  void set_last_check_allowed_time(base::Time timestamp) {
+    last_check_allowed_time_ = timestamp;
+  }
+  base::Time last_check_allowed_time() const {
+    return last_check_allowed_time_;
+  }
+
+ private:
+  base::Time last_check_allowed_time_ = base::Time::Max();
+};
+
+// The DefaultPolicy is a safe Policy implementation that doesn't fail. The
+// values returned by this policy are safe default in case of failure of the
+// actual policy being used by the UpdateManager.
+class DefaultPolicy : public Policy {
+ public:
+  explicit DefaultPolicy(chromeos_update_engine::ClockInterface* clock);
+  DefaultPolicy() : DefaultPolicy(nullptr) {}
+  ~DefaultPolicy() override {}
+
+  // Policy overrides.
+  EvalStatus UpdateCheckAllowed(
+      EvaluationContext* ec, State* state, std::string* error,
+      UpdateCheckParams* result) const override;
+
+  EvalStatus UpdateCanStart(
+      EvaluationContext* ec, State* state, std::string* error,
+      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,
+      bool* result) const override;
+
+  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 "DefaultPolicy"; }
+
+ private:
+  // A clock interface.
+  chromeos_update_engine::ClockInterface* clock_;
+
+  // An auxiliary state object.
+  std::unique_ptr<DefaultPolicyState> aux_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(DefaultPolicy);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_DEFAULT_POLICY_H_
diff --git a/update_manager/device_policy_provider.h b/update_manager/device_policy_provider.h
new file mode 100644
index 0000000..f75d470
--- /dev/null
+++ b/update_manager/device_policy_provider.h
@@ -0,0 +1,75 @@
+//
+// 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_UPDATE_MANAGER_DEVICE_POLICY_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_DEVICE_POLICY_PROVIDER_H_
+
+#include <set>
+#include <string>
+
+#include <base/time/time.h>
+#include <policy/libpolicy.h>
+
+#include "update_engine/update_manager/provider.h"
+#include "update_engine/update_manager/shill_provider.h"
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+// Provides access to the current DevicePolicy.
+class DevicePolicyProvider : public Provider {
+ public:
+  ~DevicePolicyProvider() override {}
+
+  // Variable stating whether the DevicePolicy was loaded.
+  virtual Variable<bool>* var_device_policy_is_loaded() = 0;
+
+  // Variables mapping the information received on the DevicePolicy protobuf.
+  virtual Variable<std::string>* var_release_channel() = 0;
+
+  virtual Variable<bool>* var_release_channel_delegated() = 0;
+
+  virtual Variable<bool>* var_update_disabled() = 0;
+
+  virtual Variable<std::string>* var_target_version_prefix() = 0;
+
+  // Returns a non-negative scatter interval used for updates.
+  virtual Variable<base::TimeDelta>* var_scatter_factor() = 0;
+
+  // Variable returning the set of connection types allowed for updates. The
+  // identifiers returned are consistent with the ones returned by the
+  // ShillProvider.
+  virtual Variable<std::set<ConnectionType>>*
+      var_allowed_connection_types_for_update() = 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;
+
+  virtual Variable<bool>* var_au_p2p_enabled() = 0;
+
+ protected:
+  DevicePolicyProvider() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DevicePolicyProvider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_DEVICE_POLICY_PROVIDER_H_
diff --git a/update_manager/evaluation_context-inl.h b/update_manager/evaluation_context-inl.h
new file mode 100644
index 0000000..937adf4
--- /dev/null
+++ b/update_manager/evaluation_context-inl.h
@@ -0,0 +1,55 @@
+//
+// 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_UPDATE_MANAGER_EVALUATION_CONTEXT_INL_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_EVALUATION_CONTEXT_INL_H_
+
+#include <string>
+
+#include <base/logging.h>
+
+namespace chromeos_update_manager {
+
+template<typename T>
+const T* EvaluationContext::GetValue(Variable<T>* var) {
+  if (var == nullptr) {
+    LOG(ERROR) << "GetValue received an uninitialized variable.";
+    return nullptr;
+  }
+
+  // Search for the value on the cache first.
+  ValueCacheMap::iterator it = value_cache_.find(var);
+  if (it != value_cache_.end())
+    return reinterpret_cast<const T*>(it->second.value());
+
+  // Get the value from the variable if not found on the cache.
+  std::string errmsg;
+  const T* result = var->GetValue(RemainingTime(evaluation_monotonic_deadline_),
+                                  &errmsg);
+  if (result == nullptr) {
+    LOG(WARNING) << "Error reading Variable " << var->GetName() << ": \""
+        << errmsg << "\"";
+  }
+  // Cache the value for the next time. The map of CachedValues keeps the
+  // ownership of the pointer until the map is destroyed.
+  value_cache_.emplace(
+    static_cast<BaseVariable*>(var), BoxedValue(result));
+  return result;
+}
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_EVALUATION_CONTEXT_INL_H_
diff --git a/update_manager/evaluation_context.cc b/update_manager/evaluation_context.cc
new file mode 100644
index 0000000..03ac0b7
--- /dev/null
+++ b/update_manager/evaluation_context.cc
@@ -0,0 +1,252 @@
+//
+// 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/update_manager/evaluation_context.h"
+
+#include <algorithm>
+#include <memory>
+#include <string>
+
+#include <base/bind.h>
+#include <base/json/json_writer.h>
+#include <base/location.h>
+#include <base/strings/string_util.h>
+#include <base/values.h>
+
+#include "update_engine/common/utils.h"
+
+using base::Callback;
+using base::Closure;
+using base::Time;
+using base::TimeDelta;
+using brillo::MessageLoop;
+using chromeos_update_engine::ClockInterface;
+using std::string;
+using std::unique_ptr;
+
+namespace {
+
+// Returns whether |curr_time| surpassed |ref_time|; if not, also checks whether
+// |ref_time| is sooner than the current value of |*reeval_time|, in which case
+// the latter is updated to the former.
+bool IsTimeGreaterThanHelper(Time ref_time, Time curr_time,
+                             Time* reeval_time) {
+  if (curr_time > ref_time)
+    return true;
+  // Remember the nearest reference we've checked against in this evaluation.
+  if (*reeval_time > ref_time)
+    *reeval_time = ref_time;
+  return false;
+}
+
+// If |expires| never happens (maximal value), returns the maximal interval;
+// otherwise, returns the difference between |expires| and |curr|.
+TimeDelta GetTimeout(Time curr, Time expires) {
+  if (expires.is_max())
+    return TimeDelta::Max();
+  return expires - curr;
+}
+
+}  // namespace
+
+namespace chromeos_update_manager {
+
+EvaluationContext::EvaluationContext(
+    ClockInterface* clock,
+    TimeDelta evaluation_timeout,
+    TimeDelta expiration_timeout,
+    unique_ptr<Callback<void(EvaluationContext*)>> unregister_cb)
+    : clock_(clock),
+      evaluation_timeout_(evaluation_timeout),
+      expiration_timeout_(expiration_timeout),
+      unregister_cb_(std::move(unregister_cb)),
+      weak_ptr_factory_(this) {
+  ResetEvaluation();
+  ResetExpiration();
+}
+
+EvaluationContext::~EvaluationContext() {
+  RemoveObserversAndTimeout();
+  if (unregister_cb_.get())
+    unregister_cb_->Run(this);
+}
+
+unique_ptr<Closure> EvaluationContext::RemoveObserversAndTimeout() {
+  for (auto& it : value_cache_) {
+    if (it.first->GetMode() == kVariableModeAsync)
+      it.first->RemoveObserver(this);
+  }
+  MessageLoop::current()->CancelTask(timeout_event_);
+  timeout_event_ = MessageLoop::kTaskIdNull;
+
+  return unique_ptr<Closure>(callback_.release());
+}
+
+TimeDelta EvaluationContext::RemainingTime(Time monotonic_deadline) const {
+  if (monotonic_deadline.is_max())
+    return TimeDelta::Max();
+  TimeDelta remaining = monotonic_deadline - clock_->GetMonotonicTime();
+  return std::max(remaining, TimeDelta());
+}
+
+Time EvaluationContext::MonotonicDeadline(TimeDelta timeout) {
+  return (timeout.is_max() ? Time::Max() :
+          clock_->GetMonotonicTime() + timeout);
+}
+
+void EvaluationContext::ValueChanged(BaseVariable* var) {
+  DLOG(INFO) << "ValueChanged() called for variable " << var->GetName();
+  OnValueChangedOrTimeout();
+}
+
+void EvaluationContext::OnTimeout() {
+  DLOG(INFO) << "OnTimeout() called due to "
+             << (timeout_marks_expiration_ ? "expiration" : "poll interval");
+  timeout_event_ = MessageLoop::kTaskIdNull;
+  is_expired_ = timeout_marks_expiration_;
+  OnValueChangedOrTimeout();
+}
+
+void EvaluationContext::OnValueChangedOrTimeout() {
+  // Copy the callback handle locally, allowing it to be reassigned.
+  unique_ptr<Closure> callback = RemoveObserversAndTimeout();
+
+  if (callback.get())
+    callback->Run();
+}
+
+bool EvaluationContext::IsWallclockTimeGreaterThan(Time timestamp) {
+  return IsTimeGreaterThanHelper(timestamp, evaluation_start_wallclock_,
+                                 &reevaluation_time_wallclock_);
+}
+
+bool EvaluationContext::IsMonotonicTimeGreaterThan(Time timestamp) {
+  return IsTimeGreaterThanHelper(timestamp, evaluation_start_monotonic_,
+                                 &reevaluation_time_monotonic_);
+}
+
+void EvaluationContext::ResetEvaluation() {
+  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_);
+
+  // Remove the cached values of non-const variables
+  for (auto it = value_cache_.begin(); it != value_cache_.end(); ) {
+    if (it->first->GetMode() == kVariableModeConst) {
+      ++it;
+    } else {
+      it = value_cache_.erase(it);
+    }
+  }
+}
+
+void EvaluationContext::ResetExpiration() {
+  expiration_monotonic_deadline_ = MonotonicDeadline(expiration_timeout_);
+  is_expired_ = false;
+}
+
+bool EvaluationContext::RunOnValueChangeOrTimeout(Closure callback) {
+  // Check that the method was not called more than once.
+  if (callback_.get()) {
+    LOG(ERROR) << "RunOnValueChangeOrTimeout called more than once.";
+    return false;
+  }
+
+  // Check that the context did not yet expire.
+  if (is_expired()) {
+    LOG(ERROR) << "RunOnValueChangeOrTimeout called on an expired context.";
+    return false;
+  }
+
+  // Handle reevaluation due to a Is{Wallclock,Monotonic}TimeGreaterThan(). We
+  // choose the smaller of the differences between evaluation start time and
+  // reevaluation time among the wallclock and monotonic scales.
+  TimeDelta timeout = std::min(
+      GetTimeout(evaluation_start_wallclock_, reevaluation_time_wallclock_),
+      GetTimeout(evaluation_start_monotonic_, reevaluation_time_monotonic_));
+
+  // Handle reevaluation due to async or poll variables.
+  bool waiting_for_value_change = false;
+  for (auto& it : value_cache_) {
+    switch (it.first->GetMode()) {
+      case kVariableModeAsync:
+        DLOG(INFO) << "Waiting for value on " << it.first->GetName();
+        it.first->AddObserver(this);
+        waiting_for_value_change = true;
+        break;
+      case kVariableModePoll:
+        timeout = std::min(timeout, it.first->GetPollInterval());
+        break;
+      case kVariableModeConst:
+        // Ignored.
+        break;
+    }
+  }
+
+  // Check if the re-evaluation is actually being scheduled. If there are no
+  // events waited for, this function should return false.
+  if (!waiting_for_value_change && timeout.is_max())
+    return false;
+
+  // Ensure that we take into account the expiration timeout.
+  TimeDelta expiration = RemainingTime(expiration_monotonic_deadline_);
+  timeout_marks_expiration_ = expiration < timeout;
+  if (timeout_marks_expiration_)
+    timeout = expiration;
+
+  // Store the reevaluation callback.
+  callback_.reset(new Closure(callback));
+
+  // Schedule a timeout event, if one is set.
+  if (!timeout.is_max()) {
+    DLOG(INFO) << "Waiting for timeout in "
+               << chromeos_update_engine::utils::FormatTimeDelta(timeout);
+    timeout_event_ = MessageLoop::current()->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&EvaluationContext::OnTimeout,
+                   weak_ptr_factory_.GetWeakPtr()),
+        timeout);
+  }
+
+  return true;
+}
+
+string EvaluationContext::DumpContext() const {
+  base::DictionaryValue* variables = new base::DictionaryValue();
+  for (auto& it : value_cache_) {
+    variables->SetString(it.first->GetName(), it.second.ToString());
+  }
+
+  base::DictionaryValue value;
+  value.Set("variables", variables);  // Adopts |variables|.
+  value.SetString(
+      "evaluation_start_wallclock",
+      chromeos_update_engine::utils::ToString(evaluation_start_wallclock_));
+  value.SetString(
+      "evaluation_start_monotonic",
+      chromeos_update_engine::utils::ToString(evaluation_start_monotonic_));
+
+  string json_str;
+  base::JSONWriter::WriteWithOptions(
+      value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_str);
+  base::TrimWhitespaceASCII(json_str, base::TRIM_TRAILING, &json_str);
+
+  return json_str;
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/evaluation_context.h b/update_manager/evaluation_context.h
new file mode 100644
index 0000000..df5816a
--- /dev/null
+++ b/update_manager/evaluation_context.h
@@ -0,0 +1,221 @@
+//
+// 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_UPDATE_MANAGER_EVALUATION_CONTEXT_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_EVALUATION_CONTEXT_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#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"
+
+namespace chromeos_update_manager {
+
+// The EvaluationContext class is the interface between a policy implementation
+// and the state. The EvaluationContext tracks the variables used by a policy
+// request and caches the returned values, owning those cached values.
+// The same EvaluationContext should be re-used for all the evaluations of the
+// same policy request (an AsyncPolicyRequest might involve several
+// re-evaluations). Each evaluation of the EvaluationContext is run at a given
+// point in time, which is used as a reference for the evaluation timeout and
+// the time based queries of the policy, such as
+// Is{Wallclock,Monotonic}TimeGreaterThan().
+//
+// Example:
+//
+//   scoped_refptr<EvaluationContext> ec = new EvaluationContext(...);
+//
+//   ...
+//   // The following call to ResetEvaluation() is optional. Use it to reset the
+//   // evaluation time if the EvaluationContext isn't used right after its
+//   // construction.
+//   ec->ResetEvaluation();
+//   EvalStatus status = policy->SomeMethod(ec, state, &result, args...);
+//
+//   ...
+//   // Run a closure when any of the used async variables changes its value or
+//   // the timeout for re-query the values happens again.
+//   ec->RunOnValueChangeOrTimeout(closure);
+//   // If the provided |closure| wants to re-evaluate the policy, it should
+//   // call ec->ResetEvaluation() to start a new evaluation.
+//
+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);
+  EvaluationContext(chromeos_update_engine::ClockInterface* clock,
+                    base::TimeDelta evaluation_timeout)
+      : EvaluationContext(
+          clock, evaluation_timeout, base::TimeDelta::Max(),
+          std::unique_ptr<base::Callback<void(EvaluationContext*)>>()) {}
+  ~EvaluationContext();
+
+  // Returns a pointer to the value returned by the passed variable |var|. The
+  // EvaluationContext instance keeps the ownership of the returned object. The
+  // returned object is valid during the life of the evaluation, even if the
+  // passed Variable changes it.
+  //
+  // In case of error, a null value is returned.
+  template<typename T>
+  const T* GetValue(Variable<T>* var);
+
+  // Returns whether the evaluation time has surpassed |timestamp|, on either
+  // the ClockInterface::GetWallclockTime() or
+  // ClockInterface::GetMonotonicTime() scales, respectively.
+  bool IsWallclockTimeGreaterThan(base::Time timestamp);
+  bool IsMonotonicTimeGreaterThan(base::Time timestamp);
+
+  // Returns whether the evaluation context has expired.
+  bool is_expired() const { return is_expired_; }
+
+  // TODO(deymo): Move the following methods to an interface only visible by the
+  // UpdateManager class and not the policy implementations.
+
+  // Resets the EvaluationContext to its initial state removing all the
+  // non-const cached variables and re-setting the evaluation time. This should
+  // be called right before any new evaluation starts.
+  void ResetEvaluation();
+
+  // Clears the expiration status of the EvaluationContext and resets its
+  // expiration timeout based on |expiration_timeout_|. This should be called if
+  // expiration occurred, prior to re-evaluating the policy.
+  void ResetExpiration();
+
+  // Schedules the passed |callback| closure to be called when a cached
+  // variable changes its value, a polling interval passes, or the context
+  // expiration occurs. If none of these events can happen, for example if
+  // there's no cached variable, this method returns false.
+  //
+  // Right before the passed closure is called the EvaluationContext is
+  // reseted, removing all the non-const cached values.
+  bool RunOnValueChangeOrTimeout(base::Closure callback);
+
+  // Returns a textual representation of the evaluation context,
+  // including the variables and their values. This is intended only
+  // to help with debugging and the format may change in the future.
+  std::string DumpContext() const;
+
+  // Removes all the Observers callbacks and timeout events scheduled by
+  // RunOnValueChangeOrTimeout(). Also releases and returns the closure
+  // associated with these events. This method is idempotent.
+  std::unique_ptr<base::Closure> RemoveObserversAndTimeout();
+
+ private:
+  friend class UmEvaluationContextTest;
+
+  // BaseVariable::ObserverInterface override.
+  void ValueChanged(BaseVariable* var) override;
+
+  // Called from the main loop when a scheduled timeout has passed.
+  void OnTimeout();
+
+  // Removes the observers from the used Variables and cancels the timeout,
+  // then executes the scheduled callback.
+  void OnValueChangedOrTimeout();
+
+  // If |monotonic_deadline| is not Time::Max(), returns the remaining time
+  // until it is reached, or zero if it has passed. Otherwise, returns
+  // TimeDelta::Max().
+  base::TimeDelta RemainingTime(base::Time monotonic_deadline) const;
+
+  // Returns a monotonic clock timestamp at which |timeout| will have elapsed
+  // since the current time.
+  base::Time MonotonicDeadline(base::TimeDelta timeout);
+
+  // A map to hold the cached values for every variable.
+  typedef std::map<BaseVariable*, BoxedValue> ValueCacheMap;
+
+  // The cached values of the called Variables.
+  ValueCacheMap value_cache_;
+
+  // A callback used for triggering re-evaluation upon a value change or poll
+  // timeout, or notifying about the evaluation context expiration. It is up to
+  // the caller to determine whether or not expiration occurred via
+  // is_expired().
+  std::unique_ptr<base::Closure> callback_;
+
+  // The TaskId returned by the message loop identifying the timeout callback.
+  // Used for canceling the timeout callback.
+  brillo::MessageLoop::TaskId timeout_event_ =
+      brillo::MessageLoop::kTaskIdNull;
+
+  // Whether a timeout event firing marks the expiration of the evaluation
+  // context.
+  bool timeout_marks_expiration_;
+
+  // 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
+  // every time ResetEvaluation() is called.
+  base::Time evaluation_start_wallclock_;
+  base::Time evaluation_start_monotonic_;
+
+  // The timestamps when a reevaluation should be triggered due to various
+  // expected value changes, corresponding to ClockInterface::GetWallclockTime()
+  // and ClockInterface::GetMonotonicTIme(), respectively. These timestamps are
+  // greater or equal to corresponding |evaluation_start_{wallclock,monotonic}_|
+  // counterparts since they are in the future; however, they may be smaller
+  // than the current corresponding times during the course of evaluation.
+  base::Time reevaluation_time_wallclock_;
+  base::Time reevaluation_time_monotonic_;
+
+  // The timeout of an evaluation.
+  const base::TimeDelta evaluation_timeout_;
+
+  // The timestamp in the ClockInterface::GetMonotonicTime() scale at which the
+  // current evaluation should finish.
+  base::Time evaluation_monotonic_deadline_;
+
+  // The expiration timeout of the evaluation context.
+  const base::TimeDelta expiration_timeout_;
+
+  // The monotonic clock deadline at which expiration occurs.
+  base::Time expiration_monotonic_deadline_;
+
+  // A callback for unregistering the context upon destruction.
+  std::unique_ptr<base::Callback<void(EvaluationContext*)>> unregister_cb_;
+
+  base::WeakPtrFactory<EvaluationContext> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(EvaluationContext);
+};
+
+}  // namespace chromeos_update_manager
+
+// Include the implementation of the template methods.
+#include "update_engine/update_manager/evaluation_context-inl.h"
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_EVALUATION_CONTEXT_H_
diff --git a/update_manager/evaluation_context_unittest.cc b/update_manager/evaluation_context_unittest.cc
new file mode 100644
index 0000000..1e61db7
--- /dev/null
+++ b/update_manager/evaluation_context_unittest.cc
@@ -0,0 +1,489 @@
+//
+// 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/update_manager/evaluation_context.h"
+
+#include <memory>
+#include <string>
+
+#include <base/bind.h>
+#include <brillo/message_loops/fake_message_loop.h>
+#include <brillo/message_loops/message_loop_utils.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/fake_clock.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"
+#include "update_engine/update_manager/umtest_utils.h"
+
+using base::Bind;
+using base::Closure;
+using base::Time;
+using base::TimeDelta;
+using brillo::MessageLoop;
+using brillo::MessageLoopRunMaxIterations;
+using brillo::MessageLoopRunUntil;
+using chromeos_update_engine::FakeClock;
+using std::string;
+using std::unique_ptr;
+using testing::Return;
+using testing::StrictMock;
+using testing::_;
+
+namespace chromeos_update_manager {
+
+namespace {
+
+void DoNothing() {}
+
+// Sets the value of the passed pointer to true.
+void SetTrue(bool* value) {
+  *value = true;
+}
+
+bool GetBoolean(bool* value) {
+  return *value;
+}
+
+template<typename T>
+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, scoped_refptr<EvaluationContext> ec,
+                        int* count_p, bool* done_p) {
+  evaluation.Run();
+
+  // Schedule reevaluation if needed.
+  if (*count_p > 0) {
+    Closure closure = Bind(EvaluateRepeatedly, evaluation, ec, count_p, done_p);
+    ASSERT_TRUE(ec->RunOnValueChangeOrTimeout(closure))
+        << "Failed to schedule reevaluation, count_p=" << *count_p;
+    (*count_p)--;
+  } else {
+    *done_p = true;
+  }
+}
+
+}  // namespace
+
+class UmEvaluationContextTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    loop_.SetAsCurrent();
+    // Apr 22, 2009 19:25:00 UTC (this is a random reference point).
+    fake_clock_.SetMonotonicTime(Time::FromTimeT(1240428300));
+    // Mar 2, 2006 1:23:45 UTC.
+    fake_clock_.SetWallclockTime(Time::FromTimeT(1141262625));
+    eval_ctx_ = new EvaluationContext(
+        &fake_clock_, default_timeout_, default_timeout_,
+        unique_ptr<base::Callback<void(EvaluationContext*)>>(nullptr));
+  }
+
+  void TearDown() override {
+    // Ensure that the evaluation context did not leak and is actually being
+    // destroyed.
+    if (eval_ctx_) {
+      base::WeakPtr<EvaluationContext> eval_ctx_weak_alias =
+          eval_ctx_->weak_ptr_factory_.GetWeakPtr();
+      ASSERT_NE(nullptr, eval_ctx_weak_alias.get());
+      eval_ctx_ = nullptr;
+      EXPECT_EQ(nullptr, eval_ctx_weak_alias.get())
+          << "The evaluation context was not destroyed! This is likely a bug "
+             "in how the test was written, look for leaking handles to the EC, "
+             "possibly through closure objects.";
+    }
+
+    // Check that the evaluation context removed all the observers.
+    EXPECT_TRUE(fake_int_var_.observer_list_.empty());
+    EXPECT_TRUE(fake_async_var_.observer_list_.empty());
+    EXPECT_TRUE(fake_const_var_.observer_list_.empty());
+    EXPECT_TRUE(fake_poll_var_.observer_list_.empty());
+
+    EXPECT_FALSE(loop_.PendingTasks());
+  }
+
+  TimeDelta default_timeout_ = TimeDelta::FromSeconds(5);
+
+  brillo::FakeMessageLoop loop_{nullptr};
+  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
+  // test does, which keeps a reference to them.
+  FakeVariable<bool> fail_var_ = {"fail_var", kVariableModePoll};
+  FakeVariable<int> fake_int_var_ = {"fake_int", kVariableModePoll};
+  FakeVariable<string> fake_async_var_ = {"fake_async", kVariableModeAsync};
+  FakeVariable<string> fake_const_var_ = {"fake_const", kVariableModeConst};
+  FakeVariable<string> fake_poll_var_ = {"fake_poll",
+                                         TimeDelta::FromSeconds(1)};
+  StrictMock<MockVariable<string>> mock_var_async_ {
+    "mock_var_async", kVariableModeAsync};
+  StrictMock<MockVariable<string>> mock_var_poll_ {
+    "mock_var_poll", kVariableModePoll};
+};
+
+TEST_F(UmEvaluationContextTest, GetValueFails) {
+  // FakeVariable is initialized as returning null.
+  EXPECT_EQ(nullptr, eval_ctx_->GetValue(&fake_int_var_));
+}
+
+TEST_F(UmEvaluationContextTest, GetValueFailsWithInvalidVar) {
+  EXPECT_EQ(nullptr, eval_ctx_->GetValue(static_cast<Variable<int>*>(nullptr)));
+}
+
+TEST_F(UmEvaluationContextTest, GetValueReturns) {
+  const int* p_fake_int;
+
+  fake_int_var_.reset(new int(42));
+  p_fake_int = eval_ctx_->GetValue(&fake_int_var_);
+  ASSERT_NE(nullptr, p_fake_int);
+  EXPECT_EQ(42, *p_fake_int);
+}
+
+TEST_F(UmEvaluationContextTest, GetValueCached) {
+  const int* p_fake_int;
+
+  fake_int_var_.reset(new int(42));
+  p_fake_int = eval_ctx_->GetValue(&fake_int_var_);
+
+  // Check that if the variable changes, the EvaluationContext keeps returning
+  // the cached value.
+  fake_int_var_.reset(new int(5));
+
+  p_fake_int = eval_ctx_->GetValue(&fake_int_var_);
+  ASSERT_NE(nullptr, p_fake_int);
+  EXPECT_EQ(42, *p_fake_int);
+}
+
+TEST_F(UmEvaluationContextTest, GetValueCachesNull) {
+  const int* p_fake_int = eval_ctx_->GetValue(&fake_int_var_);
+  EXPECT_EQ(nullptr, p_fake_int);
+
+  fake_int_var_.reset(new int(42));
+  // A second attempt to read the variable should not work because this
+  // EvaluationContext already got a null value.
+  p_fake_int = eval_ctx_->GetValue(&fake_int_var_);
+  EXPECT_EQ(nullptr, p_fake_int);
+}
+
+TEST_F(UmEvaluationContextTest, GetValueMixedTypes) {
+  const int* p_fake_int;
+  const string* p_fake_string;
+
+  fake_int_var_.reset(new int(42));
+  fake_poll_var_.reset(new string("Hello world!"));
+  // Check that the EvaluationContext can handle multiple Variable types. This
+  // is mostly a compile-time check due to the template nature of this method.
+  p_fake_int = eval_ctx_->GetValue(&fake_int_var_);
+  p_fake_string = eval_ctx_->GetValue(&fake_poll_var_);
+
+  ASSERT_NE(nullptr, p_fake_int);
+  EXPECT_EQ(42, *p_fake_int);
+
+  ASSERT_NE(nullptr, p_fake_string);
+  EXPECT_EQ("Hello world!", *p_fake_string);
+}
+
+// Test that we don't schedule an event if there's no variable to wait for.
+TEST_F(UmEvaluationContextTest, RunOnValueChangeOrTimeoutWithoutVariables) {
+  fake_const_var_.reset(new string("Hello world!"));
+  EXPECT_EQ(*eval_ctx_->GetValue(&fake_const_var_), "Hello world!");
+
+  EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&DoNothing)));
+}
+
+// Test that reevaluation occurs when an async variable it depends on changes.
+TEST_F(UmEvaluationContextTest, RunOnValueChangeOrTimeoutWithVariables) {
+  fake_async_var_.reset(new string("Async value"));
+  eval_ctx_->GetValue(&fake_async_var_);
+
+  bool value = false;
+  EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
+  // Check that the scheduled callback isn't run until we signal a ValueChaged.
+  MessageLoopRunMaxIterations(MessageLoop::current(), 100);
+  EXPECT_FALSE(value);
+
+  fake_async_var_.NotifyValueChanged();
+  EXPECT_FALSE(value);
+  // Ensure that the scheduled callback isn't run until we are back on the main
+  // loop.
+  MessageLoopRunMaxIterations(MessageLoop::current(), 100);
+  EXPECT_TRUE(value);
+}
+
+// Test that we don't re-schedule the events if we are attending one.
+TEST_F(UmEvaluationContextTest, RunOnValueChangeOrTimeoutCalledTwice) {
+  fake_async_var_.reset(new string("Async value"));
+  eval_ctx_->GetValue(&fake_async_var_);
+
+  bool value = false;
+  EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
+  EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
+
+  // The scheduled event should still work.
+  fake_async_var_.NotifyValueChanged();
+  MessageLoopRunMaxIterations(MessageLoop::current(), 100);
+  EXPECT_TRUE(value);
+}
+
+// Test that reevaluation occurs when a polling timeout fires.
+TEST_F(UmEvaluationContextTest, RunOnValueChangeOrTimeoutRunsFromTimeout) {
+  fake_poll_var_.reset(new string("Polled value"));
+  eval_ctx_->GetValue(&fake_poll_var_);
+
+  bool value = false;
+  EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
+  // Check that the scheduled callback isn't run until the timeout occurs.
+  MessageLoopRunMaxIterations(MessageLoop::current(), 10);
+  EXPECT_FALSE(value);
+  MessageLoopRunUntil(MessageLoop::current(),
+                      TimeDelta::FromSeconds(10),
+                      Bind(&GetBoolean, &value));
+  EXPECT_TRUE(value);
+}
+
+// Test that callback is called when evaluation context expires, and that it
+// cannot be used again unless the expiration deadline is reset.
+TEST_F(UmEvaluationContextTest, RunOnValueChangeOrTimeoutExpires) {
+  fake_async_var_.reset(new string("Async value"));
+  eval_ctx_->GetValue(&fake_async_var_);
+
+  bool value = false;
+  EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
+  // Check that the scheduled callback isn't run until the timeout occurs.
+  MessageLoopRunMaxIterations(MessageLoop::current(), 10);
+  EXPECT_FALSE(value);
+  MessageLoopRunUntil(MessageLoop::current(),
+                      TimeDelta::FromSeconds(10),
+                      Bind(&GetBoolean, &value));
+  EXPECT_TRUE(value);
+
+  // Ensure that we cannot reschedule an evaluation.
+  EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&DoNothing)));
+
+  // Ensure that we can reschedule an evaluation after resetting expiration.
+  eval_ctx_->ResetExpiration();
+  EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&DoNothing)));
+}
+
+// Test that we clear the events when destroying the EvaluationContext.
+TEST_F(UmEvaluationContextTest, RemoveObserversAndTimeoutTest) {
+  fake_async_var_.reset(new string("Async value"));
+  eval_ctx_->GetValue(&fake_async_var_);
+
+  bool value = false;
+  EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
+  eval_ctx_ = nullptr;
+
+  // This should not trigger the callback since the EvaluationContext waiting
+  // for it is gone, and it should have remove all its observers.
+  fake_async_var_.NotifyValueChanged();
+  MessageLoopRunMaxIterations(MessageLoop::current(), 100);
+  EXPECT_FALSE(value);
+}
+
+// Scheduling two reevaluations from the callback should succeed.
+TEST_F(UmEvaluationContextTest,
+       RunOnValueChangeOrTimeoutReevaluatesRepeatedly) {
+  fake_poll_var_.reset(new string("Polled value"));
+  Closure evaluation = Bind(ReadVar<string>, eval_ctx_, &fake_poll_var_);
+  int num_reevaluations = 2;
+  bool done = false;
+
+  // Run the evaluation once.
+  evaluation.Run();
+
+  // Schedule repeated reevaluations.
+  Closure closure = Bind(EvaluateRepeatedly, evaluation, eval_ctx_,
+                         &num_reevaluations, &done);
+  ASSERT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(closure));
+  MessageLoopRunUntil(MessageLoop::current(),
+                      TimeDelta::FromSeconds(10),
+                      Bind(&GetBoolean, &done));
+  EXPECT_EQ(0, num_reevaluations);
+}
+
+// Test that we can delete the EvaluationContext while having pending events.
+TEST_F(UmEvaluationContextTest, ObjectDeletedWithPendingEventsTest) {
+  fake_async_var_.reset(new string("Async value"));
+  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(Bind(&DoNothing)));
+  // TearDown() checks for leaked observers on this async_variable, which means
+  // that our object is still alive after removing its reference.
+}
+
+// Test that timed events fired after removal of the EvaluationContext don't
+// crash.
+TEST_F(UmEvaluationContextTest, TimeoutEventAfterDeleteTest) {
+  FakeVariable<string> fake_short_poll_var = {"fake_short_poll",
+                                              TimeDelta::FromSeconds(1)};
+  fake_short_poll_var.reset(new string("Polled value"));
+  eval_ctx_->GetValue(&fake_short_poll_var);
+  bool value = false;
+  EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
+  // Remove the last reference to the EvaluationContext and run the loop for
+  // 10 seconds to give time to the main loop to trigger the timeout Event (of 1
+  // second). Our callback should not be called because the EvaluationContext
+  // was removed before the timeout event is attended.
+  eval_ctx_ = nullptr;
+  MessageLoopRunUntil(MessageLoop::current(),
+                      TimeDelta::FromSeconds(10),
+                      Bind(&GetBoolean, &value));
+  EXPECT_FALSE(value);
+}
+
+TEST_F(UmEvaluationContextTest, DefaultTimeout) {
+  // Test that the evaluation timeout calculation uses the default timeout on
+  // setup.
+  EXPECT_CALL(mock_var_async_, GetValue(default_timeout_, _))
+      .WillOnce(Return(nullptr));
+  EXPECT_EQ(nullptr, eval_ctx_->GetValue(&mock_var_async_));
+}
+
+TEST_F(UmEvaluationContextTest, TimeoutUpdatesWithMonotonicTime) {
+  fake_clock_.SetMonotonicTime(
+      fake_clock_.GetMonotonicTime() + TimeDelta::FromSeconds(1));
+
+  TimeDelta timeout = default_timeout_ - TimeDelta::FromSeconds(1);
+
+  EXPECT_CALL(mock_var_async_, GetValue(timeout, _))
+      .WillOnce(Return(nullptr));
+  EXPECT_EQ(nullptr, eval_ctx_->GetValue(&mock_var_async_));
+}
+
+TEST_F(UmEvaluationContextTest, ResetEvaluationResetsTimesWallclock) {
+  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));
+
+  EXPECT_TRUE(eval_ctx_->IsWallclockTimeGreaterThan(
+          cur_time - TimeDelta::FromSeconds(1)));
+  EXPECT_FALSE(eval_ctx_->IsWallclockTimeGreaterThan(cur_time));
+  EXPECT_FALSE(eval_ctx_->IsWallclockTimeGreaterThan(
+          cur_time + TimeDelta::FromSeconds(1)));
+  // Call ResetEvaluation now, which should use the new evaluation time.
+  eval_ctx_->ResetEvaluation();
+
+  cur_time = fake_clock_.GetWallclockTime();
+  EXPECT_TRUE(eval_ctx_->IsWallclockTimeGreaterThan(
+          cur_time - TimeDelta::FromSeconds(1)));
+  EXPECT_FALSE(eval_ctx_->IsWallclockTimeGreaterThan(cur_time));
+  EXPECT_FALSE(eval_ctx_->IsWallclockTimeGreaterThan(
+          cur_time + TimeDelta::FromSeconds(1)));
+}
+
+TEST_F(UmEvaluationContextTest, ResetEvaluationResetsTimesMonotonic) {
+  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));
+
+  EXPECT_TRUE(eval_ctx_->IsMonotonicTimeGreaterThan(
+          cur_time - TimeDelta::FromSeconds(1)));
+  EXPECT_FALSE(eval_ctx_->IsMonotonicTimeGreaterThan(cur_time));
+  EXPECT_FALSE(eval_ctx_->IsMonotonicTimeGreaterThan(
+          cur_time + TimeDelta::FromSeconds(1)));
+  // Call ResetEvaluation now, which should use the new evaluation time.
+  eval_ctx_->ResetEvaluation();
+
+  cur_time = fake_clock_.GetMonotonicTime();
+  EXPECT_TRUE(eval_ctx_->IsMonotonicTimeGreaterThan(
+          cur_time - TimeDelta::FromSeconds(1)));
+  EXPECT_FALSE(eval_ctx_->IsMonotonicTimeGreaterThan(cur_time));
+  EXPECT_FALSE(eval_ctx_->IsMonotonicTimeGreaterThan(
+          cur_time + TimeDelta::FromSeconds(1)));
+}
+
+TEST_F(UmEvaluationContextTest,
+       IsWallclockTimeGreaterThanSignalsTriggerReevaluation) {
+  EXPECT_FALSE(eval_ctx_->IsWallclockTimeGreaterThan(
+      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(Bind(&DoNothing)));
+}
+
+TEST_F(UmEvaluationContextTest,
+       IsMonotonicTimeGreaterThanSignalsTriggerReevaluation) {
+  EXPECT_FALSE(eval_ctx_->IsMonotonicTimeGreaterThan(
+      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(Bind(&DoNothing)));
+}
+
+TEST_F(UmEvaluationContextTest,
+       IsWallclockTimeGreaterThanDoesntRecordPastTimestamps) {
+  // IsWallclockTimeGreaterThan() should ignore timestamps on the past for
+  // reevaluation.
+  EXPECT_TRUE(eval_ctx_->IsWallclockTimeGreaterThan(
+      fake_clock_.GetWallclockTime() - TimeDelta::FromSeconds(20)));
+  EXPECT_TRUE(eval_ctx_->IsWallclockTimeGreaterThan(
+      fake_clock_.GetWallclockTime() - TimeDelta::FromSeconds(1)));
+
+  // Callback should not be scheduled.
+  EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&DoNothing)));
+}
+
+TEST_F(UmEvaluationContextTest,
+       IsMonotonicTimeGreaterThanDoesntRecordPastTimestamps) {
+  // IsMonotonicTimeGreaterThan() should ignore timestamps on the past for
+  // reevaluation.
+  EXPECT_TRUE(eval_ctx_->IsMonotonicTimeGreaterThan(
+      fake_clock_.GetMonotonicTime() - TimeDelta::FromSeconds(20)));
+  EXPECT_TRUE(eval_ctx_->IsMonotonicTimeGreaterThan(
+      fake_clock_.GetMonotonicTime() - TimeDelta::FromSeconds(1)));
+
+  // Callback should not be scheduled.
+  EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&DoNothing)));
+}
+
+TEST_F(UmEvaluationContextTest, DumpContext) {
+  // |fail_var_| yield "(no value)" since it is unset.
+  eval_ctx_->GetValue(&fail_var_);
+
+  // Check that this is included.
+  fake_int_var_.reset(new int(42));
+  eval_ctx_->GetValue(&fake_int_var_);
+
+  // Check that double-quotes are escaped properly.
+  fake_poll_var_.reset(new string("Hello \"world\"!"));
+  eval_ctx_->GetValue(&fake_poll_var_);
+
+  // Note that the variables are printed in alphabetical order. Also
+  // see UmEvaluationContextText::SetUp() where the values used for
+  // |evaluation_start_{monotonic,wallclock| are set.
+  EXPECT_EQ("{\n"
+            "   \"evaluation_start_monotonic\": \"4/22/2009 19:25:00 GMT\",\n"
+            "   \"evaluation_start_wallclock\": \"3/2/2006 1:23:45 GMT\",\n"
+            "   \"variables\": {\n"
+            "      \"fail_var\": \"(no value)\",\n"
+            "      \"fake_int\": \"42\",\n"
+            "      \"fake_poll\": \"Hello \\\"world\\\"!\"\n"
+            "   }\n"
+            "}",
+            eval_ctx_->DumpContext());
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/fake_config_provider.h b/update_manager/fake_config_provider.h
new file mode 100644
index 0000000..6a324df
--- /dev/null
+++ b/update_manager/fake_config_provider.h
@@ -0,0 +1,43 @@
+//
+// 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_UPDATE_MANAGER_FAKE_CONFIG_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_FAKE_CONFIG_PROVIDER_H_
+
+#include "update_engine/update_manager/config_provider.h"
+#include "update_engine/update_manager/fake_variable.h"
+
+namespace chromeos_update_manager {
+
+// Fake implementation of the ConfigProvider base class.
+class FakeConfigProvider : public ConfigProvider {
+ public:
+  FakeConfigProvider() {}
+
+  FakeVariable<bool>* var_is_oobe_enabled() override {
+    return &var_is_oobe_enabled_;
+  }
+
+ private:
+  FakeVariable<bool> var_is_oobe_enabled_{  // NOLINT(whitespace/braces)
+      "is_oobe_enabled", kVariableModeConst};
+
+  DISALLOW_COPY_AND_ASSIGN(FakeConfigProvider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_FAKE_CONFIG_PROVIDER_H_
diff --git a/update_manager/fake_device_policy_provider.h b/update_manager/fake_device_policy_provider.h
new file mode 100644
index 0000000..ad15c99
--- /dev/null
+++ b/update_manager/fake_device_policy_provider.h
@@ -0,0 +1,100 @@
+//
+// 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_UPDATE_MANAGER_FAKE_DEVICE_POLICY_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_FAKE_DEVICE_POLICY_PROVIDER_H_
+
+#include <set>
+#include <string>
+
+#include "update_engine/update_manager/device_policy_provider.h"
+#include "update_engine/update_manager/fake_variable.h"
+
+namespace chromeos_update_manager {
+
+// Fake implementation of the DevicePolicyProvider base class.
+class FakeDevicePolicyProvider : public DevicePolicyProvider {
+ public:
+  FakeDevicePolicyProvider() {}
+
+  FakeVariable<bool>* var_device_policy_is_loaded() override {
+    return &var_device_policy_is_loaded_;
+  }
+
+  FakeVariable<std::string>* var_release_channel() override {
+    return &var_release_channel_;
+  }
+
+  FakeVariable<bool>* var_release_channel_delegated() override {
+    return &var_release_channel_delegated_;
+  }
+
+  FakeVariable<bool>* var_update_disabled() override {
+    return &var_update_disabled_;
+  }
+
+  FakeVariable<std::string>* var_target_version_prefix() override {
+    return &var_target_version_prefix_;
+  }
+
+  FakeVariable<base::TimeDelta>* var_scatter_factor() override {
+    return &var_scatter_factor_;
+  }
+
+  FakeVariable<std::set<ConnectionType>>*
+      var_allowed_connection_types_for_update() override {
+    return &var_allowed_connection_types_for_update_;
+  }
+
+  FakeVariable<std::string>* var_owner() override {
+    return &var_owner_;
+  }
+
+  FakeVariable<bool>* var_http_downloads_enabled() override {
+    return &var_http_downloads_enabled_;
+  }
+
+  FakeVariable<bool>* var_au_p2p_enabled() override {
+    return &var_au_p2p_enabled_;
+  }
+
+ private:
+  FakeVariable<bool> var_device_policy_is_loaded_{
+      "policy_is_loaded", kVariableModePoll};
+  FakeVariable<std::string> var_release_channel_{
+      "release_channel", kVariableModePoll};
+  FakeVariable<bool> var_release_channel_delegated_{
+      "release_channel_delegated", kVariableModePoll};
+  FakeVariable<bool> var_update_disabled_{
+      "update_disabled", kVariableModePoll};
+  FakeVariable<std::string> var_target_version_prefix_{
+      "target_version_prefix", kVariableModePoll};
+  FakeVariable<base::TimeDelta> var_scatter_factor_{
+      "scatter_factor", kVariableModePoll};
+  FakeVariable<std::set<ConnectionType>>
+      var_allowed_connection_types_for_update_{
+          "allowed_connection_types_for_update", 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};
+
+  DISALLOW_COPY_AND_ASSIGN(FakeDevicePolicyProvider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_FAKE_DEVICE_POLICY_PROVIDER_H_
diff --git a/update_manager/fake_random_provider.h b/update_manager/fake_random_provider.h
new file mode 100644
index 0000000..643a194
--- /dev/null
+++ b/update_manager/fake_random_provider.h
@@ -0,0 +1,40 @@
+//
+// 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_UPDATE_MANAGER_FAKE_RANDOM_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_FAKE_RANDOM_PROVIDER_H_
+
+#include "update_engine/update_manager/fake_variable.h"
+#include "update_engine/update_manager/random_provider.h"
+
+namespace chromeos_update_manager {
+
+// Fake implementation of the RandomProvider base class.
+class FakeRandomProvider : public RandomProvider {
+ public:
+  FakeRandomProvider() {}
+
+  FakeVariable<uint64_t>* var_seed() override { return &var_seed_; }
+
+ private:
+  FakeVariable<uint64_t> var_seed_{"seed", kVariableModePoll};
+
+  DISALLOW_COPY_AND_ASSIGN(FakeRandomProvider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_FAKE_RANDOM_PROVIDER_H_
diff --git a/update_manager/fake_shill_provider.h b/update_manager/fake_shill_provider.h
new file mode 100644
index 0000000..b68e858
--- /dev/null
+++ b/update_manager/fake_shill_provider.h
@@ -0,0 +1,60 @@
+//
+// 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_UPDATE_MANAGER_FAKE_SHILL_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_FAKE_SHILL_PROVIDER_H_
+
+#include "update_engine/update_manager/fake_variable.h"
+#include "update_engine/update_manager/shill_provider.h"
+
+namespace chromeos_update_manager {
+
+// Fake implementation of the ShillProvider base class.
+class FakeShillProvider : public ShillProvider {
+ public:
+  FakeShillProvider() {}
+
+  FakeVariable<bool>* var_is_connected() override {
+    return &var_is_connected_;
+  }
+
+  FakeVariable<ConnectionType>* var_conn_type() override {
+    return &var_conn_type_;
+  }
+
+  FakeVariable<ConnectionTethering>*
+      var_conn_tethering() override {
+    return &var_conn_tethering_;
+  }
+
+  FakeVariable<base::Time>* var_conn_last_changed() override {
+    return &var_conn_last_changed_;
+  }
+
+ private:
+  FakeVariable<bool> var_is_connected_{"is_connected", kVariableModePoll};
+  FakeVariable<ConnectionType> var_conn_type_{"conn_type", kVariableModePoll};
+  FakeVariable<ConnectionTethering> var_conn_tethering_{
+      "conn_tethering", kVariableModePoll};
+  FakeVariable<base::Time> var_conn_last_changed_{
+      "conn_last_changed", kVariableModePoll};
+
+  DISALLOW_COPY_AND_ASSIGN(FakeShillProvider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_FAKE_SHILL_PROVIDER_H_
diff --git a/update_manager/fake_state.h b/update_manager/fake_state.h
new file mode 100644
index 0000000..fd7a88c
--- /dev/null
+++ b/update_manager/fake_state.h
@@ -0,0 +1,91 @@
+//
+// 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_UPDATE_MANAGER_FAKE_STATE_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_FAKE_STATE_H_
+
+#include "update_engine/update_manager/fake_config_provider.h"
+#include "update_engine/update_manager/fake_device_policy_provider.h"
+#include "update_engine/update_manager/fake_random_provider.h"
+#include "update_engine/update_manager/fake_shill_provider.h"
+#include "update_engine/update_manager/fake_system_provider.h"
+#include "update_engine/update_manager/fake_time_provider.h"
+#include "update_engine/update_manager/fake_updater_provider.h"
+#include "update_engine/update_manager/state.h"
+
+namespace chromeos_update_manager {
+
+// A fake State class that creates fake providers for all the providers.
+// This fake can be used in unit testing of Policy subclasses. To fake out the
+// value a variable is exposing, just call FakeVariable<T>::SetValue() on the
+// variable you fake out. For example:
+//
+//   FakeState fake_state_;
+//   fake_state_.random_provider_->var_seed()->SetValue(new uint64_t(12345));
+//
+// You can call SetValue more than once and the FakeVariable will take care of
+// the memory, but only the last value will remain.
+class FakeState : public State {
+ public:
+  // Creates and initializes the FakeState using fake providers.
+  FakeState() {}
+
+  ~FakeState() override {}
+
+  // Downcasted getters to access the fake instances during testing.
+  FakeConfigProvider* config_provider() override {
+    return &config_provider_;
+  }
+
+  FakeDevicePolicyProvider* device_policy_provider() override {
+    return &device_policy_provider_;
+  }
+
+  FakeRandomProvider* random_provider() override {
+    return &random_provider_;
+  }
+
+  FakeShillProvider* shill_provider() override {
+    return &shill_provider_;
+  }
+
+  FakeSystemProvider* system_provider() override {
+    return &system_provider_;
+  }
+
+  FakeTimeProvider* time_provider() override {
+    return &time_provider_;
+  }
+
+  FakeUpdaterProvider* updater_provider() override {
+    return &updater_provider_;
+  }
+
+ private:
+  FakeConfigProvider config_provider_;
+  FakeDevicePolicyProvider device_policy_provider_;
+  FakeRandomProvider random_provider_;
+  FakeShillProvider shill_provider_;
+  FakeSystemProvider system_provider_;
+  FakeTimeProvider time_provider_;
+  FakeUpdaterProvider updater_provider_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeState);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_FAKE_STATE_H_
diff --git a/update_manager/fake_system_provider.h b/update_manager/fake_system_provider.h
new file mode 100644
index 0000000..6036198
--- /dev/null
+++ b/update_manager/fake_system_provider.h
@@ -0,0 +1,60 @@
+//
+// 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_UPDATE_MANAGER_FAKE_SYSTEM_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_FAKE_SYSTEM_PROVIDER_H_
+
+#include "update_engine/update_manager/fake_variable.h"
+#include "update_engine/update_manager/system_provider.h"
+
+namespace chromeos_update_manager {
+
+// Fake implementation of the SystemProvider base class.
+class FakeSystemProvider : public SystemProvider {
+ public:
+  FakeSystemProvider() {}
+
+  FakeVariable<bool>* var_is_normal_boot_mode() override {
+    return &var_is_normal_boot_mode_;
+  }
+
+  FakeVariable<bool>* var_is_official_build() override {
+    return &var_is_official_build_;
+  }
+
+  FakeVariable<bool>* var_is_oobe_complete() override {
+    return &var_is_oobe_complete_;
+  }
+
+  FakeVariable<unsigned int>* var_num_slots() override {
+    return &var_num_slots_;
+  }
+
+ private:
+  FakeVariable<bool> var_is_normal_boot_mode_{  // NOLINT(whitespace/braces)
+    "is_normal_boot_mode", kVariableModeConst};
+  FakeVariable<bool> var_is_official_build_{  // NOLINT(whitespace/braces)
+    "is_official_build", kVariableModeConst};
+  FakeVariable<bool> var_is_oobe_complete_{  // NOLINT(whitespace/braces)
+    "is_oobe_complete", kVariableModePoll};
+  FakeVariable<unsigned int> var_num_slots_{"num_slots", kVariableModePoll};
+
+  DISALLOW_COPY_AND_ASSIGN(FakeSystemProvider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_FAKE_SYSTEM_PROVIDER_H_
diff --git a/update_manager/fake_time_provider.h b/update_manager/fake_time_provider.h
new file mode 100644
index 0000000..2aea2e7
--- /dev/null
+++ b/update_manager/fake_time_provider.h
@@ -0,0 +1,42 @@
+//
+// 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_UPDATE_MANAGER_FAKE_TIME_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_FAKE_TIME_PROVIDER_H_
+
+#include "update_engine/update_manager/fake_variable.h"
+#include "update_engine/update_manager/time_provider.h"
+
+namespace chromeos_update_manager {
+
+// Fake implementation of the TimeProvider base class.
+class FakeTimeProvider : public TimeProvider {
+ public:
+  FakeTimeProvider() {}
+
+  FakeVariable<base::Time>* var_curr_date() override { return &var_curr_date_; }
+  FakeVariable<int>* var_curr_hour() override { return &var_curr_hour_; }
+
+ private:
+  FakeVariable<base::Time> var_curr_date_{"curr_date", kVariableModePoll};
+  FakeVariable<int> var_curr_hour_{"curr_hour", kVariableModePoll};
+
+  DISALLOW_COPY_AND_ASSIGN(FakeTimeProvider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_FAKE_TIME_PROVIDER_H_
diff --git a/update_manager/fake_update_manager.h b/update_manager/fake_update_manager.h
new file mode 100644
index 0000000..2ea00b6
--- /dev/null
+++ b/update_manager/fake_update_manager.h
@@ -0,0 +1,49 @@
+//
+// 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_UPDATE_MANAGER_FAKE_UPDATE_MANAGER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_FAKE_UPDATE_MANAGER_H_
+
+#include "update_engine/update_manager/update_manager.h"
+
+#include "update_engine/update_manager/default_policy.h"
+#include "update_engine/update_manager/fake_state.h"
+
+namespace chromeos_update_manager {
+
+class FakeUpdateManager : public UpdateManager {
+ public:
+  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(clock));
+  }
+
+  // UpdateManager overrides.
+  using UpdateManager::set_policy;
+
+  FakeState* state() {
+    return reinterpret_cast<FakeState*>(UpdateManager::state());
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FakeUpdateManager);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_FAKE_UPDATE_MANAGER_H_
diff --git a/update_manager/fake_updater_provider.h b/update_manager/fake_updater_provider.h
new file mode 100644
index 0000000..44389f4
--- /dev/null
+++ b/update_manager/fake_updater_provider.h
@@ -0,0 +1,130 @@
+//
+// 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_UPDATE_MANAGER_FAKE_UPDATER_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_FAKE_UPDATER_PROVIDER_H_
+
+#include <string>
+
+#include "update_engine/update_manager/fake_variable.h"
+#include "update_engine/update_manager/updater_provider.h"
+
+namespace chromeos_update_manager {
+
+// Fake implementation of the UpdaterProvider base class.
+class FakeUpdaterProvider : public UpdaterProvider {
+ public:
+  FakeUpdaterProvider() {}
+
+  FakeVariable<base::Time>* var_updater_started_time() override {
+    return &var_updater_started_time_;
+  }
+
+  FakeVariable<base::Time>* var_last_checked_time() override {
+    return &var_last_checked_time_;
+  }
+
+  FakeVariable<base::Time>* var_update_completed_time() override {
+    return &var_update_completed_time_;
+  }
+
+  FakeVariable<double>* var_progress() override {
+    return &var_progress_;
+  }
+
+  FakeVariable<Stage>* var_stage() override {
+    return &var_stage_;
+  }
+
+  FakeVariable<std::string>* var_new_version() override {
+    return &var_new_version_;
+  }
+
+  FakeVariable<int64_t>* var_payload_size() override {
+    return &var_payload_size_;
+  }
+
+  FakeVariable<std::string>* var_curr_channel() override {
+    return &var_curr_channel_;
+  }
+
+  FakeVariable<std::string>* var_new_channel() override {
+    return &var_new_channel_;
+  }
+
+  FakeVariable<bool>* var_p2p_enabled() override {
+    return &var_p2p_enabled_;
+  }
+
+  FakeVariable<bool>* var_cellular_enabled() override {
+    return &var_cellular_enabled_;
+  }
+
+  FakeVariable<unsigned int>* var_consecutive_failed_update_checks() override {
+    return &var_consecutive_failed_update_checks_;
+  }
+
+  FakeVariable<unsigned int>* var_server_dictated_poll_interval() override {
+    return &var_server_dictated_poll_interval_;
+  }
+
+  FakeVariable<UpdateRequestStatus>* var_forced_update_requested() override {
+    return &var_forced_update_requested_;
+  }
+
+ private:
+  FakeVariable<base::Time>
+      var_updater_started_time_{  // NOLINT(whitespace/braces)
+    "updater_started_time", kVariableModePoll};
+  FakeVariable<base::Time> var_last_checked_time_{  // NOLINT(whitespace/braces)
+    "last_checked_time", kVariableModePoll};
+  FakeVariable<base::Time>
+      var_update_completed_time_{  // NOLINT(whitespace/braces)
+    "update_completed_time", kVariableModePoll};
+  FakeVariable<double> var_progress_{  // NOLINT(whitespace/braces)
+    "progress", kVariableModePoll};
+  FakeVariable<Stage> var_stage_{  // NOLINT(whitespace/braces)
+    "stage", kVariableModePoll};
+  FakeVariable<std::string> var_new_version_{  // NOLINT(whitespace/braces)
+    "new_version", kVariableModePoll};
+  FakeVariable<int64_t> var_payload_size_{  // NOLINT(whitespace/braces)
+    "payload_size", kVariableModePoll};
+  FakeVariable<std::string> var_curr_channel_{  // NOLINT(whitespace/braces)
+    "curr_channel", kVariableModePoll};
+  FakeVariable<std::string> var_new_channel_{  // NOLINT(whitespace/braces)
+    "new_channel", kVariableModePoll};
+  FakeVariable<bool> var_p2p_enabled_{// NOLINT(whitespace/braces)
+                                      "p2p_enabled",
+                                      kVariableModeAsync};
+  FakeVariable<bool> var_cellular_enabled_{// NOLINT(whitespace/braces)
+                                           "cellular_enabled",
+                                           kVariableModeAsync};
+  FakeVariable<unsigned int>
+      var_consecutive_failed_update_checks_{  // NOLINT(whitespace/braces)
+    "consecutive_failed_update_checks", kVariableModePoll};
+  FakeVariable<unsigned int>
+      var_server_dictated_poll_interval_{  // NOLINT(whitespace/braces)
+    "server_dictated_poll_interval", kVariableModePoll};
+  FakeVariable<UpdateRequestStatus>
+      var_forced_update_requested_{  // NOLINT(whitespace/braces)
+    "forced_update_requested", kVariableModeAsync};
+
+  DISALLOW_COPY_AND_ASSIGN(FakeUpdaterProvider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_FAKE_UPDATER_PROVIDER_H_
diff --git a/update_manager/fake_variable.h b/update_manager/fake_variable.h
new file mode 100644
index 0000000..2f8e079
--- /dev/null
+++ b/update_manager/fake_variable.h
@@ -0,0 +1,74 @@
+//
+// 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_UPDATE_MANAGER_FAKE_VARIABLE_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_FAKE_VARIABLE_H_
+
+#include <memory>
+#include <string>
+
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+// A fake typed variable to use while testing policy implementations. The
+// variable can be instructed to return any object of its type.
+template<typename T>
+class FakeVariable : public Variable<T> {
+ public:
+  FakeVariable(const std::string& name, VariableMode mode)
+      : Variable<T>(name, mode) {}
+  FakeVariable(const std::string& name, base::TimeDelta poll_interval)
+      : Variable<T>(name, poll_interval) {}
+  ~FakeVariable() override {}
+
+  // Sets the next value of this variable to the passed |p_value| pointer. Once
+  // returned by GetValue(), the pointer is released and has to be set again.
+  // A value of null means that the GetValue() call will fail and return
+  // null.
+  void reset(const T* p_value) {
+    ptr_.reset(p_value);
+  }
+
+  // Make the NotifyValueChanged() public for FakeVariables.
+  void NotifyValueChanged() {
+    Variable<T>::NotifyValueChanged();
+  }
+
+ protected:
+  // Variable<T> overrides.
+  // Returns the pointer set with reset(). The ownership of the object is passed
+  // to the caller and the pointer is release from the FakeVariable. A second
+  // call to GetValue() without reset() will return null and set the error
+  // message.
+  const T* GetValue(base::TimeDelta /* timeout */,
+                    std::string* errmsg) override {
+    if (ptr_ == nullptr && errmsg != nullptr)
+      *errmsg = this->GetName() + " is an empty FakeVariable";
+    // Passes the pointer ownership to the caller.
+    return ptr_.release();
+  }
+
+ private:
+  // The pointer returned by GetValue().
+  std::unique_ptr<const T> ptr_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeVariable);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_FAKE_VARIABLE_H_
diff --git a/update_manager/generic_variables.h b/update_manager/generic_variables.h
new file mode 100644
index 0000000..f87a05e
--- /dev/null
+++ b/update_manager/generic_variables.h
@@ -0,0 +1,218 @@
+//
+// 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.
+//
+
+// Generic and provider-independent Variable subclasses. These variables can be
+// used by any state provider to implement simple variables to avoid repeat the
+// same common code on different state providers.
+
+#ifndef UPDATE_ENGINE_UPDATE_MANAGER_GENERIC_VARIABLES_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_GENERIC_VARIABLES_H_
+
+#include <string>
+
+#include <base/callback.h>
+
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+// Variable class returning a copy of a given object using the copy constructor.
+// This template class can be used to define variables that expose as a variable
+// any fixed object, such as the a provider's private member. The variable will
+// create copies of the provided object using the copy constructor of that
+// class.
+//
+// For example, a state provider exposing a private member as a variable can
+// implement this as follows:
+//
+//   class SomethingProvider {
+//    public:
+//      SomethingProvider(...) {
+//        var_something_foo = new PollCopyVariable<MyType>(foo_);
+//      }
+//      ...
+//    private:
+//     MyType foo_;
+//   };
+template<typename T>
+class PollCopyVariable : public Variable<T> {
+ public:
+  // Creates the variable returning copies of the passed |ref|. The reference to
+  // this object is kept and it should be available whenever the GetValue()
+  // method is called. If |is_set_p| is not null, then this flag will be
+  // consulted prior to returning the value, and an |errmsg| will be returned if
+  // it is not set.
+  PollCopyVariable(const std::string& name, const T& ref, const bool* is_set_p,
+                   const std::string& errmsg)
+      : Variable<T>(name, kVariableModePoll), ref_(ref), is_set_p_(is_set_p),
+        errmsg_(errmsg) {}
+  PollCopyVariable(const std::string& name, const T& ref, const bool* is_set_p)
+      : PollCopyVariable(name, ref, is_set_p, std::string()) {}
+  PollCopyVariable(const std::string& name, const T& ref)
+      : PollCopyVariable(name, ref, nullptr) {}
+
+  PollCopyVariable(const std::string& name, const base::TimeDelta poll_interval,
+                   const T& ref, const bool* is_set_p,
+                   const std::string& errmsg)
+      : Variable<T>(name, poll_interval), ref_(ref), is_set_p_(is_set_p),
+        errmsg_(errmsg) {}
+  PollCopyVariable(const std::string& name, const base::TimeDelta poll_interval,
+                   const T& ref, const bool* is_set_p)
+      : PollCopyVariable(name, poll_interval, ref, is_set_p, std::string()) {}
+  PollCopyVariable(const std::string& name, const base::TimeDelta poll_interval,
+                   const T& ref)
+      : PollCopyVariable(name, poll_interval, ref, nullptr) {}
+
+ protected:
+  FRIEND_TEST(UmPollCopyVariableTest, SimpleTest);
+  FRIEND_TEST(UmPollCopyVariableTest, UseCopyConstructorTest);
+
+  // Variable override.
+  inline const T* GetValue(base::TimeDelta /* timeout */,
+                           std::string* errmsg) override {
+    if (is_set_p_ && !(*is_set_p_)) {
+      if (errmsg) {
+        if (errmsg_.empty())
+          *errmsg = "No value set for " + this->GetName();
+        else
+          *errmsg = errmsg_;
+      }
+      return nullptr;
+    }
+    return new T(ref_);
+  }
+
+ private:
+  // Reference to the object to be copied by GetValue().
+  const T& ref_;
+
+  // A pointer to a flag indicating whether the value is set. If null, then the
+  // value is assumed to be set.
+  const bool* const is_set_p_;
+
+  // An error message to be returned when attempting to get an unset value.
+  const std::string errmsg_;
+};
+
+// Variable class returning a constant value that is cached on the variable when
+// it is created.
+template<typename T>
+class ConstCopyVariable : public Variable<T> {
+ public:
+  // Creates the variable returning copies of the passed |obj|. The value passed
+  // is copied in this variable, and new copies of it will be returned by
+  // GetValue().
+  ConstCopyVariable(const std::string& name, const T& obj)
+      : Variable<T>(name, kVariableModeConst), obj_(obj) {}
+
+ protected:
+  // Variable override.
+  const T* GetValue(base::TimeDelta /* timeout */,
+                    std::string* /* errmsg */) override {
+    return new T(obj_);
+  }
+
+ private:
+  // Value to be copied by GetValue().
+  const T obj_;
+};
+
+// Variable class returning a copy of a value returned by a given function. The
+// function is called every time the variable is being polled.
+template<typename T>
+class CallCopyVariable : public Variable<T> {
+ public:
+  CallCopyVariable(const std::string& name, base::Callback<T(void)> func)
+      : Variable<T>(name, kVariableModePoll), func_(func) {}
+  CallCopyVariable(const std::string& name,
+                   const base::TimeDelta poll_interval,
+                   base::Callback<T(void)> func)
+      : Variable<T>(name, poll_interval), func_(func) {}
+
+ protected:
+  // Variable override.
+  const T* GetValue(base::TimeDelta /* timeout */,
+                    std::string* /* errmsg */) override {
+    if (func_.is_null())
+      return nullptr;
+    return new T(func_.Run());
+  }
+
+ private:
+  FRIEND_TEST(UmCallCopyVariableTest, SimpleTest);
+
+  // The function to be called, stored as a base::Callback.
+  base::Callback<T(void)> func_;
+
+  DISALLOW_COPY_AND_ASSIGN(CallCopyVariable);
+};
+
+
+// A Variable class to implement simple Async variables. It provides two methods
+// SetValue and UnsetValue to modify the current value of the variable and
+// notify the registered observers whenever the value changed.
+//
+// The type T needs to be copy-constructible, default-constructible and have an
+// operator== (to determine if the value changed), which makes this class
+// suitable for basic types.
+template<typename T>
+class AsyncCopyVariable : public Variable<T> {
+ public:
+  explicit AsyncCopyVariable(const std::string& name)
+      : Variable<T>(name, kVariableModeAsync), has_value_(false) {}
+
+  AsyncCopyVariable(const std::string& name, const T value)
+      : Variable<T>(name, kVariableModeAsync),
+        has_value_(true), value_(value) {}
+
+  void SetValue(const T& new_value) {
+    bool should_notify = !(has_value_ && new_value == value_);
+    value_ = new_value;
+    has_value_ = true;
+    if (should_notify)
+      this->NotifyValueChanged();
+  }
+
+  void UnsetValue() {
+    if (has_value_) {
+      has_value_ = false;
+      this->NotifyValueChanged();
+    }
+  }
+
+ protected:
+  // Variable override.
+  const T* GetValue(base::TimeDelta /* timeout */,
+                    std::string* errmsg) override {
+    if (!has_value_) {
+      if (errmsg)
+        *errmsg = "No value set for " + this->GetName();
+      return nullptr;
+    }
+    return new T(value_);
+  }
+
+ private:
+  // Whether the variable has a value set.
+  bool has_value_;
+
+  // Copy of the object to be returned by GetValue().
+  T value_;
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_GENERIC_VARIABLES_H_
diff --git a/update_manager/generic_variables_unittest.cc b/update_manager/generic_variables_unittest.cc
new file mode 100644
index 0000000..cb0c48f
--- /dev/null
+++ b/update_manager/generic_variables_unittest.cc
@@ -0,0 +1,224 @@
+//
+// 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/update_manager/generic_variables.h"
+
+#include <memory>
+
+#include <base/callback.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 "update_engine/update_manager/umtest_utils.h"
+
+using brillo::MessageLoop;
+using brillo::MessageLoopRunMaxIterations;
+using std::unique_ptr;
+
+namespace chromeos_update_manager {
+
+class UmPollCopyVariableTest : public ::testing::Test {};
+
+
+TEST_F(UmPollCopyVariableTest, SimpleTest) {
+  // Tests that copies are generated as intended.
+  int source = 5;
+  PollCopyVariable<int> var("var", source);
+
+  // Generate and validate a copy.
+  unique_ptr<const int> copy_1(var.GetValue(
+          UmTestUtils::DefaultTimeout(), nullptr));
+  ASSERT_NE(nullptr, copy_1.get());
+  EXPECT_EQ(5, *copy_1);
+
+  // Assign a different value to the source variable.
+  source = 42;
+
+  // Check that the content of the copy was not affected (distinct instance).
+  EXPECT_EQ(5, *copy_1);
+
+  // Generate and validate a second copy.
+  UmTestUtils::ExpectVariableHasValue(42, &var);
+}
+
+TEST_F(UmPollCopyVariableTest, SetFlagTest) {
+  // Tests that the set flag is being referred to as expected.
+  int source = 5;
+  bool is_set = false;
+  PollCopyVariable<int> var("var", source, &is_set);
+
+  // Flag marked unset, nothing should be returned.
+  UmTestUtils::ExpectVariableNotSet(&var);
+
+  // Flag marked set, we should be getting a value.
+  is_set = true;
+  UmTestUtils::ExpectVariableHasValue(5, &var);
+}
+
+
+class CopyConstructorTestClass {
+ public:
+  CopyConstructorTestClass(void) : copied_(false) {}
+  CopyConstructorTestClass(const CopyConstructorTestClass& other)
+      : copied_(true), val_(other.val_ * 2) {}
+
+  // Tells if the instance was constructed using the copy-constructor.
+  const bool copied_;
+
+  // An auxiliary internal value.
+  int val_ = 0;
+};
+
+
+TEST_F(UmPollCopyVariableTest, UseCopyConstructorTest) {
+  // Ensures that CopyVariables indeed uses the copy constructor.
+  const CopyConstructorTestClass source;
+  ASSERT_FALSE(source.copied_);
+
+  PollCopyVariable<CopyConstructorTestClass> var("var", source);
+  unique_ptr<const CopyConstructorTestClass> copy(
+      var.GetValue(UmTestUtils::DefaultTimeout(), nullptr));
+  ASSERT_NE(nullptr, copy.get());
+  EXPECT_TRUE(copy->copied_);
+}
+
+
+class UmConstCopyVariableTest : public ::testing::Test {};
+
+TEST_F(UmConstCopyVariableTest, SimpleTest) {
+  int source = 5;
+  ConstCopyVariable<int> var("var", source);
+  UmTestUtils::ExpectVariableHasValue(5, &var);
+
+  // Ensure the value is cached.
+  source = 42;
+  UmTestUtils::ExpectVariableHasValue(5, &var);
+}
+
+
+class UmCallCopyVariableTest : public ::testing::Test {};
+
+CopyConstructorTestClass test_func(CopyConstructorTestClass* obj) {
+  obj->val_++;  // So we can check that the function was called.
+  return *obj;
+}
+
+TEST_F(UmCallCopyVariableTest, SimpleTest) {
+  // Tests that the returned value is generated by copying the value returned by
+  // the function call.
+
+  CopyConstructorTestClass test_obj;
+  ASSERT_FALSE(test_obj.copied_);
+  test_obj.val_ = 5;
+
+  base::Callback<CopyConstructorTestClass(void)> cb = base::Bind(
+      test_func, &test_obj);
+  CallCopyVariable<CopyConstructorTestClass> var("var", cb);
+
+  unique_ptr<const CopyConstructorTestClass> copy(
+      var.GetValue(UmTestUtils::DefaultTimeout(), nullptr));
+  EXPECT_EQ(6, test_obj.val_);  // Check that the function was called.
+  ASSERT_NE(nullptr, copy.get());
+  EXPECT_TRUE(copy->copied_);
+  EXPECT_EQ(12, copy->val_);  // Check that copying occurred once.
+}
+
+TEST_F(UmCallCopyVariableTest, NullTest) {
+  // Ensures that the variable returns null when the callback is null.
+
+  base::Callback<bool(void)> cb;
+  CallCopyVariable<bool> var("var", cb);
+  UmTestUtils::ExpectVariableNotSet(&var);
+}
+
+class UmAsyncCopyVariableTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    loop_.SetAsCurrent();
+  }
+
+  void TearDown() override {
+    // No remaining event on the main loop.
+    EXPECT_FALSE(loop_.PendingTasks());
+  }
+
+
+  brillo::FakeMessageLoop loop_{nullptr};
+};
+
+TEST_F(UmAsyncCopyVariableTest, ConstructorTest) {
+  AsyncCopyVariable<int> var("var");
+  UmTestUtils::ExpectVariableNotSet(&var);
+  EXPECT_EQ(kVariableModeAsync, var.GetMode());
+}
+
+TEST_F(UmAsyncCopyVariableTest, SetValueTest) {
+  AsyncCopyVariable<int> var("var");
+  var.SetValue(5);
+  UmTestUtils::ExpectVariableHasValue(5, &var);
+  // Execute all the pending observers.
+  MessageLoopRunMaxIterations(MessageLoop::current(), 100);
+}
+
+TEST_F(UmAsyncCopyVariableTest, UnsetValueTest) {
+  AsyncCopyVariable<int> var("var", 42);
+  var.UnsetValue();
+  UmTestUtils::ExpectVariableNotSet(&var);
+  // Execute all the pending observers.
+  MessageLoopRunMaxIterations(MessageLoop::current(), 100);
+}
+
+class CallCounterObserver : public BaseVariable::ObserverInterface {
+ public:
+  void ValueChanged(BaseVariable* variable) {
+    calls_count_++;
+  }
+
+  int calls_count_ = 0;
+};
+
+TEST_F(UmAsyncCopyVariableTest, ObserverCalledTest) {
+  AsyncCopyVariable<int> var("var", 42);
+  CallCounterObserver observer;
+  var.AddObserver(&observer);
+  EXPECT_EQ(0, observer.calls_count_);
+
+  // Check that a different value fires the notification.
+  var.SetValue(5);
+  MessageLoopRunMaxIterations(MessageLoop::current(), 100);
+  EXPECT_EQ(1, observer.calls_count_);
+
+  // Check the same value doesn't.
+  var.SetValue(5);
+  MessageLoopRunMaxIterations(MessageLoop::current(), 100);
+  EXPECT_EQ(1, observer.calls_count_);
+
+  // Check that unsetting a previously set value fires the notification.
+  var.UnsetValue();
+  MessageLoopRunMaxIterations(MessageLoop::current(), 100);
+  EXPECT_EQ(2, observer.calls_count_);
+
+  // Check that unsetting again doesn't.
+  var.UnsetValue();
+  MessageLoopRunMaxIterations(MessageLoop::current(), 100);
+  EXPECT_EQ(2, observer.calls_count_);
+
+  var.RemoveObserver(&observer);
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/mock_policy.h b/update_manager/mock_policy.h
new file mode 100644
index 0000000..14470e9
--- /dev/null
+++ b/update_manager/mock_policy.h
@@ -0,0 +1,92 @@
+//
+// 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_UPDATE_MANAGER_MOCK_POLICY_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_MOCK_POLICY_H_
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include "update_engine/update_manager/default_policy.h"
+#include "update_engine/update_manager/policy.h"
+
+namespace chromeos_update_manager {
+
+// A mocked implementation of Policy.
+class MockPolicy : public Policy {
+ public:
+  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::_))
+        .WillByDefault(testing::Invoke(
+                &default_policy_, &DefaultPolicy::UpdateCheckAllowed));
+    ON_CALL(*this, UpdateCanStart(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));
+    ON_CALL(*this, P2PEnabledChanged(testing::_, testing::_, testing::_,
+                                     testing::_, testing::_))
+        .WillByDefault(testing::Invoke(
+                &default_policy_, &DefaultPolicy::P2PEnabledChanged));
+  }
+
+  MockPolicy() : MockPolicy(nullptr) {}
+  ~MockPolicy() override {}
+
+  // Policy overrides.
+  MOCK_CONST_METHOD4(UpdateCheckAllowed,
+                     EvalStatus(EvaluationContext*, State*, std::string*,
+                                UpdateCheckParams*));
+
+  MOCK_CONST_METHOD5(UpdateCanStart,
+                     EvalStatus(EvaluationContext*, State*, std::string*,
+                                UpdateDownloadParams*, UpdateState));
+
+  MOCK_CONST_METHOD4(UpdateDownloadAllowed,
+                     EvalStatus(EvaluationContext*, State*, std::string*,
+                                bool*));
+
+  MOCK_CONST_METHOD4(P2PEnabled,
+                     EvalStatus(EvaluationContext*, State*, std::string*,
+                                bool*));
+
+  MOCK_CONST_METHOD5(P2PEnabledChanged,
+                     EvalStatus(EvaluationContext*, State*, std::string*,
+                                bool*, bool));
+
+ protected:
+  // Policy override.
+  std::string PolicyName() const override { return "MockPolicy"; }
+
+ private:
+  DefaultPolicy default_policy_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockPolicy);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_MOCK_POLICY_H_
diff --git a/update_manager/mock_variable.h b/update_manager/mock_variable.h
new file mode 100644
index 0000000..1493491
--- /dev/null
+++ b/update_manager/mock_variable.h
@@ -0,0 +1,42 @@
+//
+// 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_UPDATE_MANAGER_MOCK_VARIABLE_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_MOCK_VARIABLE_H_
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+// This is a generic mock of the Variable class.
+template<typename T>
+class MockVariable : public Variable<T> {
+ public:
+  using Variable<T>::Variable;
+
+  MOCK_METHOD2_T(GetValue, const T*(base::TimeDelta, std::string*));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockVariable);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_MOCK_VARIABLE_H_
diff --git a/update_manager/policy.cc b/update_manager/policy.cc
new file mode 100644
index 0000000..151c225
--- /dev/null
+++ b/update_manager/policy.cc
@@ -0,0 +1,37 @@
+//
+// 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/update_manager/policy.h"
+
+#include <string>
+
+using std::string;
+
+namespace chromeos_update_manager {
+
+string ToString(EvalStatus status) {
+  switch (status) {
+    case EvalStatus::kFailed:
+      return "kFailed";
+    case EvalStatus::kSucceeded:
+      return "kSucceeded";
+    case EvalStatus::kAskMeAgainLater:
+      return "kAskMeAgainLater";
+  }
+  return "Invalid";
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/policy.h b/update_manager/policy.h
new file mode 100644
index 0000000..fae1494
--- /dev/null
+++ b/update_manager/policy.h
@@ -0,0 +1,290 @@
+//
+// 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_UPDATE_MANAGER_POLICY_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_POLICY_H_
+
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "update_engine/common/error_code.h"
+#include "update_engine/update_manager/evaluation_context.h"
+#include "update_engine/update_manager/state.h"
+
+namespace chromeos_update_manager {
+
+// The three different results of a policy request.
+enum class EvalStatus {
+  kFailed,
+  kSucceeded,
+  kAskMeAgainLater,
+};
+
+std::string ToString(EvalStatus status);
+
+// Parameters of an update check. These parameters are determined by the
+// UpdateCheckAllowed policy.
+struct UpdateCheckParams {
+  bool updates_enabled;  // Whether the auto-updates are enabled on this build.
+
+  // 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;
+  // A target channel, if so imposed by policy; otherwise, an empty string.
+  std::string target_channel;
+
+  // Whether the allowed update is interactive (user-initiated) or periodic.
+  bool is_interactive;
+};
+
+// Input arguments to UpdateCanStart.
+//
+// A snapshot of the state of the current update process. This includes
+// everything that a policy might need and that occurred since the first time
+// the current payload was first seen and attempted (consecutively).
+struct UpdateState {
+  // Information pertaining to the current update payload and/or check.
+  //
+  // Whether the current update check is an interactive one. The caller should
+  // feed the value returned by the preceding call to UpdateCheckAllowed().
+  bool is_interactive;
+  // Whether it is a delta payload.
+  bool is_delta_payload;
+  // Wallclock time when payload was first (consecutively) offered by Omaha.
+  base::Time first_seen;
+  // Number of consecutive update checks returning the current update.
+  int num_checks;
+  // Number of update payload failures and the wallclock time when it was last
+  // updated by the updater. These should both be nullified whenever a new
+  // update is seen; they are updated at the policy's descretion (via
+  // UpdateDownloadParams.do_increment_failures) once all of the usable download
+  // URLs for the payload have been used without success. They should be
+  // persisted across reboots.
+  int num_failures;
+  base::Time failures_last_updated;
+
+  // Information pertaining to downloading and applying of the current update.
+  //
+  // An array of download URLs provided by Omaha.
+  std::vector<std::string> download_urls;
+  // Max number of errors allowed per download URL.
+  int download_errors_max;
+  // The index of the URL to download from, as determined in the previous call
+  // to the policy. For a newly seen payload, this should be -1.
+  int last_download_url_idx;
+  // The number of successive download errors pertaining to this last URL, as
+  // determined in the previous call to the policy. For a newly seen payload,
+  // this should be zero.
+  int last_download_url_num_errors;
+  // An array of errors that occurred while trying to download this update since
+  // the previous call to this policy has returned, or since this payload was
+  // first seen, or since the updater process has started (whichever is later).
+  // Includes the URL index attempted, the error code, and the wallclock-based
+  // timestamp when it occurred.
+  std::vector<std::tuple<int, chromeos_update_engine::ErrorCode, base::Time>>
+      download_errors;
+  // Whether Omaha forbids use of P2P for downloading and/or sharing.
+  bool p2p_downloading_disabled;
+  bool p2p_sharing_disabled;
+  // The number of P2P download attempts and wallclock-based time when P2P
+  // download was first attempted.
+  int p2p_num_attempts;
+  base::Time p2p_first_attempted;
+
+  // Information pertaining to update backoff mechanism.
+  //
+  // The currently known (persisted) wallclock-based backoff expiration time;
+  // zero if none.
+  base::Time backoff_expiry;
+  // Whether backoff is disabled by Omaha.
+  bool is_backoff_disabled;
+
+  // Information pertaining to update scattering.
+  //
+  // The currently knwon (persisted) scattering wallclock-based wait period and
+  // update check threshold; zero if none.
+  base::TimeDelta scatter_wait_period;
+  int scatter_check_threshold;
+  // Maximum wait period allowed for this update, as determined by Omaha.
+  base::TimeDelta scatter_wait_period_max;
+  // Minimum/maximum check threshold values.
+  // TODO(garnold) These appear to not be related to the current update and so
+  // should probably be obtained as variables via UpdaterProvider.
+  int scatter_check_threshold_min;
+  int scatter_check_threshold_max;
+};
+
+// Results regarding the downloading and applying of an update, as determined by
+// UpdateCanStart.
+//
+// An enumerator for the reasons of not allowing an update to start.
+enum class UpdateCannotStartReason {
+  kUndefined,
+  kCheckDue,
+  kScattering,
+  kBackoff,
+  kCannotDownload,
+};
+
+struct UpdateDownloadParams {
+  // Whether the update attempt is allowed to proceed.
+  bool update_can_start;
+  // If update cannot proceed, a reason code for why it cannot do so.
+  UpdateCannotStartReason cannot_start_reason;
+
+  // Download related attributes. The update engine uses them to choose the
+  // means for downloading and applying an update.
+  //
+  // The index of the download URL to use (-1 means no suitable URL was found)
+  // and whether it can be used. Even if there's no URL or its use is not
+  // allowed (backoff, scattering) there may still be other means for download
+  // (like P2P).  The URL index needs to be persisted and handed back to the
+  // policy on the next time it is called.
+  int download_url_idx;
+  bool download_url_allowed;
+  // The number of download errors associated with this download URL. This value
+  // needs to be persisted and handed back to the policy on the next time it is
+  // called.
+  int download_url_num_errors;
+  // Whether P2P download and sharing are allowed.
+  bool p2p_downloading_allowed;
+  bool p2p_sharing_allowed;
+
+  // Other values that need to be persisted and handed to the policy as need on
+  // the next call.
+  //
+  // Whether an update failure has been identified by the policy. The client
+  // should increment and persist its update failure count, and record the time
+  // when this was done; it needs to hand these values back to the policy
+  // (UpdateState.{num_failures,failures_last_updated}) on the next time it is
+  // called.
+  bool do_increment_failures;
+  // The current backof expiry.
+  base::Time backoff_expiry;
+  // The scattering wait period and check threshold.
+  base::TimeDelta scatter_wait_period;
+  int scatter_check_threshold;
+};
+
+// The Policy class is an interface to the ensemble of policy requests that the
+// client can make. A derived class includes the policy implementations of
+// these.
+//
+// When compile-time selection of the policy is required due to missing or extra
+// parts in a given platform, a different Policy subclass can be used.
+class Policy {
+ public:
+  virtual ~Policy() {}
+
+  // Returns the name of a public policy request.
+  // IMPORTANT: Be sure to add a conditional for each new public policy that is
+  // being added to this class in the future.
+  template<typename R, typename... Args>
+  std::string PolicyRequestName(
+      EvalStatus (Policy::*policy_method)(EvaluationContext*, State*,
+                                          std::string*, R*,
+                                          Args...) const) const {
+    std::string class_name = PolicyName() + "::";
+
+    if (reinterpret_cast<typeof(&Policy::UpdateCheckAllowed)>(
+            policy_method) == &Policy::UpdateCheckAllowed)
+      return class_name + "UpdateCheckAllowed";
+    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";
+    if (reinterpret_cast<typeof(&Policy::P2PEnabledChanged)>(
+            policy_method) == &Policy::P2PEnabledChanged)
+      return class_name + "P2PEnabledChanged";
+
+    NOTREACHED();
+    return class_name + "(unknown)";
+  }
+
+
+  // List of policy requests. A policy request takes an EvaluationContext as the
+  // first argument, a State instance, a returned error message, a returned
+  // value and optionally followed by one or more arbitrary constant arguments.
+  //
+  // When the implementation fails, the method returns EvalStatus::kFailed and
+  // sets the |error| string.
+
+  // UpdateCheckAllowed returns whether it is allowed to request an update check
+  // to Omaha.
+  virtual EvalStatus UpdateCheckAllowed(
+      EvaluationContext* ec, State* state, std::string* error,
+      UpdateCheckParams* result) const = 0;
+
+  // Returns EvalStatus::kSucceeded if either an update can start being
+  // processed, or the attempt needs to be aborted. In cases where the update
+  // needs to wait for some condition to be satisfied, but none of the values
+  // that need to be persisted has changed, returns
+  // EvalStatus::kAskMeAgainLater. Arguments include an |update_state| that
+  // encapsulates data pertaining to the current ongoing update process.
+  virtual EvalStatus UpdateCanStart(
+      EvaluationContext* ec,
+      State* state,
+      std::string* error,
+      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, State* state, std::string* error,
+      bool* result) const = 0;
+
+  // Checks whether P2P is enabled, but blocks (returns
+  // |EvalStatus::kAskMeAgainLater|) until it is different from |prev_result|.
+  // If the P2P enabled status is not expected to change, will return
+  // immediately with |EvalStatus::kSucceeded|. This internally uses the
+  // P2PEnabled() policy above.
+  virtual EvalStatus P2PEnabledChanged(
+      EvaluationContext* ec, State* state, std::string* error,
+      bool* result, bool prev_result) const = 0;
+
+ protected:
+  Policy() {}
+
+  // Returns the name of the actual policy class.
+  virtual std::string PolicyName() const = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Policy);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_POLICY_H_
diff --git a/update_manager/policy_utils.h b/update_manager/policy_utils.h
new file mode 100644
index 0000000..960987e
--- /dev/null
+++ b/update_manager/policy_utils.h
@@ -0,0 +1,38 @@
+//
+// 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_UPDATE_MANAGER_POLICY_UTILS_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_POLICY_UTILS_H_
+
+#include "update_engine/update_manager/policy.h"
+
+// Checks that the passed pointer value is not null, returning kFailed on the
+// current context and setting the *error description when it is null. The
+// intended use is to validate variable failures while using
+// EvaluationContext::GetValue, for example:
+//
+//   const int* my_value = ec->GetValue(state->my_provider()->var_my_value());
+//   POLICY_CHECK_VALUE_AND_FAIL(my_value, error);
+//
+#define POLICY_CHECK_VALUE_AND_FAIL(ptr, error) \
+    do { \
+      if ((ptr) == nullptr) { \
+        *(error) = #ptr " is required but is null."; \
+        return EvalStatus::kFailed; \
+      } \
+    } while (false)
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_POLICY_UTILS_H_
diff --git a/update_manager/prng.h b/update_manager/prng.h
new file mode 100644
index 0000000..64f886c
--- /dev/null
+++ b/update_manager/prng.h
@@ -0,0 +1,51 @@
+//
+// 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_UPDATE_MANAGER_PRNG_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_PRNG_H_
+
+#include <random>
+
+#include <base/logging.h>
+
+namespace chromeos_update_manager {
+
+// A thread-safe, unsecure, 32-bit pseudo-random number generator based on
+// std::mt19937.
+class PRNG {
+ public:
+  // Initializes the generator with the passed |seed| value.
+  explicit PRNG(uint32_t seed) : gen_(seed) {}
+
+  // Returns a random unsigned 32-bit integer.
+  uint32_t Rand() { return gen_(); }
+
+  // Returns a random integer uniformly distributed in the range [min, max].
+  int RandMinMax(int min, int max) {
+    DCHECK_LE(min, max);
+    return std::uniform_int_distribution<>(min, max)(gen_);
+  }
+
+ private:
+  // A pseudo-random number generator.
+  std::mt19937 gen_;
+
+  DISALLOW_COPY_AND_ASSIGN(PRNG);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_PRNG_H_
diff --git a/update_manager/prng_unittest.cc b/update_manager/prng_unittest.cc
new file mode 100644
index 0000000..2a3f689
--- /dev/null
+++ b/update_manager/prng_unittest.cc
@@ -0,0 +1,79 @@
+//
+// 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/update_manager/prng.h"
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+using std::vector;
+
+namespace chromeos_update_manager {
+
+TEST(UmPRNGTest, ShouldBeDeterministic) {
+  PRNG a(42);
+  PRNG b(42);
+
+  for (int i = 0; i < 1000; ++i) {
+    EXPECT_EQ(a.Rand(), b.Rand()) << "Iteration i=" << i;
+  }
+}
+
+TEST(UmPRNGTest, SeedChangesGeneratedSequence) {
+  PRNG a(42);
+  PRNG b(5);
+
+  vector<uint32_t> values_a;
+  vector<uint32_t> values_b;
+
+  for (int i = 0; i < 100; ++i) {
+    values_a.push_back(a.Rand());
+    values_b.push_back(b.Rand());
+  }
+  EXPECT_NE(values_a, values_b);
+}
+
+TEST(UmPRNGTest, IsNotConstant) {
+  PRNG prng(5);
+
+  uint32_t initial_value = prng.Rand();
+  bool prng_is_constant = true;
+  for (int i = 0; i < 100; ++i) {
+    if (prng.Rand() != initial_value) {
+      prng_is_constant = false;
+      break;
+    }
+  }
+  EXPECT_FALSE(prng_is_constant) << "After 100 iterations.";
+}
+
+TEST(UmPRNGTest, RandCoversRange) {
+  PRNG a(42);
+  int hits[11] = { 0 };
+
+  for (int i = 0; i < 1000; i++) {
+    int r = a.RandMinMax(0, 10);
+    ASSERT_LE(0, r);
+    ASSERT_GE(10, r);
+    hits[r]++;
+  }
+
+  for (auto& hit : hits)
+    EXPECT_LT(0, hit);
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/provider.h b/update_manager/provider.h
new file mode 100644
index 0000000..84335a2
--- /dev/null
+++ b/update_manager/provider.h
@@ -0,0 +1,38 @@
+//
+// 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_UPDATE_MANAGER_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_PROVIDER_H_
+
+#include <base/macros.h>
+
+namespace chromeos_update_manager {
+
+// Abstract base class for a policy provider.
+class Provider {
+ public:
+  virtual ~Provider() {}
+
+ protected:
+  Provider() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Provider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_PROVIDER_H_
diff --git a/update_manager/random_provider.h b/update_manager/random_provider.h
new file mode 100644
index 0000000..60df62d
--- /dev/null
+++ b/update_manager/random_provider.h
@@ -0,0 +1,45 @@
+//
+// 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_UPDATE_MANAGER_RANDOM_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_RANDOM_PROVIDER_H_
+
+#include "update_engine/update_manager/provider.h"
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+// Provider of random values.
+class RandomProvider : public Provider {
+ public:
+  ~RandomProvider() override {}
+
+  // Return a random number every time it is requested. Note that values
+  // returned by the variables are cached by the EvaluationContext, so the
+  // returned value will be the same during the same policy request. If more
+  // random values are needed use a PRNG seeded with this value.
+  virtual Variable<uint64_t>* var_seed() = 0;
+
+ protected:
+  RandomProvider() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(RandomProvider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_RANDOM_PROVIDER_H_
diff --git a/update_manager/real_config_provider.cc b/update_manager/real_config_provider.cc
new file mode 100644
index 0000000..2d17a7f
--- /dev/null
+++ b/update_manager/real_config_provider.cc
@@ -0,0 +1,64 @@
+//
+// 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/update_manager/real_config_provider.h"
+
+#include <base/files/file_path.h>
+#include <base/logging.h>
+#include <brillo/key_value_store.h>
+
+#include "update_engine/common/constants.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/update_manager/generic_variables.h"
+
+using brillo::KeyValueStore;
+
+namespace {
+
+const char* kConfigFilePath = "/etc/update_manager.conf";
+
+// Config options:
+const char* kConfigOptsIsOOBEEnabled = "is_oobe_enabled";
+
+}  // namespace
+
+namespace chromeos_update_manager {
+
+bool RealConfigProvider::Init() {
+  KeyValueStore store;
+
+  if (hardware_->IsNormalBootMode()) {
+    store.Load(base::FilePath(root_prefix_ + kConfigFilePath));
+  } else {
+    if (store.Load(base::FilePath(root_prefix_ +
+                                  chromeos_update_engine::kStatefulPartition +
+                                  kConfigFilePath))) {
+      LOG(INFO) << "UpdateManager Config loaded from stateful partition.";
+    } else {
+      store.Load(base::FilePath(root_prefix_ + kConfigFilePath));
+    }
+  }
+
+  bool is_oobe_enabled;
+  if (!store.GetBoolean(kConfigOptsIsOOBEEnabled, &is_oobe_enabled))
+    is_oobe_enabled = true;  // Default value.
+  var_is_oobe_enabled_.reset(
+      new ConstCopyVariable<bool>(kConfigOptsIsOOBEEnabled, is_oobe_enabled));
+
+  return true;
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/real_config_provider.h b/update_manager/real_config_provider.h
new file mode 100644
index 0000000..4de910c
--- /dev/null
+++ b/update_manager/real_config_provider.h
@@ -0,0 +1,65 @@
+//
+// 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_UPDATE_MANAGER_REAL_CONFIG_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_REAL_CONFIG_PROVIDER_H_
+
+#include <memory>
+#include <string>
+
+#include "update_engine/common/hardware_interface.h"
+#include "update_engine/update_manager/config_provider.h"
+#include "update_engine/update_manager/generic_variables.h"
+
+namespace chromeos_update_manager {
+
+// ConfigProvider concrete implementation.
+class RealConfigProvider : public ConfigProvider {
+ public:
+  explicit RealConfigProvider(
+      chromeos_update_engine::HardwareInterface* hardware)
+      : hardware_(hardware) {}
+
+  // Initializes the provider and returns whether it succeeded.
+  bool Init();
+
+  Variable<bool>* var_is_oobe_enabled() override {
+    return var_is_oobe_enabled_.get();
+  }
+
+ private:
+  friend class UmRealConfigProviderTest;
+
+  // Used for testing. Sets the root prefix, which is by default "". Call this
+  // method before calling Init() in order to mock out the place where the files
+  // are being read from.
+  void SetRootPrefix(const std::string& prefix) {
+    root_prefix_ = prefix;
+  }
+
+  std::unique_ptr<ConstCopyVariable<bool>> var_is_oobe_enabled_;
+
+  chromeos_update_engine::HardwareInterface* hardware_;
+
+  // Prefix to prepend to the file paths. Useful for testing.
+  std::string root_prefix_;
+
+  DISALLOW_COPY_AND_ASSIGN(RealConfigProvider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_REAL_CONFIG_PROVIDER_H_
diff --git a/update_manager/real_config_provider_unittest.cc b/update_manager/real_config_provider_unittest.cc
new file mode 100644
index 0000000..2d7dc0d
--- /dev/null
+++ b/update_manager/real_config_provider_unittest.cc
@@ -0,0 +1,102 @@
+//
+// 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/update_manager/real_config_provider.h"
+
+#include <memory>
+
+#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_hardware.h"
+#include "update_engine/common/test_utils.h"
+#include "update_engine/update_manager/umtest_utils.h"
+
+using base::TimeDelta;
+using chromeos_update_engine::test_utils::WriteFileString;
+using std::string;
+using std::unique_ptr;
+
+namespace chromeos_update_manager {
+
+class UmRealConfigProviderTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ASSERT_TRUE(root_dir_.CreateUniqueTempDir());
+    provider_.reset(new RealConfigProvider(&fake_hardware_));
+    provider_->SetRootPrefix(root_dir_.path().value());
+  }
+
+  void WriteStatefulConfig(const string& config) {
+    base::FilePath kFile(root_dir_.path().value()
+                         + chromeos_update_engine::kStatefulPartition
+                         + "/etc/update_manager.conf");
+    ASSERT_TRUE(base::CreateDirectory(kFile.DirName()));
+    ASSERT_TRUE(WriteFileString(kFile.value(), config));
+  }
+
+  void WriteRootfsConfig(const string& config) {
+    base::FilePath kFile(root_dir_.path().value()
+                         + "/etc/update_manager.conf");
+    ASSERT_TRUE(base::CreateDirectory(kFile.DirName()));
+    ASSERT_TRUE(WriteFileString(kFile.value(), config));
+  }
+
+  unique_ptr<RealConfigProvider> provider_;
+  chromeos_update_engine::FakeHardware fake_hardware_;
+  TimeDelta default_timeout_ = TimeDelta::FromSeconds(1);
+  base::ScopedTempDir root_dir_;
+};
+
+TEST_F(UmRealConfigProviderTest, InitTest) {
+  EXPECT_TRUE(provider_->Init());
+  EXPECT_NE(nullptr, provider_->var_is_oobe_enabled());
+}
+
+TEST_F(UmRealConfigProviderTest, NoFileFoundReturnsDefault) {
+  EXPECT_TRUE(provider_->Init());
+  UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_oobe_enabled());
+}
+
+TEST_F(UmRealConfigProviderTest, DontReadStatefulInNormalMode) {
+  fake_hardware_.SetIsNormalBootMode(true);
+  WriteStatefulConfig("is_oobe_enabled=false");
+
+  EXPECT_TRUE(provider_->Init());
+  UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_oobe_enabled());
+}
+
+TEST_F(UmRealConfigProviderTest, ReadStatefulInDevMode) {
+  fake_hardware_.SetIsNormalBootMode(false);
+  WriteRootfsConfig("is_oobe_enabled=true");
+  // Since the stateful is present, this should read that one.
+  WriteStatefulConfig("is_oobe_enabled=false");
+
+  EXPECT_TRUE(provider_->Init());
+  UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_oobe_enabled());
+}
+
+TEST_F(UmRealConfigProviderTest, ReadRootfsIfStatefulNotFound) {
+  fake_hardware_.SetIsNormalBootMode(false);
+  WriteRootfsConfig("is_oobe_enabled=false");
+
+  EXPECT_TRUE(provider_->Init());
+  UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_oobe_enabled());
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/real_device_policy_provider.cc b/update_manager/real_device_policy_provider.cc
new file mode 100644
index 0000000..0abd9f7
--- /dev/null
+++ b/update_manager/real_device_policy_provider.cc
@@ -0,0 +1,187 @@
+//
+// 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/update_manager/real_device_policy_provider.h"
+
+#include <stdint.h>
+
+#include <base/location.h>
+#include <base/logging.h>
+#include <base/time/time.h>
+#include <policy/device_policy.h>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/update_manager/generic_variables.h"
+#include "update_engine/update_manager/real_shill_provider.h"
+
+using base::TimeDelta;
+using brillo::MessageLoop;
+using policy::DevicePolicy;
+using std::set;
+using std::string;
+
+namespace {
+
+const int kDevicePolicyRefreshRateInMinutes = 60;
+
+}  // namespace
+
+namespace chromeos_update_manager {
+
+RealDevicePolicyProvider::~RealDevicePolicyProvider() {
+  MessageLoop::current()->CancelTask(scheduled_refresh_);
+}
+
+bool RealDevicePolicyProvider::Init() {
+  CHECK(policy_provider_ != nullptr);
+
+  // On Init() we try to get the device policy and keep updating it.
+  RefreshDevicePolicyAndReschedule();
+
+  // We also listen for signals from the session manager to force a device
+  // policy refresh.
+  session_manager_proxy_->RegisterPropertyChangeCompleteSignalHandler(
+      base::Bind(&RealDevicePolicyProvider::OnPropertyChangedCompletedSignal,
+                 base::Unretained(this)),
+      base::Bind(&RealDevicePolicyProvider::OnSignalConnected,
+                 base::Unretained(this)));
+  return true;
+}
+
+void RealDevicePolicyProvider::OnPropertyChangedCompletedSignal(
+    const string& success) {
+  if (success != "success") {
+    LOG(WARNING) << "Received device policy updated signal with a failure.";
+  }
+  // We refresh the policy file even if the payload string is kSignalFailure.
+  LOG(INFO) << "Reloading and re-scheduling device policy due to signal "
+               "received.";
+  MessageLoop::current()->CancelTask(scheduled_refresh_);
+  scheduled_refresh_ = MessageLoop::kTaskIdNull;
+  RefreshDevicePolicyAndReschedule();
+}
+
+void RealDevicePolicyProvider::OnSignalConnected(const string& interface_name,
+                                                 const string& signal_name,
+                                                 bool successful) {
+  if (!successful) {
+    LOG(WARNING) << "We couldn't connect to SessionManager signal for updates "
+                    "on the device policy blob. We will reload the policy file "
+                    "periodically.";
+  }
+  // We do a one-time refresh of the DevicePolicy just in case we missed a
+  // signal between the first refresh and the time the signal handler was
+  // actually connected.
+  RefreshDevicePolicy();
+}
+
+void RealDevicePolicyProvider::RefreshDevicePolicyAndReschedule() {
+  RefreshDevicePolicy();
+  scheduled_refresh_ = MessageLoop::current()->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&RealDevicePolicyProvider::RefreshDevicePolicyAndReschedule,
+                 base::Unretained(this)),
+      TimeDelta::FromMinutes(kDevicePolicyRefreshRateInMinutes));
+}
+
+template<typename T>
+void RealDevicePolicyProvider::UpdateVariable(
+    AsyncCopyVariable<T>* var,
+    bool (DevicePolicy::*getter_method)(T*) const) {
+  T new_value;
+  if (policy_provider_->device_policy_is_loaded() &&
+      (policy_provider_->GetDevicePolicy().*getter_method)(&new_value)) {
+    var->SetValue(new_value);
+  } else {
+    var->UnsetValue();
+  }
+}
+
+template<typename T>
+void RealDevicePolicyProvider::UpdateVariable(
+    AsyncCopyVariable<T>* var,
+    bool (RealDevicePolicyProvider::*getter_method)(T*) const) {
+  T new_value;
+  if (policy_provider_->device_policy_is_loaded() &&
+      (this->*getter_method)(&new_value)) {
+    var->SetValue(new_value);
+  } else {
+    var->UnsetValue();
+  }
+}
+
+bool RealDevicePolicyProvider::ConvertAllowedConnectionTypesForUpdate(
+      set<ConnectionType>* allowed_types) const {
+  set<string> allowed_types_str;
+  if (!policy_provider_->GetDevicePolicy()
+      .GetAllowedConnectionTypesForUpdate(&allowed_types_str)) {
+    return false;
+  }
+  allowed_types->clear();
+  for (auto& type_str : allowed_types_str) {
+    ConnectionType type =
+        RealShillProvider::ParseConnectionType(type_str.c_str());
+    if (type != ConnectionType::kUnknown) {
+      allowed_types->insert(type);
+    } else {
+      LOG(WARNING) << "Policy includes unknown connection type: " << type_str;
+    }
+  }
+  return true;
+}
+
+bool RealDevicePolicyProvider::ConvertScatterFactor(
+    TimeDelta* scatter_factor) const {
+  int64_t scatter_factor_in_seconds;
+  if (!policy_provider_->GetDevicePolicy().GetScatterFactorInSeconds(
+      &scatter_factor_in_seconds)) {
+    return false;
+  }
+  if (scatter_factor_in_seconds < 0) {
+    LOG(WARNING) << "Ignoring negative scatter factor: "
+                 << scatter_factor_in_seconds;
+    return false;
+  }
+  *scatter_factor = TimeDelta::FromSeconds(scatter_factor_in_seconds);
+  return true;
+}
+
+void RealDevicePolicyProvider::RefreshDevicePolicy() {
+  if (!policy_provider_->Reload()) {
+    LOG(INFO) << "No device policies/settings present.";
+  }
+
+  var_device_policy_is_loaded_.SetValue(
+      policy_provider_->device_policy_is_loaded());
+
+  UpdateVariable(&var_release_channel_, &DevicePolicy::GetReleaseChannel);
+  UpdateVariable(&var_release_channel_delegated_,
+                 &DevicePolicy::GetReleaseChannelDelegated);
+  UpdateVariable(&var_update_disabled_, &DevicePolicy::GetUpdateDisabled);
+  UpdateVariable(&var_target_version_prefix_,
+                 &DevicePolicy::GetTargetVersionPrefix);
+  UpdateVariable(&var_scatter_factor_,
+                 &RealDevicePolicyProvider::ConvertScatterFactor);
+  UpdateVariable(
+      &var_allowed_connection_types_for_update_,
+      &RealDevicePolicyProvider::ConvertAllowedConnectionTypesForUpdate);
+  UpdateVariable(&var_owner_, &DevicePolicy::GetOwner);
+  UpdateVariable(&var_http_downloads_enabled_,
+                 &DevicePolicy::GetHttpDownloadsEnabled);
+  UpdateVariable(&var_au_p2p_enabled_, &DevicePolicy::GetAuP2PEnabled);
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/real_device_policy_provider.h b/update_manager/real_device_policy_provider.h
new file mode 100644
index 0000000..6094e93
--- /dev/null
+++ b/update_manager/real_device_policy_provider.h
@@ -0,0 +1,166 @@
+//
+// 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_UPDATE_MANAGER_REAL_DEVICE_POLICY_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_REAL_DEVICE_POLICY_PROVIDER_H_
+
+#include <set>
+#include <string>
+
+#include <brillo/message_loops/message_loop.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+#include <policy/libpolicy.h>
+#include <session_manager/dbus-proxies.h>
+
+#include "update_engine/update_manager/device_policy_provider.h"
+#include "update_engine/update_manager/generic_variables.h"
+
+namespace chromeos_update_manager {
+
+// DevicePolicyProvider concrete implementation.
+class RealDevicePolicyProvider : public DevicePolicyProvider {
+ public:
+  RealDevicePolicyProvider(org::chromium::SessionManagerInterfaceProxyInterface*
+                               session_manager_proxy,
+                           policy::PolicyProvider* policy_provider)
+      : policy_provider_(policy_provider),
+        session_manager_proxy_(session_manager_proxy) {}
+  ~RealDevicePolicyProvider();
+
+  // Initializes the provider and returns whether it succeeded.
+  bool Init();
+
+  Variable<bool>* var_device_policy_is_loaded() override {
+    return &var_device_policy_is_loaded_;
+  }
+
+  Variable<std::string>* var_release_channel() override {
+    return &var_release_channel_;
+  }
+
+  Variable<bool>* var_release_channel_delegated() override {
+    return &var_release_channel_delegated_;
+  }
+
+  Variable<bool>* var_update_disabled() override {
+    return &var_update_disabled_;
+  }
+
+  Variable<std::string>* var_target_version_prefix() override {
+    return &var_target_version_prefix_;
+  }
+
+  Variable<base::TimeDelta>* var_scatter_factor() override {
+    return &var_scatter_factor_;
+  }
+
+  Variable<std::set<ConnectionType>>*
+      var_allowed_connection_types_for_update() override {
+    return &var_allowed_connection_types_for_update_;
+  }
+
+  Variable<std::string>* var_owner() override {
+    return &var_owner_;
+  }
+
+  Variable<bool>* var_http_downloads_enabled() override {
+    return &var_http_downloads_enabled_;
+  }
+
+  Variable<bool>* var_au_p2p_enabled() override {
+    return &var_au_p2p_enabled_;
+  }
+
+ private:
+  FRIEND_TEST(UmRealDevicePolicyProviderTest, RefreshScheduledTest);
+  FRIEND_TEST(UmRealDevicePolicyProviderTest, NonExistentDevicePolicyReloaded);
+  FRIEND_TEST(UmRealDevicePolicyProviderTest, ValuesUpdated);
+
+  // 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
+  // connected.
+  void OnSignalConnected(const std::string& interface_name,
+                         const std::string& signal_name,
+                         bool successful);
+
+  // Schedules a call to periodically refresh the device policy.
+  void RefreshDevicePolicyAndReschedule();
+
+  // Reloads the device policy and updates all the exposed variables.
+  void RefreshDevicePolicy();
+
+  // Updates the async variable |var| based on the result value of the method
+  // passed, which is a DevicePolicy getter method.
+  template<typename T>
+  void UpdateVariable(AsyncCopyVariable<T>* var,
+                      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_method)(T*) const);
+
+  // 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.
+  bool ConvertAllowedConnectionTypesForUpdate(
+      std::set<ConnectionType>* allowed_types) const;
+
+  // Used for fetching information about the device policy.
+  policy::PolicyProvider* policy_provider_;
+
+  // Used to schedule refreshes of the device policy.
+  brillo::MessageLoop::TaskId scheduled_refresh_{
+      brillo::MessageLoop::kTaskIdNull};
+
+  // The DBus (mockable) session manager proxy, owned by the caller.
+  org::chromium::SessionManagerInterfaceProxyInterface* session_manager_proxy_{
+      nullptr};
+
+  // Variable exposing whether the policy is loaded.
+  AsyncCopyVariable<bool> var_device_policy_is_loaded_{
+      "policy_is_loaded", false};
+
+  // 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<bool> var_update_disabled_{"update_disabled"};
+  AsyncCopyVariable<std::string> var_target_version_prefix_{
+      "target_version_prefix"};
+  AsyncCopyVariable<base::TimeDelta> var_scatter_factor_{"scatter_factor"};
+  AsyncCopyVariable<std::set<ConnectionType>>
+      var_allowed_connection_types_for_update_{
+          "allowed_connection_types_for_update"};
+  AsyncCopyVariable<std::string> var_owner_{"owner"};
+  AsyncCopyVariable<bool> var_http_downloads_enabled_{"http_downloads_enabled"};
+  AsyncCopyVariable<bool> var_au_p2p_enabled_{"au_p2p_enabled"};
+
+  DISALLOW_COPY_AND_ASSIGN(RealDevicePolicyProvider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_REAL_DEVICE_POLICY_PROVIDER_H_
diff --git a/update_manager/real_device_policy_provider_unittest.cc b/update_manager/real_device_policy_provider_unittest.cc
new file mode 100644
index 0000000..c480b60
--- /dev/null
+++ b/update_manager/real_device_policy_provider_unittest.cc
@@ -0,0 +1,227 @@
+//
+// 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/update_manager/real_device_policy_provider.h"
+
+#include <memory>
+
+#include <brillo/message_loops/fake_message_loop.h>
+#include <brillo/message_loops/message_loop.h>
+#include <brillo/message_loops/message_loop_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <policy/mock_device_policy.h>
+#include <policy/mock_libpolicy.h>
+#include <session_manager/dbus-proxies.h>
+#include <session_manager/dbus-proxy-mocks.h>
+
+#include "update_engine/common/test_utils.h"
+#include "update_engine/dbus_test_utils.h"
+#include "update_engine/update_manager/umtest_utils.h"
+
+using base::TimeDelta;
+using brillo::MessageLoop;
+using chromeos_update_engine::dbus_test_utils::MockSignalHandler;
+using std::set;
+using std::string;
+using std::unique_ptr;
+using testing::DoAll;
+using testing::Mock;
+using testing::Return;
+using testing::ReturnRef;
+using testing::SetArgPointee;
+using testing::_;
+
+namespace chromeos_update_manager {
+
+class UmRealDevicePolicyProviderTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    loop_.SetAsCurrent();
+    provider_.reset(new RealDevicePolicyProvider(&session_manager_proxy_mock_,
+                                                 &mock_policy_provider_));
+    // By default, we have a device policy loaded. Tests can call
+    // SetUpNonExistentDevicePolicy() to override this.
+    SetUpExistentDevicePolicy();
+
+    // Setup the session manager_proxy such that it will accept the signal
+    // handler and store it in the |property_change_complete_| once registered.
+    MOCK_SIGNAL_HANDLER_EXPECT_SIGNAL_HANDLER(property_change_complete_,
+                                              session_manager_proxy_mock_,
+                                              PropertyChangeComplete);
+  }
+
+  void TearDown() override {
+    provider_.reset();
+    // Check for leaked callbacks on the main loop.
+    EXPECT_FALSE(loop_.PendingTasks());
+  }
+
+  void SetUpNonExistentDevicePolicy() {
+    ON_CALL(mock_policy_provider_, Reload())
+        .WillByDefault(Return(false));
+    ON_CALL(mock_policy_provider_, device_policy_is_loaded())
+        .WillByDefault(Return(false));
+    EXPECT_CALL(mock_policy_provider_, GetDevicePolicy()).Times(0);
+  }
+
+  void SetUpExistentDevicePolicy() {
+    // Setup the default behavior of the mocked PolicyProvider.
+    ON_CALL(mock_policy_provider_, Reload())
+        .WillByDefault(Return(true));
+    ON_CALL(mock_policy_provider_, device_policy_is_loaded())
+        .WillByDefault(Return(true));
+    ON_CALL(mock_policy_provider_, GetDevicePolicy())
+        .WillByDefault(ReturnRef(mock_device_policy_));
+  }
+
+  brillo::FakeMessageLoop loop_{nullptr};
+  org::chromium::SessionManagerInterfaceProxyMock session_manager_proxy_mock_;
+  testing::NiceMock<policy::MockDevicePolicy> mock_device_policy_;
+  testing::NiceMock<policy::MockPolicyProvider> mock_policy_provider_;
+  unique_ptr<RealDevicePolicyProvider> provider_;
+
+  // The registered signal handler for the signal.
+  MockSignalHandler<void(const string&)> property_change_complete_;
+};
+
+TEST_F(UmRealDevicePolicyProviderTest, RefreshScheduledTest) {
+  // Check that the RefreshPolicy gets scheduled by checking the TaskId.
+  EXPECT_TRUE(provider_->Init());
+  EXPECT_NE(MessageLoop::kTaskIdNull, provider_->scheduled_refresh_);
+  loop_.RunOnce(false);
+}
+
+TEST_F(UmRealDevicePolicyProviderTest, FirstReload) {
+  // Checks that the policy is reloaded and the DevicePolicy is consulted twice:
+  // once on Init() and once again when the signal is connected.
+  EXPECT_CALL(mock_policy_provider_, Reload());
+  EXPECT_TRUE(provider_->Init());
+  Mock::VerifyAndClearExpectations(&mock_policy_provider_);
+
+  EXPECT_CALL(mock_policy_provider_, Reload());
+  loop_.RunOnce(false);
+}
+
+TEST_F(UmRealDevicePolicyProviderTest, NonExistentDevicePolicyReloaded) {
+  // Checks that the policy is reloaded by RefreshDevicePolicy().
+  SetUpNonExistentDevicePolicy();
+  EXPECT_CALL(mock_policy_provider_, Reload()).Times(3);
+  EXPECT_TRUE(provider_->Init());
+  loop_.RunOnce(false);
+  // Force the policy refresh.
+  provider_->RefreshDevicePolicy();
+}
+
+TEST_F(UmRealDevicePolicyProviderTest, SessionManagerSignalForcesReload) {
+  // Checks that a signal from the SessionManager forces a reload.
+  SetUpNonExistentDevicePolicy();
+  EXPECT_CALL(mock_policy_provider_, Reload()).Times(2);
+  EXPECT_TRUE(provider_->Init());
+  loop_.RunOnce(false);
+  Mock::VerifyAndClearExpectations(&mock_policy_provider_);
+
+  EXPECT_CALL(mock_policy_provider_, Reload());
+  ASSERT_TRUE(property_change_complete_.IsHandlerRegistered());
+  property_change_complete_.signal_callback().Run("success");
+}
+
+TEST_F(UmRealDevicePolicyProviderTest, NonExistentDevicePolicyEmptyVariables) {
+  SetUpNonExistentDevicePolicy();
+  EXPECT_CALL(mock_policy_provider_, GetDevicePolicy()).Times(0);
+  EXPECT_TRUE(provider_->Init());
+  loop_.RunOnce(false);
+
+  UmTestUtils::ExpectVariableHasValue(false,
+                                      provider_->var_device_policy_is_loaded());
+
+  UmTestUtils::ExpectVariableNotSet(provider_->var_release_channel());
+  UmTestUtils::ExpectVariableNotSet(provider_->var_release_channel_delegated());
+  UmTestUtils::ExpectVariableNotSet(provider_->var_update_disabled());
+  UmTestUtils::ExpectVariableNotSet(provider_->var_target_version_prefix());
+  UmTestUtils::ExpectVariableNotSet(provider_->var_scatter_factor());
+  UmTestUtils::ExpectVariableNotSet(
+      provider_->var_allowed_connection_types_for_update());
+  UmTestUtils::ExpectVariableNotSet(provider_->var_owner());
+  UmTestUtils::ExpectVariableNotSet(provider_->var_http_downloads_enabled());
+  UmTestUtils::ExpectVariableNotSet(provider_->var_au_p2p_enabled());
+}
+
+TEST_F(UmRealDevicePolicyProviderTest, ValuesUpdated) {
+  SetUpNonExistentDevicePolicy();
+  EXPECT_TRUE(provider_->Init());
+  loop_.RunOnce(false);
+  Mock::VerifyAndClearExpectations(&mock_policy_provider_);
+
+  // Reload the policy with a good one and set some values as present. The
+  // remaining values are false.
+  SetUpExistentDevicePolicy();
+  EXPECT_CALL(mock_device_policy_, GetReleaseChannel(_))
+      .WillOnce(DoAll(SetArgPointee<0>(string("mychannel")), Return(true)));
+  EXPECT_CALL(mock_device_policy_, GetAllowedConnectionTypesForUpdate(_))
+      .WillOnce(Return(false));
+
+  provider_->RefreshDevicePolicy();
+
+  UmTestUtils::ExpectVariableHasValue(true,
+                                      provider_->var_device_policy_is_loaded());
+
+  // Test that at least one variable is set, to ensure the refresh occurred.
+  UmTestUtils::ExpectVariableHasValue(string("mychannel"),
+                                      provider_->var_release_channel());
+  UmTestUtils::ExpectVariableNotSet(
+      provider_->var_allowed_connection_types_for_update());
+}
+
+TEST_F(UmRealDevicePolicyProviderTest, ScatterFactorConverted) {
+  SetUpExistentDevicePolicy();
+  EXPECT_CALL(mock_device_policy_, GetScatterFactorInSeconds(_))
+      .Times(2)
+      .WillRepeatedly(DoAll(SetArgPointee<0>(1234), Return(true)));
+  EXPECT_TRUE(provider_->Init());
+  loop_.RunOnce(false);
+
+  UmTestUtils::ExpectVariableHasValue(TimeDelta::FromSeconds(1234),
+                                      provider_->var_scatter_factor());
+}
+
+TEST_F(UmRealDevicePolicyProviderTest, NegativeScatterFactorIgnored) {
+  SetUpExistentDevicePolicy();
+  EXPECT_CALL(mock_device_policy_, GetScatterFactorInSeconds(_))
+      .Times(2)
+      .WillRepeatedly(DoAll(SetArgPointee<0>(-1), Return(true)));
+  EXPECT_TRUE(provider_->Init());
+  loop_.RunOnce(false);
+
+  UmTestUtils::ExpectVariableNotSet(provider_->var_scatter_factor());
+}
+
+TEST_F(UmRealDevicePolicyProviderTest, AllowedTypesConverted) {
+  SetUpExistentDevicePolicy();
+  EXPECT_CALL(mock_device_policy_, GetAllowedConnectionTypesForUpdate(_))
+      .Times(2)
+      .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::kBluetooth},
+      provider_->var_allowed_connection_types_for_update());
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/real_random_provider.cc b/update_manager/real_random_provider.cc
new file mode 100644
index 0000000..ed0eb4d
--- /dev/null
+++ b/update_manager/real_random_provider.cc
@@ -0,0 +1,89 @@
+//
+// 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/update_manager/real_random_provider.h"
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/files/scoped_file.h>
+#include <base/strings/stringprintf.h>
+
+#include "update_engine/update_manager/variable.h"
+
+using std::string;
+
+namespace {
+
+// The device providing randomness.
+const char* kRandomDevice = "/dev/urandom";
+
+}  // namespace
+
+namespace chromeos_update_manager {
+
+// A random seed variable.
+class RandomSeedVariable : public Variable<uint64_t> {
+ public:
+  // RandomSeedVariable is initialized as kVariableModeConst to let the
+  // EvaluationContext cache the value between different evaluations of the same
+  // policy request.
+  RandomSeedVariable(const string& name, FILE* fp)
+      : Variable<uint64_t>(name, kVariableModeConst), fp_(fp) {}
+  ~RandomSeedVariable() override {}
+
+ protected:
+  const uint64_t* GetValue(base::TimeDelta /* timeout */,
+                           string* errmsg) override {
+    uint64_t result;
+    // Aliasing via char pointer abides by the C/C++ strict-aliasing rules.
+    char* const buf = reinterpret_cast<char*>(&result);
+    unsigned int buf_rd = 0;
+
+    while (buf_rd < sizeof(result)) {
+      int rd = fread(buf + buf_rd, 1, sizeof(result) - buf_rd, fp_.get());
+      if (rd == 0 || ferror(fp_.get())) {
+        // Either EOF on fp or read failed.
+        if (errmsg) {
+          *errmsg = base::StringPrintf(
+              "Error reading from the random device: %s", kRandomDevice);
+        }
+        return nullptr;
+      }
+      buf_rd += rd;
+    }
+
+    return new uint64_t(result);
+  }
+
+ private:
+  base::ScopedFILE fp_;
+
+  DISALLOW_COPY_AND_ASSIGN(RandomSeedVariable);
+};
+
+bool RealRandomProvider::Init(void) {
+  FILE* fp = fopen(kRandomDevice, "r");
+  if (!fp)
+    return false;
+  var_seed_.reset(new RandomSeedVariable("seed", fp));
+  return true;
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/real_random_provider.h b/update_manager/real_random_provider.h
new file mode 100644
index 0000000..14ce7a3
--- /dev/null
+++ b/update_manager/real_random_provider.h
@@ -0,0 +1,45 @@
+//
+// 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_UPDATE_MANAGER_REAL_RANDOM_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_REAL_RANDOM_PROVIDER_H_
+
+#include <memory>
+
+#include "update_engine/update_manager/random_provider.h"
+
+namespace chromeos_update_manager {
+
+// RandomProvider implementation class.
+class RealRandomProvider : public RandomProvider {
+ public:
+  RealRandomProvider() {}
+
+  Variable<uint64_t>* var_seed() override { return var_seed_.get(); }
+
+  // Initializes the provider and returns whether it succeeded.
+  bool Init();
+
+ private:
+  // The seed() scoped variable.
+  std::unique_ptr<Variable<uint64_t>> var_seed_;
+
+  DISALLOW_COPY_AND_ASSIGN(RealRandomProvider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_REAL_RANDOM_PROVIDER_H_
diff --git a/update_manager/real_random_provider_unittest.cc b/update_manager/real_random_provider_unittest.cc
new file mode 100644
index 0000000..ca67da6
--- /dev/null
+++ b/update_manager/real_random_provider_unittest.cc
@@ -0,0 +1,67 @@
+//
+// 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/update_manager/real_random_provider.h"
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "update_engine/update_manager/umtest_utils.h"
+
+using std::unique_ptr;
+
+namespace chromeos_update_manager {
+
+class UmRealRandomProviderTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    // The provider initializes correctly.
+    provider_.reset(new RealRandomProvider());
+    ASSERT_NE(nullptr, provider_.get());
+    ASSERT_TRUE(provider_->Init());
+
+    provider_->var_seed();
+  }
+
+  unique_ptr<RealRandomProvider> provider_;
+};
+
+TEST_F(UmRealRandomProviderTest, InitFinalize) {
+  // The provider initializes all variables with valid objects.
+  EXPECT_NE(nullptr, provider_->var_seed());
+}
+
+TEST_F(UmRealRandomProviderTest, GetRandomValues) {
+  // Should not return the same random seed repeatedly.
+  unique_ptr<const uint64_t> value(
+      provider_->var_seed()->GetValue(UmTestUtils::DefaultTimeout(), nullptr));
+  ASSERT_NE(nullptr, value.get());
+
+  // Test that at least the returned values are different. This test fails,
+  // by design, once every 2^320 runs.
+  bool is_same_value = true;
+  for (int i = 0; i < 5; i++) {
+    unique_ptr<const uint64_t> other_value(
+        provider_->var_seed()->GetValue(UmTestUtils::DefaultTimeout(),
+                                        nullptr));
+    ASSERT_NE(nullptr, other_value.get());
+    is_same_value = is_same_value && *other_value == *value;
+  }
+  EXPECT_FALSE(is_same_value);
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/real_shill_provider.cc b/update_manager/real_shill_provider.cc
new file mode 100644
index 0000000..7938180
--- /dev/null
+++ b/update_manager/real_shill_provider.cc
@@ -0,0 +1,191 @@
+//
+// 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/update_manager/real_shill_provider.h"
+
+#include <string>
+
+#include <base/logging.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/type_name_undecorate.h>
+#include <shill/dbus-constants.h>
+#include <shill/dbus-proxies.h>
+
+using org::chromium::flimflam::ManagerProxyInterface;
+using org::chromium::flimflam::ServiceProxyInterface;
+using std::string;
+
+namespace chromeos_update_manager {
+
+ConnectionType RealShillProvider::ParseConnectionType(const string& type_str) {
+  if (type_str == shill::kTypeEthernet) {
+    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;
+  }
+  return ConnectionType::kUnknown;
+}
+
+ConnectionTethering RealShillProvider::ParseConnectionTethering(
+    const string& tethering_str) {
+  if (tethering_str == shill::kTetheringNotDetectedState) {
+    return ConnectionTethering::kNotDetected;
+  } else if (tethering_str == shill::kTetheringSuspectedState) {
+    return ConnectionTethering::kSuspected;
+  } else if (tethering_str == shill::kTetheringConfirmedState) {
+    return ConnectionTethering::kConfirmed;
+  }
+  return ConnectionTethering::kUnknown;
+}
+
+bool RealShillProvider::Init() {
+  ManagerProxyInterface* manager_proxy = shill_proxy_->GetManagerProxy();
+  if (!manager_proxy)
+    return false;
+
+  // Subscribe to the manager's PropertyChanged signal.
+  manager_proxy->RegisterPropertyChangedSignalHandler(
+      base::Bind(&RealShillProvider::OnManagerPropertyChanged,
+                 base::Unretained(this)),
+      base::Bind(&RealShillProvider::OnSignalConnected,
+                 base::Unretained(this)));
+
+  // Attempt to read initial connection status. Even if this fails because shill
+  // is not responding (e.g. it is down) we'll be notified via "PropertyChanged"
+  // signal as soon as it comes up, so this is not a critical step.
+  brillo::VariantDictionary properties;
+  brillo::ErrorPtr error;
+  if (!manager_proxy->GetProperties(&properties, &error))
+    return true;
+
+  const auto& prop_default_service =
+      properties.find(shill::kDefaultServiceProperty);
+  if (prop_default_service != properties.end()) {
+    OnManagerPropertyChanged(prop_default_service->first,
+                             prop_default_service->second);
+  }
+
+  return true;
+}
+
+void RealShillProvider::OnManagerPropertyChanged(const string& name,
+                                                 const brillo::Any& value) {
+  if (name == shill::kDefaultServiceProperty) {
+    dbus::ObjectPath service_path = value.TryGet<dbus::ObjectPath>();
+    if (!service_path.IsValid()) {
+      LOG(WARNING) << "Got an invalid DefaultService path. The property value "
+                      "contains a "
+                   << value.GetUndecoratedTypeName()
+                   << ", read as the object path: '" << service_path.value()
+                   << "'";
+    }
+    ProcessDefaultService(service_path);
+  }
+}
+
+void RealShillProvider::OnSignalConnected(const string& interface_name,
+                                          const string& signal_name,
+                                          bool successful) {
+  if (!successful) {
+    LOG(ERROR) << "Couldn't connect to the signal " << interface_name << "."
+               << signal_name;
+  }
+}
+
+bool RealShillProvider::ProcessDefaultService(
+    const dbus::ObjectPath& default_service_path) {
+  // We assume that if the service path didn't change, then the connection
+  // type and the tethering status of it also didn't change.
+  if (default_service_path_ == default_service_path)
+    return true;
+
+  // Update the connection status.
+  default_service_path_ = default_service_path;
+  bool is_connected = (default_service_path_.IsValid() &&
+                       default_service_path_.value() != "/");
+  var_is_connected_.SetValue(is_connected);
+  var_conn_last_changed_.SetValue(clock_->GetWallclockTime());
+
+  if (!is_connected) {
+    var_conn_type_.UnsetValue();
+    var_conn_tethering_.UnsetValue();
+    return true;
+  }
+
+  // We create and dispose the ServiceProxyInterface on every request.
+  std::unique_ptr<ServiceProxyInterface> service =
+      shill_proxy_->GetServiceForPath(default_service_path_);
+
+  // Get the connection properties synchronously.
+  brillo::VariantDictionary properties;
+  brillo::ErrorPtr error;
+  if (!service->GetProperties(&properties, &error)) {
+    var_conn_type_.UnsetValue();
+    var_conn_tethering_.UnsetValue();
+    return false;
+  }
+
+  // Get the connection tethering mode.
+  const auto& prop_tethering = properties.find(shill::kTetheringProperty);
+  if (prop_tethering == properties.end()) {
+    // Remove the value if not present on the service. This most likely means an
+    // error in shill and the policy will handle it, but we will print a log
+    // message as well for accessing an unused variable.
+    var_conn_tethering_.UnsetValue();
+    LOG(ERROR) << "Could not find connection type (service: "
+               << default_service_path_.value() << ")";
+  } else {
+    // If the property doesn't contain a string value, the empty string will
+    // become kUnknown.
+    var_conn_tethering_.SetValue(
+        ParseConnectionTethering(prop_tethering->second.TryGet<string>()));
+  }
+
+  // Get the connection type.
+  const auto& prop_type = properties.find(shill::kTypeProperty);
+  if (prop_type == properties.end()) {
+    var_conn_type_.UnsetValue();
+    LOG(ERROR) << "Could not find connection tethering mode (service: "
+               << default_service_path_.value() << ")";
+  } else {
+    string type_str = prop_type->second.TryGet<string>();
+    if (type_str == shill::kTypeVPN) {
+      const auto& prop_physical =
+          properties.find(shill::kPhysicalTechnologyProperty);
+      if (prop_physical == properties.end()) {
+        LOG(ERROR) << "No PhysicalTechnology property found for a VPN"
+                   << " connection (service: " << default_service_path_.value()
+                   << "). Using default kUnknown value.";
+        var_conn_type_.SetValue(ConnectionType::kUnknown);
+      } else {
+        var_conn_type_.SetValue(
+            ParseConnectionType(prop_physical->second.TryGet<string>()));
+      }
+    } else {
+      var_conn_type_.SetValue(ParseConnectionType(type_str));
+    }
+  }
+
+  return true;
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/real_shill_provider.h b/update_manager/real_shill_provider.h
new file mode 100644
index 0000000..dbd6fc5
--- /dev/null
+++ b/update_manager/real_shill_provider.h
@@ -0,0 +1,104 @@
+//
+// 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_UPDATE_MANAGER_REAL_SHILL_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_REAL_SHILL_PROVIDER_H_
+
+// TODO(garnold) Much of the functionality in this module was adapted from the
+// update engine's connection_manager.  We need to make sure to deprecate use of
+// connection manager when the time comes.
+
+#include <string>
+
+#include <base/time/time.h>
+#include <dbus/object_path.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"
+
+namespace chromeos_update_manager {
+
+// ShillProvider concrete implementation.
+class RealShillProvider : public ShillProvider {
+ public:
+  RealShillProvider(chromeos_update_engine::ShillProxyInterface* shill_proxy,
+                    chromeos_update_engine::ClockInterface* clock)
+      : shill_proxy_(shill_proxy), clock_(clock) {}
+
+  ~RealShillProvider() override = default;
+
+  // Initializes the provider and returns whether it succeeded.
+  bool Init();
+
+  Variable<bool>* var_is_connected() override {
+    return &var_is_connected_;
+  }
+
+  Variable<ConnectionType>* var_conn_type() override {
+    return &var_conn_type_;
+  }
+
+  Variable<ConnectionTethering>* var_conn_tethering() override {
+    return &var_conn_tethering_;
+  }
+
+  Variable<base::Time>* var_conn_last_changed() override {
+    return &var_conn_last_changed_;
+  }
+
+  // Helper methods for converting shill strings into symbolic values.
+  static ConnectionType ParseConnectionType(const std::string& type_str);
+  static ConnectionTethering ParseConnectionTethering(
+      const std::string& tethering_str);
+
+ private:
+  // A handler for ManagerProxy.PropertyChanged signal.
+  void OnManagerPropertyChanged(const std::string& name,
+                                const brillo::Any& value);
+
+  // Called when the signal in ManagerProxy.PropertyChanged is connected.
+  void OnSignalConnected(const std::string& interface_name,
+                         const std::string& signal_name,
+                         bool successful);
+
+  // Get the connection and populate the type and tethering status of the given
+  // default connection.
+  bool ProcessDefaultService(const dbus::ObjectPath& default_service_path);
+
+  // The current default service path, if connected. "/" means not connected.
+  dbus::ObjectPath default_service_path_{"uninitialized"};
+
+  // The mockable interface to access the shill DBus proxies, owned by the
+  // caller.
+  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<ConnectionType> var_conn_type_{"conn_type"};
+  AsyncCopyVariable<ConnectionTethering> var_conn_tethering_{"conn_tethering"};
+  AsyncCopyVariable<base::Time> var_conn_last_changed_{"conn_last_changed"};
+
+  DISALLOW_COPY_AND_ASSIGN(RealShillProvider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_REAL_SHILL_PROVIDER_H_
diff --git a/update_manager/real_shill_provider_unittest.cc b/update_manager/real_shill_provider_unittest.cc
new file mode 100644
index 0000000..2fa0628
--- /dev/null
+++ b/update_manager/real_shill_provider_unittest.cc
@@ -0,0 +1,530 @@
+//
+// 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/update_manager/real_shill_provider.h"
+
+#include <memory>
+#include <utility>
+
+#include <base/time/time.h>
+#include <brillo/make_unique_ptr.h>
+#include <brillo/message_loops/fake_message_loop.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <shill/dbus-constants.h>
+#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/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::FakeClock;
+using org::chromium::flimflam::ManagerProxyMock;
+using org::chromium::flimflam::ServiceProxyMock;
+using std::unique_ptr;
+using testing::Mock;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::_;
+
+namespace {
+
+// 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";
+
+}  // namespace
+
+namespace chromeos_update_manager {
+
+class UmRealShillProviderTest : public ::testing::Test {
+ protected:
+  // Initialize the RealShillProvider under test.
+  void SetUp() override {
+    fake_clock_.SetWallclockTime(InitTime());
+    loop_.SetAsCurrent();
+    provider_.reset(new RealShillProvider(&fake_shill_proxy_, &fake_clock_));
+
+    ManagerProxyMock* manager_proxy_mock = fake_shill_proxy_.GetManagerProxy();
+
+    // The PropertyChanged signal should be subscribed to.
+    MOCK_SIGNAL_HANDLER_EXPECT_SIGNAL_HANDLER(
+        manager_property_changed_, *manager_proxy_mock, PropertyChanged);
+  }
+
+  void TearDown() override {
+    provider_.reset();
+    // Check for leaked callbacks on the main loop.
+    EXPECT_FALSE(loop_.PendingTasks());
+  }
+
+  // These methods generate fixed timestamps for use in faking the current time.
+  Time InitTime() {
+    Time::Exploded now_exp;
+    now_exp.year = 2014;
+    now_exp.month = 3;
+    now_exp.day_of_week = 2;
+    now_exp.day_of_month = 18;
+    now_exp.hour = 8;
+    now_exp.minute = 5;
+    now_exp.second = 33;
+    now_exp.millisecond = 675;
+    return Time::FromLocalExploded(now_exp);
+  }
+
+  Time ConnChangedTime() {
+    return InitTime() + TimeDelta::FromSeconds(10);
+  }
+
+  // Sets the default_service object path in the response from the
+  // ManagerProxyMock instance.
+  void SetManagerReply(const char* default_service, bool reply_succeeds);
+
+  // Sets the |service_type|, |physical_technology| and |service_tethering|
+  // properties in the mocked service |service_path|. If any of the three
+  // const char* is a nullptr, the corresponding property will not be included
+  // in the response.
+  // Returns the mock object pointer, owned by the |fake_shill_proxy_|.
+  ServiceProxyMock* SetServiceReply(const std::string& service_path,
+                                    const char* service_type,
+                                    const char* physical_technology,
+                                    const char* service_tethering);
+
+  void InitWithDefaultService(const char* default_service) {
+    SetManagerReply(default_service, true);
+    // Check that provider initializes correctly.
+    EXPECT_TRUE(provider_->Init());
+    // RunOnce to notify the signal handler was connected properly.
+    EXPECT_TRUE(loop_.RunOnce(false));
+  }
+
+  // Sends a signal informing the provider about a default connection
+  // |service_path|. Sets the fake connection change time in
+  // |conn_change_time_p| if provided.
+  void SendDefaultServiceSignal(const std::string& service_path,
+                                Time* conn_change_time_p) {
+    const Time conn_change_time = ConnChangedTime();
+    fake_clock_.SetWallclockTime(conn_change_time);
+    ASSERT_TRUE(manager_property_changed_.IsHandlerRegistered());
+    manager_property_changed_.signal_callback().Run(
+        shill::kDefaultServiceProperty, dbus::ObjectPath(service_path));
+    fake_clock_.SetWallclockTime(conn_change_time + TimeDelta::FromSeconds(5));
+    if (conn_change_time_p)
+      *conn_change_time_p = conn_change_time;
+  }
+
+  // Sets up expectations for detection of a connection |service_path| with type
+  // |shill_type_str| and tethering mode |shill_tethering_str|. Ensures that the
+  // new connection status and change time are properly detected by the
+  // provider. Writes the fake connection change time to |conn_change_time_p|,
+  // if provided.
+  void SetupConnectionAndAttrs(const std::string& service_path,
+                               const char* shill_type,
+                               const char* shill_tethering,
+                               Time* conn_change_time_p) {
+    SetServiceReply(service_path, shill_type, nullptr, shill_tethering);
+    // Note: We don't setup this |service_path| as the default service path but
+    // we instead send a signal notifying the change since the code won't call
+    // GetProperties on the Manager object at this point.
+
+    // Send a signal about a new default service.
+    Time conn_change_time;
+    SendDefaultServiceSignal(service_path, &conn_change_time);
+
+    // Query the connection status, ensure last change time reported correctly.
+    UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_connected());
+    UmTestUtils::ExpectVariableHasValue(conn_change_time,
+                                        provider_->var_conn_last_changed());
+
+    // Write the connection change time to the output argument.
+    if (conn_change_time_p)
+      *conn_change_time_p = conn_change_time;
+  }
+
+  // Sets up a connection and tests that its type is being properly detected by
+  // the provider.
+  void SetupConnectionAndTestType(const char* service_path,
+                                  const char* shill_type,
+                                  ConnectionType expected_conn_type) {
+    // Set up and test the connection, record the change time.
+    Time conn_change_time;
+    SetupConnectionAndAttrs(service_path,
+                            shill_type,
+                            shill::kTetheringNotDetectedState,
+                            &conn_change_time);
+
+    // Query the connection type, ensure last change time did not change.
+    UmTestUtils::ExpectVariableHasValue(expected_conn_type,
+                                        provider_->var_conn_type());
+    UmTestUtils::ExpectVariableHasValue(conn_change_time,
+                                        provider_->var_conn_last_changed());
+  }
+
+  // Sets up a connection and tests that its tethering mode is being properly
+  // detected by the provider.
+  void SetupConnectionAndTestTethering(
+      const char* service_path,
+      const char* shill_tethering,
+      ConnectionTethering expected_conn_tethering) {
+    // Set up and test the connection, record the change time.
+    Time conn_change_time;
+    SetupConnectionAndAttrs(
+        service_path, shill::kTypeEthernet, shill_tethering, &conn_change_time);
+
+    // Query the connection tethering, ensure last change time did not change.
+    UmTestUtils::ExpectVariableHasValue(expected_conn_tethering,
+                                        provider_->var_conn_tethering());
+    UmTestUtils::ExpectVariableHasValue(conn_change_time,
+                                        provider_->var_conn_last_changed());
+  }
+
+  brillo::FakeMessageLoop loop_{nullptr};
+  FakeClock fake_clock_;
+  chromeos_update_engine::FakeShillProxy fake_shill_proxy_;
+
+  // The registered signal handler for the signal Manager.PropertyChanged.
+  chromeos_update_engine::dbus_test_utils::MockSignalHandler<
+      void(const std::string&, const brillo::Any&)> manager_property_changed_;
+
+  unique_ptr<RealShillProvider> provider_;
+};
+
+void UmRealShillProviderTest::SetManagerReply(const char* default_service,
+                                              bool reply_succeeds) {
+  ManagerProxyMock* manager_proxy_mock = fake_shill_proxy_.GetManagerProxy();
+  if (!reply_succeeds) {
+    EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _))
+        .WillOnce(Return(false));
+    return;
+  }
+
+  // Create a dictionary of properties and optionally include the default
+  // service.
+  brillo::VariantDictionary reply_dict;
+  reply_dict["SomeOtherProperty"] = 0xC0FFEE;
+
+  if (default_service) {
+    reply_dict[shill::kDefaultServiceProperty] =
+        dbus::ObjectPath(default_service);
+  }
+  EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _))
+      .WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true)));
+}
+
+ServiceProxyMock* UmRealShillProviderTest::SetServiceReply(
+    const std::string& service_path,
+    const char* service_type,
+    const char* physical_technology,
+    const char* service_tethering) {
+  brillo::VariantDictionary reply_dict;
+  reply_dict["SomeOtherProperty"] = 0xC0FFEE;
+
+  if (service_type)
+    reply_dict[shill::kTypeProperty] = std::string(service_type);
+
+  if (physical_technology) {
+    reply_dict[shill::kPhysicalTechnologyProperty] =
+        std::string(physical_technology);
+  }
+
+  if (service_tethering)
+    reply_dict[shill::kTetheringProperty] = std::string(service_tethering);
+
+  ServiceProxyMock* service_proxy_mock = new ServiceProxyMock();
+
+  // Plumb return value into mock object.
+  EXPECT_CALL(*service_proxy_mock, GetProperties(_, _, _))
+      .WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true)));
+
+  fake_shill_proxy_.SetServiceForPath(
+      dbus::ObjectPath(service_path),
+      brillo::make_unique_ptr(service_proxy_mock));
+  return service_proxy_mock;
+}
+
+
+// Query the connection status, type and time last changed, as they were set
+// during initialization (no signals).
+TEST_F(UmRealShillProviderTest, ReadBaseValues) {
+  InitWithDefaultService("/");
+  // Query the provider variables.
+  UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_connected());
+  UmTestUtils::ExpectVariableNotSet(provider_->var_conn_type());
+  UmTestUtils::ExpectVariableHasValue(InitTime(),
+                                      provider_->var_conn_last_changed());
+}
+
+// Ensure that invalid DBus paths are ignored.
+TEST_F(UmRealShillProviderTest, InvalidServicePath) {
+  InitWithDefaultService("invalid");
+  UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_connected());
+  UmTestUtils::ExpectVariableNotSet(provider_->var_conn_type());
+  UmTestUtils::ExpectVariableHasValue(InitTime(),
+                                      provider_->var_conn_last_changed());
+}
+
+// Ensure that a service path property including a different type is ignored.
+TEST_F(UmRealShillProviderTest, InvalidServicePathType) {
+  ManagerProxyMock* manager_proxy_mock = fake_shill_proxy_.GetManagerProxy();
+  brillo::VariantDictionary reply_dict;
+  reply_dict[shill::kDefaultServiceProperty] = "/not/an/object/path";
+  EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _))
+      .WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true)));
+
+  EXPECT_TRUE(provider_->Init());
+  EXPECT_TRUE(loop_.RunOnce(false));
+
+  UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_connected());
+}
+
+// Test that Ethernet connection is identified correctly.
+TEST_F(UmRealShillProviderTest, ReadConnTypeEthernet) {
+  InitWithDefaultService("/");
+  SetupConnectionAndTestType(kFakeEthernetServicePath,
+                             shill::kTypeEthernet,
+                             ConnectionType::kEthernet);
+}
+
+// Test that Wifi connection is identified correctly.
+TEST_F(UmRealShillProviderTest, ReadConnTypeWifi) {
+  InitWithDefaultService("/");
+  SetupConnectionAndTestType(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("/");
+  SetupConnectionAndTestType(kFakeCellularServicePath,
+                             shill::kTypeCellular,
+                             ConnectionType::kCellular);
+}
+
+// Test that an unknown connection is identified as such.
+TEST_F(UmRealShillProviderTest, ReadConnTypeUnknown) {
+  InitWithDefaultService("/");
+  SetupConnectionAndTestType(kFakeUnknownServicePath,
+                             "FooConnectionType",
+                             ConnectionType::kUnknown);
+}
+
+// Tests that VPN connection is identified correctly.
+TEST_F(UmRealShillProviderTest, ReadConnTypeVpn) {
+  InitWithDefaultService("/");
+  // Mock logic for returning a default service path and its type.
+  SetServiceReply(kFakeVpnServicePath,
+                  shill::kTypeVPN,
+                  shill::kTypeWifi,
+                  shill::kTetheringNotDetectedState);
+
+  // Send a signal about a new default service.
+  Time conn_change_time;
+  SendDefaultServiceSignal(kFakeVpnServicePath, &conn_change_time);
+
+  // Query the connection type, ensure last change time reported correctly.
+  UmTestUtils::ExpectVariableHasValue(ConnectionType::kWifi,
+                                      provider_->var_conn_type());
+  UmTestUtils::ExpectVariableHasValue(conn_change_time,
+                                      provider_->var_conn_last_changed());
+}
+
+// Ensure that the connection type is properly cached in the provider through
+// subsequent variable readings.
+TEST_F(UmRealShillProviderTest, ConnTypeCacheUsed) {
+  InitWithDefaultService("/");
+  SetupConnectionAndTestType(kFakeEthernetServicePath,
+                             shill::kTypeEthernet,
+                             ConnectionType::kEthernet);
+
+  UmTestUtils::ExpectVariableHasValue(ConnectionType::kEthernet,
+                                      provider_->var_conn_type());
+}
+
+// Ensure that the cached connection type remains valid even when a default
+// connection signal occurs but the connection is not changed.
+TEST_F(UmRealShillProviderTest, ConnTypeCacheRemainsValid) {
+  InitWithDefaultService("/");
+  SetupConnectionAndTestType(kFakeEthernetServicePath,
+                             shill::kTypeEthernet,
+                             ConnectionType::kEthernet);
+
+  SendDefaultServiceSignal(kFakeEthernetServicePath, nullptr);
+
+  UmTestUtils::ExpectVariableHasValue(ConnectionType::kEthernet,
+                                      provider_->var_conn_type());
+}
+
+// Ensure that the cached connection type is invalidated and re-read when the
+// default connection changes.
+TEST_F(UmRealShillProviderTest, ConnTypeCacheInvalidated) {
+  InitWithDefaultService("/");
+  SetupConnectionAndTestType(kFakeEthernetServicePath,
+                             shill::kTypeEthernet,
+                             ConnectionType::kEthernet);
+
+  SetupConnectionAndTestType(kFakeWifiServicePath,
+                             shill::kTypeWifi,
+                             ConnectionType::kWifi);
+}
+
+// Test that a non-tethering mode is identified correctly.
+TEST_F(UmRealShillProviderTest, ReadConnTetheringNotDetected) {
+  InitWithDefaultService("/");
+  SetupConnectionAndTestTethering(kFakeWifiServicePath,
+                                  shill::kTetheringNotDetectedState,
+                                  ConnectionTethering::kNotDetected);
+}
+
+// Test that a suspected tethering mode is identified correctly.
+TEST_F(UmRealShillProviderTest, ReadConnTetheringSuspected) {
+  InitWithDefaultService("/");
+  SetupConnectionAndTestTethering(kFakeWifiServicePath,
+                                  shill::kTetheringSuspectedState,
+                                  ConnectionTethering::kSuspected);
+}
+
+// Test that a confirmed tethering mode is identified correctly.
+TEST_F(UmRealShillProviderTest, ReadConnTetheringConfirmed) {
+  InitWithDefaultService("/");
+  SetupConnectionAndTestTethering(kFakeWifiServicePath,
+                                  shill::kTetheringConfirmedState,
+                                  ConnectionTethering::kConfirmed);
+}
+
+// Test that an unknown tethering mode is identified as such.
+TEST_F(UmRealShillProviderTest, ReadConnTetheringUnknown) {
+  InitWithDefaultService("/");
+  SetupConnectionAndTestTethering(kFakeWifiServicePath,
+                                  "FooConnTethering",
+                                  ConnectionTethering::kUnknown);
+}
+
+// Ensure that the connection tethering mode is properly cached in the provider.
+TEST_F(UmRealShillProviderTest, ConnTetheringCacheUsed) {
+  InitWithDefaultService("/");
+  SetupConnectionAndTestTethering(kFakeEthernetServicePath,
+                                  shill::kTetheringNotDetectedState,
+                                  ConnectionTethering::kNotDetected);
+
+  UmTestUtils::ExpectVariableHasValue(ConnectionTethering::kNotDetected,
+                                      provider_->var_conn_tethering());
+}
+
+// Ensure that the cached connection tethering mode remains valid even when a
+// default connection signal occurs but the connection is not changed.
+TEST_F(UmRealShillProviderTest, ConnTetheringCacheRemainsValid) {
+  InitWithDefaultService("/");
+  SetupConnectionAndTestTethering(kFakeEthernetServicePath,
+                                  shill::kTetheringNotDetectedState,
+                                  ConnectionTethering::kNotDetected);
+
+  SendDefaultServiceSignal(kFakeEthernetServicePath, nullptr);
+
+  UmTestUtils::ExpectVariableHasValue(ConnectionTethering::kNotDetected,
+                                      provider_->var_conn_tethering());
+}
+
+// Ensure that the cached connection tethering mode is invalidated and re-read
+// when the default connection changes.
+TEST_F(UmRealShillProviderTest, ConnTetheringCacheInvalidated) {
+  InitWithDefaultService("/");
+  SetupConnectionAndTestTethering(kFakeEthernetServicePath,
+                                  shill::kTetheringNotDetectedState,
+                                  ConnectionTethering::kNotDetected);
+
+  SetupConnectionAndTestTethering(kFakeWifiServicePath,
+                                  shill::kTetheringConfirmedState,
+                                  ConnectionTethering::kConfirmed);
+}
+
+// Fake two DBus signals prompting a default connection change, but otherwise
+// give the same service path. Check connection status and the time it was last
+// changed, making sure that it is the time when the first signal was sent (and
+// not the second).
+TEST_F(UmRealShillProviderTest, ReadLastChangedTimeTwoSignals) {
+  InitWithDefaultService("/");
+  // Send a default service signal twice, advancing the clock in between.
+  Time conn_change_time;
+  SetupConnectionAndAttrs(kFakeEthernetServicePath,
+                          shill::kTypeEthernet,
+                          shill::kTetheringNotDetectedState,
+                          &conn_change_time);
+  // This will set the service path to the same value, so it should not call
+  // GetProperties() again.
+  SendDefaultServiceSignal(kFakeEthernetServicePath, nullptr);
+
+  // Query the connection status, ensure last change time reported as the first
+  // time the signal was sent.
+  UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_connected());
+  UmTestUtils::ExpectVariableHasValue(conn_change_time,
+                                      provider_->var_conn_last_changed());
+}
+
+// Make sure that the provider initializes correctly even if shill is not
+// responding, that variables can be obtained, and that they all return a null
+// value (indicating that the underlying values were not set).
+TEST_F(UmRealShillProviderTest, NoInitConnStatusReadBaseValues) {
+  // Initialize the provider, no initial connection status response.
+  SetManagerReply(nullptr, false);
+  EXPECT_TRUE(provider_->Init());
+  EXPECT_TRUE(loop_.RunOnce(false));
+  UmTestUtils::ExpectVariableNotSet(provider_->var_is_connected());
+  UmTestUtils::ExpectVariableNotSet(provider_->var_conn_type());
+  UmTestUtils::ExpectVariableNotSet(provider_->var_conn_last_changed());
+}
+
+// Test that, once a signal is received, the connection status and other info
+// can be read correctly.
+TEST_F(UmRealShillProviderTest, NoInitConnStatusReadConnTypeEthernet) {
+  // Initialize the provider with no initial connection status response.
+  SetManagerReply(nullptr, false);
+  EXPECT_TRUE(provider_->Init());
+  EXPECT_TRUE(loop_.RunOnce(false));
+
+  SetupConnectionAndAttrs(kFakeEthernetServicePath,
+                          shill::kTypeEthernet,
+                          shill::kTetheringNotDetectedState,
+                          nullptr);
+  UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_connected());
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/real_state.h b/update_manager/real_state.h
new file mode 100644
index 0000000..e83c49d
--- /dev/null
+++ b/update_manager/real_state.h
@@ -0,0 +1,82 @@
+//
+// 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_UPDATE_MANAGER_REAL_STATE_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_REAL_STATE_H_
+
+#include <memory>
+
+#include "update_engine/update_manager/state.h"
+
+namespace chromeos_update_manager {
+
+// State concrete implementation.
+class RealState : public State {
+ public:
+  ~RealState() override {}
+
+  RealState(ConfigProvider* config_provider,
+            DevicePolicyProvider* device_policy_provider,
+            RandomProvider* random_provider,
+            ShillProvider* shill_provider,
+            SystemProvider* system_provider,
+            TimeProvider* time_provider,
+            UpdaterProvider* updater_provider) :
+      config_provider_(config_provider),
+      device_policy_provider_(device_policy_provider),
+      random_provider_(random_provider),
+      shill_provider_(shill_provider),
+      system_provider_(system_provider),
+      time_provider_(time_provider),
+      updater_provider_(updater_provider) {}
+
+  // These methods return the given provider.
+  ConfigProvider* config_provider() override {
+    return config_provider_.get();
+  }
+  DevicePolicyProvider* device_policy_provider() override {
+    return device_policy_provider_.get();
+  }
+  RandomProvider* random_provider() override {
+    return random_provider_.get();
+  }
+  ShillProvider* shill_provider() override {
+    return shill_provider_.get();
+  }
+  SystemProvider* system_provider() override {
+    return system_provider_.get();
+  }
+  TimeProvider* time_provider() override {
+    return time_provider_.get();
+  }
+  UpdaterProvider* updater_provider() override {
+    return updater_provider_.get();
+  }
+
+ private:
+  // Instances of the providers.
+  std::unique_ptr<ConfigProvider> config_provider_;
+  std::unique_ptr<DevicePolicyProvider> device_policy_provider_;
+  std::unique_ptr<RandomProvider> random_provider_;
+  std::unique_ptr<ShillProvider> shill_provider_;
+  std::unique_ptr<SystemProvider> system_provider_;
+  std::unique_ptr<TimeProvider> time_provider_;
+  std::unique_ptr<UpdaterProvider> updater_provider_;
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_REAL_STATE_H_
diff --git a/update_manager/real_system_provider.cc b/update_manager/real_system_provider.cc
new file mode 100644
index 0000000..040f37c
--- /dev/null
+++ b/update_manager/real_system_provider.cc
@@ -0,0 +1,60 @@
+//
+// 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/update_manager/real_system_provider.h"
+
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/strings/stringprintf.h>
+#include <base/time/time.h>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/update_manager/generic_variables.h"
+
+using std::string;
+
+namespace chromeos_update_manager {
+
+bool RealSystemProvider::Init() {
+  var_is_normal_boot_mode_.reset(
+      new ConstCopyVariable<bool>("is_normal_boot_mode",
+                                  hardware_->IsNormalBootMode()));
+
+  var_is_official_build_.reset(
+      new ConstCopyVariable<bool>("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(hardware_), nullptr)));
+
+  var_num_slots_.reset(
+      new ConstCopyVariable<unsigned int>(
+          "num_slots", boot_control_->GetNumSlots()));
+
+  return true;
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/real_system_provider.h b/update_manager/real_system_provider.h
new file mode 100644
index 0000000..0329d74
--- /dev/null
+++ b/update_manager/real_system_provider.h
@@ -0,0 +1,70 @@
+//
+// 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_UPDATE_MANAGER_REAL_SYSTEM_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_REAL_SYSTEM_PROVIDER_H_
+
+#include <memory>
+#include <string>
+
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/hardware_interface.h"
+#include "update_engine/update_manager/system_provider.h"
+
+namespace chromeos_update_manager {
+
+// SystemProvider concrete implementation.
+class RealSystemProvider : public SystemProvider {
+ public:
+  explicit RealSystemProvider(
+      chromeos_update_engine::HardwareInterface* hardware,
+      chromeos_update_engine::BootControlInterface* boot_control)
+      : hardware_(hardware), boot_control_(boot_control) {}
+
+  // Initializes the provider and returns whether it succeeded.
+  bool Init();
+
+  Variable<bool>* var_is_normal_boot_mode() override {
+    return var_is_normal_boot_mode_.get();
+  }
+
+  Variable<bool>* var_is_official_build() override {
+    return var_is_official_build_.get();
+  }
+
+  Variable<bool>* var_is_oobe_complete() override {
+    return var_is_oobe_complete_.get();
+  }
+
+  Variable<unsigned int>* var_num_slots() override {
+    return var_num_slots_.get();
+  }
+
+ private:
+  std::unique_ptr<Variable<bool>> var_is_normal_boot_mode_;
+  std::unique_ptr<Variable<bool>> var_is_official_build_;
+  std::unique_ptr<Variable<bool>> var_is_oobe_complete_;
+  std::unique_ptr<Variable<unsigned int>> var_num_slots_;
+
+  chromeos_update_engine::HardwareInterface* hardware_;
+  chromeos_update_engine::BootControlInterface* boot_control_;
+
+  DISALLOW_COPY_AND_ASSIGN(RealSystemProvider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_REAL_SYSTEM_PROVIDER_H_
diff --git a/update_manager/real_system_provider_unittest.cc b/update_manager/real_system_provider_unittest.cc
new file mode 100644
index 0000000..5ee4137
--- /dev/null
+++ b/update_manager/real_system_provider_unittest.cc
@@ -0,0 +1,61 @@
+//
+// 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/update_manager/real_system_provider.h"
+
+#include <memory>
+
+#include <base/time/time.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/fake_boot_control.h"
+#include "update_engine/common/fake_hardware.h"
+#include "update_engine/update_manager/umtest_utils.h"
+
+using std::unique_ptr;
+
+namespace chromeos_update_manager {
+
+class UmRealSystemProviderTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    provider_.reset(
+        new RealSystemProvider(&fake_hardware_, &fake_boot_control_));
+    EXPECT_TRUE(provider_->Init());
+  }
+
+  chromeos_update_engine::FakeHardware fake_hardware_;
+  chromeos_update_engine::FakeBootControl fake_boot_control_;
+  unique_ptr<RealSystemProvider> provider_;
+};
+
+TEST_F(UmRealSystemProviderTest, InitTest) {
+  EXPECT_NE(nullptr, provider_->var_is_normal_boot_mode());
+  EXPECT_NE(nullptr, provider_->var_is_official_build());
+  EXPECT_NE(nullptr, provider_->var_is_oobe_complete());
+}
+
+TEST_F(UmRealSystemProviderTest, IsOOBECompleteTrue) {
+  fake_hardware_.SetIsOOBEComplete(base::Time());
+  UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_oobe_complete());
+}
+
+TEST_F(UmRealSystemProviderTest, IsOOBECompleteFalse) {
+  fake_hardware_.UnsetIsOOBEComplete();
+  UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_oobe_complete());
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/real_time_provider.cc b/update_manager/real_time_provider.cc
new file mode 100644
index 0000000..ca3acad
--- /dev/null
+++ b/update_manager/real_time_provider.cc
@@ -0,0 +1,83 @@
+//
+// 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/update_manager/real_time_provider.h"
+
+#include <string>
+
+#include <base/time/time.h>
+
+#include "update_engine/common/clock_interface.h"
+
+using base::Time;
+using base::TimeDelta;
+using chromeos_update_engine::ClockInterface;
+using std::string;
+
+namespace chromeos_update_manager {
+
+// A variable returning the current date.
+class CurrDateVariable : public Variable<Time> {
+ public:
+  // TODO(garnold) Turn this into an async variable with the needed callback
+  // logic for when it value changes.
+  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;
+    clock_->GetWallclockTime().LocalExplode(&now_exp);
+    now_exp.hour = now_exp.minute = now_exp.second = now_exp.millisecond = 0;
+    return new Time(Time::FromLocalExploded(now_exp));
+  }
+
+ private:
+  ClockInterface* clock_;
+
+  DISALLOW_COPY_AND_ASSIGN(CurrDateVariable);
+};
+
+// A variable returning the current hour in local time.
+class CurrHourVariable : public Variable<int> {
+ public:
+  // TODO(garnold) Turn this into an async variable with the needed callback
+  // logic for when it value changes.
+  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;
+    clock_->GetWallclockTime().LocalExplode(&exploded);
+    return new int(exploded.hour);
+  }
+
+ private:
+  ClockInterface* clock_;
+
+  DISALLOW_COPY_AND_ASSIGN(CurrHourVariable);
+};
+
+bool RealTimeProvider::Init() {
+  var_curr_date_.reset(new CurrDateVariable("curr_date", clock_));
+  var_curr_hour_.reset(new CurrHourVariable("curr_hour", clock_));
+  return true;
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/real_time_provider.h b/update_manager/real_time_provider.h
new file mode 100644
index 0000000..e7cae94
--- /dev/null
+++ b/update_manager/real_time_provider.h
@@ -0,0 +1,58 @@
+//
+// 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_UPDATE_MANAGER_REAL_TIME_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_REAL_TIME_PROVIDER_H_
+
+#include <memory>
+
+#include <base/time/time.h>
+
+#include "update_engine/common/clock_interface.h"
+#include "update_engine/update_manager/time_provider.h"
+
+namespace chromeos_update_manager {
+
+// TimeProvider concrete implementation.
+class RealTimeProvider : public TimeProvider {
+ public:
+  explicit RealTimeProvider(chromeos_update_engine::ClockInterface* clock)
+      : clock_(clock) {}
+
+  // Initializes the provider and returns whether it succeeded.
+  bool Init();
+
+  Variable<base::Time>* var_curr_date() override {
+    return var_curr_date_.get();
+  }
+
+  Variable<int>* var_curr_hour() override {
+    return var_curr_hour_.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_;
+
+  DISALLOW_COPY_AND_ASSIGN(RealTimeProvider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_REAL_TIME_PROVIDER_H_
diff --git a/update_manager/real_time_provider_unittest.cc b/update_manager/real_time_provider_unittest.cc
new file mode 100644
index 0000000..0e1ef34
--- /dev/null
+++ b/update_manager/real_time_provider_unittest.cc
@@ -0,0 +1,84 @@
+//
+// 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/update_manager/real_time_provider.h"
+
+#include <memory>
+
+#include <base/logging.h>
+#include <base/time/time.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/fake_clock.h"
+#include "update_engine/update_manager/umtest_utils.h"
+
+using base::Time;
+using chromeos_update_engine::FakeClock;
+using std::unique_ptr;
+
+namespace chromeos_update_manager {
+
+class UmRealTimeProviderTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    // The provider initializes correctly.
+    provider_.reset(new RealTimeProvider(&fake_clock_));
+    ASSERT_NE(nullptr, provider_.get());
+    ASSERT_TRUE(provider_->Init());
+  }
+
+  // Generates a fixed timestamp for use in faking the current time.
+  Time CurrTime() {
+    Time::Exploded now_exp;
+    now_exp.year = 2014;
+    now_exp.month = 3;
+    now_exp.day_of_week = 2;
+    now_exp.day_of_month = 18;
+    now_exp.hour = 8;
+    now_exp.minute = 5;
+    now_exp.second = 33;
+    now_exp.millisecond = 675;
+    return Time::FromLocalExploded(now_exp);
+  }
+
+  FakeClock fake_clock_;
+  unique_ptr<RealTimeProvider> provider_;
+};
+
+TEST_F(UmRealTimeProviderTest, CurrDateValid) {
+  const Time now = CurrTime();
+  Time::Exploded exploded;
+  now.LocalExplode(&exploded);
+  exploded.hour = 0;
+  exploded.minute = 0;
+  exploded.second = 0;
+  exploded.millisecond = 0;
+  const Time expected = Time::FromLocalExploded(exploded);
+
+  fake_clock_.SetWallclockTime(now);
+  UmTestUtils::ExpectVariableHasValue(expected, provider_->var_curr_date());
+}
+
+TEST_F(UmRealTimeProviderTest, CurrHourValid) {
+  const Time now = CurrTime();
+  Time::Exploded expected;
+  now.LocalExplode(&expected);
+  fake_clock_.SetWallclockTime(now);
+  UmTestUtils::ExpectVariableHasValue(expected.hour,
+                                      provider_->var_curr_hour());
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/real_updater_provider.cc b/update_manager/real_updater_provider.cc
new file mode 100644
index 0000000..1a3e65a
--- /dev/null
+++ b/update_manager/real_updater_provider.cc
@@ -0,0 +1,453 @@
+//
+// 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/update_manager/real_updater_provider.h"
+
+#include <inttypes.h>
+
+#include <string>
+
+#include <base/bind.h>
+#include <base/strings/stringprintf.h>
+#include <base/time/time.h>
+#include <update_engine/dbus-constants.h>
+
+#include "update_engine/common/clock_interface.h"
+#include "update_engine/common/prefs.h"
+#include "update_engine/omaha_request_params.h"
+#include "update_engine/update_attempter.h"
+
+using base::StringPrintf;
+using base::Time;
+using base::TimeDelta;
+using chromeos_update_engine::OmahaRequestParams;
+using chromeos_update_engine::SystemState;
+using std::string;
+
+namespace chromeos_update_manager {
+
+// A templated base class for all update related variables. Provides uniform
+// construction and a system state handle.
+template<typename T>
+class UpdaterVariableBase : public Variable<T> {
+ public:
+  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:
+  GetStatusHelper(SystemState* system_state, string* errmsg) {
+    is_success_ = system_state->update_attempter()->GetStatus(
+        &last_checked_time_, &progress_, &update_status_, &new_version_,
+        &payload_size_);
+    if (!is_success_ && errmsg)
+      *errmsg = "Failed to get a status update from the update engine";
+  }
+
+  inline bool is_success() { return is_success_; }
+  inline int64_t last_checked_time() { return last_checked_time_; }
+  inline double progress() { return progress_; }
+  inline const string& update_status() { return update_status_; }
+  inline const string& new_version() { return new_version_; }
+  inline int64_t payload_size() { return payload_size_; }
+
+ private:
+  bool is_success_;
+  int64_t last_checked_time_;
+  double progress_;
+  string update_status_;
+  string new_version_;
+  int64_t payload_size_;
+};
+
+// A variable reporting the time when a last update check was issued.
+class LastCheckedTimeVariable : public UpdaterVariableBase<Time> {
+ public:
+  LastCheckedTimeVariable(const string& name, SystemState* system_state)
+      : UpdaterVariableBase<Time>(name, kVariableModePoll, system_state) {}
+
+ private:
+  const Time* GetValue(TimeDelta /* timeout */, string* errmsg) override {
+    GetStatusHelper raw(system_state(), errmsg);
+    if (!raw.is_success())
+      return nullptr;
+
+    return new Time(Time::FromTimeT(raw.last_checked_time()));
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(LastCheckedTimeVariable);
+};
+
+// A variable reporting the update (download) progress as a decimal fraction
+// between 0.0 and 1.0.
+class ProgressVariable : public UpdaterVariableBase<double> {
+ public:
+  ProgressVariable(const string& name, SystemState* system_state)
+      : UpdaterVariableBase<double>(name, kVariableModePoll, system_state) {}
+
+ private:
+  const double* GetValue(TimeDelta /* timeout */, string* errmsg) override {
+    GetStatusHelper raw(system_state(), errmsg);
+    if (!raw.is_success())
+      return nullptr;
+
+    if (raw.progress() < 0.0 || raw.progress() > 1.0) {
+      if (errmsg) {
+        *errmsg = StringPrintf("Invalid progress value received: %f",
+                               raw.progress());
+      }
+      return nullptr;
+    }
+
+    return new double(raw.progress());
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(ProgressVariable);
+};
+
+// A variable reporting the stage in which the update process is.
+class StageVariable : public UpdaterVariableBase<Stage> {
+ public:
+  StageVariable(const string& name, SystemState* system_state)
+      : UpdaterVariableBase<Stage>(name, kVariableModePoll, system_state) {}
+
+ private:
+  struct CurrOpStrToStage {
+    const char* str;
+    Stage stage;
+  };
+  static const CurrOpStrToStage curr_op_str_to_stage[];
+
+  // Note: the method is defined outside the class so arraysize can work.
+  const Stage* GetValue(TimeDelta /* timeout */, string* errmsg) override;
+
+  DISALLOW_COPY_AND_ASSIGN(StageVariable);
+};
+
+const StageVariable::CurrOpStrToStage StageVariable::curr_op_str_to_stage[] = {
+  {update_engine::kUpdateStatusIdle, Stage::kIdle},
+  {update_engine::kUpdateStatusCheckingForUpdate, Stage::kCheckingForUpdate},
+  {update_engine::kUpdateStatusUpdateAvailable, Stage::kUpdateAvailable},
+  {update_engine::kUpdateStatusDownloading, Stage::kDownloading},
+  {update_engine::kUpdateStatusVerifying, Stage::kVerifying},
+  {update_engine::kUpdateStatusFinalizing, Stage::kFinalizing},
+  {update_engine::kUpdateStatusUpdatedNeedReboot, Stage::kUpdatedNeedReboot},
+  {  // NOLINT(whitespace/braces)
+    update_engine::kUpdateStatusReportingErrorEvent,
+    Stage::kReportingErrorEvent
+  },
+  {update_engine::kUpdateStatusAttemptingRollback, Stage::kAttemptingRollback},
+};
+
+const Stage* StageVariable::GetValue(TimeDelta /* timeout */,
+                                     string* errmsg) {
+  GetStatusHelper raw(system_state(), errmsg);
+  if (!raw.is_success())
+    return nullptr;
+
+  for (auto& key_val : curr_op_str_to_stage)
+    if (raw.update_status() == key_val.str)
+      return new Stage(key_val.stage);
+
+  if (errmsg)
+    *errmsg = string("Unknown update status: ") + raw.update_status();
+  return nullptr;
+}
+
+// A variable reporting the version number that an update is updating to.
+class NewVersionVariable : public UpdaterVariableBase<string> {
+ public:
+  NewVersionVariable(const string& name, SystemState* system_state)
+      : UpdaterVariableBase<string>(name, kVariableModePoll, system_state) {}
+
+ private:
+  const string* GetValue(TimeDelta /* timeout */, string* errmsg) override {
+    GetStatusHelper raw(system_state(), errmsg);
+    if (!raw.is_success())
+      return nullptr;
+
+    return new string(raw.new_version());
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(NewVersionVariable);
+};
+
+// A variable reporting the size of the update being processed in bytes.
+class PayloadSizeVariable : public UpdaterVariableBase<int64_t> {
+ public:
+  PayloadSizeVariable(const string& name, SystemState* system_state)
+      : UpdaterVariableBase<int64_t>(name, kVariableModePoll, system_state) {}
+
+ private:
+  const int64_t* GetValue(TimeDelta /* timeout */, string* errmsg) override {
+    GetStatusHelper raw(system_state(), errmsg);
+    if (!raw.is_success())
+      return nullptr;
+
+    if (raw.payload_size() < 0) {
+      if (errmsg)
+        *errmsg = string("Invalid payload size: %" PRId64, raw.payload_size());
+      return nullptr;
+    }
+
+    return new int64_t(raw.payload_size());
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(PayloadSizeVariable);
+};
+
+// A variable reporting the point in time an update last completed in the
+// current boot cycle.
+//
+// TODO(garnold) In general, both the current boottime and wallclock time
+// readings should come from the time provider and be moderated by the
+// evaluation context, so that they are uniform throughout the evaluation of a
+// policy request.
+class UpdateCompletedTimeVariable : public UpdaterVariableBase<Time> {
+ public:
+  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 (!system_state()->update_attempter()->GetBootTimeAtUpdate(
+            &update_boottime)) {
+      if (errmsg)
+        *errmsg = "Update completed time could not be read";
+      return nullptr;
+    }
+
+    chromeos_update_engine::ClockInterface* clock = system_state()->clock();
+    Time curr_boottime = clock->GetBootTime();
+    if (curr_boottime < update_boottime) {
+      if (errmsg)
+        *errmsg = "Update completed time more recent than current time";
+      return nullptr;
+    }
+    TimeDelta duration_since_update = curr_boottime - update_boottime;
+    return new Time(clock->GetWallclockTime() - duration_since_update);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(UpdateCompletedTimeVariable);
+};
+
+// Variables reporting the current image channel.
+class CurrChannelVariable : public UpdaterVariableBase<string> {
+ public:
+  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 = system_state()->request_params();
+    string channel = request_params->current_channel();
+    if (channel.empty()) {
+      if (errmsg)
+        *errmsg = "No current channel";
+      return nullptr;
+    }
+    return new string(channel);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(CurrChannelVariable);
+};
+
+// Variables reporting the new image channel.
+class NewChannelVariable : public UpdaterVariableBase<string> {
+ public:
+  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 = system_state()->request_params();
+    string channel = request_params->target_channel();
+    if (channel.empty()) {
+      if (errmsg)
+        *errmsg = "No new channel";
+      return nullptr;
+    }
+    return new string(channel);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(NewChannelVariable);
+};
+
+// A variable class for reading Boolean prefs values.
+class BooleanPrefVariable
+    : public AsyncCopyVariable<bool>,
+      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) {
+    prefs->AddObserver(key, this);
+    OnPrefSet(key);
+  }
+  ~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_;
+    if (prefs_ && prefs_->Exists(key_) && !prefs_->GetBoolean(key_, &result))
+      result = default_val_;
+    // AsyncCopyVariable will take care of values that didn't change.
+    SetValue(result);
+  }
+
+  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_;
+
+  DISALLOW_COPY_AND_ASSIGN(BooleanPrefVariable);
+};
+
+// A variable returning the number of consecutive failed update checks.
+class ConsecutiveFailedUpdateChecksVariable
+    : public UpdaterVariableBase<unsigned int> {
+ public:
+  ConsecutiveFailedUpdateChecksVariable(const string& name,
+                                        SystemState* system_state)
+      : UpdaterVariableBase<unsigned int>(name, kVariableModePoll,
+                                          system_state) {}
+
+ private:
+  const unsigned int* GetValue(TimeDelta /* timeout */,
+                               string* /* errmsg */) override {
+    return new unsigned int(
+        system_state()->update_attempter()->consecutive_failed_update_checks());
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(ConsecutiveFailedUpdateChecksVariable);
+};
+
+// A variable returning the server-dictated poll interval.
+class ServerDictatedPollIntervalVariable
+    : public UpdaterVariableBase<unsigned int> {
+ public:
+  ServerDictatedPollIntervalVariable(const string& name,
+                                     SystemState* system_state)
+      : UpdaterVariableBase<unsigned int>(name, kVariableModePoll,
+                                          system_state) {}
+
+ private:
+  const unsigned int* GetValue(TimeDelta /* timeout */,
+                               string* /* errmsg */) override {
+    return new unsigned int(
+        system_state()->update_attempter()->server_dictated_poll_interval());
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(ServerDictatedPollIntervalVariable);
+};
+
+// An async variable that tracks changes to forced update requests.
+class ForcedUpdateRequestedVariable
+    : public UpdaterVariableBase<UpdateRequestStatus> {
+ public:
+  ForcedUpdateRequestedVariable(const string& name, SystemState* system_state)
+      : UpdaterVariableBase<UpdateRequestStatus>::UpdaterVariableBase(
+          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))));
+  }
+
+ private:
+  const UpdateRequestStatus* GetValue(TimeDelta /* timeout */,
+                                      string* /* errmsg */) override {
+    return new UpdateRequestStatus(update_request_status_);
+  }
+
+  void Reset(bool forced_update_requested, bool is_interactive) {
+    UpdateRequestStatus new_value = UpdateRequestStatus::kNone;
+    if (forced_update_requested)
+      new_value = (is_interactive ? UpdateRequestStatus::kInteractive :
+                   UpdateRequestStatus::kPeriodic);
+    if (update_request_status_ != new_value) {
+      update_request_status_ = new_value;
+      NotifyValueChanged();
+    }
+  }
+
+  UpdateRequestStatus update_request_status_ = UpdateRequestStatus::kNone;
+
+  DISALLOW_COPY_AND_ASSIGN(ForcedUpdateRequestedVariable);
+};
+
+// RealUpdaterProvider methods.
+
+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", system_state_)),
+    var_server_dictated_poll_interval_(
+        new ServerDictatedPollIntervalVariable(
+            "server_dictated_poll_interval", system_state_)),
+    var_forced_update_requested_(
+        new ForcedUpdateRequestedVariable(
+            "forced_update_requested", system_state_)) {}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/real_updater_provider.h b/update_manager/real_updater_provider.h
new file mode 100644
index 0000000..b99bcc5
--- /dev/null
+++ b/update_manager/real_updater_provider.h
@@ -0,0 +1,124 @@
+//
+// 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_UPDATE_MANAGER_REAL_UPDATER_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_REAL_UPDATER_PROVIDER_H_
+
+#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"
+
+namespace chromeos_update_manager {
+
+// A concrete UpdaterProvider implementation using local (in-process) bindings.
+class RealUpdaterProvider : public UpdaterProvider {
+ public:
+  // We assume that any other object handle we get from the system state is
+  // "volatile", and so must be re-acquired whenever access is needed; this
+  // 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.
+  explicit RealUpdaterProvider(
+      chromeos_update_engine::SystemState* system_state);
+
+  // Initializes the provider and returns whether it succeeded.
+  bool Init() { return true; }
+
+  Variable<base::Time>* var_updater_started_time() override {
+    return &var_updater_started_time_;
+  }
+
+  Variable<base::Time>* var_last_checked_time() override {
+    return var_last_checked_time_.get();
+  }
+
+  Variable<base::Time>* var_update_completed_time() override {
+    return var_update_completed_time_.get();
+  }
+
+  Variable<double>* var_progress() override {
+    return var_progress_.get();
+  }
+
+  Variable<Stage>* var_stage() override {
+    return var_stage_.get();
+  }
+
+  Variable<std::string>* var_new_version() override {
+    return var_new_version_.get();
+  }
+
+  Variable<int64_t>* var_payload_size() override {
+    return var_payload_size_.get();
+  }
+
+  Variable<std::string>* var_curr_channel() override {
+    return var_curr_channel_.get();
+  }
+
+  Variable<std::string>* var_new_channel() override {
+    return var_new_channel_.get();
+  }
+
+  Variable<bool>* var_p2p_enabled() override {
+    return var_p2p_enabled_.get();
+  }
+
+  Variable<bool>* var_cellular_enabled() override {
+    return var_cellular_enabled_.get();
+  }
+
+  Variable<unsigned int>* var_consecutive_failed_update_checks() override {
+    return var_consecutive_failed_update_checks_.get();
+  }
+
+  Variable<unsigned int>* var_server_dictated_poll_interval() override {
+    return var_server_dictated_poll_interval_.get();
+  }
+
+  Variable<UpdateRequestStatus>* var_forced_update_requested() override {
+    return var_forced_update_requested_.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_;
+  std::unique_ptr<Variable<base::Time>> var_update_completed_time_;
+  std::unique_ptr<Variable<double>> var_progress_;
+  std::unique_ptr<Variable<Stage>> var_stage_;
+  std::unique_ptr<Variable<std::string>> var_new_version_;
+  std::unique_ptr<Variable<int64_t>> var_payload_size_;
+  std::unique_ptr<Variable<std::string>> var_curr_channel_;
+  std::unique_ptr<Variable<std::string>> var_new_channel_;
+  std::unique_ptr<Variable<bool>> var_p2p_enabled_;
+  std::unique_ptr<Variable<bool>> var_cellular_enabled_;
+  std::unique_ptr<Variable<unsigned int>> var_consecutive_failed_update_checks_;
+  std::unique_ptr<Variable<unsigned int>> var_server_dictated_poll_interval_;
+  std::unique_ptr<Variable<UpdateRequestStatus>> var_forced_update_requested_;
+
+  DISALLOW_COPY_AND_ASSIGN(RealUpdaterProvider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_REAL_UPDATER_PROVIDER_H_
diff --git a/update_manager/real_updater_provider_unittest.cc b/update_manager/real_updater_provider_unittest.cc
new file mode 100644
index 0000000..14eb30b
--- /dev/null
+++ b/update_manager/real_updater_provider_unittest.cc
@@ -0,0 +1,443 @@
+//
+// 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/update_manager/real_updater_provider.h"
+
+#include <memory>
+#include <string>
+
+#include <base/time/time.h>
+#include <gtest/gtest.h>
+#include <update_engine/dbus-constants.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;
+using std::string;
+using std::unique_ptr;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::_;
+
+namespace {
+
+// Generates a fixed timestamp for use in faking the current time.
+Time FixedTime() {
+  Time::Exploded now_exp;
+  now_exp.year = 2014;
+  now_exp.month = 3;
+  now_exp.day_of_week = 2;
+  now_exp.day_of_month = 18;
+  now_exp.hour = 8;
+  now_exp.minute = 5;
+  now_exp.second = 33;
+  now_exp.millisecond = 675;
+  return Time::FromLocalExploded(now_exp);
+}
+
+// Rounds down a timestamp to the nearest second. This is useful when faking
+// times that are converted to time_t (no sub-second resolution).
+Time RoundedToSecond(Time time) {
+  Time::Exploded exp;
+  time.LocalExplode(&exp);
+  exp.millisecond = 0;
+  return Time::FromLocalExploded(exp);
+}
+
+}  // namespace
+
+namespace chromeos_update_manager {
+
+class UmRealUpdaterProviderTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    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());
+  }
+
+  // Sets up mock expectations for testing the update completed time reporting.
+  // |valid| determines whether the returned time is valid. Returns the expected
+  // update completed time value.
+  Time SetupUpdateCompletedTime(bool valid) {
+    const TimeDelta kDurationSinceUpdate = TimeDelta::FromMinutes(7);
+    const Time kUpdateBootTime = Time() + kDurationSinceUpdate * 2;
+    const Time kCurrBootTime = (valid ?
+                                kUpdateBootTime + kDurationSinceUpdate :
+                                kUpdateBootTime - kDurationSinceUpdate);
+    const Time kCurrWallclockTime = FixedTime();
+    EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+                GetBootTimeAtUpdate(_))
+        .WillOnce(DoAll(SetArgPointee<0>(kUpdateBootTime), Return(true)));
+    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) {
+  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(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<0>(FixedTime().ToTimeT()), Return(true)));
+  UmTestUtils::ExpectVariableHasValue(RoundedToSecond(FixedTime()),
+                                      provider_->var_last_checked_time());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetLastCheckedTimeFailNoValue) {
+  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(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<1>(0.0), Return(true)));
+  UmTestUtils::ExpectVariableHasValue(0.0, provider_->var_progress());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetProgressOkayMid) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<1>(0.3), Return(true)));
+  UmTestUtils::ExpectVariableHasValue(0.3, provider_->var_progress());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetProgressOkayMax) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<1>(1.0), Return(true)));
+  UmTestUtils::ExpectVariableHasValue(1.0, provider_->var_progress());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetProgressFailNoValue) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(Return(false));
+  UmTestUtils::ExpectVariableNotSet(provider_->var_progress());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetProgressFailTooSmall) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<1>(-2.0), Return(true)));
+  UmTestUtils::ExpectVariableNotSet(provider_->var_progress());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetProgressFailTooBig) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<1>(2.0), Return(true)));
+  UmTestUtils::ExpectVariableNotSet(provider_->var_progress());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageOkayIdle) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<2>(update_engine::kUpdateStatusIdle),
+                      Return(true)));
+  UmTestUtils::ExpectVariableHasValue(Stage::kIdle, provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageOkayCheckingForUpdate) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(
+              SetArgPointee<2>(update_engine::kUpdateStatusCheckingForUpdate),
+              Return(true)));
+  UmTestUtils::ExpectVariableHasValue(Stage::kCheckingForUpdate,
+                                      provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageOkayUpdateAvailable) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(
+              SetArgPointee<2>(update_engine::kUpdateStatusUpdateAvailable),
+              Return(true)));
+  UmTestUtils::ExpectVariableHasValue(Stage::kUpdateAvailable,
+                                      provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageOkayDownloading) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<2>(update_engine::kUpdateStatusDownloading),
+                      Return(true)));
+  UmTestUtils::ExpectVariableHasValue(Stage::kDownloading,
+                                      provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageOkayVerifying) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<2>(update_engine::kUpdateStatusVerifying),
+                      Return(true)));
+  UmTestUtils::ExpectVariableHasValue(Stage::kVerifying,
+                                      provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageOkayFinalizing) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<2>(update_engine::kUpdateStatusFinalizing),
+                      Return(true)));
+  UmTestUtils::ExpectVariableHasValue(Stage::kFinalizing,
+                                      provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageOkayUpdatedNeedReboot) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(
+              SetArgPointee<2>(update_engine::kUpdateStatusUpdatedNeedReboot),
+              Return(true)));
+  UmTestUtils::ExpectVariableHasValue(Stage::kUpdatedNeedReboot,
+                                      provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageOkayReportingErrorEvent) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(
+              SetArgPointee<2>(update_engine::kUpdateStatusReportingErrorEvent),
+              Return(true)));
+  UmTestUtils::ExpectVariableHasValue(Stage::kReportingErrorEvent,
+                                      provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageOkayAttemptingRollback) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(
+              SetArgPointee<2>(update_engine::kUpdateStatusAttemptingRollback),
+              Return(true)));
+  UmTestUtils::ExpectVariableHasValue(Stage::kAttemptingRollback,
+                                      provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageFailNoValue) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(Return(false));
+  UmTestUtils::ExpectVariableNotSet(provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageFailUnknown) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<2>("FooUpdateEngineState"),
+                      Return(true)));
+  UmTestUtils::ExpectVariableNotSet(provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageFailEmpty) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<2>(""), Return(true)));
+  UmTestUtils::ExpectVariableNotSet(provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetNewVersionOkay) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<3>("1.2.0"), Return(true)));
+  UmTestUtils::ExpectVariableHasValue(string("1.2.0"),
+                                      provider_->var_new_version());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetNewVersionFailNoValue) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(Return(false));
+  UmTestUtils::ExpectVariableNotSet(provider_->var_new_version());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetPayloadSizeOkayZero) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<4>(static_cast<int64_t>(0)), Return(true)));
+  UmTestUtils::ExpectVariableHasValue(static_cast<int64_t>(0),
+                                      provider_->var_payload_size());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetPayloadSizeOkayArbitrary) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<4>(static_cast<int64_t>(567890)),
+                      Return(true)));
+  UmTestUtils::ExpectVariableHasValue(static_cast<int64_t>(567890),
+                                      provider_->var_payload_size());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetPayloadSizeOkayTwoGigabytes) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<4>(static_cast<int64_t>(1) << 31),
+                      Return(true)));
+  UmTestUtils::ExpectVariableHasValue(static_cast<int64_t>(1) << 31,
+                                      provider_->var_payload_size());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetPayloadSizeFailNoValue) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(Return(false));
+  UmTestUtils::ExpectVariableNotSet(provider_->var_payload_size());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetPayloadSizeFailNegative) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              GetStatus(_, _, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<4>(static_cast<int64_t>(-1024)),
+                      Return(true)));
+  UmTestUtils::ExpectVariableNotSet(provider_->var_payload_size());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetCurrChannelOkay) {
+  const string kChannelName("foo-channel");
+  OmahaRequestParams request_params(&fake_sys_state_);
+  request_params.Init("", "", false);
+  request_params.set_current_channel(kChannelName);
+  fake_sys_state_.set_request_params(&request_params);
+  UmTestUtils::ExpectVariableHasValue(kChannelName,
+                                      provider_->var_curr_channel());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetCurrChannelFailEmpty) {
+  OmahaRequestParams request_params(&fake_sys_state_);
+  request_params.Init("", "", false);
+  request_params.set_current_channel("");
+  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(&fake_sys_state_);
+  request_params.Init("", "", false);
+  request_params.set_target_channel(kChannelName);
+  fake_sys_state_.set_request_params(&request_params);
+  UmTestUtils::ExpectVariableHasValue(kChannelName,
+                                      provider_->var_new_channel());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetNewChannelFailEmpty) {
+  OmahaRequestParams request_params(&fake_sys_state_);
+  request_params.Init("", "", false);
+  request_params.set_target_channel("");
+  fake_sys_state_.set_request_params(&request_params);
+  UmTestUtils::ExpectVariableNotSet(provider_->var_new_channel());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetP2PEnabledOkayPrefDoesntExist) {
+  UmTestUtils::ExpectVariableHasValue(false, provider_->var_p2p_enabled());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetP2PEnabledOkayPrefReadsFalse) {
+  fake_prefs_.SetBoolean(chromeos_update_engine::kPrefsP2PEnabled, false);
+  UmTestUtils::ExpectVariableHasValue(false, provider_->var_p2p_enabled());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetP2PEnabledReadWhenInitialized) {
+  fake_prefs_.SetBoolean(chromeos_update_engine::kPrefsP2PEnabled, true);
+  SetUp();
+  UmTestUtils::ExpectVariableHasValue(true, provider_->var_p2p_enabled());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetP2PEnabledUpdated) {
+  fake_prefs_.SetBoolean(chromeos_update_engine::kPrefsP2PEnabled, false);
+  UmTestUtils::ExpectVariableHasValue(false, provider_->var_p2p_enabled());
+  fake_prefs_.SetBoolean(chromeos_update_engine::kPrefsP2PEnabled, true);
+  UmTestUtils::ExpectVariableHasValue(true, provider_->var_p2p_enabled());
+  fake_prefs_.Delete(chromeos_update_engine::kPrefsP2PEnabled);
+  UmTestUtils::ExpectVariableHasValue(false, provider_->var_p2p_enabled());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetCellularEnabledOkayPrefDoesntExist) {
+  UmTestUtils::ExpectVariableHasValue(false, provider_->var_cellular_enabled());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetCellularEnabledOkayPrefReadsTrue) {
+  fake_prefs_.SetBoolean(
+      chromeos_update_engine::kPrefsUpdateOverCellularPermission, true);
+  UmTestUtils::ExpectVariableHasValue(true, provider_->var_cellular_enabled());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetUpdateCompletedTimeOkay) {
+  Time expected = SetupUpdateCompletedTime(true);
+  UmTestUtils::ExpectVariableHasValue(expected,
+                                      provider_->var_update_completed_time());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetUpdateCompletedTimeFailNoValue) {
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetBootTimeAtUpdate(_))
+      .WillOnce(Return(false));
+  UmTestUtils::ExpectVariableNotSet(provider_->var_update_completed_time());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetUpdateCompletedTimeFailInvalidValue) {
+  SetupUpdateCompletedTime(false);
+  UmTestUtils::ExpectVariableNotSet(provider_->var_update_completed_time());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetConsecutiveFailedUpdateChecks) {
+  const unsigned int kNumFailedChecks = 3;
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              consecutive_failed_update_checks())
+      .WillRepeatedly(Return(kNumFailedChecks));
+  UmTestUtils::ExpectVariableHasValue(
+      kNumFailedChecks, provider_->var_consecutive_failed_update_checks());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetServerDictatedPollInterval) {
+  const unsigned int kPollInterval = 2 * 60 * 60;  // Two hours.
+  EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+              server_dictated_poll_interval())
+      .WillRepeatedly(Return(kPollInterval));
+  UmTestUtils::ExpectVariableHasValue(
+      kPollInterval, provider_->var_server_dictated_poll_interval());
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/shill_provider.h b/update_manager/shill_provider.h
new file mode 100644
index 0000000..b40f255
--- /dev/null
+++ b/update_manager/shill_provider.h
@@ -0,0 +1,72 @@
+//
+// 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_UPDATE_MANAGER_SHILL_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_SHILL_PROVIDER_H_
+
+#include <base/time/time.h>
+
+#include "update_engine/update_manager/provider.h"
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+enum class ConnectionType {
+  kEthernet,
+  kWifi,
+  kWimax,
+  kBluetooth,
+  kCellular,
+  kUnknown
+};
+
+enum class ConnectionTethering {
+  kNotDetected,
+  kSuspected,
+  kConfirmed,
+  kUnknown,
+};
+
+// Provider for networking related information.
+class ShillProvider : public Provider {
+ public:
+  ~ShillProvider() override {}
+
+  // A variable returning whether we currently have network connectivity.
+  virtual Variable<bool>* var_is_connected() = 0;
+
+  // A variable returning the current network connection type. Unknown if not
+  // connected.
+  virtual Variable<ConnectionType>* var_conn_type() = 0;
+
+  // A variable returning the tethering mode of a network connection. Unknown if
+  // not connected.
+  virtual Variable<ConnectionTethering>* var_conn_tethering() = 0;
+
+  // A variable returning the time when network connection last changed.
+  // Initialized to current time.
+  virtual Variable<base::Time>* var_conn_last_changed() = 0;
+
+ protected:
+  ShillProvider() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ShillProvider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_SHILL_PROVIDER_H_
diff --git a/update_manager/state.h b/update_manager/state.h
new file mode 100644
index 0000000..d428059
--- /dev/null
+++ b/update_manager/state.h
@@ -0,0 +1,54 @@
+//
+// 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_UPDATE_MANAGER_STATE_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_STATE_H_
+
+#include "update_engine/update_manager/config_provider.h"
+#include "update_engine/update_manager/device_policy_provider.h"
+#include "update_engine/update_manager/random_provider.h"
+#include "update_engine/update_manager/shill_provider.h"
+#include "update_engine/update_manager/system_provider.h"
+#include "update_engine/update_manager/time_provider.h"
+#include "update_engine/update_manager/updater_provider.h"
+
+namespace chromeos_update_manager {
+
+// The State class is an interface to the ensemble of providers. This class
+// gives visibility of the state providers to policy implementations.
+class State {
+ public:
+  virtual ~State() {}
+
+  // These methods return the given provider.
+  virtual ConfigProvider* config_provider() = 0;
+  virtual DevicePolicyProvider* device_policy_provider() = 0;
+  virtual RandomProvider* random_provider() = 0;
+  virtual ShillProvider* shill_provider() = 0;
+  virtual SystemProvider* system_provider() = 0;
+  virtual TimeProvider* time_provider() = 0;
+  virtual UpdaterProvider* updater_provider() = 0;
+
+ protected:
+  State() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(State);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_STATE_H_
diff --git a/update_manager/state_factory.cc b/update_manager/state_factory.cc
new file mode 100644
index 0000000..d4f4aa4
--- /dev/null
+++ b/update_manager/state_factory.cc
@@ -0,0 +1,77 @@
+//
+// 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/update_manager/state_factory.h"
+
+#include <memory>
+
+#include <base/logging.h>
+
+#include "update_engine/common/clock_interface.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"
+
+using std::unique_ptr;
+
+namespace chromeos_update_manager {
+
+State* DefaultStateFactory(
+    policy::PolicyProvider* policy_provider,
+    chromeos_update_engine::ShillProxy* shill_proxy,
+    org::chromium::SessionManagerInterfaceProxyInterface* session_manager_proxy,
+    chromeos_update_engine::SystemState* system_state) {
+  chromeos_update_engine::ClockInterface* const clock = system_state->clock();
+  unique_ptr<RealConfigProvider> config_provider(
+      new RealConfigProvider(system_state->hardware()));
+  unique_ptr<RealDevicePolicyProvider> device_policy_provider(
+      new RealDevicePolicyProvider(session_manager_proxy, policy_provider));
+  unique_ptr<RealRandomProvider> random_provider(new RealRandomProvider());
+  unique_ptr<RealShillProvider> shill_provider(
+      new RealShillProvider(shill_proxy, clock));
+  unique_ptr<RealSystemProvider> system_provider(
+      new RealSystemProvider(system_state->hardware(),
+                             system_state->boot_control()));
+  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() &&
+        shill_provider->Init() &&
+        system_provider->Init() &&
+        time_provider->Init() &&
+        updater_provider->Init())) {
+    LOG(ERROR) << "Error initializing providers";
+    return nullptr;
+  }
+
+  return new RealState(config_provider.release(),
+                       device_policy_provider.release(),
+                       random_provider.release(),
+                       shill_provider.release(),
+                       system_provider.release(),
+                       time_provider.release(),
+                       updater_provider.release());
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/state_factory.h b/update_manager/state_factory.h
new file mode 100644
index 0000000..f15fd83
--- /dev/null
+++ b/update_manager/state_factory.h
@@ -0,0 +1,41 @@
+//
+// 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_UPDATE_MANAGER_STATE_FACTORY_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_STATE_FACTORY_H_
+
+#include <session_manager/dbus-proxies.h>
+
+#include "update_engine/shill_proxy.h"
+#include "update_engine/system_state.h"
+#include "update_engine/update_manager/state.h"
+
+namespace chromeos_update_manager {
+
+// Creates and initializes a new UpdateManager State instance containing real
+// providers instantiated using the passed interfaces. The State doesn't take
+// ownership of the passed interfaces, which need to remain available during the
+// life of this instance.  Returns null if one of the underlying providers fails
+// to initialize.
+State* DefaultStateFactory(
+    policy::PolicyProvider* policy_provider,
+    chromeos_update_engine::ShillProxy* shill_proxy,
+    org::chromium::SessionManagerInterfaceProxyInterface* session_manager_proxy,
+    chromeos_update_engine::SystemState* system_state);
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_STATE_FACTORY_H_
diff --git a/update_manager/system_provider.h b/update_manager/system_provider.h
new file mode 100644
index 0000000..00fb9af
--- /dev/null
+++ b/update_manager/system_provider.h
@@ -0,0 +1,54 @@
+//
+// 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_UPDATE_MANAGER_SYSTEM_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_SYSTEM_PROVIDER_H_
+
+#include "update_engine/update_manager/provider.h"
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+// Provider for system information, mostly constant, such as the information
+// reported by crossystem, the kernel boot command line and the partition table.
+class SystemProvider : public Provider {
+ public:
+  ~SystemProvider() override {}
+
+  // Returns true if the boot mode is normal or if it's unable to
+  // determine the boot mode. Returns false if the boot mode is
+  // developer.
+  virtual Variable<bool>* var_is_normal_boot_mode() = 0;
+
+  // Returns whether this is an official Chrome OS build.
+  virtual Variable<bool>* var_is_official_build() = 0;
+
+  // Returns a variable that tells whether OOBE was completed.
+  virtual Variable<bool>* var_is_oobe_complete() = 0;
+
+  // Returns a variable that tells the number of slots in the system.
+  virtual Variable<unsigned int>* var_num_slots() = 0;
+
+ protected:
+  SystemProvider() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SystemProvider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_SYSTEM_PROVIDER_H_
diff --git a/update_manager/time_provider.h b/update_manager/time_provider.h
new file mode 100644
index 0000000..663ec2c
--- /dev/null
+++ b/update_manager/time_provider.h
@@ -0,0 +1,48 @@
+//
+// 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_UPDATE_MANAGER_TIME_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_TIME_PROVIDER_H_
+
+#include <base/time/time.h>
+
+#include "update_engine/update_manager/provider.h"
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+// Provider for time related information.
+class TimeProvider : public Provider {
+ public:
+  ~TimeProvider() override {}
+
+  // Returns the current date. The time of day component will be zero.
+  virtual Variable<base::Time>* var_curr_date() = 0;
+
+  // Returns the current hour (0 to 23) in local time. The type is int to keep
+  // consistent with base::Time.
+  virtual Variable<int>* var_curr_hour() = 0;
+
+ protected:
+  TimeProvider() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TimeProvider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_TIME_PROVIDER_H_
diff --git a/update_manager/umtest_utils.cc b/update_manager/umtest_utils.cc
new file mode 100644
index 0000000..aa88141
--- /dev/null
+++ b/update_manager/umtest_utils.cc
@@ -0,0 +1,29 @@
+//
+// 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/update_manager/umtest_utils.h"
+
+#include <base/time/time.h>
+
+namespace chromeos_update_manager {
+
+const unsigned UmTestUtils::kDefaultTimeoutInSeconds = 1;
+
+void PrintTo(const EvalStatus& status, ::std::ostream* os) {
+  *os << ToString(status);
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/umtest_utils.h b/update_manager/umtest_utils.h
new file mode 100644
index 0000000..80693db
--- /dev/null
+++ b/update_manager/umtest_utils.h
@@ -0,0 +1,68 @@
+//
+// 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_UPDATE_MANAGER_UMTEST_UTILS_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_UMTEST_UTILS_H_
+
+#include <iostream>  // NOLINT(readability/streams)
+#include <memory>
+
+#include <base/time/time.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/update_manager/policy.h"
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+// A help class with common functionality for use in Update Manager testing.
+class UmTestUtils {
+ public:
+  // A default timeout to use when making various queries.
+  static const base::TimeDelta DefaultTimeout() {
+    return base::TimeDelta::FromSeconds(kDefaultTimeoutInSeconds);
+  }
+
+  // Calls GetValue on |variable| and expects its result to be |expected|.
+  template<typename T>
+  static void ExpectVariableHasValue(const T& expected, Variable<T>* variable) {
+    ASSERT_NE(nullptr, variable);
+    std::unique_ptr<const T> value(
+        variable->GetValue(DefaultTimeout(), nullptr));
+    ASSERT_NE(nullptr, value.get()) << "Variable: " << variable->GetName();
+    EXPECT_EQ(expected, *value) << "Variable: " << variable->GetName();
+  }
+
+  // Calls GetValue on |variable| and expects its result to be null.
+  template<typename T>
+  static void ExpectVariableNotSet(Variable<T>* variable) {
+    ASSERT_NE(nullptr, variable);
+    std::unique_ptr<const T> value(
+        variable->GetValue(DefaultTimeout(), nullptr));
+    EXPECT_EQ(nullptr, value.get()) << "Variable: " << variable->GetName();
+  }
+
+ private:
+  static const unsigned kDefaultTimeoutInSeconds;
+};
+
+// PrintTo() functions are used by gtest to print these values. They need to be
+// defined on the same namespace where the type was defined.
+void PrintTo(const EvalStatus& status, ::std::ostream* os);
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_UMTEST_UTILS_H_
diff --git a/update_manager/update_manager-inl.h b/update_manager/update_manager-inl.h
new file mode 100644
index 0000000..77224cf
--- /dev/null
+++ b/update_manager/update_manager-inl.h
@@ -0,0 +1,165 @@
+//
+// 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_UPDATE_MANAGER_UPDATE_MANAGER_INL_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_MANAGER_INL_H_
+
+#include <memory>
+#include <string>
+
+#include <base/bind.h>
+#include <base/location.h>
+#include <brillo/message_loops/message_loop.h>
+
+#include "update_engine/update_manager/evaluation_context.h"
+
+namespace chromeos_update_manager {
+
+template<typename R, typename... Args>
+EvalStatus UpdateManager::EvaluatePolicy(
+    EvaluationContext* ec,
+    EvalStatus (Policy::*policy_method)(EvaluationContext*, State*,
+                                        std::string*, R*,
+                                        Args...) const,
+    R* result, Args... args) {
+  // If expiration timeout fired, dump the context and reset expiration.
+  // IMPORTANT: We must still proceed with evaluation of the policy in this
+  // case, so that the evaluation time (and corresponding reevaluation timeouts)
+  // are readjusted.
+  if (ec->is_expired()) {
+    LOG(WARNING) << "Request timed out, evaluation context: "
+                 << ec->DumpContext();
+    ec->ResetExpiration();
+  }
+
+  // Reset the evaluation context.
+  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;
+  EvalStatus status = (policy_.get()->*policy_method)(ec, state_.get(), &error,
+                                                      result, args...);
+  // If evaluating the main policy failed, defer to the default policy.
+  if (status == EvalStatus::kFailed) {
+    LOG(WARNING) << "Evaluating policy failed: " << error
+                 << "\nEvaluation context: " << ec->DumpContext();
+    error.clear();
+    status = (default_policy_.*policy_method)(ec, state_.get(), &error, result,
+                                              args...);
+    if (status == EvalStatus::kFailed) {
+      LOG(WARNING) << "Evaluating default policy failed: " << error;
+    } else if (status == EvalStatus::kAskMeAgainLater) {
+      LOG(ERROR)
+          << "Default policy would block; this is a bug, forcing failure.";
+      status = EvalStatus::kFailed;
+    }
+  }
+
+  LOG(INFO) << policy_name << ": END";
+
+  return status;
+}
+
+template<typename R, typename... Args>
+void UpdateManager::OnPolicyReadyToEvaluate(
+    scoped_refptr<EvaluationContext> ec,
+    base::Callback<void(EvalStatus status, const R& result)> callback,
+    EvalStatus (Policy::*policy_method)(EvaluationContext*, State*,
+                                        std::string*, R*,
+                                        Args...) const,
+    Args... args) {
+  // Evaluate the policy.
+  R result;
+  EvalStatus status = EvaluatePolicy(ec.get(), policy_method, &result, args...);
+
+  if (status != EvalStatus::kAskMeAgainLater) {
+    // AsyncPolicyRequest finished.
+    callback.Run(status, result);
+    return;
+  }
+
+  // Re-schedule the policy request based on used variables.
+  base::Closure reeval_callback = base::Bind(
+      &UpdateManager::OnPolicyReadyToEvaluate<R, Args...>,
+      base::Unretained(this), ec, callback,
+      policy_method, args...);
+  if (ec->RunOnValueChangeOrTimeout(reeval_callback))
+    return;  // Reevaluation scheduled successfully.
+
+  // Scheduling a reevaluation can fail because policy method didn't use any
+  // non-const variable nor there's any time-based event that will change the
+  // status of evaluation.  Alternatively, this may indicate an error in the use
+  // of the scheduling interface.
+  LOG(ERROR) << "Failed to schedule a reevaluation of policy "
+             << policy_->PolicyRequestName(policy_method) << "; this is a bug.";
+  callback.Run(status, result);
+}
+
+template<typename R, typename... ActualArgs, typename... ExpectedArgs>
+EvalStatus UpdateManager::PolicyRequest(
+    EvalStatus (Policy::*policy_method)(EvaluationContext*, State*,
+                                        std::string*, R*,
+                                        ExpectedArgs...) const,
+    R* result, ActualArgs... args) {
+  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
+  // explicitly instantiate EvaluatePolicy with the latter in lieu of the
+  // former.
+  EvalStatus ret = EvaluatePolicy<R, ExpectedArgs...>(ec.get(), policy_method,
+                                                      result, args...);
+  // Sync policy requests must not block, if they do then this is an error.
+  DCHECK(EvalStatus::kAskMeAgainLater != ret);
+  LOG_IF(WARNING, EvalStatus::kAskMeAgainLater == ret)
+      << "Sync request used with an async policy; this is a bug";
+  return ret;
+}
+
+template<typename R, typename... ActualArgs, typename... ExpectedArgs>
+void UpdateManager::AsyncPolicyRequest(
+    base::Callback<void(EvalStatus, const R& result)> callback,
+    EvalStatus (Policy::*policy_method)(EvaluationContext*, State*,
+                                        std::string*, R*,
+                                        ExpectedArgs...) const,
+    ActualArgs... args) {
+  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.get()).second) {
+    LOG(ERROR) << "Failed to register evaluation context; this is a bug.";
+  }
+
+  // IMPORTANT: To ensure that ActualArgs can be converted to ExpectedArgs, we
+  // explicitly instantiate UpdateManager::OnPolicyReadyToEvaluate with the
+  // latter in lieu of the former.
+  base::Closure eval_callback = base::Bind(
+      &UpdateManager::OnPolicyReadyToEvaluate<R, ExpectedArgs...>,
+      base::Unretained(this), ec, callback, policy_method, args...);
+  brillo::MessageLoop::current()->PostTask(FROM_HERE, eval_callback);
+}
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_MANAGER_INL_H_
diff --git a/update_manager/update_manager.cc b/update_manager/update_manager.cc
new file mode 100644
index 0000000..8e9b221
--- /dev/null
+++ b/update_manager/update_manager.cc
@@ -0,0 +1,50 @@
+//
+// 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/update_manager/update_manager.h"
+
+#include "update_engine/update_manager/chromeos_policy.h"
+#include "update_engine/update_manager/state.h"
+
+namespace chromeos_update_manager {
+
+UpdateManager::UpdateManager(chromeos_update_engine::ClockInterface* clock,
+                             base::TimeDelta evaluation_timeout,
+                             base::TimeDelta expiration_timeout, State* state)
+      : default_policy_(clock), state_(state), clock_(clock),
+        evaluation_timeout_(evaluation_timeout),
+        expiration_timeout_(expiration_timeout),
+        weak_ptr_factory_(this) {
+  // TODO(deymo): Make it possible to replace this policy with a different
+  // implementation with a build-time flag.
+  policy_.reset(new ChromeOSPolicy());
+}
+
+UpdateManager::~UpdateManager() {
+  // Remove pending main loop events associated with any of the outstanding
+  // evaluation contexts. This will prevent dangling pending events, causing
+  // these contexts to be destructed once the repo itself is destructed.
+  for (auto& ec : ec_repo_)
+    ec->RemoveObserversAndTimeout();
+}
+
+void UpdateManager::UnregisterEvalContext(EvaluationContext* ec) {
+  if (!ec_repo_.erase(ec)) {
+    LOG(ERROR) << "Unregistering an unknown evaluation context, this is a bug.";
+  }
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/update_manager.conf.example b/update_manager/update_manager.conf.example
new file mode 100644
index 0000000..2d77974
--- /dev/null
+++ b/update_manager/update_manager.conf.example
@@ -0,0 +1,18 @@
+# Configuration file for the update-manager component of update_engine.
+#
+# Normally this file is loaded from /etc/update_manager.conf. If
+# running update_engine in developer mode (and only if running in
+# developer mode), we attempt to load
+#
+#  /mnt/stateful_partition/etc/update_manager.conf
+#
+# and use it if it exists. If it doesn't exist, we fall back to
+# /etc/update_manager.conf.
+#
+# Note: changes to this file are not automatically applied. Use the
+# command "restart update-engine" from a root shell to make your
+# changes take effect.
+
+# Set to true if the device supports the concept of OOBE
+# (Out-Of-the-Box-Experience), false if it doesn't.
+is_oobe_enabled=true
diff --git a/update_manager/update_manager.h b/update_manager/update_manager.h
new file mode 100644
index 0000000..a2f35df
--- /dev/null
+++ b/update_manager/update_manager.h
@@ -0,0 +1,178 @@
+//
+// 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_UPDATE_MANAGER_UPDATE_MANAGER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_MANAGER_H_
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include <base/callback.h>
+#include <base/memory/ref_counted.h>
+#include <base/time/time.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"
+#include "update_engine/update_manager/state.h"
+
+namespace chromeos_update_manager {
+
+// 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:
+  // Creates the UpdateManager instance, assuming ownership on the provided
+  // |state|.
+  UpdateManager(chromeos_update_engine::ClockInterface* clock,
+                base::TimeDelta evaluation_timeout,
+                base::TimeDelta expiration_timeout, State* state);
+
+  virtual ~UpdateManager();
+
+  // PolicyRequest() evaluates the given policy with the provided arguments and
+  // returns the result. The |policy_method| is the pointer-to-method of the
+  // Policy class for the policy request to call. The UpdateManager will call
+  // this method on the right policy. The pointer |result| must not be null
+  // and the remaining |args| depend on the arguments required by the passed
+  // |policy_method|.
+  //
+  // When the policy request succeeds, the |result| is set and the method
+  // returns EvalStatus::kSucceeded, otherwise, the |result| may not be set.  A
+  // policy called with this method should not block (i.e. return
+  // EvalStatus::kAskMeAgainLater), which is considered a programming error. On
+  // failure, EvalStatus::kFailed is returned.
+  //
+  // An example call to this method is:
+  //   um.PolicyRequest(&Policy::SomePolicyMethod, &bool_result, arg1, arg2);
+  template<typename R, typename... ActualArgs, typename... ExpectedArgs>
+  EvalStatus PolicyRequest(
+      EvalStatus (Policy::*policy_method)(EvaluationContext*, State*,
+                                          std::string*, R*,
+                                          ExpectedArgs...) const,
+      R* result, ActualArgs...);
+
+  // Evaluates the given |policy_method| policy with the provided |args|
+  // arguments and calls the |callback| callback with the result when done.
+  //
+  // If the policy implementation should block, returning a
+  // EvalStatus::kAskMeAgainLater status the Update Manager will re-evaluate the
+  // policy until another status is returned. If the policy implementation based
+  // its return value solely on const variables, the callback will be called
+  // with the EvalStatus::kAskMeAgainLater status (which indicates an error).
+  template<typename R, typename... ActualArgs, typename... ExpectedArgs>
+  void AsyncPolicyRequest(
+      base::Callback<void(EvalStatus, const R& result)> callback,
+      EvalStatus (Policy::*policy_method)(EvaluationContext*, State*,
+                                          std::string*, R*,
+                                          ExpectedArgs...) const,
+      ActualArgs... args);
+
+ protected:
+  // The UpdateManager receives ownership of the passed Policy instance.
+  void set_policy(const Policy* policy) {
+    policy_.reset(policy);
+  }
+
+  // State getter used for testing.
+  State* state() { return state_.get(); }
+
+ private:
+  FRIEND_TEST(UmUpdateManagerTest, PolicyRequestCallsPolicy);
+  FRIEND_TEST(UmUpdateManagerTest, PolicyRequestCallsDefaultOnError);
+  FRIEND_TEST(UmUpdateManagerTest, PolicyRequestDoesntBlockDeathTest);
+  FRIEND_TEST(UmUpdateManagerTest, AsyncPolicyRequestDelaysEvaluation);
+  FRIEND_TEST(UmUpdateManagerTest, AsyncPolicyRequestTimeoutDoesNotFire);
+  FRIEND_TEST(UmUpdateManagerTest, AsyncPolicyRequestTimesOut);
+
+  // EvaluatePolicy() evaluates the passed |policy_method| method on the current
+  // policy with the given |args| arguments. If the method fails, the default
+  // policy is used instead.
+  template<typename R, typename... Args>
+  EvalStatus EvaluatePolicy(
+      EvaluationContext* ec,
+      EvalStatus (Policy::*policy_method)(EvaluationContext*, State*,
+                                          std::string*, R*,
+                                          Args...) const,
+      R* result, Args... args);
+
+  // OnPolicyReadyToEvaluate() is called by the main loop when the evaluation
+  // of the given |policy_method| should be executed. If the evaluation finishes
+  // the |callback| callback is called passing the |result| and the |status|
+  // returned by the policy. If the evaluation returns an
+  // EvalStatus::kAskMeAgainLater state, the |callback| will NOT be called and
+  // the evaluation will be re-scheduled to be called later.
+  template<typename R, typename... Args>
+  void OnPolicyReadyToEvaluate(
+      scoped_refptr<EvaluationContext> ec,
+      base::Callback<void(EvalStatus status, const R& result)> callback,
+      EvalStatus (Policy::*policy_method)(EvaluationContext*, State*,
+                                          std::string*, R*,
+                                          Args...) const,
+      Args... args);
+
+  // Unregisters (removes from repo) a previously created EvaluationContext.
+  void UnregisterEvalContext(EvaluationContext* ec);
+
+  // The policy used by the UpdateManager. Note that since it is a const Policy,
+  // policy implementations are not allowed to persist state on this class.
+  std::unique_ptr<const Policy> policy_;
+
+  // A safe default value to the current policy. This policy is used whenever
+  // a policy implementation fails with EvalStatus::kFailed.
+  const DefaultPolicy default_policy_;
+
+  // 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_;
+
+  // Timeout for expiration of the evaluation context, used for async requests.
+  const base::TimeDelta expiration_timeout_;
+
+  // Repository of previously created EvaluationContext objects. These are being
+  // unregistered (and the reference released) when the context is being
+  // 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<scoped_refptr<EvaluationContext>,
+           ScopedRefPtrLess<EvaluationContext>> ec_repo_;
+
+  base::WeakPtrFactory<UpdateManager> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(UpdateManager);
+};
+
+}  // namespace chromeos_update_manager
+
+// Include the implementation of the template methods.
+#include "update_engine/update_manager/update_manager-inl.h"
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_MANAGER_H_
diff --git a/update_manager/update_manager_unittest.cc b/update_manager/update_manager_unittest.cc
new file mode 100644
index 0000000..4cc738d
--- /dev/null
+++ b/update_manager/update_manager_unittest.cc
@@ -0,0 +1,325 @@
+//
+// 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/update_manager/update_manager.h"
+
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/test/simple_test_clock.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 <gmock/gmock.h>
+#include <gtest/gtest.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"
+#include "update_engine/update_manager/umtest_utils.h"
+
+using base::Bind;
+using base::Callback;
+using base::Time;
+using base::TimeDelta;
+using brillo::MessageLoop;
+using brillo::MessageLoopRunMaxIterations;
+using chromeos_update_engine::ErrorCode;
+using chromeos_update_engine::FakeClock;
+using std::pair;
+using std::string;
+using std::tuple;
+using std::unique_ptr;
+using std::vector;
+
+namespace {
+
+// Generates a fixed timestamp for use in faking the current time.
+Time FixedTime() {
+  Time::Exploded now_exp;
+  now_exp.year = 2014;
+  now_exp.month = 3;
+  now_exp.day_of_week = 2;
+  now_exp.day_of_month = 18;
+  now_exp.hour = 8;
+  now_exp.minute = 5;
+  now_exp.second = 33;
+  now_exp.millisecond = 675;
+  return Time::FromLocalExploded(now_exp);
+}
+
+}  // namespace
+
+namespace chromeos_update_manager {
+
+class UmUpdateManagerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    loop_.SetAsCurrent();
+    fake_state_ = new FakeState();
+    umut_.reset(new UpdateManager(&fake_clock_, TimeDelta::FromSeconds(5),
+                                  TimeDelta::FromSeconds(1), fake_state_));
+  }
+
+  void TearDown() override {
+    EXPECT_FALSE(loop_.PendingTasks());
+  }
+
+  base::SimpleTestClock test_clock_;
+  brillo::FakeMessageLoop loop_{&test_clock_};
+  FakeState* fake_state_;  // Owned by the umut_.
+  FakeClock fake_clock_;
+  unique_ptr<UpdateManager> umut_;
+};
+
+// The FailingPolicy implements a single method and make it always fail. This
+// class extends the DefaultPolicy class to allow extensions of the Policy
+// class without extending nor changing this test.
+class FailingPolicy : public DefaultPolicy {
+ public:
+  explicit FailingPolicy(int* num_called_p) : num_called_p_(num_called_p) {}
+  FailingPolicy() : FailingPolicy(nullptr) {}
+  EvalStatus UpdateCheckAllowed(EvaluationContext* ec, State* state,
+                                string* error,
+                                UpdateCheckParams* result) const override {
+    if (num_called_p_)
+      (*num_called_p_)++;
+    *error = "FailingPolicy failed.";
+    return EvalStatus::kFailed;
+  }
+
+ protected:
+  string PolicyName() const override { return "FailingPolicy"; }
+
+ private:
+  int* num_called_p_;
+};
+
+// The LazyPolicy always returns EvalStatus::kAskMeAgainLater.
+class LazyPolicy : public DefaultPolicy {
+  EvalStatus UpdateCheckAllowed(EvaluationContext* ec, State* state,
+                                string* error,
+                                UpdateCheckParams* result) const override {
+    return EvalStatus::kAskMeAgainLater;
+  }
+
+ protected:
+  string PolicyName() const override { return "LazyPolicy"; }
+};
+
+// A policy that sleeps for a predetermined amount of time, then checks for a
+// wallclock-based time threshold (if given) and returns
+// EvalStatus::kAskMeAgainLater if not passed; otherwise, returns
+// EvalStatus::kSucceeded. Increments a counter every time it is being queried,
+// if a pointer to it is provided.
+class DelayPolicy : public DefaultPolicy {
+ public:
+  DelayPolicy(int sleep_secs, Time time_threshold, int* num_called_p)
+      : sleep_secs_(sleep_secs), time_threshold_(time_threshold),
+        num_called_p_(num_called_p) {}
+  EvalStatus UpdateCheckAllowed(EvaluationContext* ec, State* state,
+                                string* error,
+                                UpdateCheckParams* result) const override {
+    if (num_called_p_)
+      (*num_called_p_)++;
+
+    // Sleep for a predetermined amount of time.
+    if (sleep_secs_ > 0)
+      sleep(sleep_secs_);
+
+    // Check for a time threshold. This can be used to ensure that the policy
+    // has some non-constant dependency.
+    if (time_threshold_ < Time::Max() &&
+        ec->IsWallclockTimeGreaterThan(time_threshold_))
+      return EvalStatus::kSucceeded;
+
+    return EvalStatus::kAskMeAgainLater;
+  }
+
+ protected:
+  string PolicyName() const override { return "DelayPolicy"; }
+
+ private:
+  int sleep_secs_;
+  Time time_threshold_;
+  int* num_called_p_;
+};
+
+// AccumulateCallsCallback() adds to the passed |acc| accumulator vector pairs
+// of EvalStatus and T instances. This allows to create a callback that keeps
+// track of when it is called and the arguments passed to it, to be used with
+// the UpdateManager::AsyncPolicyRequest().
+template<typename T>
+static void AccumulateCallsCallback(vector<pair<EvalStatus, T>>* acc,
+                                    EvalStatus status, const T& result) {
+  acc->push_back(std::make_pair(status, result));
+}
+
+// Tests that policy requests are completed successfully. It is important that
+// this tests cover all policy requests as defined in Policy.
+TEST_F(UmUpdateManagerTest, PolicyRequestCallUpdateCheckAllowed) {
+  UpdateCheckParams result;
+  EXPECT_EQ(EvalStatus::kSucceeded, umut_->PolicyRequest(
+      &Policy::UpdateCheckAllowed, &result));
+}
+
+TEST_F(UmUpdateManagerTest, PolicyRequestCallUpdateCanStart) {
+  UpdateState update_state = UpdateState();
+  update_state.is_interactive = true;
+  update_state.is_delta_payload = false;
+  update_state.first_seen = FixedTime();
+  update_state.num_checks = 1;
+  update_state.num_failures = 0;
+  update_state.failures_last_updated = Time();
+  update_state.download_urls = vector<string>{"http://fake/url/"};
+  update_state.download_errors_max = 10;
+  update_state.p2p_downloading_disabled = false;
+  update_state.p2p_sharing_disabled = false;
+  update_state.p2p_num_attempts = 0;
+  update_state.p2p_first_attempted = Time();
+  update_state.last_download_url_idx = -1;
+  update_state.last_download_url_num_errors = 0;
+  update_state.download_errors = vector<tuple<int, ErrorCode, Time>>();
+  update_state.backoff_expiry = Time();
+  update_state.is_backoff_disabled = false;
+  update_state.scatter_wait_period = TimeDelta::FromSeconds(15);
+  update_state.scatter_check_threshold = 4;
+  update_state.scatter_wait_period_max = TimeDelta::FromSeconds(60);
+  update_state.scatter_check_threshold_min = 2;
+  update_state.scatter_check_threshold_max = 8;
+
+  UpdateDownloadParams result;
+  EXPECT_EQ(EvalStatus::kSucceeded,
+            umut_->PolicyRequest(&Policy::UpdateCanStart, &result,
+                                 update_state));
+}
+
+TEST_F(UmUpdateManagerTest, PolicyRequestCallsDefaultOnError) {
+  umut_->set_policy(new FailingPolicy());
+
+  // Tests that the DefaultPolicy instance is called when the method fails,
+  // which will set this as true.
+  UpdateCheckParams result;
+  result.updates_enabled = false;
+  EvalStatus status = umut_->PolicyRequest(
+      &Policy::UpdateCheckAllowed, &result);
+  EXPECT_EQ(EvalStatus::kSucceeded, status);
+  EXPECT_TRUE(result.updates_enabled);
+}
+
+// This test only applies to debug builds where DCHECK is enabled.
+#if DCHECK_IS_ON
+TEST_F(UmUpdateManagerTest, PolicyRequestDoesntBlockDeathTest) {
+  // The update manager should die (DCHECK) if a policy called synchronously
+  // returns a kAskMeAgainLater value.
+  UpdateCheckParams result;
+  umut_->set_policy(new LazyPolicy());
+  EXPECT_DEATH(umut_->PolicyRequest(&Policy::UpdateCheckAllowed, &result), "");
+}
+#endif  // DCHECK_IS_ON
+
+TEST_F(UmUpdateManagerTest, AsyncPolicyRequestDelaysEvaluation) {
+  // To avoid differences in code execution order between an AsyncPolicyRequest
+  // call on a policy that returns AskMeAgainLater the first time and one that
+  // succeeds the first time, we ensure that the passed callback is called from
+  // the main loop in both cases even when we could evaluate it right now.
+  umut_->set_policy(new FailingPolicy());
+
+  vector<pair<EvalStatus, UpdateCheckParams>> calls;
+  Callback<void(EvalStatus, const UpdateCheckParams&)> callback = Bind(
+      AccumulateCallsCallback<UpdateCheckParams>, &calls);
+
+  umut_->AsyncPolicyRequest(callback, &Policy::UpdateCheckAllowed);
+  // The callback should wait until we run the main loop for it to be executed.
+  EXPECT_EQ(0, calls.size());
+  MessageLoopRunMaxIterations(MessageLoop::current(), 100);
+  EXPECT_EQ(1, calls.size());
+}
+
+TEST_F(UmUpdateManagerTest, AsyncPolicyRequestTimeoutDoesNotFire) {
+  // Set up an async policy call to return immediately, then wait a little and
+  // ensure that the timeout event does not fire.
+  int num_called = 0;
+  umut_->set_policy(new FailingPolicy(&num_called));
+
+  vector<pair<EvalStatus, UpdateCheckParams>> calls;
+  Callback<void(EvalStatus, const UpdateCheckParams&)> callback =
+      Bind(AccumulateCallsCallback<UpdateCheckParams>, &calls);
+
+  umut_->AsyncPolicyRequest(callback, &Policy::UpdateCheckAllowed);
+  // Run the main loop, ensure that policy was attempted once before deferring
+  // to the default.
+  MessageLoopRunMaxIterations(MessageLoop::current(), 100);
+  EXPECT_EQ(1, num_called);
+  ASSERT_EQ(1, calls.size());
+  EXPECT_EQ(EvalStatus::kSucceeded, calls[0].first);
+  // Wait for the timeout to expire, run the main loop again, ensure that
+  // nothing happened.
+  test_clock_.Advance(TimeDelta::FromSeconds(2));
+  MessageLoopRunMaxIterations(MessageLoop::current(), 10);
+  EXPECT_EQ(1, num_called);
+  EXPECT_EQ(1, calls.size());
+}
+
+TEST_F(UmUpdateManagerTest, AsyncPolicyRequestTimesOut) {
+  // 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),
+          &num_called));
+
+  vector<pair<EvalStatus, UpdateCheckParams>> calls;
+  Callback<void(EvalStatus, const UpdateCheckParams&)> callback =
+      Bind(AccumulateCallsCallback<UpdateCheckParams>, &calls);
+
+  umut_->AsyncPolicyRequest(callback, &Policy::UpdateCheckAllowed);
+  // Run the main loop, ensure that policy was attempted once but the callback
+  // was not invoked.
+  MessageLoopRunMaxIterations(MessageLoop::current(), 100);
+  EXPECT_EQ(1, num_called);
+  EXPECT_EQ(0, calls.size());
+  // Wait for the expiration timeout to expire, run the main loop again,
+  // 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() +
+                               TimeDelta::FromSeconds(2));
+  MessageLoopRunMaxIterations(MessageLoop::current(), 10);
+  EXPECT_EQ(2, num_called);
+  EXPECT_EQ(0, calls.size());
+  // 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() +
+                               TimeDelta::FromSeconds(2));
+  MessageLoopRunMaxIterations(MessageLoop::current(), 10);
+  EXPECT_EQ(3, num_called);
+  ASSERT_EQ(1, calls.size());
+  EXPECT_EQ(EvalStatus::kSucceeded, calls[0].first);
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_manager/updater_provider.h b/update_manager/updater_provider.h
new file mode 100644
index 0000000..8048d38
--- /dev/null
+++ b/update_manager/updater_provider.h
@@ -0,0 +1,117 @@
+//
+// 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_UPDATE_MANAGER_UPDATER_PROVIDER_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_UPDATER_PROVIDER_H_
+
+#include <string>
+
+#include <base/time/time.h>
+
+#include "update_engine/update_manager/provider.h"
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+enum class Stage {
+  kIdle,
+  kCheckingForUpdate,
+  kUpdateAvailable,
+  kDownloading,
+  kVerifying,
+  kFinalizing,
+  kUpdatedNeedReboot,
+  kReportingErrorEvent,
+  kAttemptingRollback,
+};
+
+enum class UpdateRequestStatus {
+  kNone,
+  kInteractive,
+  kPeriodic,
+};
+
+// Provider for Chrome OS update related information.
+class UpdaterProvider : public Provider {
+ public:
+  ~UpdaterProvider() override {}
+
+  // A variable returning the timestamp when the update engine was started in
+  // wallclock time.
+  virtual Variable<base::Time>* var_updater_started_time() = 0;
+
+  // A variable returning the last update check time.
+  virtual Variable<base::Time>* var_last_checked_time() = 0;
+
+  // A variable reporting the time when an update was last completed in the
+  // current boot cycle. Returns an error if an update completed time could not
+  // be read (e.g. no update was completed in the current boot cycle) or is
+  // invalid.
+  //
+  // IMPORTANT: The time reported is not the wallclock time reading at the time
+  // of the update, rather it is the point in time when the update completed
+  // relative to the current wallclock time reading. Therefore, the gap between
+  // the reported value and the current wallclock time is guaranteed to be
+  // monotonically increasing.
+  virtual Variable<base::Time>* var_update_completed_time() = 0;
+
+  // A variable returning the update progress (0.0 to 1.0).
+  virtual Variable<double>* var_progress() = 0;
+
+  // A variable returning the current update status.
+  virtual Variable<Stage>* var_stage() = 0;
+
+  // A variable returning the update target version.
+  virtual Variable<std::string>* var_new_version() = 0;
+
+  // A variable returning the update payload size. The payload size is
+  // guaranteed to be non-negative.
+  virtual Variable<int64_t>* var_payload_size() = 0;
+
+  // A variable returning the current channel.
+  virtual Variable<std::string>* var_curr_channel() = 0;
+
+  // A variable returning the update target channel.
+  virtual Variable<std::string>* var_new_channel() = 0;
+
+  // A variable indicating whether user settings allow P2P updates.
+  virtual Variable<bool>* var_p2p_enabled() = 0;
+
+  // A variable indicating whether user settings allow updates over a cellular
+  // network.
+  virtual Variable<bool>* var_cellular_enabled() = 0;
+
+  // A variable returning the number of consecutive failed update checks.
+  virtual Variable<unsigned int>* var_consecutive_failed_update_checks() = 0;
+
+  // A server-dictated update check interval in seconds, if one was given.
+  virtual Variable<unsigned int>* var_server_dictated_poll_interval() = 0;
+
+  // A variable denoting whether a forced update was request but no update check
+  // performed yet; also tells whether this request is for an interactive or
+  // scheduled update.
+  virtual Variable<UpdateRequestStatus>* var_forced_update_requested() = 0;
+
+ protected:
+  UpdaterProvider() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(UpdaterProvider);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_UPDATER_PROVIDER_H_
diff --git a/update_manager/variable.h b/update_manager/variable.h
new file mode 100644
index 0000000..98774ef
--- /dev/null
+++ b/update_manager/variable.h
@@ -0,0 +1,217 @@
+//
+// 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_UPDATE_MANAGER_VARIABLE_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_VARIABLE_H_
+
+#include <algorithm>
+#include <list>
+#include <string>
+
+#include <base/bind.h>
+#include <base/location.h>
+#include <base/logging.h>
+#include <base/time/time.h>
+#include <brillo/message_loops/message_loop.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+namespace chromeos_update_manager {
+
+// The VariableMode specifies important behavior of the variable in terms of
+// whether, how and when the value of the variable changes.
+enum VariableMode {
+  // Const variables never changes during the life of a policy request, so the
+  // EvaluationContext caches the value even between different evaluations of
+  // the same policy request.
+  kVariableModeConst,
+
+  // Poll variables, or synchronous variables, represent a variable with a value
+  // that can be queried at any time, but it is not known when the value
+  // changes on the source of information. In order to detect if the value of
+  // the variable changes, it has to be queried again.
+  kVariableModePoll,
+
+  // Async variables are able to produce a signal or callback whenever the
+  // value changes. This means that it's not required to poll the value to
+  // detect when it changes, instead, you should register an observer to get
+  // a notification when that happens.
+  kVariableModeAsync,
+};
+
+// This class is a base class with the common functionality that doesn't
+// depend on the variable's type, implemented by all the variables.
+class BaseVariable {
+ public:
+  // Interface for observing changes on variable value.
+  class ObserverInterface {
+   public:
+    virtual ~ObserverInterface() {}
+
+    // Called when the value on the variable changes.
+    virtual void ValueChanged(BaseVariable* variable) = 0;
+  };
+
+  virtual ~BaseVariable() {
+    if (!observer_list_.empty()) {
+      LOG(WARNING) << "Variable " << name_ << " deleted with "
+                   << observer_list_.size() << " observers.";
+    }
+    DCHECK(observer_list_.empty()) << "Don't destroy the variable without "
+                                      "removing the observers.";
+  }
+
+  // Returns the variable name as a string.
+  const std::string& GetName() const {
+    return name_;
+  }
+
+  // Returns the variable mode.
+  VariableMode GetMode() const {
+    return mode_;
+  }
+
+  // For VariableModePoll variables, it returns the polling interval of this
+  // variable. In other case, it returns 0.
+  base::TimeDelta GetPollInterval() const {
+    return poll_interval_;
+  }
+
+  // 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.
+  virtual void AddObserver(BaseVariable::ObserverInterface* observer) {
+    if (std::find(observer_list_.begin(), observer_list_.end(), observer) ==
+        observer_list_.end()) {
+      observer_list_.push_back(observer);
+    }
+  }
+
+  virtual void RemoveObserver(BaseVariable::ObserverInterface* observer) {
+    observer_list_.remove(observer);
+  }
+
+ protected:
+  // Creates a BaseVariable using the default polling interval (5 minutes).
+  BaseVariable(const std::string& name, VariableMode mode)
+      : BaseVariable(name, mode,
+                     base::TimeDelta::FromMinutes(kDefaultPollMinutes)) {}
+
+  // Creates a BaseVariable with mode kVariableModePoll and the provided
+  // polling interval.
+  BaseVariable(const std::string& name, base::TimeDelta poll_interval)
+      : BaseVariable(name, kVariableModePoll, poll_interval) {}
+
+  // Calls ValueChanged on all the observers.
+  void NotifyValueChanged() {
+    // Fire all the observer methods from the main loop as single call. In order
+    // to avoid scheduling these callbacks when it is not needed, we check
+    // first the list of observers.
+    if (!observer_list_.empty()) {
+      brillo::MessageLoop::current()->PostTask(
+          FROM_HERE,
+          base::Bind(&BaseVariable::OnValueChangedNotification,
+                     base::Unretained(this)));
+    }
+  }
+
+ private:
+  friend class UmEvaluationContextTest;
+  FRIEND_TEST(UmBaseVariableTest, RepeatedObserverTest);
+  FRIEND_TEST(UmBaseVariableTest, NotifyValueChangedTest);
+  FRIEND_TEST(UmBaseVariableTest, NotifyValueRemovesObserversTest);
+
+  BaseVariable(const std::string& name, VariableMode mode,
+               base::TimeDelta poll_interval)
+    : name_(name), mode_(mode),
+      poll_interval_(mode == kVariableModePoll ?
+                     poll_interval : base::TimeDelta()) {}
+
+  void OnValueChangedNotification() {
+    // A ValueChanged() method can change the list of observers, for example
+    // removing itself and invalidating the iterator, so we create a snapshot
+    // of the observers first. Also, to support the case when *another* observer
+    // is removed, we check for them.
+    std::list<BaseVariable::ObserverInterface*> observer_list_copy(
+        observer_list_);
+
+    for (auto& observer : observer_list_copy) {
+      if (std::find(observer_list_.begin(), observer_list_.end(), observer) !=
+          observer_list_.end()) {
+        observer->ValueChanged(this);
+      }
+    }
+  }
+
+  // The default PollInterval in minutes.
+  static constexpr int kDefaultPollMinutes = 5;
+
+  // The variable's name as a string.
+  const std::string name_;
+
+  // The variable's mode.
+  const VariableMode mode_;
+
+  // The variable's polling interval for VariableModePoll variable and 0 for
+  // other modes.
+  const base::TimeDelta poll_interval_;
+
+  // The list of value changes observers.
+  std::list<BaseVariable::ObserverInterface*> observer_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(BaseVariable);
+};
+
+// Interface to an Update Manager variable of a given type. Implementation
+// internals are hidden as protected members, since policies should not be
+// using them directly.
+template<typename T>
+class Variable : public BaseVariable {
+ public:
+  ~Variable() override {}
+
+ protected:
+  // Only allow to get values through the EvaluationContext class and not
+  // directly from the variable.
+  friend class EvaluationContext;
+
+  // Needed to be able to verify variable contents during unit testing.
+  friend class UmTestUtils;
+  FRIEND_TEST(UmRealRandomProviderTest, GetRandomValues);
+
+  Variable(const std::string& name, VariableMode mode)
+      : BaseVariable(name, mode) {}
+
+  Variable(const std::string& name, const base::TimeDelta poll_interval)
+      : BaseVariable(name, poll_interval) {}
+
+  // Gets the current value of the variable. The current value is copied to a
+  // new object and returned. The caller of this method owns the object and
+  // should delete it.
+  //
+  // In case of and error getting the current value or the |timeout| timeout is
+  // exceeded, a null value is returned and the |errmsg| is set.
+  //
+  // The caller can pass a null value for |errmsg|, in which case the error
+  // message won't be set.
+  virtual const T* GetValue(base::TimeDelta timeout, std::string* errmsg) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Variable);
+};
+
+}  // namespace chromeos_update_manager
+
+#endif  // UPDATE_ENGINE_UPDATE_MANAGER_VARIABLE_H_
diff --git a/update_manager/variable_unittest.cc b/update_manager/variable_unittest.cc
new file mode 100644
index 0000000..13cceb1
--- /dev/null
+++ b/update_manager/variable_unittest.cc
@@ -0,0 +1,181 @@
+//
+// 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/update_manager/variable.h"
+
+#include <vector>
+
+#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>
+
+using base::TimeDelta;
+using brillo::MessageLoop;
+using brillo::MessageLoopRunMaxIterations;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_manager {
+
+// Variable class that returns a value constructed with the default value.
+template <typename T>
+class DefaultVariable : public Variable<T> {
+ public:
+  DefaultVariable(const string& name, VariableMode mode)
+      : Variable<T>(name, mode) {}
+  DefaultVariable(const string& name, const TimeDelta& poll_interval)
+      : Variable<T>(name, poll_interval) {}
+  ~DefaultVariable() override {}
+
+ protected:
+  const T* GetValue(TimeDelta /* timeout */,
+                    string* /* errmsg */) override {
+    return new T();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DefaultVariable);
+};
+
+class UmBaseVariableTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    loop_.SetAsCurrent();
+  }
+
+  brillo::FakeMessageLoop loop_{nullptr};
+};
+
+TEST_F(UmBaseVariableTest, GetNameTest) {
+  DefaultVariable<int> var("var", kVariableModeConst);
+  EXPECT_EQ(var.GetName(), string("var"));
+}
+
+TEST_F(UmBaseVariableTest, GetModeTest) {
+  DefaultVariable<int> var("var", kVariableModeConst);
+  EXPECT_EQ(var.GetMode(), kVariableModeConst);
+  DefaultVariable<int> other_var("other_var", kVariableModePoll);
+  EXPECT_EQ(other_var.GetMode(), kVariableModePoll);
+}
+
+TEST_F(UmBaseVariableTest, DefaultPollIntervalTest) {
+  DefaultVariable<int> const_var("const_var", kVariableModeConst);
+  EXPECT_EQ(const_var.GetPollInterval(), TimeDelta());
+  DefaultVariable<int> poll_var("poll_var", kVariableModePoll);
+  EXPECT_EQ(poll_var.GetPollInterval(), TimeDelta::FromMinutes(5));
+}
+
+TEST_F(UmBaseVariableTest, GetPollIntervalTest) {
+  DefaultVariable<int> var("var", TimeDelta::FromMinutes(3));
+  EXPECT_EQ(var.GetMode(), kVariableModePoll);
+  EXPECT_EQ(var.GetPollInterval(), TimeDelta::FromMinutes(3));
+}
+
+class BaseVariableObserver : public BaseVariable::ObserverInterface {
+ public:
+  void ValueChanged(BaseVariable* variable) {
+    calls_.push_back(variable);
+  }
+
+  // List of called functions.
+  vector<BaseVariable*> calls_;
+};
+
+TEST_F(UmBaseVariableTest, RepeatedObserverTest) {
+  DefaultVariable<int> var("var", kVariableModeAsync);
+  BaseVariableObserver observer;
+  var.AddObserver(&observer);
+  EXPECT_EQ(var.observer_list_.size(), 1);
+  var.AddObserver(&observer);
+  EXPECT_EQ(var.observer_list_.size(), 1);
+  var.RemoveObserver(&observer);
+  EXPECT_EQ(var.observer_list_.size(), 0);
+  var.RemoveObserver(&observer);
+  EXPECT_EQ(var.observer_list_.size(), 0);
+}
+
+TEST_F(UmBaseVariableTest, NotifyValueChangedTest) {
+  DefaultVariable<int> var("var", kVariableModeAsync);
+  BaseVariableObserver observer1;
+  var.AddObserver(&observer1);
+  // Simulate a value change on the variable's implementation.
+  var.NotifyValueChanged();
+  ASSERT_EQ(0, observer1.calls_.size());
+  MessageLoopRunMaxIterations(MessageLoop::current(), 100);
+
+  ASSERT_EQ(1, observer1.calls_.size());
+  // Check that the observer is called with the right argument.
+  EXPECT_EQ(&var, observer1.calls_[0]);
+
+  BaseVariableObserver observer2;
+  var.AddObserver(&observer2);
+  var.NotifyValueChanged();
+  MessageLoopRunMaxIterations(MessageLoop::current(), 100);
+
+  // Check that all the observers are called.
+  EXPECT_EQ(2, observer1.calls_.size());
+  EXPECT_EQ(1, observer2.calls_.size());
+
+  var.RemoveObserver(&observer1);
+  var.RemoveObserver(&observer2);
+}
+
+class BaseVariableObserverRemover : public BaseVariable::ObserverInterface {
+ public:
+  BaseVariableObserverRemover() : calls_(0) {}
+
+  void ValueChanged(BaseVariable* variable) override {
+    for (auto& observer : remove_observers_) {
+      variable->RemoveObserver(observer);
+    }
+    calls_++;
+  }
+
+  void OnCallRemoveObserver(BaseVariable::ObserverInterface* observer) {
+    remove_observers_.push_back(observer);
+  }
+
+  int get_calls() { return calls_; }
+
+ private:
+  vector<BaseVariable::ObserverInterface*> remove_observers_;
+  int calls_;
+};
+
+// Tests that we can remove an observer from a Variable on the ValueChanged()
+// call to that observer.
+TEST_F(UmBaseVariableTest, NotifyValueRemovesObserversTest) {
+  DefaultVariable<int> var("var", kVariableModeAsync);
+  BaseVariableObserverRemover observer1;
+  BaseVariableObserverRemover observer2;
+
+  var.AddObserver(&observer1);
+  var.AddObserver(&observer2);
+
+  // Make each observer remove both observers on ValueChanged.
+  observer1.OnCallRemoveObserver(&observer1);
+  observer1.OnCallRemoveObserver(&observer2);
+  observer2.OnCallRemoveObserver(&observer1);
+  observer2.OnCallRemoveObserver(&observer2);
+
+  var.NotifyValueChanged();
+  MessageLoopRunMaxIterations(MessageLoop::current(), 100);
+
+  EXPECT_EQ(1, observer1.get_calls() + observer2.get_calls());
+}
+
+}  // namespace chromeos_update_manager
diff --git a/update_metadata.proto b/update_metadata.proto
new file mode 100644
index 0000000..c856405
--- /dev/null
+++ b/update_metadata.proto
@@ -0,0 +1,277 @@
+//
+// 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.
+//
+
+// Update file format: A delta update file contains all the deltas needed
+// to update a system from one specific version to another specific
+// version. The update format is represented by this struct pseudocode:
+// struct delta_update_file {
+//   char magic[4] = "CrAU";
+//   uint64 file_format_version;
+//   uint64 manifest_size;  // Size of protobuf DeltaArchiveManifest
+//
+//   // Only present if format_version > 1:
+//   uint32 metadata_signature_size;
+//
+//   // The Bzip2 compressed DeltaArchiveManifest
+//   char manifest[];
+//
+//   // The signature of the metadata (from the beginning of the payload up to
+//   // this location, not including the signature itself). This is a serialized
+//   // Signatures message.
+//   char medatada_signature_message[metadata_signature_size];
+//
+//   // Data blobs for files, no specific format. The specific offset
+//   // and length of each data blob is recorded in the DeltaArchiveManifest.
+//   struct {
+//     char data[];
+//   } blobs[];
+//
+//   // These two are not signed:
+//   uint64 payload_signatures_message_size;
+//   char payload_signatures_message[];
+//
+// };
+
+// The DeltaArchiveManifest protobuf is an ordered list of InstallOperation
+// objects. These objects are stored in a linear array in the
+// DeltaArchiveManifest. Each operation is applied in order by the client.
+
+// The DeltaArchiveManifest also contains the initial and final
+// checksums for the device.
+
+// The client will perform each InstallOperation in order, beginning even
+// before the entire delta file is downloaded (but after at least the
+// protobuf is downloaded). The types of operations are explained:
+// - REPLACE: Replace the dst_extents on the drive with the attached data,
+//   zero padding out to block size.
+// - REPLACE_BZ: bzip2-uncompress the attached data and write it into
+//   dst_extents on the drive, zero padding to block size.
+// - MOVE: Copy the data in src_extents to dst_extents. Extents may overlap,
+//   so it may be desirable to read all src_extents data into memory before
+//   writing it out.
+// - SOURCE_COPY: Copy the data in src_extents in the old partition to
+//   dst_extents in the new partition. There's no overlapping of data because
+//   the extents are in different partitions.
+// - BSDIFF: Read src_length bytes from src_extents into memory, perform
+//   bspatch with attached data, write new data to dst_extents, zero padding
+//   to block size.
+// - SOURCE_BSDIFF: Read the data in src_extents in the old partition, perform
+//   bspatch with the attached data and write the new data to dst_extents in the
+//   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 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.
+//
+// The operations allowed in the payload (supported by the client) depend on the
+// major and minor version. See InstallOperation.Type bellow for details.
+
+package chromeos_update_engine;
+option optimize_for = LITE_RUNTIME;
+
+// Data is packed into blocks on disk, always starting from the beginning
+// of the block. If a file's data is too large for one block, it overflows
+// into another block, which may or may not be the following block on the
+// physical partition. An ordered list of extents is another
+// representation of an ordered list of blocks. For example, a file stored
+// in blocks 9, 10, 11, 2, 18, 12 (in that order) would be stored in
+// extents { {9, 3}, {2, 1}, {18, 1}, {12, 1} } (in that order).
+// In general, files are stored sequentially on disk, so it's more efficient
+// to use extents to encode the block lists (this is effectively
+// run-length encoding).
+// A sentinel value (kuint64max) as the start block denotes a sparse-hole
+// in a file whose block-length is specified by num_blocks.
+
+// Signatures: Updates may be signed by the OS vendor. The client verifies
+// an update's signature by hashing the entire download. The section of the
+// download that contains the signature is at the end of the file, so when
+// signing a file, only the part up to the signature part is signed.
+// Then, the client looks inside the download's Signatures message for a
+// Signature message that it knows how to handle. Generally, a client will
+// only know how to handle one type of signature, but an update may contain
+// many signatures to support many different types of client. Then client
+// selects a Signature message and uses that, along with a known public key,
+// to verify the download. The public key is expected to be part of the
+// client.
+
+message Extent {
+  optional uint64 start_block = 1;
+  optional uint64 num_blocks = 2;
+}
+
+message Signatures {
+  message Signature {
+    optional uint32 version = 1;
+    optional bytes data = 2;
+  }
+  repeated Signature signatures = 1;
+}
+
+message PartitionInfo {
+  optional uint64 size = 1;
+  optional bytes hash = 2;
+}
+
+// Describe an image we are based on in a human friendly way.
+// Examples:
+//   dev-channel, x86-alex, 1.2.3, mp-v3
+//   nplusone-channel, x86-alex, 1.2.4, mp-v3, dev-channel, 1.2.3
+//
+// All fields will be set, if this message is present.
+message ImageInfo {
+  optional string board = 1;
+  optional string key = 2;
+  optional string channel = 3;
+  optional string version = 4;
+
+  // If these values aren't present, they should be assumed to match
+  // the equivalent value above. They are normally only different for
+  // special image types such as nplusone images.
+  optional string build_channel = 5;
+  optional string build_version = 6;
+}
+
+message InstallOperation {
+  enum Type {
+    REPLACE = 0;  // Replace destination extents w/ attached data
+    REPLACE_BZ = 1;  // Replace destination extents w/ attached bzipped data
+    MOVE = 2;  // Move source extents to destination extents
+    BSDIFF = 3;  // 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
+
+    // On minor version 3 or newer and on major version 2 or newer, these
+    // operations are supported:
+    ZERO = 6;  // Write zeros in the destination.
+    DISCARD = 7;  // Discard the destination blocks, reading as undefined.
+    REPLACE_XZ = 8; // Replace destination extents w/ attached xz data.
+  }
+  required Type type = 1;
+  // The offset into the delta file (after the protobuf)
+  // where the data (if any) is stored
+  optional uint32 data_offset = 2;
+  // The length of the data in the delta file
+  optional uint32 data_length = 3;
+
+  // Ordered list of extents that are read from (if any) and written to.
+  repeated Extent src_extents = 4;
+  // Byte length of src, equal to the number of blocks in src_extents *
+  // block_size. It is used for BSDIFF, because we need to pass that
+  // external program the number of bytes to read from the blocks we pass it.
+  // This is not used in any other operation.
+  optional uint64 src_length = 5;
+
+  repeated Extent dst_extents = 6;
+  // Byte length of dst, equal to the number of blocks in dst_extents *
+  // block_size. Used for BSDIFF, but not in any other operation.
+  optional uint64 dst_length = 7;
+
+  // Optional SHA 256 hash of the blob associated with this operation.
+  // This is used as a primary validation for http-based downloads and
+  // as a defense-in-depth validation for https-based downloads. If
+  // the operation doesn't refer to any blob, this field will have
+  // zero bytes.
+  optional bytes data_sha256_hash = 8;
+
+  // Indicates the SHA 256 hash of the source data referenced in src_extents at
+  // the time of applying the operation. If present, the update_engine daemon
+  // MUST read and verify the source data before applying the operation.
+  optional bytes src_sha256_hash = 9;
+}
+
+// Describes the update to apply to a single partition.
+message PartitionUpdate {
+  // A platform-specific name to identify the partition set being updated. For
+  // example, in Chrome OS this could be "ROOT" or "KERNEL".
+  required string partition_name = 1;
+
+  // Whether this partition carries a filesystem with post-install program that
+  // must be run to finalize the update process. See also |postinstall_path| and
+  // |filesystem_type|.
+  optional bool run_postinstall = 2;
+
+  // The path of the executable program to run during the post-install step,
+  // relative to the root of this filesystem. If not set, the default "postinst"
+  // will be used. This setting is only used when |run_postinstall| is set and
+  // true.
+  optional string postinstall_path = 3;
+
+  // The filesystem type as passed to the mount(2) syscall when mounting the new
+  // filesystem to run the post-install program. If not set, a fixed list of
+  // filesystems will be attempted. This setting is only used if
+  // |run_postinstall| is set and true.
+  optional string filesystem_type = 4;
+
+  // If present, a list of signatures of the new_partition_info.hash signed with
+  // different keys. If the update_engine daemon requires vendor-signed images
+  // and has its public key installed, one of the signatures should be valid
+  // for /postinstall to run.
+  repeated Signatures.Signature new_partition_signature = 5;
+
+  optional PartitionInfo old_partition_info = 6;
+  optional PartitionInfo new_partition_info = 7;
+
+  // The list of operations to be performed to apply this PartitionUpdate. The
+  // associated operation blobs (in operations[i].data_offset, data_length)
+  // should be stored contiguously and in the same order.
+  repeated InstallOperation operations = 8;
+}
+
+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;
+  repeated InstallOperation kernel_install_operations = 2;
+
+  // (At time of writing) usually 4096
+  optional uint32 block_size = 3 [default = 4096];
+
+  // If signatures are present, the offset into the blobs, generally
+  // tacked onto the end of the file, and the length. We use an offset
+  // rather than a bool to allow for more flexibility in future file formats.
+  // If either is absent, it means signatures aren't supported in this
+  // file.
+  optional uint64 signatures_offset = 4;
+  optional uint64 signatures_size = 5;
+
+  // 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;
+  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;
+
+  optional ImageInfo new_image_info = 11;
+
+  // The minor version, also referred as "delta version", of the payload.
+  optional uint32 minor_version = 12 [default = 0];
+
+  // Only present in major version >= 2. List of partitions that will be
+  // updated, in the order they will be updated. This field replaces the
+  // |install_operations|, |kernel_install_operations| and the
+  // |{old,new}_{kernel,rootfs}_info| fields used in major version = 1. This
+  // array can have more than two partitions if needed, and they are identified
+  // by the partition name.
+  repeated PartitionUpdate partitions = 13;
+}
diff --git a/update_payload_key/README b/update_payload_key/README
new file mode 100644
index 0000000..cf87148
--- /dev/null
+++ b/update_payload_key/README
@@ -0,0 +1,2 @@
+This directory contains the public half of the payload signing key. This is
+baked into the system image as part of update_engine install.
diff --git a/update_payload_key/brillo-update-payload-key.pub.pem b/update_payload_key/brillo-update-payload-key.pub.pem
new file mode 100644
index 0000000..7e5164c
--- /dev/null
+++ b/update_payload_key/brillo-update-payload-key.pub.pem
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxAxPqfII4vIe3cqKzdvl
+gwjBhj9kyF+6ig73yZq0o4wLOq3nsRUToaIOtQmcjr1G+hhSXBU3WTbfZLlm07Fb
+B535o2zhYghs8Br7xobjX+gikEnxnFuTtB2sB4Gpan4hKwU+BuZhJDSl1oZwUJJ4
+eiGJpH5xJswbyO/bA81BCMjU3rm+G6SzOLQTK0YEnhn7bB69UucM57GM7l+dCl8r
+RhKjbpP7E1fVtgX++BGs6pKciPLxYfXVup0MgH0h8VdSDMiHkshIXYvcCV1KOBFX
+9GrYvXLtq41Hm5hC5l48mwLi0ALdIfbPQ5oHLl2u+etLmGwbMpzhybTCZQA/SgEl
+HwIDAQAB
+-----END PUBLIC KEY-----
diff --git a/update_status_utils.cc b/update_status_utils.cc
new file mode 100644
index 0000000..ff039b8
--- /dev/null
+++ b/update_status_utils.cc
@@ -0,0 +1,89 @@
+//
+// 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/update_status_utils.h"
+
+#include <base/logging.h>
+#include <update_engine/dbus-constants.h>
+
+using update_engine::UpdateStatus;
+
+namespace chromeos_update_engine {
+
+const char* UpdateStatusToString(const UpdateStatus& status) {
+  switch (status) {
+    case UpdateStatus::IDLE:
+      return update_engine::kUpdateStatusIdle;
+    case UpdateStatus::CHECKING_FOR_UPDATE:
+      return update_engine::kUpdateStatusCheckingForUpdate;
+    case UpdateStatus::UPDATE_AVAILABLE:
+      return update_engine::kUpdateStatusUpdateAvailable;
+    case UpdateStatus::DOWNLOADING:
+      return update_engine::kUpdateStatusDownloading;
+    case UpdateStatus::VERIFYING:
+      return update_engine::kUpdateStatusVerifying;
+    case UpdateStatus::FINALIZING:
+      return update_engine::kUpdateStatusFinalizing;
+    case UpdateStatus::UPDATED_NEED_REBOOT:
+      return update_engine::kUpdateStatusUpdatedNeedReboot;
+    case UpdateStatus::REPORTING_ERROR_EVENT:
+      return update_engine::kUpdateStatusReportingErrorEvent;
+    case UpdateStatus::ATTEMPTING_ROLLBACK:
+      return update_engine::kUpdateStatusAttemptingRollback;
+    case UpdateStatus::DISABLED:
+      return update_engine::kUpdateStatusDisabled;
+  }
+
+  NOTREACHED();
+  return nullptr;
+}
+
+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::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;
+  }
+  return false;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/update_status_utils.h b/update_status_utils.h
new file mode 100644
index 0000000..30ae53b
--- /dev/null
+++ b/update_status_utils.h
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_UPDATE_STATUS_UTILS_H_
+#define UPDATE_ENGINE_UPDATE_STATUS_UTILS_H_
+
+#include <string>
+
+#include "update_engine/client_library/include/update_engine/update_status.h"
+
+namespace chromeos_update_engine {
+
+const char* UpdateStatusToString(const update_engine::UpdateStatus& status);
+
+bool StringToUpdateStatus(const std::string& update_status_as_string,
+                          update_engine::UpdateStatus* status);
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_UPDATE_STATUS_UTILS_H_