update_engine: Merge remote-tracking branch 'cros/upstream' into cros/master

Done with:
git merge cros/upstream --commit -s recursive

- Added EC key support and its unittests.
- Resolved a conlict on error codes. Since Android versions are not
  uploading any UMA metrics, I gave the priority to the Android version
  Since they can't be changed.
- Changed the openssl functions to get1 version (from get0) version
  because of a current issue with gale. Once the issue is resolved we
  need to change them back.
- Some remaining styling issues fixed by clang-format

BUG=b:163153182
TEST=CQ passes
TEST=unittests

Change-Id: Ib95034422b92433ce26e28336bc4806b34910d38
diff --git a/common/action.h b/common/action.h
index 9e2f5ff..fd82c2d 100644
--- a/common/action.h
+++ b/common/action.h
@@ -222,6 +222,17 @@
       out_pipe_;
 };
 
+// An action that does nothing and completes with kSuccess immediately.
+class NoOpAction : public AbstractAction {
+ public:
+  ~NoOpAction() override {}
+  void PerformAction() override {
+    processor_->ActionComplete(this, ErrorCode::kSuccess);
+  }
+  static std::string StaticType() { return "NoOpAction"; }
+  std::string Type() const override { return StaticType(); }
+};
+
 };  // namespace chromeos_update_engine
 
 #endif  // UPDATE_ENGINE_COMMON_ACTION_H_
diff --git a/common/action_pipe.h b/common/action_pipe.h
index 0c98ee1..4c56812 100644
--- a/common/action_pipe.h
+++ b/common/action_pipe.h
@@ -79,6 +79,8 @@
 
  private:
   ObjectType contents_;
+  // Give unit test access
+  friend class DownloadActionTest;
 
   // The ctor is private. This is because this class should construct itself
   // via the static Bond() method.
diff --git a/common/action_processor.h b/common/action_processor.h
index 735a106..ad98cc9 100644
--- a/common/action_processor.h
+++ b/common/action_processor.h
@@ -89,7 +89,7 @@
   // But this call deletes the action if there no other object has a reference
   // to it, so in that case, the caller should not try to access any of its
   // member variables after this call.
-  void ActionComplete(AbstractAction* actionptr, ErrorCode code);
+  virtual void ActionComplete(AbstractAction* actionptr, ErrorCode code);
 
  private:
   FRIEND_TEST(ActionProcessorTest, ChainActionsTest);
diff --git a/common/boot_control_interface.h b/common/boot_control_interface.h
index 392d785..c93de5c 100644
--- a/common/boot_control_interface.h
+++ b/common/boot_control_interface.h
@@ -25,6 +25,9 @@
 #include <base/callback.h>
 #include <base/macros.h>
 
+#include "update_engine/common/dynamic_partition_control_interface.h"
+#include "update_engine/update_metadata.pb.h"
+
 namespace chromeos_update_engine {
 
 // The abstract boot control interface defines the interaction with the
@@ -35,19 +38,6 @@
  public:
   using Slot = unsigned int;
 
-  struct PartitionMetadata {
-    struct Partition {
-      std::string name;
-      uint64_t size;
-    };
-    struct Group {
-      std::string name;
-      uint64_t size;
-      std::vector<Partition> partitions;
-    };
-    std::vector<Group> groups;
-  };
-
   static const Slot kInvalidSlot = UINT_MAX;
 
   virtual ~BootControlInterface() = default;
@@ -67,9 +57,20 @@
   // 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. In order to access the dynamic partitions in the target slot,
-  // InitPartitionMetadata() must be called (once per payload) prior to calling
-  // this function. On success, returns true and stores the block device in
-  // |device|.
+  // GetDynamicPartitionControl()->PreparePartitionsForUpdate() must be called
+  // (with |update| == true for the first time for a payload, and |false| for
+  // for the rest of the times) prior to calling this function.
+  // The handling may be different based on whether the partition is included
+  // in the update payload. On success, returns true; and stores the block
+  // device in |device|, if the partition is dynamic in |is_dynamic|.
+  virtual bool GetPartitionDevice(const std::string& partition_name,
+                                  Slot slot,
+                                  bool not_in_payload,
+                                  std::string* device,
+                                  bool* is_dynamic) const = 0;
+
+  // Overload of the above function. We assume the partition is always included
+  // in the payload.
   virtual bool GetPartitionDevice(const std::string& partition_name,
                                   Slot slot,
                                   std::string* device) const = 0;
@@ -94,17 +95,11 @@
   // of the operation.
   virtual bool MarkBootSuccessfulAsync(base::Callback<void(bool)> callback) = 0;
 
-  // Initializes the metadata of the underlying partitions for a given |slot|
-  // and sets up the states for accessing dynamic partitions.
-  // |partition_metadata| will be written to the specified |slot| if
-  // |update_metadata| is set.
-  virtual bool InitPartitionMetadata(
-      Slot slot,
-      const PartitionMetadata& partition_metadata,
-      bool update_metadata) = 0;
+  // Check if |slot| is marked boot successfully.
+  virtual bool IsSlotMarkedSuccessful(Slot slot) const = 0;
 
-  // Do necessary clean-up operations after the whole update.
-  virtual void Cleanup() = 0;
+  // Return the dynamic partition control interface.
+  virtual DynamicPartitionControlInterface* GetDynamicPartitionControl() = 0;
 
   // Return a human-readable slot name used for logging.
   static std::string SlotName(Slot slot) {
diff --git a/common/boot_control_stub.cc b/common/boot_control_stub.cc
index 0fe8a98..907f670 100644
--- a/common/boot_control_stub.cc
+++ b/common/boot_control_stub.cc
@@ -15,6 +15,7 @@
 //
 
 #include "update_engine/common/boot_control_stub.h"
+#include "update_engine/common/dynamic_partition_control_stub.h"
 
 #include <base/logging.h>
 
@@ -22,6 +23,9 @@
 
 namespace chromeos_update_engine {
 
+BootControlStub::BootControlStub()
+    : dynamic_partition_control_(new DynamicPartitionControlStub()) {}
+
 unsigned int BootControlStub::GetNumSlots() const {
   return 0;
 }
@@ -31,6 +35,15 @@
   return 0;
 }
 
+bool BootControlStub::GetPartitionDevice(const std::string& partition_name,
+                                         BootControlInterface::Slot slot,
+                                         bool not_in_payload,
+                                         std::string* device,
+                                         bool* is_dynamic) const {
+  LOG(ERROR) << __FUNCTION__ << " should never be called.";
+  return false;
+}
+
 bool BootControlStub::GetPartitionDevice(const string& partition_name,
                                          Slot slot,
                                          string* device) const {
@@ -59,16 +72,14 @@
   return false;
 }
 
-bool BootControlStub::InitPartitionMetadata(
-    Slot slot,
-    const PartitionMetadata& partition_metadata,
-    bool update_metadata) {
+bool BootControlStub::IsSlotMarkedSuccessful(Slot slot) const {
   LOG(ERROR) << __FUNCTION__ << " should never be called.";
   return false;
 }
 
-void BootControlStub::Cleanup() {
-  LOG(ERROR) << __FUNCTION__ << " should never be called.";
+DynamicPartitionControlInterface*
+BootControlStub::GetDynamicPartitionControl() {
+  return dynamic_partition_control_.get();
 }
 
 }  // namespace chromeos_update_engine
diff --git a/common/boot_control_stub.h b/common/boot_control_stub.h
index 8dfaffc..a1bdb96 100644
--- a/common/boot_control_stub.h
+++ b/common/boot_control_stub.h
@@ -17,9 +17,11 @@
 #ifndef UPDATE_ENGINE_COMMON_BOOT_CONTROL_STUB_H_
 #define UPDATE_ENGINE_COMMON_BOOT_CONTROL_STUB_H_
 
+#include <memory>
 #include <string>
 
 #include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/dynamic_partition_control_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -32,25 +34,30 @@
 // implementation is in use.
 class BootControlStub : public BootControlInterface {
  public:
-  BootControlStub() = default;
+  BootControlStub();
   ~BootControlStub() = default;
 
   // BootControlInterface overrides.
   unsigned int GetNumSlots() const override;
   BootControlInterface::Slot GetCurrentSlot() const override;
   bool GetPartitionDevice(const std::string& partition_name,
+                          Slot slot,
+                          bool not_in_payload,
+                          std::string* device,
+                          bool* is_dynamic) const override;
+  bool GetPartitionDevice(const std::string& partition_name,
                           BootControlInterface::Slot slot,
                           std::string* device) const override;
   bool IsSlotBootable(BootControlInterface::Slot slot) const override;
   bool MarkSlotUnbootable(BootControlInterface::Slot slot) override;
   bool SetActiveBootSlot(BootControlInterface::Slot slot) override;
   bool MarkBootSuccessfulAsync(base::Callback<void(bool)> callback) override;
-  bool InitPartitionMetadata(Slot slot,
-                             const PartitionMetadata& partition_metadata,
-                             bool update_metadata) override;
-  void Cleanup() override;
+  bool IsSlotMarkedSuccessful(BootControlInterface::Slot slot) const override;
+  DynamicPartitionControlInterface* GetDynamicPartitionControl() override;
 
  private:
+  std::unique_ptr<DynamicPartitionControlInterface> dynamic_partition_control_;
+
   DISALLOW_COPY_AND_ASSIGN(BootControlStub);
 };
 
diff --git a/common/cleanup_previous_update_action_delegate.h b/common/cleanup_previous_update_action_delegate.h
new file mode 100644
index 0000000..7dad9c5
--- /dev/null
+++ b/common/cleanup_previous_update_action_delegate.h
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_CLEANUP_PREVIOUS_UPDATE_ACTION_DELEGETE_H_
+#define UPDATE_ENGINE_CLEANUP_PREVIOUS_UPDATE_ACTION_DELEGETE_H_
+
+namespace chromeos_update_engine {
+
+// Delegate interface for CleanupPreviousUpdateAction.
+class CleanupPreviousUpdateActionDelegateInterface {
+ public:
+  virtual ~CleanupPreviousUpdateActionDelegateInterface() {}
+  // |progress| is within [0, 1]
+  virtual void OnCleanupProgressUpdate(double progress) = 0;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_CLEANUP_PREVIOUS_UPDATE_ACTION_DELEGETE_H_
diff --git a/common/constants.cc b/common/constants.cc
index ad511d5..8883668 100644
--- a/common/constants.cc
+++ b/common/constants.cc
@@ -110,6 +110,7 @@
 const char kPrefsWallClockScatteringWaitPeriod[] = "wall-clock-wait-period";
 const char kPrefsWallClockStagingWaitPeriod[] =
     "wall-clock-staging-wait-period";
+const char kPrefsManifestBytes[] = "manifest-bytes";
 
 // These four fields are generated by scripts/brillo_update_payload.
 const char kPayloadPropertyFileSize[] = "FILE_SIZE";
@@ -130,8 +131,7 @@
 // Set "SWITCH_SLOT_ON_REBOOT=0" to skip marking the updated partitions active.
 // The default is 1 (always switch slot if update succeeded).
 const char kPayloadPropertySwitchSlotOnReboot[] = "SWITCH_SLOT_ON_REBOOT";
-// Set "RUN_POST_INSTALL=0" to skip running post install, this will only be
-// honored if we're resuming an update and post install has already succeeded.
+// Set "RUN_POST_INSTALL=0" to skip running optional post install.
 // The default is 1 (always run post install).
 const char kPayloadPropertyRunPostInstall[] = "RUN_POST_INSTALL";
 
diff --git a/common/constants.h b/common/constants.h
index 446b147..3685102 100644
--- a/common/constants.h
+++ b/common/constants.h
@@ -105,6 +105,7 @@
 extern const char kPrefsVerityWritten[];
 extern const char kPrefsWallClockScatteringWaitPeriod[];
 extern const char kPrefsWallClockStagingWaitPeriod[];
+extern const char kPrefsManifestBytes[];
 
 // Keys used when storing and loading payload properties.
 extern const char kPayloadPropertyFileSize[];
@@ -215,6 +216,9 @@
 const int kDownloadConnectTimeoutSeconds = 30;
 const int kDownloadP2PConnectTimeoutSeconds = 5;
 
+// Size in bytes of SHA256 hash.
+const int kSHA256Size = 32;
+
 }  // namespace chromeos_update_engine
 
 #endif  // UPDATE_ENGINE_COMMON_CONSTANTS_H_
diff --git a/common/dynamic_partition_control_interface.h b/common/dynamic_partition_control_interface.h
new file mode 100644
index 0000000..7c2d0b0
--- /dev/null
+++ b/common/dynamic_partition_control_interface.h
@@ -0,0 +1,144 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_COMMON_DYNAMIC_PARTITION_CONTROL_INTERFACE_H_
+#define UPDATE_ENGINE_COMMON_DYNAMIC_PARTITION_CONTROL_INTERFACE_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "update_engine/common/action.h"
+#include "update_engine/common/cleanup_previous_update_action_delegate.h"
+#include "update_engine/common/error_code.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+struct FeatureFlag {
+  enum class Value { NONE = 0, RETROFIT, LAUNCH };
+  constexpr explicit FeatureFlag(Value value) : value_(value) {}
+  constexpr bool IsEnabled() const { return value_ != Value::NONE; }
+  constexpr bool IsRetrofit() const { return value_ == Value::RETROFIT; }
+  constexpr bool IsLaunch() const { return value_ == Value::LAUNCH; }
+
+ private:
+  Value value_;
+};
+
+class BootControlInterface;
+class PrefsInterface;
+
+class DynamicPartitionControlInterface {
+ public:
+  virtual ~DynamicPartitionControlInterface() = default;
+
+  // Return the feature flags of dynamic partitions on this device.
+  // Return RETROFIT iff dynamic partitions is retrofitted on this device,
+  //        LAUNCH iff this device is launched with dynamic partitions,
+  //        NONE iff dynamic partitions is disabled on this device.
+  virtual FeatureFlag GetDynamicPartitionsFeatureFlag() = 0;
+
+  // Return the feature flags of Virtual A/B on this device.
+  virtual FeatureFlag GetVirtualAbFeatureFlag() = 0;
+
+  // Attempt to optimize |operation|.
+  // If successful, |optimized| contains an operation with extents that
+  // needs to be written.
+  // If failed, no optimization is available, and caller should perform
+  // |operation| directly.
+  // |partition_name| should not have the slot suffix; implementation of
+  // DynamicPartitionControlInterface checks partition at the target slot
+  // previously set with PreparePartitionsForUpdate().
+  virtual bool OptimizeOperation(const std::string& partition_name,
+                                 const InstallOperation& operation,
+                                 InstallOperation* optimized) = 0;
+
+  // Do necessary cleanups before destroying the object.
+  virtual void Cleanup() = 0;
+
+  // Prepare all partitions for an update specified in |manifest|.
+  // This is needed before calling MapPartitionOnDeviceMapper(), otherwise the
+  // device would be mapped in an inconsistent way.
+  // If |update| is set, create snapshots and writes super partition metadata.
+  // If |required_size| is not null and call fails due to insufficient space,
+  // |required_size| will be set to total free space required on userdata
+  // partition to apply the update. Otherwise (call succeeds, or fails
+  // due to other errors), |required_size| is set to zero.
+  virtual bool PreparePartitionsForUpdate(uint32_t source_slot,
+                                          uint32_t target_slot,
+                                          const DeltaArchiveManifest& manifest,
+                                          bool update,
+                                          uint64_t* required_size) = 0;
+
+  // After writing to new partitions, before rebooting into the new slot, call
+  // this function to indicate writes to new partitions are done.
+  virtual bool FinishUpdate(bool powerwash_required) = 0;
+
+  // Get an action to clean up previous update.
+  // Return NoOpAction on non-Virtual A/B devices.
+  // Before applying the next update, run this action to clean up previous
+  // update files. This function blocks until delta files are merged into
+  // current OS partitions and finished cleaning up.
+  // - If successful, action completes with kSuccess.
+  // - If any error, but caller should retry after reboot, action completes with
+  //   kError.
+  // - If any irrecoverable failures, action completes with kDeviceCorrupted.
+  //
+  // See ResetUpdate for differences between CleanuPreviousUpdateAction and
+  // ResetUpdate.
+  virtual std::unique_ptr<AbstractAction> GetCleanupPreviousUpdateAction(
+      BootControlInterface* boot_control,
+      PrefsInterface* prefs,
+      CleanupPreviousUpdateActionDelegateInterface* delegate) = 0;
+
+  // Called after an unwanted payload has been successfully applied and the
+  // device has not yet been rebooted.
+  //
+  // For snapshot updates (Virtual A/B), it calls
+  // DeltaPerformer::ResetUpdateProgress(false /* quick */) and
+  // frees previously allocated space; the next update will need to be
+  // started over.
+  //
+  // Note: CleanupPreviousUpdateAction does not do anything if an update is in
+  // progress, while ResetUpdate() forcefully free previously
+  // allocated space for snapshot updates.
+  virtual bool ResetUpdate(PrefsInterface* prefs) = 0;
+
+  // Reads the dynamic partitions metadata from the current slot, and puts the
+  // name of the dynamic partitions with the current suffix to |partitions|.
+  // Returns true on success.
+  virtual bool ListDynamicPartitionsForSlot(
+      uint32_t current_slot, std::vector<std::string>* partitions) = 0;
+
+  // Finds a possible location that list all block devices by name; and puts
+  // the result in |path|. Returns true on success.
+  // Sample result: /dev/block/by-name/
+  virtual bool GetDeviceDir(std::string* path) = 0;
+
+  // Verifies that the untouched dynamic partitions in the target metadata have
+  // the same extents as the source metadata.
+  virtual bool VerifyExtentsForUntouchedPartitions(
+      uint32_t source_slot,
+      uint32_t target_slot,
+      const std::vector<std::string>& partitions) = 0;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_DYNAMIC_PARTITION_CONTROL_INTERFACE_H_
diff --git a/common/dynamic_partition_control_stub.cc b/common/dynamic_partition_control_stub.cc
new file mode 100644
index 0000000..5a8ca43
--- /dev/null
+++ b/common/dynamic_partition_control_stub.cc
@@ -0,0 +1,86 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include <base/logging.h>
+
+#include "update_engine/common/dynamic_partition_control_stub.h"
+
+namespace chromeos_update_engine {
+
+FeatureFlag DynamicPartitionControlStub::GetDynamicPartitionsFeatureFlag() {
+  return FeatureFlag(FeatureFlag::Value::NONE);
+}
+
+FeatureFlag DynamicPartitionControlStub::GetVirtualAbFeatureFlag() {
+  return FeatureFlag(FeatureFlag::Value::NONE);
+}
+
+bool DynamicPartitionControlStub::OptimizeOperation(
+    const std::string& partition_name,
+    const InstallOperation& operation,
+    InstallOperation* optimized) {
+  return false;
+}
+
+void DynamicPartitionControlStub::Cleanup() {}
+
+bool DynamicPartitionControlStub::PreparePartitionsForUpdate(
+    uint32_t source_slot,
+    uint32_t target_slot,
+    const DeltaArchiveManifest& manifest,
+    bool update,
+    uint64_t* required_size) {
+  return true;
+}
+
+bool DynamicPartitionControlStub::FinishUpdate(bool powerwash_required) {
+  return true;
+}
+
+std::unique_ptr<AbstractAction>
+DynamicPartitionControlStub::GetCleanupPreviousUpdateAction(
+    BootControlInterface* boot_control,
+    PrefsInterface* prefs,
+    CleanupPreviousUpdateActionDelegateInterface* delegate) {
+  return std::make_unique<NoOpAction>();
+}
+
+bool DynamicPartitionControlStub::ResetUpdate(PrefsInterface* prefs) {
+  return false;
+}
+
+bool DynamicPartitionControlStub::ListDynamicPartitionsForSlot(
+    uint32_t current_slot, std::vector<std::string>* partitions) {
+  return true;
+}
+
+bool DynamicPartitionControlStub::GetDeviceDir(std::string* path) {
+  return true;
+}
+
+bool DynamicPartitionControlStub::VerifyExtentsForUntouchedPartitions(
+    uint32_t source_slot,
+    uint32_t target_slot,
+    const std::vector<std::string>& partitions) {
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/dynamic_partition_control_stub.h b/common/dynamic_partition_control_stub.h
new file mode 100644
index 0000000..94dba1b
--- /dev/null
+++ b/common/dynamic_partition_control_stub.h
@@ -0,0 +1,63 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_COMMON_DYNAMIC_PARTITION_CONTROL_STUB_H_
+#define UPDATE_ENGINE_COMMON_DYNAMIC_PARTITION_CONTROL_STUB_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "update_engine/common/dynamic_partition_control_interface.h"
+
+namespace chromeos_update_engine {
+
+class DynamicPartitionControlStub : public DynamicPartitionControlInterface {
+ public:
+  FeatureFlag GetDynamicPartitionsFeatureFlag() override;
+  FeatureFlag GetVirtualAbFeatureFlag() override;
+  bool OptimizeOperation(const std::string& partition_name,
+                         const InstallOperation& operation,
+                         InstallOperation* optimized) override;
+  void Cleanup() override;
+  bool PreparePartitionsForUpdate(uint32_t source_slot,
+                                  uint32_t target_slot,
+                                  const DeltaArchiveManifest& manifest,
+                                  bool update,
+                                  uint64_t* required_size) override;
+
+  bool FinishUpdate(bool powerwash_required) override;
+  std::unique_ptr<AbstractAction> GetCleanupPreviousUpdateAction(
+      BootControlInterface* boot_control,
+      PrefsInterface* prefs,
+      CleanupPreviousUpdateActionDelegateInterface* delegate) override;
+  bool ResetUpdate(PrefsInterface* prefs) override;
+
+  bool ListDynamicPartitionsForSlot(
+      uint32_t current_slot, std::vector<std::string>* partitions) override;
+  bool GetDeviceDir(std::string* path) override;
+
+  bool VerifyExtentsForUntouchedPartitions(
+      uint32_t source_slot,
+      uint32_t target_slot,
+      const std::vector<std::string>& partitions) override;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_DYNAMIC_PARTITION_CONTROL_STUB_H_
diff --git a/common/error_code.h b/common/error_code.h
index 7acb3b6..7d9cfff 100644
--- a/common/error_code.h
+++ b/common/error_code.h
@@ -83,7 +83,9 @@
   kInternalLibCurlError = 57,
   kUnresolvedHostError = 58,
   kUnresolvedHostRecovered = 59,
-  kPackageExcludedFromUpdate = 60,
+  kNotEnoughSpace = 60,
+  kDeviceCorrupted = 61,
+  kPackageExcludedFromUpdate = 62,
 
   // VERY IMPORTANT! When adding new error codes:
   //
diff --git a/common/error_code_utils.cc b/common/error_code_utils.cc
index 55d876f..cda4c7e 100644
--- a/common/error_code_utils.cc
+++ b/common/error_code_utils.cc
@@ -167,6 +167,10 @@
       return "ErrorCode::kUnresolvedHostError";
     case ErrorCode::kUnresolvedHostRecovered:
       return "ErrorCode::kUnresolvedHostRecovered";
+    case ErrorCode::kNotEnoughSpace:
+      return "ErrorCode::kNotEnoughSpace";
+    case ErrorCode::kDeviceCorrupted:
+      return "ErrorCode::kDeviceCorrupted";
     case ErrorCode::kPackageExcludedFromUpdate:
       return "ErrorCode::kPackageExcludedFromUpdate";
       // Don't add a default case to let the compiler warn about newly added
diff --git a/common/fake_boot_control.h b/common/fake_boot_control.h
index 3d65075..98b93e6 100644
--- a/common/fake_boot_control.h
+++ b/common/fake_boot_control.h
@@ -18,12 +18,14 @@
 #define UPDATE_ENGINE_COMMON_FAKE_BOOT_CONTROL_H_
 
 #include <map>
+#include <memory>
 #include <string>
 #include <vector>
 
 #include <base/time/time.h>
 
 #include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/dynamic_partition_control_stub.h"
 
 namespace chromeos_update_engine {
 
@@ -34,6 +36,8 @@
     SetNumSlots(num_slots_);
     // The current slot should be bootable.
     is_bootable_[current_slot_] = true;
+
+    dynamic_partition_control_.reset(new DynamicPartitionControlStub());
   }
 
   // BootControlInterface overrides.
@@ -44,16 +48,27 @@
 
   bool GetPartitionDevice(const std::string& partition_name,
                           BootControlInterface::Slot slot,
-                          std::string* device) const override {
+                          bool not_in_payload,
+                          std::string* device,
+                          bool* is_dynamic) 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;
+    if (is_dynamic != nullptr) {
+      *is_dynamic = false;
+    }
     return true;
   }
 
+  bool GetPartitionDevice(const std::string& partition_name,
+                          BootControlInterface::Slot slot,
+                          std::string* device) const override {
+    return GetPartitionDevice(partition_name, slot, false, device, nullptr);
+  }
+
   bool IsSlotBootable(BootControlInterface::Slot slot) const override {
     return slot < num_slots_ && is_bootable_[slot];
   }
@@ -70,22 +85,20 @@
   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.
+    is_marked_successful_[GetCurrentSlot()] = true;
     callback.Run(true);
     return true;
   }
 
-  bool InitPartitionMetadata(Slot slot,
-                             const PartitionMetadata& partition_metadata,
-                             bool update_metadata) override {
-    return true;
+  bool IsSlotMarkedSuccessful(Slot slot) const override {
+    return slot < num_slots_ && is_marked_successful_[slot];
   }
 
-  void Cleanup() override {}
-
   // Setters
   void SetNumSlots(unsigned int num_slots) {
     num_slots_ = num_slots;
     is_bootable_.resize(num_slots_, false);
+    is_marked_successful_.resize(num_slots_, false);
     devices_.resize(num_slots_);
   }
 
@@ -103,13 +116,20 @@
     is_bootable_[slot] = bootable;
   }
 
+  DynamicPartitionControlInterface* GetDynamicPartitionControl() override {
+    return dynamic_partition_control_.get();
+  }
+
  private:
   BootControlInterface::Slot num_slots_{2};
   BootControlInterface::Slot current_slot_{0};
 
   std::vector<bool> is_bootable_;
+  std::vector<bool> is_marked_successful_;
   std::vector<std::map<std::string, std::string>> devices_;
 
+  std::unique_ptr<DynamicPartitionControlInterface> dynamic_partition_control_;
+
   DISALLOW_COPY_AND_ASSIGN(FakeBootControl);
 };
 
diff --git a/common/fake_hardware.h b/common/fake_hardware.h
index 6604534..82382ff 100644
--- a/common/fake_hardware.h
+++ b/common/fake_hardware.h
@@ -19,10 +19,13 @@
 
 #include <map>
 #include <string>
+#include <utility>
 
 #include <base/time/time.h>
 
+#include "update_engine/common/error_code.h"
 #include "update_engine/common/hardware_interface.h"
+#include "update_engine/common/utils.h"
 
 namespace chromeos_update_engine {
 
@@ -132,6 +135,8 @@
 
   int64_t GetBuildTimestamp() const override { return build_timestamp_; }
 
+  bool AllowDowngrade() const override { return false; }
+
   bool GetFirstActiveOmahaPingSent() const override {
     return first_active_omaha_ping_sent_;
   }
@@ -197,12 +202,27 @@
     build_timestamp_ = build_timestamp;
   }
 
+  void SetWarmReset(bool warm_reset) override { warm_reset_ = warm_reset; }
+
   // Getters to verify state.
   int GetMaxKernelKeyRollforward() const { return kernel_max_rollforward_; }
 
   bool GetIsRollbackPowerwashScheduled() const {
     return powerwash_scheduled_ && save_rollback_data_;
   }
+  std::string GetVersionForLogging(
+      const std::string& partition_name) const override {
+    return partition_timestamps_[partition_name];
+  }
+  void SetVersion(const std::string& partition_name, std::string timestamp) {
+    partition_timestamps_[partition_name] = std::move(timestamp);
+  }
+  ErrorCode IsPartitionUpdateValid(
+      const std::string& partition_name,
+      const std::string& new_version) const override {
+    const auto old_version = GetVersionForLogging(partition_name);
+    return utils::IsTimestampNewer(old_version, new_version);
+  }
 
  private:
   bool is_official_build_{true};
@@ -225,6 +245,8 @@
   bool save_rollback_data_{false};
   int64_t build_timestamp_{0};
   bool first_active_omaha_ping_sent_{false};
+  bool warm_reset_{false};
+  mutable std::map<std::string, std::string> partition_timestamps_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeHardware);
 };
diff --git a/common/file_fetcher.cc b/common/file_fetcher.cc
index 3836e54..7134fd6 100644
--- a/common/file_fetcher.cc
+++ b/common/file_fetcher.cc
@@ -43,8 +43,9 @@
 // static
 bool FileFetcher::SupportedUrl(const string& url) {
   // Note that we require the file path to start with a "/".
-  return base::StartsWith(
-      url, "file:///", base::CompareCase::INSENSITIVE_ASCII);
+  return (
+      base::StartsWith(url, "file:///", base::CompareCase::INSENSITIVE_ASCII) ||
+      base::StartsWith(url, "fd://", base::CompareCase::INSENSITIVE_ASCII));
 }
 
 FileFetcher::~FileFetcher() {
@@ -67,12 +68,20 @@
     return;
   }
 
-  string file_path = url.substr(strlen("file://"));
-  stream_ =
-      brillo::FileStream::Open(base::FilePath(file_path),
-                               brillo::Stream::AccessMode::READ,
-                               brillo::FileStream::Disposition::OPEN_EXISTING,
-                               nullptr);
+  string file_path;
+
+  if (base::StartsWith(url, "fd://", base::CompareCase::INSENSITIVE_ASCII)) {
+    int fd = std::stoi(url.substr(strlen("fd://")));
+    file_path = url;
+    stream_ = brillo::FileStream::FromFileDescriptor(fd, false, nullptr);
+  } else {
+    file_path = url.substr(strlen("file://"));
+    stream_ =
+        brillo::FileStream::Open(base::FilePath(file_path),
+                                 brillo::Stream::AccessMode::READ,
+                                 brillo::FileStream::Disposition::OPEN_EXISTING,
+                                 nullptr);
+  }
 
   if (!stream_) {
     LOG(ERROR) << "Couldn't open " << file_path;
@@ -183,5 +192,4 @@
   transfer_in_progress_ = false;
   transfer_paused_ = false;
 }
-
 }  // namespace chromeos_update_engine
diff --git a/common/hardware_interface.h b/common/hardware_interface.h
index da9f10e..b37b007 100644
--- a/common/hardware_interface.h
+++ b/common/hardware_interface.h
@@ -25,6 +25,8 @@
 #include <base/files/file_path.h>
 #include <base/time/time.h>
 
+#include "update_engine/common/error_code.h"
+
 namespace chromeos_update_engine {
 
 // The hardware interface allows access to the crossystem exposed properties,
@@ -126,6 +128,10 @@
   // Returns the timestamp of the current OS build.
   virtual int64_t GetBuildTimestamp() const = 0;
 
+  // Returns true if the current OS build allows installing the payload with an
+  // older timestamp.
+  virtual bool AllowDowngrade() const = 0;
+
   // Returns whether the first active ping was sent to Omaha at some point, and
   // that the value is persisted across recovery (and powerwash) once set with
   // |SetFirstActiveOmahaPingSent()|.
@@ -134,6 +140,30 @@
   // Persist the fact that first active ping was sent to omaha and returns false
   // if failed to persist it.
   virtual bool SetFirstActiveOmahaPingSent() = 0;
+
+  // If |warm_reset| is true, sets the warm reset to indicate a warm reset is
+  // needed on the next reboot. Otherwise, clears the flag.
+  virtual void SetWarmReset(bool warm_reset) = 0;
+
+  // Return the version/timestamp for partition `partition_name`.
+  // Don't make any assumption about the formatting of returned string.
+  // Only used for logging/debugging purposes.
+  virtual std::string GetVersionForLogging(
+      const std::string& partition_name) const = 0;
+
+  // Return true if and only if `new_version` is "newer" than the
+  // version number of partition `partition_name`. The notion of
+  // "newer" is defined by this function. Caller should not make
+  // any assumption about the underlying logic.
+  // Return:
+  // - kSuccess if update is valid.
+  // - kPayloadTimestampError if downgrade is detected
+  // - kDownloadManifestParseError if |new_version| has an incorrect format
+  // - Other error values if the source of error is known, or kError for
+  //   a generic error on the device.
+  virtual ErrorCode IsPartitionUpdateValid(
+      const std::string& partition_name,
+      const std::string& new_version) const = 0;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/common/http_fetcher_unittest.cc b/common/http_fetcher_unittest.cc
index 3ecb996..9338087 100644
--- a/common/http_fetcher_unittest.cc
+++ b/common/http_fetcher_unittest.cc
@@ -37,7 +37,11 @@
 #include <brillo/message_loops/base_message_loop.h>
 #include <brillo/message_loops/message_loop.h>
 #include <brillo/message_loops/message_loop_utils.h>
+#ifdef __CHROMEOS__
 #include <brillo/process/process.h>
+#else
+#include <brillo/process.h>
+#endif  // __CHROMEOS__
 #include <brillo/streams/file_stream.h>
 #include <brillo/streams/stream.h>
 #include <gtest/gtest.h>
diff --git a/common/mock_action_processor.h b/common/mock_action_processor.h
index 4c62109..9785776 100644
--- a/common/mock_action_processor.h
+++ b/common/mock_action_processor.h
@@ -32,6 +32,8 @@
   MOCK_METHOD0(StartProcessing, void());
   MOCK_METHOD1(EnqueueAction, void(AbstractAction* action));
 
+  MOCK_METHOD2(ActionComplete, void(AbstractAction*, ErrorCode));
+
   // This is a legacy workaround described in:
   // https://github.com/google/googletest/blob/master/googlemock/docs/CookBook.md#legacy-workarounds-for-move-only-types-legacymoveonly
   void EnqueueAction(std::unique_ptr<AbstractAction> action) override {
diff --git a/common/mock_http_fetcher.cc b/common/mock_http_fetcher.cc
index 10e3b9e..1b3cd7d 100644
--- a/common/mock_http_fetcher.cc
+++ b/common/mock_http_fetcher.cc
@@ -22,6 +22,7 @@
 #include <base/logging.h>
 #include <base/strings/string_util.h>
 #include <base/time/time.h>
+#include <brillo/message_loops/message_loop.h>
 #include <gtest/gtest.h>
 
 // This is a mock implementation of HttpFetcher which is useful for testing.
@@ -43,12 +44,12 @@
     SignalTransferComplete();
     return;
   }
-  if (sent_size_ < data_.size())
+  if (sent_offset_ < data_.size())
     SendData(true);
 }
 
 void MockHttpFetcher::SendData(bool skip_delivery) {
-  if (fail_transfer_ || sent_size_ == data_.size()) {
+  if (fail_transfer_ || sent_offset_ == data_.size()) {
     SignalTransferComplete();
     return;
   }
@@ -60,19 +61,22 @@
 
   // Setup timeout callback even if the transfer is about to be completed in
   // order to get a call to |TransferComplete|.
-  if (timeout_id_ == MessageLoop::kTaskIdNull) {
+  if (timeout_id_ == MessageLoop::kTaskIdNull && delay_) {
+    CHECK(MessageLoop::current());
     timeout_id_ = MessageLoop::current()->PostDelayedTask(
         FROM_HERE,
         base::Bind(&MockHttpFetcher::TimeoutCallback, base::Unretained(this)),
         base::TimeDelta::FromMilliseconds(10));
   }
 
-  if (!skip_delivery) {
+  if (!skip_delivery || !delay_) {
     const size_t chunk_size =
-        min(kMockHttpFetcherChunkSize, data_.size() - sent_size_);
-    sent_size_ += chunk_size;
+        min(kMockHttpFetcherChunkSize, data_.size() - sent_offset_);
+    sent_offset_ += chunk_size;
+    bytes_sent_ += chunk_size;
     CHECK(delegate_);
-    delegate_->ReceivedBytes(this, &data_[sent_size_ - chunk_size], chunk_size);
+    delegate_->ReceivedBytes(
+        this, &data_[sent_offset_ - chunk_size], chunk_size);
   }
   // We may get terminated and deleted right after |ReceivedBytes| call, so we
   // should not access any class member variable after this call.
@@ -81,7 +85,7 @@
 void MockHttpFetcher::TimeoutCallback() {
   CHECK(!paused_);
   timeout_id_ = MessageLoop::kTaskIdNull;
-  CHECK_LE(sent_size_, data_.size());
+  CHECK_LE(sent_offset_, data_.size());
   // Same here, we should not access any member variable after this call.
   SendData(false);
 }
@@ -90,10 +94,15 @@
 // The transfer cannot be resumed.
 void MockHttpFetcher::TerminateTransfer() {
   LOG(INFO) << "Terminating transfer.";
-  // Kill any timeout, it is ok to call with kTaskIdNull.
-  MessageLoop::current()->CancelTask(timeout_id_);
-  timeout_id_ = MessageLoop::kTaskIdNull;
-  delegate_->TransferTerminated(this);
+  // During testing, MessageLoop may or may not be available.
+  // So don't call CancelTask() unless necessary.
+  if (timeout_id_ != MessageLoop::kTaskIdNull) {
+    MessageLoop::current()->CancelTask(timeout_id_);
+    timeout_id_ = MessageLoop::kTaskIdNull;
+  }
+  if (delegate_) {
+    delegate_->TransferTerminated(this);
+  }
 }
 
 void MockHttpFetcher::SetHeader(const std::string& header_name,
diff --git a/common/mock_http_fetcher.h b/common/mock_http_fetcher.h
index 0f04319..ea5b83d 100644
--- a/common/mock_http_fetcher.h
+++ b/common/mock_http_fetcher.h
@@ -46,7 +46,7 @@
                   size_t size,
                   ProxyResolver* proxy_resolver)
       : HttpFetcher(proxy_resolver),
-        sent_size_(0),
+        sent_offset_(0),
         timeout_id_(brillo::MessageLoop::kTaskIdNull),
         paused_(false),
         fail_transfer_(false),
@@ -64,7 +64,7 @@
 
   // Ignores this.
   void SetOffset(off_t offset) override {
-    sent_size_ = offset;
+    sent_offset_ = offset;
     if (delegate_)
       delegate_->SeekToOffset(offset);
   }
@@ -76,8 +76,8 @@
   void set_connect_timeout(int connect_timeout_seconds) override {}
   void set_max_retry_count(int max_retry_count) override {}
 
-  // Dummy: no bytes were downloaded.
-  size_t GetBytesDownloaded() override { return sent_size_; }
+  // No bytes were downloaded in the mock class.
+  size_t GetBytesDownloaded() override { return bytes_sent_; }
 
   // Begins the transfer if it hasn't already begun.
   void BeginTransfer(const std::string& url) override;
@@ -113,6 +113,8 @@
 
   const brillo::Blob& post_data() const { return post_data_; }
 
+  void set_delay(bool delay) { delay_ = delay; }
+
  private:
   // Sends data to the delegate and sets up a timeout callback if needed. There
   // must be a delegate. If |skip_delivery| is true, no bytes will be delivered,
@@ -129,8 +131,11 @@
   // 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 current offset, marks the first byte that will be sent next
+  size_t sent_offset_{0};
+
+  // Total number of bytes transferred
+  size_t bytes_sent_{0};
 
   // The extra headers set.
   std::map<std::string, std::string> extra_headers_;
@@ -140,13 +145,16 @@
   brillo::MessageLoop::TaskId timeout_id_;
 
   // True iff the fetcher is paused.
-  bool paused_;
+  bool paused_{false};
 
   // Set to true if the transfer should fail.
-  bool fail_transfer_;
+  bool fail_transfer_{false};
 
   // Set to true if BeginTransfer should EXPECT fail.
-  bool never_use_;
+  bool never_use_{false};
+
+  // Whether it should wait for 10ms before sending data to delegates
+  bool delay_{true};
 
   DISALLOW_COPY_AND_ASSIGN(MockHttpFetcher);
 };
diff --git a/common/platform_constants.h b/common/platform_constants.h
index 6eaa940..243af69 100644
--- a/common/platform_constants.h
+++ b/common/platform_constants.h
@@ -38,6 +38,10 @@
 // whole payload.
 extern const char kUpdatePayloadPublicKeyPath[];
 
+// Path to the location of the zip archive file that contains PEM encoded X509
+// certificates. e.g. 'system/etc/security/otacerts.zip'.
+extern const char kUpdateCertificatesPath[];
+
 // 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).
diff --git a/common/platform_constants_android.cc b/common/platform_constants_android.cc
index 9d8d30e..f468c3b 100644
--- a/common/platform_constants_android.cc
+++ b/common/platform_constants_android.cc
@@ -25,8 +25,8 @@
     "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 kUpdatePayloadPublicKeyPath[] = "";
+const char kUpdateCertificatesPath[] = "/system/etc/security/otacerts.zip";
 const char kCACertificatesPath[] = "/system/etc/security/cacerts_google";
 // No deadline file API support on Android.
 const char kOmahaResponseDeadlineFile[] = "";
diff --git a/common/platform_constants_chromeos.cc b/common/platform_constants_chromeos.cc
index f1ac490..fe94a45 100644
--- a/common/platform_constants_chromeos.cc
+++ b/common/platform_constants_chromeos.cc
@@ -27,6 +27,7 @@
 const char kOmahaPlatformName[] = "Chrome OS";
 const char kUpdatePayloadPublicKeyPath[] =
     "/usr/share/update_engine/update-payload-key.pub.pem";
+const char kUpdateCertificatesPath[] = "";
 const char kCACertificatesPath[] = "/usr/share/chromeos-ca-certificates";
 const char kOmahaResponseDeadlineFile[] = "/tmp/update-check-response-deadline";
 // This directory is wiped during powerwash.
diff --git a/common/subprocess.cc b/common/subprocess.cc
index ff37472..023017b 100644
--- a/common/subprocess.cc
+++ b/common/subprocess.cc
@@ -32,11 +32,11 @@
 #include <base/stl_util.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
-#include <brillo/process/process.h>
 #include <brillo/secure_blob.h>
 
 #include "update_engine/common/utils.h"
 
+using brillo::MessageLoop;
 using std::string;
 using std::unique_ptr;
 using std::vector;
diff --git a/common/subprocess.h b/common/subprocess.h
index e1a7ce3..179a5c5 100644
--- a/common/subprocess.h
+++ b/common/subprocess.h
@@ -30,8 +30,13 @@
 #include <base/macros.h>
 #include <brillo/asynchronous_signal_handler_interface.h>
 #include <brillo/message_loops/message_loop.h>
+#ifdef __CHROMEOS__
 #include <brillo/process/process.h>
 #include <brillo/process/process_reaper.h>
+#else
+#include <brillo/process.h>
+#include <brillo/process_reaper.h>
+#endif  // __CHROMEOS__
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
 // The Subprocess class is a singleton. It's used to spawn off a subprocess
@@ -124,6 +129,7 @@
     // These are used to monitor the stdout of the running process, including
     // the stderr if it was redirected.
     std::unique_ptr<base::FileDescriptorWatcher::Controller> stdout_controller;
+
     int stdout_fd{-1};
     std::string stdout;
   };
diff --git a/common/subprocess_unittest.cc b/common/subprocess_unittest.cc
index 19b24f4..b4d068f 100644
--- a/common/subprocess_unittest.cc
+++ b/common/subprocess_unittest.cc
@@ -279,7 +279,7 @@
   // 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(120), base::Bind([] {
+      &loop_, TimeDelta::FromSeconds(20), base::Bind([] {
         return Subprocess::Get().subprocess_records_.empty();
       }));
   EXPECT_TRUE(Subprocess::Get().subprocess_records_.empty());
diff --git a/common/test_utils.cc b/common/test_utils.cc
index 50b0962..bd69d03 100644
--- a/common/test_utils.cc
+++ b/common/test_utils.cc
@@ -37,6 +37,10 @@
 #include <base/files/file_util.h>
 #include <base/logging.h>
 
+#ifdef __ANDROID__
+#include <libdm/loop_control.h>
+#endif
+
 #include "update_engine/common/error_code_utils.h"
 #include "update_engine/common/utils.h"
 #include "update_engine/payload_consumer/file_writer.h"
@@ -44,16 +48,7 @@
 using std::set;
 using std::string;
 using std::vector;
-
-namespace {
-
-#ifdef __ANDROID__
-#define kLoopDevicePrefix "/dev/block/loop"
-#else
-#define kLoopDevicePrefix "/dev/loop"
-#endif  // __ANDROID__
-
-}  // namespace
+using namespace std::chrono_literals;
 
 namespace chromeos_update_engine {
 
@@ -112,17 +107,43 @@
   return utils::WriteFile(path.c_str(), data.data(), data.size());
 }
 
-bool BindToUnusedLoopDevice(const string& filename,
-                            bool writable,
-                            string* out_lo_dev_name) {
-  CHECK(out_lo_dev_name);
+bool SetLoopDeviceStatus(int loop_device_fd,
+                         const std::string& filename,
+                         int loop_number,
+                         bool writable) {
+  struct loop_info64 device_info {};
+  device_info.lo_offset = 0;
+  device_info.lo_sizelimit = 0;  // 0 means whole file.
+  device_info.lo_flags = (writable ? 0 : LO_FLAGS_READ_ONLY);
+  device_info.lo_number = loop_number;
+  strncpy(reinterpret_cast<char*>(device_info.lo_file_name),
+          base::FilePath(filename).BaseName().value().c_str(),
+          LO_NAME_SIZE - 1);
+  device_info.lo_file_name[LO_NAME_SIZE - 1] = '\0';
+  TEST_AND_RETURN_FALSE_ERRNO(
+      ioctl(loop_device_fd, LOOP_SET_STATUS64, &device_info) == 0);
+  if (writable) {
+    // Make sure loop device isn't read only.
+    int ro = 0;
+    if (ioctl(loop_device_fd, BLKROSET, &ro) != 0) {
+      PLOG(WARNING) << "Failed to mark loop device writable.";
+    }
+  }
+
+  return true;
+}
+
+bool BindToUnusedLoopDeviceLegacy(int data_fd,
+                                  const string& filename,
+                                  bool writable,
+                                  string* out_lo_dev_name) {
   // Get the next available loop-device.
   int control_fd =
       HANDLE_EINTR(open("/dev/loop-control", O_RDWR | O_LARGEFILE));
   TEST_AND_RETURN_FALSE_ERRNO(control_fd >= 0);
   int loop_number = ioctl(control_fd, LOOP_CTL_GET_FREE);
   IGNORE_EINTR(close(control_fd));
-  *out_lo_dev_name = kLoopDevicePrefix + std::to_string(loop_number);
+  *out_lo_dev_name = "/dev/loop" + std::to_string(loop_number);
 
   // Double check that the loop exists and is free.
   int loop_device_fd =
@@ -146,32 +167,35 @@
     return false;
   }
 
-  // Open our data file and assign it to the loop device.
+  // Assign the data fd to the loop device.
+  TEST_AND_RETURN_FALSE_ERRNO(ioctl(loop_device_fd, LOOP_SET_FD, data_fd) == 0);
+  return SetLoopDeviceStatus(loop_device_fd, filename, loop_number, writable);
+}
+
+bool BindToUnusedLoopDevice(const string& filename,
+                            bool writable,
+                            string* out_lo_dev_name) {
+  CHECK(out_lo_dev_name);
   int data_fd = open(filename.c_str(),
                      (writable ? O_RDWR : O_RDONLY) | O_LARGEFILE | O_CLOEXEC);
   TEST_AND_RETURN_FALSE_ERRNO(data_fd >= 0);
   ScopedFdCloser data_fd_closer(&data_fd);
-  TEST_AND_RETURN_FALSE_ERRNO(ioctl(loop_device_fd, LOOP_SET_FD, data_fd) == 0);
 
-  memset(&device_info, 0, sizeof(device_info));
-  device_info.lo_offset = 0;
-  device_info.lo_sizelimit = 0;  // 0 means whole file.
-  device_info.lo_flags = (writable ? 0 : LO_FLAGS_READ_ONLY);
-  device_info.lo_number = loop_number;
-  strncpy(reinterpret_cast<char*>(device_info.lo_file_name),
-          base::FilePath(filename).BaseName().value().c_str(),
-          LO_NAME_SIZE - 1);
-  device_info.lo_file_name[LO_NAME_SIZE - 1] = '\0';
-  TEST_AND_RETURN_FALSE_ERRNO(
-      ioctl(loop_device_fd, LOOP_SET_STATUS64, &device_info) == 0);
-  if (writable) {
-    // Make sure loop device isn't read only.
-    int ro = 0;
-    if (ioctl(loop_device_fd, BLKROSET, &ro) != 0) {
-      PLOG(WARNING) << "Failed to mark loop device writable.";
-    }
-  }
-  return true;
+#ifdef __ANDROID__
+  // Use libdm to bind a free loop device. The library internally handles the
+  // race condition.
+  android::dm::LoopControl loop_control;
+  TEST_AND_RETURN_FALSE(loop_control.Attach(data_fd, 5s, out_lo_dev_name));
+  int loop_device_fd = open(out_lo_dev_name->c_str(), O_RDWR | O_CLOEXEC);
+  ScopedFdCloser loop_fd_closer(&loop_device_fd);
+  int loop_number;
+  TEST_AND_RETURN_FALSE(
+      sscanf(out_lo_dev_name->c_str(), "/dev/block/loop%d", &loop_number) == 1);
+  return SetLoopDeviceStatus(loop_device_fd, filename, loop_number, writable);
+#else
+  return BindToUnusedLoopDeviceLegacy(
+      data_fd, filename, writable, out_lo_dev_name);
+#endif
 }
 
 bool UnbindLoopDevice(const string& lo_dev_name) {
diff --git a/common/utils.cc b/common/utils.cc
index 50b45fa..9e1e6c5 100644
--- a/common/utils.cc
+++ b/common/utils.cc
@@ -30,6 +30,7 @@
 #include <sys/resource.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <time.h>
 #include <unistd.h>
 
 #include <algorithm>
@@ -819,7 +820,7 @@
   return base_code;
 }
 
-string StringVectorToString(const vector<string> &vec_str) {
+string StringVectorToString(const vector<string>& vec_str) {
   string str = "[";
   for (vector<string>::const_iterator i = vec_str.begin(); i != vec_str.end();
        ++i) {
@@ -848,7 +849,7 @@
                             encoded_hash.c_str());
 }
 
-bool ConvertToOmahaInstallDate(Time time, int *out_num_days) {
+bool ConvertToOmahaInstallDate(Time time, int* out_num_days) {
   time_t unix_time = time.ToTimeT();
   // Output of: date +"%s" --date="Jan 1, 2007 0:00 PST".
   const time_t kOmahaEpoch = 1167638400;
@@ -978,10 +979,58 @@
   }
 }
 
+string GetFilePath(int fd) {
+  base::FilePath proc("/proc/self/fd/" + std::to_string(fd));
+  base::FilePath file_name;
+
+  if (!base::ReadSymbolicLink(proc, &file_name)) {
+    return "not found";
+  }
+  return file_name.value();
+}
+
+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 GetExclusionName(const string& str_to_convert) {
   return base::NumberToString(base::StringPieceHash()(str_to_convert));
 }
 
+static bool ParseTimestamp(const std::string& str, int64_t* out) {
+  if (!base::StringToInt64(str, out)) {
+    LOG(WARNING) << "Invalid timestamp: " << str;
+    return false;
+  }
+  return true;
+}
+
+ErrorCode IsTimestampNewer(const std::string& old_version,
+                           const std::string& new_version) {
+  if (old_version.empty() || new_version.empty()) {
+    LOG(WARNING)
+        << "One of old/new timestamp is empty, permit update anyway. Old: "
+        << old_version << " New: " << new_version;
+    return ErrorCode::kSuccess;
+  }
+  int64_t old_ver = 0;
+  if (!ParseTimestamp(old_version, &old_ver)) {
+    return ErrorCode::kError;
+  }
+  int64_t new_ver = 0;
+  if (!ParseTimestamp(new_version, &new_ver)) {
+    return ErrorCode::kDownloadManifestParseError;
+  }
+  if (old_ver > new_ver) {
+    return ErrorCode::kPayloadTimestampError;
+  }
+  return ErrorCode::kSuccess;
+}
+
 }  // namespace utils
 
 }  // namespace chromeos_update_engine
diff --git a/common/utils.h b/common/utils.h
index b6880ed..bcaed31 100644
--- a/common/utils.h
+++ b/common/utils.h
@@ -18,6 +18,7 @@
 #define UPDATE_ENGINE_COMMON_UTILS_H_
 
 #include <errno.h>
+#include <time.h>
 #include <unistd.h>
 
 #include <algorithm>
@@ -295,6 +296,9 @@
 // shell command. Returns true on success.
 bool GetVpdValue(std::string key, std::string* result);
 
+// This function gets the file path of the file pointed to by FileDiscriptor.
+std::string GetFilePath(int fd);
+
 // Divide |x| by |y| and round up to the nearest integer.
 constexpr uint64_t DivRoundUp(uint64_t x, uint64_t y) {
   return (x + y - 1) / y;
@@ -317,10 +321,23 @@
                              uint16_t* high_version,
                              uint16_t* low_version);
 
+// Return a string representation of |utime| for log file names.
+std::string GetTimeAsString(time_t utime);
 // Returns the string format of the hashed |str_to_convert| that can be used
 // with |Excluder| as the exclusion name.
 std::string GetExclusionName(const std::string& str_to_convert);
 
+// Parse `old_version` and `new_version` as integer timestamps and
+// Return kSuccess if `new_version` is larger/newer.
+// Return kSuccess if either one is empty.
+// Return kError if |old_version| is not empty and not an integer.
+// Return kDownloadManifestParseError if |new_version| is not empty and not an
+// integer.
+// Return kPayloadTimestampError if both are integers but |new_version| <
+// |old_version|.
+ErrorCode IsTimestampNewer(const std::string& old_version,
+                           const std::string& new_version);
+
 }  // namespace utils
 
 // Utility class to close a file descriptor
diff --git a/common/utils_unittest.cc b/common/utils_unittest.cc
index f9eb596..d73b3da 100644
--- a/common/utils_unittest.cc
+++ b/common/utils_unittest.cc
@@ -472,4 +472,23 @@
   ExpectInvalidParseRollbackKeyVersion("1.99999");
 }
 
+TEST(UtilsTest, GetFilePathTest) {
+  test_utils::ScopedTempFile file;
+  int fd = HANDLE_EINTR(open(file.path().c_str(), O_RDONLY));
+  EXPECT_GE(fd, 0);
+  EXPECT_EQ(file.path(), utils::GetFilePath(fd));
+  EXPECT_EQ("not found", utils::GetFilePath(-1));
+  IGNORE_EINTR(close(fd));
+}
+
+TEST(UtilsTest, ValidatePerPartitionTimestamp) {
+  ASSERT_EQ(ErrorCode::kPayloadTimestampError,
+            utils::IsTimestampNewer("10", "5"));
+  ASSERT_EQ(ErrorCode::kSuccess, utils::IsTimestampNewer("10", "11"));
+  ASSERT_EQ(ErrorCode::kDownloadManifestParseError,
+            utils::IsTimestampNewer("10", "lol"));
+  ASSERT_EQ(ErrorCode::kError, utils::IsTimestampNewer("lol", "ZZZ"));
+  ASSERT_EQ(ErrorCode::kSuccess, utils::IsTimestampNewer("10", ""));
+}
+
 }  // namespace chromeos_update_engine