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

The following change is reverted because aosp has newer libchrome.
71818c84 Partially Revert 2b9d241

Added stub override for ReportInternalErrorCode().
Fixed RunPosinstallAction typo.

Bug: 112326236
Test: update_engine_unittests
Change-Id: Ieaae0eef425cbb1278067a48aa19b14ed056317a
diff --git a/Android.mk b/Android.mk
index d1d8488..08492c0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -661,6 +661,7 @@
     payload_generator/annotated_operation.cc \
     payload_generator/blob_file_writer.cc \
     payload_generator/block_mapping.cc \
+    payload_generator/boot_img_filesystem.cc \
     payload_generator/bzip.cc \
     payload_generator/cycle_breaker.cc \
     payload_generator/deflate_utils.cc \
@@ -962,6 +963,7 @@
     payload_generator/ab_generator_unittest.cc \
     payload_generator/blob_file_writer_unittest.cc \
     payload_generator/block_mapping_unittest.cc \
+    payload_generator/boot_img_filesystem_unittest.cc \
     payload_generator/cycle_breaker_unittest.cc \
     payload_generator/deflate_utils_unittest.cc \
     payload_generator/delta_diff_utils_unittest.cc \
@@ -1048,7 +1050,6 @@
 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)
 endif  # PRODUCT_IOT
 
diff --git a/common/error_code.h b/common/error_code.h
index 0d86a7b..86b7a3e 100644
--- a/common/error_code.h
+++ b/common/error_code.h
@@ -74,7 +74,7 @@
   kUserCanceled = 48,
   kNonCriticalUpdateInOOBE = 49,
   kOmahaUpdateIgnoredOverCellular = 50,
-  // kPayloadTimestampError = 51,
+  kPayloadTimestampError = 51,
   kUpdatedButNotActive = 52,
   kNoUpdate = 53,
   kRollbackNotPossible = 54,
diff --git a/common/error_code_utils.cc b/common/error_code_utils.cc
index 2a2a0a3..930dafe 100644
--- a/common/error_code_utils.cc
+++ b/common/error_code_utils.cc
@@ -146,6 +146,8 @@
       return "ErrorCode::kNonCriticalUpdateInOOBE";
     case ErrorCode::kOmahaUpdateIgnoredOverCellular:
       return "ErrorCode::kOmahaUpdateIgnoredOverCellular";
+    case ErrorCode::kPayloadTimestampError:
+      return "ErrorCode::kPayloadTimestampError";
     case ErrorCode::kUpdatedButNotActive:
       return "ErrorCode::kUpdatedButNotActive";
     case ErrorCode::kNoUpdate:
diff --git a/common/fake_hardware.h b/common/fake_hardware.h
index 55dcc2c..55ef32d 100644
--- a/common/fake_hardware.h
+++ b/common/fake_hardware.h
@@ -124,6 +124,8 @@
     return false;
   }
 
+  int64_t GetBuildTimestamp() const override { return build_timestamp_; }
+
   bool GetFirstActiveOmahaPingSent() const override {
     return first_active_omaha_ping_sent_;
   }
@@ -185,6 +187,10 @@
     powerwash_count_ = powerwash_count;
   }
 
+  void SetBuildTimestamp(int64_t build_timestamp) {
+    build_timestamp_ = build_timestamp;
+  }
+
   // Getters to verify state.
   int GetMaxKernelKeyRollforward() const { return kernel_max_rollforward_; }
 
@@ -205,6 +211,7 @@
   int firmware_max_rollforward_{kFirmwareMaxRollforward};
   int powerwash_count_{kPowerwashCountNotSet};
   bool powerwash_scheduled_{false};
+  int64_t build_timestamp_{0};
   bool first_active_omaha_ping_sent_{false};
 
   DISALLOW_COPY_AND_ASSIGN(FakeHardware);
diff --git a/common/hardware_interface.h b/common/hardware_interface.h
index dd42e05..bbc8660 100644
--- a/common/hardware_interface.h
+++ b/common/hardware_interface.h
@@ -17,6 +17,8 @@
 #ifndef UPDATE_ENGINE_COMMON_HARDWARE_INTERFACE_H_
 #define UPDATE_ENGINE_COMMON_HARDWARE_INTERFACE_H_
 
+#include <stdint.h>
+
 #include <string>
 #include <vector>
 
@@ -116,6 +118,9 @@
   // returns false.
   virtual bool GetPowerwashSafeDirectory(base::FilePath* path) const = 0;
 
+  // Returns the timestamp of the current OS build.
+  virtual int64_t GetBuildTimestamp() 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()|.
diff --git a/common/hash_calculator_unittest.cc b/common/hash_calculator_unittest.cc
index 233237b..79f22ad 100644
--- a/common/hash_calculator_unittest.cc
+++ b/common/hash_calculator_unittest.cc
@@ -26,6 +26,7 @@
 #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;
@@ -43,10 +44,7 @@
   0xc8, 0x8b, 0x59, 0xb2, 0xdc, 0x32, 0x7a, 0xa4
 };
 
-class HashCalculatorTest : public ::testing::Test {
- public:
-  HashCalculatorTest() {}
-};
+class HashCalculatorTest : public ::testing::Test {};
 
 TEST_F(HashCalculatorTest, SimpleTest) {
   HashCalculator calc;
@@ -54,7 +52,7 @@
   calc.Finalize();
   brillo::Blob raw_hash(std::begin(kExpectedRawHash),
                         std::end(kExpectedRawHash));
-  EXPECT_TRUE(raw_hash == calc.raw_hash());
+  EXPECT_EQ(raw_hash, calc.raw_hash());
 }
 
 TEST_F(HashCalculatorTest, MultiUpdateTest) {
@@ -64,7 +62,7 @@
   calc.Finalize();
   brillo::Blob raw_hash(std::begin(kExpectedRawHash),
                         std::end(kExpectedRawHash));
-  EXPECT_TRUE(raw_hash == calc.raw_hash());
+  EXPECT_EQ(raw_hash, calc.raw_hash());
 }
 
 TEST_F(HashCalculatorTest, ContextTest) {
@@ -78,7 +76,7 @@
   calc_next.Finalize();
   brillo::Blob raw_hash(std::begin(kExpectedRawHash),
                         std::end(kExpectedRawHash));
-  EXPECT_TRUE(raw_hash == calc_next.raw_hash());
+  EXPECT_EQ(raw_hash, calc_next.raw_hash());
 }
 
 TEST_F(HashCalculatorTest, BigTest) {
@@ -108,25 +106,21 @@
 }
 
 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));
+  test_utils::ScopedTempFile data_file("data.XXXXXX");
+  ASSERT_TRUE(test_utils::WriteFileString(data_file.path(), "hi"));
 
-  static const int kLengths[] = { -1, 2, 10 };
-  for (size_t i = 0; i < arraysize(kLengths); i++) {
+  for (const int length : {-1, 2, 10}) {
     HashCalculator calc;
-    EXPECT_EQ(2, calc.UpdateFile(data_path, kLengths[i]));
+    EXPECT_EQ(2, calc.UpdateFile(data_file.path(), length));
     EXPECT_TRUE(calc.Finalize());
     brillo::Blob raw_hash(std::begin(kExpectedRawHash),
                           std::end(kExpectedRawHash));
-    EXPECT_TRUE(raw_hash == calc.raw_hash());
+    EXPECT_EQ(raw_hash, calc.raw_hash());
   }
 
   HashCalculator calc;
-  EXPECT_EQ(0, calc.UpdateFile(data_path, 0));
-  EXPECT_EQ(1, calc.UpdateFile(data_path, 1));
+  EXPECT_EQ(0, calc.UpdateFile(data_file.path(), 0));
+  EXPECT_EQ(1, calc.UpdateFile(data_file.path(), 1));
   EXPECT_TRUE(calc.Finalize());
   // echo -n h | openssl dgst -sha256 -binary | openssl base64
   EXPECT_EQ("qqlAJmTxpB9A67xSyZk+tmrrNmYClY/fqig7ceZNsSM=",
@@ -134,21 +128,16 @@
 }
 
 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));
+  test_utils::ScopedTempFile data_file("data.XXXXXX");
+  ASSERT_TRUE(test_utils::WriteFileString(data_file.path(), "hi"));
 
-  static const int kLengths[] = { -1, 2, 10 };
-  for (size_t i = 0; i < arraysize(kLengths); i++) {
+  for (const int length : {-1, 2, 10}) {
     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);
+    EXPECT_EQ(
+        2, HashCalculator::RawHashOfFile(data_file.path(), length, &raw_hash));
+    EXPECT_EQ(exp_raw_hash, raw_hash);
   }
 }
 
diff --git a/common/http_fetcher_unittest.cc b/common/http_fetcher_unittest.cc
index a0b0522..23df67a 100644
--- a/common/http_fetcher_unittest.cc
+++ b/common/http_fetcher_unittest.cc
@@ -32,7 +32,6 @@
 #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>
diff --git a/common/subprocess_unittest.cc b/common/subprocess_unittest.cc
index 10710e8..c8996db 100644
--- a/common/subprocess_unittest.cc
+++ b/common/subprocess_unittest.cc
@@ -32,7 +32,6 @@
 #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>
diff --git a/common/utils.h b/common/utils.h
index ecb97a3..f7f285b 100644
--- a/common/utils.h
+++ b/common/utils.h
@@ -318,6 +318,16 @@
 // reboot. Returns whether it succeeded getting the boot_id.
 bool GetBootId(std::string* boot_id);
 
+// 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;
+}
+
+// Round |x| up to be a multiple of |y|.
+constexpr uint64_t RoundUp(uint64_t x, uint64_t y) {
+  return DivRoundUp(x, y) * y;
+}
+
 // Returns the integer value of the first section of |version|. E.g. for
 //  "10575.39." returns 10575. Returns 0 if |version| is empty, returns -1 if
 // first section of |version| is invalid (e.g. not a number).
diff --git a/common/utils_unittest.cc b/common/utils_unittest.cc
index 1590eeb..3405b68 100644
--- a/common/utils_unittest.cc
+++ b/common/utils_unittest.cc
@@ -59,13 +59,11 @@
 }
 
 TEST(UtilsTest, WriteFileReadFile) {
-  base::FilePath file;
-  EXPECT_TRUE(base::CreateTemporaryFile(&file));
-  ScopedPathUnlinker unlinker(file.value());
-  EXPECT_TRUE(utils::WriteFile(file.value().c_str(), "hello", 5));
+  test_utils::ScopedTempFile file;
+  EXPECT_TRUE(utils::WriteFile(file.path().c_str(), "hello", 5));
 
   brillo::Blob readback;
-  EXPECT_TRUE(utils::ReadFile(file.value().c_str(), &readback));
+  EXPECT_TRUE(utils::ReadFile(file.path().c_str(), &readback));
   EXPECT_EQ("hello", string(readback.begin(), readback.end()));
 }
 
@@ -75,24 +73,21 @@
 }
 
 TEST(UtilsTest, ReadFileChunk) {
-  base::FilePath file;
-  EXPECT_TRUE(base::CreateTemporaryFile(&file));
-  ScopedPathUnlinker unlinker(file.value());
+  test_utils::ScopedTempFile file;
   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()));
+  EXPECT_TRUE(test_utils::WriteFileVector(file.path(), data));
   brillo::Blob in_data;
-  EXPECT_TRUE(utils::ReadFileChunk(file.value().c_str(), kSize, 10, &in_data));
+  EXPECT_TRUE(utils::ReadFileChunk(file.path().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);
+  EXPECT_TRUE(utils::ReadFileChunk(file.path().c_str(), 0, -1, &in_data));
+  EXPECT_EQ(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);
+  EXPECT_TRUE(utils::ReadFileChunk(file.path().c_str(), 10, 20, &in_data));
+  EXPECT_EQ(brillo::Blob(data.begin() + 10, data.begin() + 10 + 20), in_data);
 }
 
 TEST(UtilsTest, ErrnoNumberAsStringTest) {
@@ -305,8 +300,9 @@
   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));
+  base::Time time;
+  EXPECT_TRUE(base::Time::FromUTCExploded(exploded, &time));
+  EXPECT_EQ(time, utils::TimeFromStructTimespec(&ts));
 }
 
 TEST(UtilsTest, DecodeAndStoreBase64String) {
@@ -481,20 +477,18 @@
 }
 
 TEST(UtilsTest, RunAsRootUnmountFilesystemBusyFailureTest) {
-  string tmp_image;
-  EXPECT_TRUE(utils::MakeTempFile("img.XXXXXX", &tmp_image, nullptr));
-  ScopedPathUnlinker tmp_image_unlinker(tmp_image);
+  test_utils::ScopedTempFile tmp_image("img.XXXXXX");
 
   EXPECT_TRUE(base::CopyFile(
       test_utils::GetBuildArtifactsPath().Append("gen/disk_ext2_4k.img"),
-      base::FilePath(tmp_image)));
+      base::FilePath(tmp_image.path())));
 
   base::ScopedTempDir mnt_dir;
   EXPECT_TRUE(mnt_dir.CreateUniqueTempDir());
 
   string loop_dev;
   test_utils::ScopedLoopbackDeviceBinder loop_binder(
-      tmp_image, true, &loop_dev);
+      tmp_image.path(), true, &loop_dev);
 
   EXPECT_FALSE(utils::IsMountpoint(mnt_dir.GetPath().value()));
   // This is the actual test part. While we hold a file descriptor open for the
@@ -523,10 +517,8 @@
   EXPECT_TRUE(mnt_dir.CreateUniqueTempDir());
   EXPECT_FALSE(utils::IsMountpoint(mnt_dir.GetPath().value()));
 
-  base::FilePath file;
-  EXPECT_TRUE(base::CreateTemporaryFile(&file));
-  ScopedPathUnlinker unlinker(file.value());
-  EXPECT_FALSE(utils::IsMountpoint(file.value()));
+  test_utils::ScopedTempFile file;
+  EXPECT_FALSE(utils::IsMountpoint(file.path()));
 }
 
 TEST(UtilsTest, VersionPrefix) {
diff --git a/hardware_android.cc b/hardware_android.cc
index deabc5c..9dd8bb6 100644
--- a/hardware_android.cc
+++ b/hardware_android.cc
@@ -34,6 +34,7 @@
 #include "update_engine/utils_android.h"
 
 using android::base::GetBoolProperty;
+using android::base::GetIntProperty;
 using android::base::GetProperty;
 using std::string;
 
@@ -55,6 +56,7 @@
 const char kPropProductManufacturer[] = "ro.product.manufacturer";
 const char kPropBootHardwareSKU[] = "ro.boot.hardware.sku";
 const char kPropBootRevision[] = "ro.boot.revision";
+const char kPropBuildDateUTC[] = "ro.build.date.utc";
 
 // Write a recovery command line |message| to the BCB. The arguments to recovery
 // must be separated by '\n'. An empty string will erase the BCB.
@@ -219,6 +221,10 @@
   return false;
 }
 
+int64_t HardwareAndroid::GetBuildTimestamp() const {
+  return GetIntProperty<int64_t>(kPropBuildDateUTC, 0);
+}
+
 bool HardwareAndroid::GetFirstActiveOmahaPingSent() const {
   LOG(WARNING) << "STUB: Assuming first active omaha was never set.";
   return false;
diff --git a/hardware_android.h b/hardware_android.h
index b7a6f96..920b659 100644
--- a/hardware_android.h
+++ b/hardware_android.h
@@ -52,6 +52,7 @@
   bool CancelPowerwash() override;
   bool GetNonVolatileDirectory(base::FilePath* path) const override;
   bool GetPowerwashSafeDirectory(base::FilePath* path) const override;
+  int64_t GetBuildTimestamp() const override;
   bool GetFirstActiveOmahaPingSent() const override;
   bool SetFirstActiveOmahaPingSent() override;
 
diff --git a/hardware_chromeos.cc b/hardware_chromeos.cc
index 6cfe5ef..3949328 100644
--- a/hardware_chromeos.cc
+++ b/hardware_chromeos.cc
@@ -261,6 +261,11 @@
   return true;
 }
 
+int64_t HardwareChromeOS::GetBuildTimestamp() const {
+  // TODO(senj): implement this in Chrome OS.
+  return 0;
+}
+
 void HardwareChromeOS::LoadConfig(const string& root_prefix, bool normal_mode) {
   brillo::KeyValueStore store;
 
diff --git a/hardware_chromeos.h b/hardware_chromeos.h
index 3aeeb0b..5c66641 100644
--- a/hardware_chromeos.h
+++ b/hardware_chromeos.h
@@ -57,6 +57,7 @@
   bool CancelPowerwash() override;
   bool GetNonVolatileDirectory(base::FilePath* path) const override;
   bool GetPowerwashSafeDirectory(base::FilePath* path) const override;
+  int64_t GetBuildTimestamp() const override;
   bool GetFirstActiveOmahaPingSent() const override;
   bool SetFirstActiveOmahaPingSent() override;
 
diff --git a/metrics_reporter_stub.h b/metrics_reporter_stub.h
index 486dc2f..87023ee 100644
--- a/metrics_reporter_stub.h
+++ b/metrics_reporter_stub.h
@@ -85,6 +85,8 @@
 
   void ReportInstallDateProvisioningSource(int source, int max) override {}
 
+  void ReportInternalErrorCode(ErrorCode error_code) override {}
+
   void ReportKeyVersionMetrics(int kernel_min_version,
                                int kernel_max_rollforward_version,
                                bool kernel_max_rollforward_success) override {}
diff --git a/metrics_utils.cc b/metrics_utils.cc
index b85f257..d80d394 100644
--- a/metrics_utils.cc
+++ b/metrics_utils.cc
@@ -78,6 +78,7 @@
     case ErrorCode::kDownloadPayloadVerificationError:
     case ErrorCode::kSignedDeltaPayloadExpectedError:
     case ErrorCode::kDownloadPayloadPubKeyVerificationError:
+    case ErrorCode::kPayloadTimestampError:
       return metrics::AttemptResult::kPayloadVerificationFailed;
 
     case ErrorCode::kNewRootfsVerificationError:
@@ -218,6 +219,7 @@
     case ErrorCode::kFilesystemVerifierError:
     case ErrorCode::kUserCanceled:
     case ErrorCode::kOmahaUpdateIgnoredOverCellular:
+    case ErrorCode::kPayloadTimestampError:
     case ErrorCode::kUpdatedButNotActive:
     case ErrorCode::kNoUpdate:
     case ErrorCode::kRollbackNotPossible:
diff --git a/omaha_response_handler_action_unittest.cc b/omaha_response_handler_action_unittest.cc
index bdec86f..4e101ee 100644
--- a/omaha_response_handler_action_unittest.cc
+++ b/omaha_response_handler_action_unittest.cc
@@ -176,11 +176,8 @@
 }
 
 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);
+  test_utils::ScopedTempFile test_deadline_file(
+      "omaha_response_handler_action_unittest-XXXXXX");
   {
     OmahaResponse in;
     in.update_exists = true;
@@ -193,15 +190,15 @@
     in.prompt = false;
     in.deadline = "20101020";
     InstallPlan install_plan;
-    EXPECT_TRUE(DoTest(in, test_deadline_file, &install_plan));
+    EXPECT_TRUE(DoTest(in, test_deadline_file.path(), &install_plan));
     EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
     EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
     EXPECT_EQ(1U, install_plan.target_slot);
     string deadline;
-    EXPECT_TRUE(utils::ReadFile(test_deadline_file, &deadline));
+    EXPECT_TRUE(utils::ReadFile(test_deadline_file.path(), &deadline));
     EXPECT_EQ("20101020", deadline);
     struct stat deadline_stat;
-    EXPECT_EQ(0, stat(test_deadline_file.c_str(), &deadline_stat));
+    EXPECT_EQ(0, stat(test_deadline_file.path().c_str(), &deadline_stat));
     EXPECT_EQ(
         static_cast<mode_t>(S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH),
         deadline_stat.st_mode);
@@ -220,12 +217,12 @@
     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_TRUE(DoTest(in, test_deadline_file.path(), &install_plan));
     EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
     EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
     EXPECT_EQ(0U, install_plan.target_slot);
     string deadline;
-    EXPECT_TRUE(utils::ReadFile(test_deadline_file, &deadline) &&
+    EXPECT_TRUE(utils::ReadFile(test_deadline_file.path(), &deadline) &&
                 deadline.empty());
     EXPECT_EQ(in.version, install_plan.version);
   }
@@ -245,12 +242,12 @@
     EXPECT_CALL(*(fake_system_state_.mock_payload_state()),
                 GetRollbackHappened())
         .WillOnce(Return(true));
-    EXPECT_TRUE(DoTest(in, test_deadline_file, &install_plan));
+    EXPECT_TRUE(DoTest(in, test_deadline_file.path(), &install_plan));
     EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
     EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
     EXPECT_EQ(1U, install_plan.target_slot);
     string deadline;
-    EXPECT_TRUE(utils::ReadFile(test_deadline_file, &deadline));
+    EXPECT_TRUE(utils::ReadFile(test_deadline_file.path(), &deadline));
     EXPECT_TRUE(deadline.empty());
     EXPECT_EQ(in.version, install_plan.version);
   }
@@ -268,12 +265,12 @@
     EXPECT_CALL(*(fake_system_state_.mock_payload_state()),
                 GetRollbackHappened())
         .WillOnce(Return(false));
-    EXPECT_TRUE(DoTest(in, test_deadline_file, &install_plan));
+    EXPECT_TRUE(DoTest(in, test_deadline_file.path(), &install_plan));
     EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
     EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
     EXPECT_EQ(1U, install_plan.target_slot);
     string deadline;
-    EXPECT_TRUE(utils::ReadFile(test_deadline_file, &deadline));
+    EXPECT_TRUE(utils::ReadFile(test_deadline_file.path(), &deadline));
     EXPECT_EQ("some-deadline", deadline);
     EXPECT_EQ(in.version, install_plan.version);
   }
diff --git a/payload_consumer/bzip_extent_writer_unittest.cc b/payload_consumer/bzip_extent_writer_unittest.cc
index bf050ef..4426876 100644
--- a/payload_consumer/bzip_extent_writer_unittest.cc
+++ b/payload_consumer/bzip_extent_writer_unittest.cc
@@ -100,8 +100,7 @@
   for (size_t i = 0; i < decompressed_data.size(); ++i)
     decompressed_data[i] = static_cast<uint8_t>("ABC\n"[i % 4]);
 
-  vector<Extent> extents = {
-      ExtentForRange(0, (kDecompressedLength + kBlockSize - 1) / kBlockSize)};
+  vector<Extent> extents = {ExtentForBytes(kBlockSize, 0, kDecompressedLength)};
 
   BzipExtentWriter bzip_writer(std::make_unique<DirectExtentWriter>());
   EXPECT_TRUE(
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 86bd6c3..7831c0f 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -1587,6 +1587,14 @@
     }
   }
 
+  if (manifest_.max_timestamp() < hardware_->GetBuildTimestamp()) {
+    LOG(ERROR) << "The current OS build timestamp ("
+               << hardware_->GetBuildTimestamp()
+               << ") is newer than the maximum timestamp in the manifest ("
+               << manifest_.max_timestamp() << ")";
+    return ErrorCode::kPayloadTimestampError;
+  }
+
   // TODO(garnold) we should be adding more and more manifest checks, such as
   // partition boundaries etc (see chromium-os:37661).
 
diff --git a/payload_consumer/delta_performer_integration_test.cc b/payload_consumer/delta_performer_integration_test.cc
index 78647a5..b2fda15 100644
--- a/payload_consumer/delta_performer_integration_test.cc
+++ b/payload_consumer/delta_performer_integration_test.cc
@@ -234,9 +234,7 @@
     RSA_free(rsa);
   }
   int signature_size = GetSignatureSize(private_key_path);
-  string hash_file;
-  ASSERT_TRUE(utils::MakeTempFile("hash.XXXXXX", &hash_file, nullptr));
-  ScopedPathUnlinker hash_unlinker(hash_file);
+  test_utils::ScopedTempFile hash_file("hash.XXXXXX");
   string signature_size_string;
   if (signature_test == kSignatureGeneratedShellRotateCl1 ||
       signature_test == kSignatureGeneratedShellRotateCl2)
@@ -251,28 +249,25 @@
                 delta_generator_path.c_str(),
                 payload_path.c_str(),
                 signature_size_string.c_str(),
-                hash_file.c_str())));
+                hash_file.path().c_str())));
 
   // Sign the hash
   brillo::Blob hash, signature;
-  ASSERT_TRUE(utils::ReadFile(hash_file, &hash));
+  ASSERT_TRUE(utils::ReadFile(hash_file.path(), &hash));
   ASSERT_TRUE(PayloadSigner::SignHash(hash, private_key_path, &signature));
 
-  string sig_file;
-  ASSERT_TRUE(utils::MakeTempFile("signature.XXXXXX", &sig_file, nullptr));
-  ScopedPathUnlinker sig_unlinker(sig_file);
-  ASSERT_TRUE(test_utils::WriteFileVector(sig_file, signature));
+  test_utils::ScopedTempFile sig_file("signature.XXXXXX");
+  ASSERT_TRUE(test_utils::WriteFileVector(sig_file.path(), signature));
+  string sig_files = sig_file.path();
 
-  string sig_file2;
-  ASSERT_TRUE(utils::MakeTempFile("signature.XXXXXX", &sig_file2, nullptr));
-  ScopedPathUnlinker sig2_unlinker(sig_file2);
+  test_utils::ScopedTempFile sig_file2("signature.XXXXXX");
   if (signature_test == kSignatureGeneratedShellRotateCl1 ||
       signature_test == kSignatureGeneratedShellRotateCl2) {
     ASSERT_TRUE(PayloadSigner::SignHash(
         hash, GetBuildArtifactsPath(kUnittestPrivateKey2Path), &signature));
-    ASSERT_TRUE(test_utils::WriteFileVector(sig_file2, signature));
+    ASSERT_TRUE(test_utils::WriteFileVector(sig_file2.path(), signature));
     // Append second sig file to first path
-    sig_file += ":" + sig_file2;
+    sig_files += ":" + sig_file2.path();
   }
 
   ASSERT_EQ(0,
@@ -280,7 +275,7 @@
                 "%s -in_file=%s -payload_signature_file=%s -out_file=%s",
                 delta_generator_path.c_str(),
                 payload_path.c_str(),
-                sig_file.c_str(),
+                sig_files.c_str(),
                 payload_path.c_str())));
   int verify_result = System(base::StringPrintf(
       "%s -in_file=%s -public_key=%s -public_key_version=%d",
diff --git a/payload_consumer/delta_performer_unittest.cc b/payload_consumer/delta_performer_unittest.cc
index 21f22d6..b0520e7 100644
--- a/payload_consumer/delta_performer_unittest.cc
+++ b/payload_consumer/delta_performer_unittest.cc
@@ -185,12 +185,8 @@
                                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()));
+    test_utils::ScopedTempFile blob_file("Blob-XXXXXX");
+    EXPECT_TRUE(test_utils::WriteFileVector(blob_file.path(), blob_data));
 
     PayloadGenerationConfig config;
     config.version.major = major_version;
@@ -218,16 +214,16 @@
     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);
+    test_utils::ScopedTempFile payload_file("Payload-XXXXXX");
     string private_key =
         sign_payload ? GetBuildArtifactsPath(kUnittestPrivateKeyPath) : "";
-    EXPECT_TRUE(payload.WritePayload(
-        payload_path, blob_path, private_key, &payload_.metadata_size));
+    EXPECT_TRUE(payload.WritePayload(payload_file.path(),
+                                     blob_file.path(),
+                                     private_key,
+                                     &payload_.metadata_size));
 
     brillo::Blob payload_data;
-    EXPECT_TRUE(utils::ReadFile(payload_path, &payload_data));
+    EXPECT_TRUE(utils::ReadFile(payload_file.path(), &payload_data));
     return payload_data;
   }
 
@@ -268,16 +264,13 @@
                                   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()));
+    test_utils::ScopedTempFile new_part("Partition-XXXXXX");
+    EXPECT_TRUE(test_utils::WriteFileVector(new_part.path(), target_data));
 
     // We installed the operations only in the rootfs partition, but the
     // delta performer needs to access all the partitions.
     fake_boot_control_.SetPartitionDevice(
-        kPartitionNameRoot, install_plan_.target_slot, new_part);
+        kPartitionNameRoot, install_plan_.target_slot, new_part.path());
     fake_boot_control_.SetPartitionDevice(
         kPartitionNameRoot, install_plan_.source_slot, source_path);
     fake_boot_control_.SetPartitionDevice(
@@ -290,7 +283,7 @@
     EXPECT_EQ(0, performer_.Close());
 
     brillo::Blob partition_data;
-    EXPECT_TRUE(utils::ReadFile(new_part, &partition_data));
+    EXPECT_TRUE(utils::ReadFile(new_part.path(), &partition_data));
     return partition_data;
   }
 
@@ -568,15 +561,10 @@
 
   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()));
+  test_utils::ScopedTempFile source("Source-XXXXXX");
+  EXPECT_TRUE(test_utils::WriteFileVector(source.path(), expected_data));
 
-  EXPECT_EQ(expected_data, ApplyPayload(payload_data, source_path, true));
+  EXPECT_EQ(expected_data, ApplyPayload(payload_data, source.path(), true));
 }
 
 TEST_F(DeltaPerformerTest, PuffdiffOperationTest) {
@@ -596,13 +584,11 @@
 
   brillo::Blob payload_data = GeneratePayload(puffdiff_payload, {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(), src.data(), src.size()));
+  test_utils::ScopedTempFile source("Source-XXXXXX");
+  EXPECT_TRUE(test_utils::WriteFileVector(source.path(), src));
 
   brillo::Blob dst(std::begin(dst_deflates), std::end(dst_deflates));
-  EXPECT_EQ(dst, ApplyPayload(payload_data, source_path, true));
+  EXPECT_EQ(dst, ApplyPayload(payload_data, source.path(), true));
 }
 
 TEST_F(DeltaPerformerTest, SourceHashMismatchTest) {
@@ -621,27 +607,21 @@
 
   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()));
+  test_utils::ScopedTempFile source("Source-XXXXXX");
+  EXPECT_TRUE(test_utils::WriteFileVector(source.path(), actual_data));
 
-  EXPECT_EQ(actual_data, ApplyPayload(payload_data, source_path, false));
+  EXPECT_EQ(actual_data, ApplyPayload(payload_data, source.path(), false));
 }
 
 // Test that the error-corrected file descriptor is used to read the partition
 // since the source partition doesn't match the operation hash.
 TEST_F(DeltaPerformerTest, ErrorCorrectionSourceCopyFallbackTest) {
-  const size_t kCopyOperationSize = 4 * 4096;
-  string source_path;
-  EXPECT_TRUE(utils::MakeTempFile("Source-XXXXXX", &source_path, nullptr));
-  ScopedPathUnlinker path_unlinker(source_path);
+  constexpr size_t kCopyOperationSize = 4 * 4096;
+  test_utils::ScopedTempFile source("Source-XXXXXX");
   // Write invalid data to the source image, which doesn't match the expected
   // hash.
   brillo::Blob invalid_data(kCopyOperationSize, 0x55);
-  EXPECT_TRUE(utils::WriteFile(
-      source_path.c_str(), invalid_data.data(), invalid_data.size()));
+  EXPECT_TRUE(test_utils::WriteFileVector(source.path(), invalid_data));
 
   // Setup the fec file descriptor as the fake stream, which matches
   // |expected_data|.
@@ -649,7 +629,7 @@
   brillo::Blob expected_data = FakeFileDescriptorData(kCopyOperationSize);
 
   brillo::Blob payload_data = GenerateSourceCopyPayload(expected_data, true);
-  EXPECT_EQ(expected_data, ApplyPayload(payload_data, source_path, true));
+  EXPECT_EQ(expected_data, ApplyPayload(payload_data, source.path(), true));
   // Verify that the fake_fec was actually used.
   EXPECT_EQ(1U, fake_fec->GetReadOps().size());
   EXPECT_EQ(1U, GetSourceEccRecoveredFailures());
@@ -659,14 +639,11 @@
 // when no hash is available for SOURCE_COPY but it falls back to the normal
 // file descriptor when the size of the error corrected one is too small.
 TEST_F(DeltaPerformerTest, ErrorCorrectionSourceCopyWhenNoHashFallbackTest) {
-  const size_t kCopyOperationSize = 4 * 4096;
-  string source_path;
-  EXPECT_TRUE(utils::MakeTempFile("Source-XXXXXX", &source_path, nullptr));
-  ScopedPathUnlinker path_unlinker(source_path);
+  constexpr size_t kCopyOperationSize = 4 * 4096;
+  test_utils::ScopedTempFile source("Source-XXXXXX");
   // Setup the source path with the right expected data.
   brillo::Blob expected_data = FakeFileDescriptorData(kCopyOperationSize);
-  EXPECT_TRUE(utils::WriteFile(
-      source_path.c_str(), expected_data.data(), expected_data.size()));
+  EXPECT_TRUE(test_utils::WriteFileVector(source.path(), expected_data));
 
   // Setup the fec file descriptor as the fake stream, with smaller data than
   // the expected.
@@ -674,7 +651,7 @@
 
   // The payload operation doesn't include an operation hash.
   brillo::Blob payload_data = GenerateSourceCopyPayload(expected_data, false);
-  EXPECT_EQ(expected_data, ApplyPayload(payload_data, source_path, true));
+  EXPECT_EQ(expected_data, ApplyPayload(payload_data, source.path(), true));
   // Verify that the fake_fec was attempted to be used. Since the file
   // descriptor is shorter it can actually do more than one read to realize it
   // reached the EOF.
@@ -685,18 +662,15 @@
 }
 
 TEST_F(DeltaPerformerTest, ChooseSourceFDTest) {
-  const size_t kSourceSize = 4 * 4096;
-  string source_path;
-  EXPECT_TRUE(utils::MakeTempFile("Source-XXXXXX", &source_path, nullptr));
-  ScopedPathUnlinker path_unlinker(source_path);
+  constexpr size_t kSourceSize = 4 * 4096;
+  test_utils::ScopedTempFile source("Source-XXXXXX");
   // Write invalid data to the source image, which doesn't match the expected
   // hash.
   brillo::Blob invalid_data(kSourceSize, 0x55);
-  EXPECT_TRUE(utils::WriteFile(
-      source_path.c_str(), invalid_data.data(), invalid_data.size()));
+  EXPECT_TRUE(test_utils::WriteFileVector(source.path(), invalid_data));
 
   performer_.source_fd_ = std::make_shared<EintrSafeFileDescriptor>();
-  performer_.source_fd_->Open(source_path.c_str(), O_RDONLY);
+  performer_.source_fd_->Open(source.path().c_str(), O_RDONLY);
   performer_.block_size_ = 4096;
 
   // Setup the fec file descriptor as the fake stream, which matches
@@ -861,6 +835,20 @@
                         ErrorCode::kUnsupportedMinorPayloadVersion);
 }
 
+TEST_F(DeltaPerformerTest, ValidateManifestDowngrade) {
+  // The Manifest we are validating.
+  DeltaArchiveManifest manifest;
+
+  manifest.set_minor_version(kFullPayloadMinorVersion);
+  manifest.set_max_timestamp(1);
+  fake_hardware_.SetBuildTimestamp(2);
+
+  RunManifestValidation(manifest,
+                        kMaxSupportedMajorPayloadVersion,
+                        InstallPayloadType::kFull,
+                        ErrorCode::kPayloadTimestampError);
+}
+
 TEST_F(DeltaPerformerTest, BrilloMetadataSignatureSizeTest) {
   unsigned int seed = time(nullptr);
   EXPECT_TRUE(performer_.Write(kDeltaMagic, sizeof(kDeltaMagic)));
diff --git a/payload_consumer/download_action_unittest.cc b/payload_consumer/download_action_unittest.cc
index 8efcd8e..84673c8 100644
--- a/payload_consumer/download_action_unittest.cc
+++ b/payload_consumer/download_action_unittest.cc
@@ -29,7 +29,6 @@
 #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>
 
diff --git a/payload_consumer/file_writer_unittest.cc b/payload_consumer/file_writer_unittest.cc
index 92837c8..05df307 100644
--- a/payload_consumer/file_writer_unittest.cc
+++ b/payload_consumer/file_writer_unittest.cc
@@ -36,19 +36,17 @@
 
 TEST(FileWriterTest, SimpleTest) {
   // Create a uniquely named file for testing.
-  string path;
-  ASSERT_TRUE(utils::MakeTempFile("FileWriterTest-XXXXXX", &path, nullptr));
-  ScopedPathUnlinker path_unlinker(path);
-
+  test_utils::ScopedTempFile file("FileWriterTest-XXXXXX");
   DirectFileWriter file_writer;
-  EXPECT_EQ(0, file_writer.Open(path.c_str(),
-                                O_CREAT | O_LARGEFILE | O_TRUNC | O_WRONLY,
-                                0644));
+  EXPECT_EQ(0,
+            file_writer.Open(file.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_TRUE(utils::ReadFile(file.path(), &actual_data));
 
-  EXPECT_FALSE(memcmp("test", actual_data.data(), actual_data.size()));
+  EXPECT_EQ("test", string(actual_data.begin(), actual_data.end()));
   EXPECT_EQ(0, file_writer.Close());
 }
 
@@ -61,14 +59,12 @@
 
 TEST(FileWriterTest, WriteErrorTest) {
   // Create a uniquely named file for testing.
-  string path;
-  ASSERT_TRUE(utils::MakeTempFile("FileWriterTest-XXXXXX", &path, nullptr));
-  ScopedPathUnlinker path_unlinker(path);
-
+  test_utils::ScopedTempFile file("FileWriterTest-XXXXXX");
   DirectFileWriter file_writer;
-  EXPECT_EQ(0, file_writer.Open(path.c_str(),
-                                O_CREAT | O_LARGEFILE | O_TRUNC | O_RDONLY,
-                                0644));
+  EXPECT_EQ(0,
+            file_writer.Open(file.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());
 }
diff --git a/payload_consumer/filesystem_verifier_action_unittest.cc b/payload_consumer/filesystem_verifier_action_unittest.cc
index 895d816..91a7da4 100644
--- a/payload_consumer/filesystem_verifier_action_unittest.cc
+++ b/payload_consumer/filesystem_verifier_action_unittest.cc
@@ -28,7 +28,6 @@
 #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>
@@ -99,13 +98,7 @@
 
 bool FilesystemVerifierActionTest::DoTest(bool terminate_early,
                                           bool hash_fail) {
-  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);
+  test_utils::ScopedTempFile a_loop_file("a_loop_file.XXXXXX");
 
   // Make random data for a.
   const size_t kLoopFileSize = 10 * 1024 * 1024 + 512;
@@ -113,7 +106,7 @@
   test_utils::FillWithData(&a_loop_data);
 
   // Write data to disk
-  if (!(test_utils::WriteFileVector(a_loop_file, a_loop_data))) {
+  if (!(test_utils::WriteFileVector(a_loop_file.path(), a_loop_data))) {
     ADD_FAILURE();
     return false;
   }
@@ -121,13 +114,13 @@
   // Attach loop devices to the files
   string a_dev;
   test_utils::ScopedLoopbackDeviceBinder a_dev_releaser(
-      a_loop_file, false, &a_dev);
+      a_loop_file.path(), false, &a_dev);
   if (!(a_dev_releaser.is_bound())) {
     ADD_FAILURE();
     return false;
   }
 
-  LOG(INFO) << "verifying: "  << a_loop_file << " (" << a_dev << ")";
+  LOG(INFO) << "verifying: " << a_loop_file.path() << " (" << a_dev << ")";
 
   bool success = true;
 
diff --git a/payload_consumer/postinstall_runner_action_unittest.cc b/payload_consumer/postinstall_runner_action_unittest.cc
index 3c606c1..8381472 100644
--- a/payload_consumer/postinstall_runner_action_unittest.cc
+++ b/payload_consumer/postinstall_runner_action_unittest.cc
@@ -29,7 +29,6 @@
 #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 <gmock/gmock.h>
@@ -330,7 +329,7 @@
 // SElinux labels are only set on Android.
 TEST_F(PostinstallRunnerActionTest, RunAsRootCheckFileContextsTest) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
-  RunPosinstallAction(loop.dev(), "bin/self_check_context", false, false);
+  RunPostinstallAction(loop.dev(), "bin/self_check_context", false, false);
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
 }
 #endif  // __ANDROID__
diff --git a/payload_generator/ab_generator_unittest.cc b/payload_generator/ab_generator_unittest.cc
index 25609c7..d083d8a 100644
--- a/payload_generator/ab_generator_unittest.cc
+++ b/payload_generator/ab_generator_unittest.cc
@@ -58,10 +58,6 @@
   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) {
@@ -74,7 +70,9 @@
       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));
+  test_utils::ScopedTempFile part_file(
+      "SplitReplaceOrReplaceBzTest_part.XXXXXX");
+  ASSERT_TRUE(test_utils::WriteFileVector(part_file.path(), part_data));
 
   // Create original operation and blob data.
   const size_t op_ex1_offset = op_ex1_start_block * kBlockSize;
@@ -109,15 +107,12 @@
   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);
+  test_utils::ScopedTempFile data_file(
+      "SplitReplaceOrReplaceBzTest_data.XXXXXX");
+  EXPECT_TRUE(test_utils::WriteFileVector(data_file.path(), op_blob));
+  int data_fd = open(data_file.path().c_str(), O_RDWR, 000);
   EXPECT_GE(data_fd, 0);
   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);
 
@@ -126,7 +121,7 @@
   PayloadVersion version(kChromeOSMajorPayloadVersion,
                          kSourceMinorPayloadVersion);
   ASSERT_TRUE(ABGenerator::SplitAReplaceOp(
-      version, aop, part_path, &result_ops, &blob_file));
+      version, aop, part_file.path(), &result_ops, &blob_file));
 
   // Check the result.
   InstallOperation_Type expected_type =
@@ -212,10 +207,6 @@
   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) {
@@ -228,7 +219,9 @@
       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));
+  test_utils::ScopedTempFile part_file(
+      "MergeReplaceOrReplaceBzTest_part.XXXXXX");
+  ASSERT_TRUE(test_utils::WriteFileVector(part_file.path(), part_data));
 
   // Create original operations and blob data.
   vector<AnnotatedOperation> aops;
@@ -277,23 +270,20 @@
   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);
+  test_utils::ScopedTempFile data_file(
+      "MergeReplaceOrReplaceBzTest_data.XXXXXX");
+  EXPECT_TRUE(test_utils::WriteFileVector(data_file.path(), blob_data));
+  int data_fd = open(data_file.path().c_str(), O_RDWR, 000);
   EXPECT_GE(data_fd, 0);
   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.
   PayloadVersion version(kChromeOSMajorPayloadVersion,
                          kSourceMinorPayloadVersion);
-  EXPECT_TRUE(
-      ABGenerator::MergeOperations(&aops, version, 5, part_path, &blob_file));
+  EXPECT_TRUE(ABGenerator::MergeOperations(
+      &aops, version, 5, part_file.path(), &blob_file));
 
   // Check the result.
   InstallOperation_Type expected_op_type =
@@ -570,16 +560,12 @@
   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);
+  test_utils::ScopedTempFile src_part_file("AddSourceHashTest_src_part.XXXXXX");
   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()));
+  ASSERT_TRUE(test_utils::WriteFileVector(src_part_file.path(), src_data));
 
-  EXPECT_TRUE(ABGenerator::AddSourceHash(&aops, src_part_path));
+  EXPECT_TRUE(ABGenerator::AddSourceHash(&aops, src_part_file.path()));
 
   EXPECT_TRUE(aops[0].op.has_src_sha256_hash());
   EXPECT_FALSE(aops[1].op.has_src_sha256_hash());
diff --git a/payload_generator/block_mapping_unittest.cc b/payload_generator/block_mapping_unittest.cc
index 4d09710..e1870ec 100644
--- a/payload_generator/block_mapping_unittest.cc
+++ b/payload_generator/block_mapping_unittest.cc
@@ -39,23 +39,9 @@
 
 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_;
+  test_utils::ScopedTempFile old_part_{"BlockMappingTest_old.XXXXXX"};
+  test_utils::ScopedTempFile new_part_{"BlockMappingTest_new.XXXXXX"};
 
   size_t block_size_{1024};
   BlockMapping bm_{block_size_};  // BlockMapping under test.
@@ -72,8 +58,8 @@
 }
 
 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));
+  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));
@@ -107,18 +93,18 @@
   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);
+  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);
+  test_utils::WriteFileString(new_part_.path(), new_contents);
 
   vector<BlockMapping::BlockId> old_ids, new_ids;
-  EXPECT_TRUE(MapPartitionBlocks(old_part_path_,
-                                 new_part_path_,
+  EXPECT_TRUE(MapPartitionBlocks(old_part_.path(),
+                                 new_part_.path(),
                                  old_contents.size(),
                                  new_contents.size(),
                                  block_size_,
diff --git a/payload_generator/boot_img_filesystem.cc b/payload_generator/boot_img_filesystem.cc
new file mode 100644
index 0000000..90be966
--- /dev/null
+++ b/payload_generator/boot_img_filesystem.cc
@@ -0,0 +1,110 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/payload_generator/boot_img_filesystem.h"
+
+#include <base/logging.h>
+#include <brillo/secure_blob.h>
+#include <puffin/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"
+
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+unique_ptr<BootImgFilesystem> BootImgFilesystem::CreateFromFile(
+    const string& filename) {
+  if (filename.empty())
+    return nullptr;
+
+  brillo::Blob header;
+  if (!utils::ReadFileChunk(filename, 0, sizeof(boot_img_hdr), &header) ||
+      header.size() != sizeof(boot_img_hdr) ||
+      memcmp(header.data(), BOOT_MAGIC, BOOT_MAGIC_SIZE) != 0) {
+    return nullptr;
+  }
+
+  unique_ptr<BootImgFilesystem> result(new BootImgFilesystem());
+  result->filename_ = filename;
+  memcpy(&result->hdr_, header.data(), header.size());
+  return result;
+}
+
+size_t BootImgFilesystem::GetBlockSize() const {
+  // Page size may not be 4K, but we currently only support 4K block size.
+  return kBlockSize;
+}
+
+size_t BootImgFilesystem::GetBlockCount() const {
+  return utils::DivRoundUp(utils::FileSize(filename_), kBlockSize);
+}
+
+FilesystemInterface::File BootImgFilesystem::GetFile(const string& name,
+                                                     uint64_t offset,
+                                                     uint64_t size) const {
+  File file;
+  file.name = name;
+  file.extents = {ExtentForBytes(kBlockSize, offset, size)};
+
+  brillo::Blob data;
+  if (utils::ReadFileChunk(filename_, offset, size, &data)) {
+    constexpr size_t kGZipHeaderSize = 10;
+    // Check GZip header magic.
+    if (data.size() > kGZipHeaderSize && data[0] == 0x1F && data[1] == 0x8B) {
+      if (!puffin::LocateDeflatesInGzip(data, &file.deflates)) {
+        // We still use the deflates found even if LocateDeflatesInGzip() fails,
+        // if any deflates are returned, they should be correct, it's possible
+        // something went wrong later but it shouldn't stop us from using the
+        // previous deflates. Another common case is if there's more data after
+        // the gzip, the function will try to parse that as another gzip and
+        // will fail, but we still want the deflates from the first gzip.
+        LOG(WARNING) << "Error occurred parsing gzip " << name << " at offset "
+                     << offset << " of " << filename_ << ", found "
+                     << file.deflates.size() << " deflates.";
+      }
+      for (auto& deflate : file.deflates) {
+        deflate.offset += offset * 8;
+      }
+    }
+  }
+  return file;
+}
+
+bool BootImgFilesystem::GetFiles(vector<File>* files) const {
+  files->clear();
+  const uint64_t file_size = utils::FileSize(filename_);
+  // The first page is header.
+  uint64_t offset = hdr_.page_size;
+  if (hdr_.kernel_size > 0 && offset + hdr_.kernel_size <= file_size) {
+    files->emplace_back(GetFile("<kernel>", offset, hdr_.kernel_size));
+  }
+  offset += utils::RoundUp(hdr_.kernel_size, hdr_.page_size);
+  if (hdr_.ramdisk_size > 0 && offset + hdr_.ramdisk_size <= file_size) {
+    files->emplace_back(GetFile("<ramdisk>", offset, hdr_.ramdisk_size));
+  }
+  return true;
+}
+
+bool BootImgFilesystem::LoadSettings(brillo::KeyValueStore* store) const {
+  return false;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/boot_img_filesystem.h b/payload_generator/boot_img_filesystem.h
new file mode 100644
index 0000000..87725d4
--- /dev/null
+++ b/payload_generator/boot_img_filesystem.h
@@ -0,0 +1,78 @@
+//
+// 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_PAYLOAD_GENERATOR_BOOT_IMG_FILESYSTEM_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_BOOT_IMG_FILESYSTEM_H_
+
+#include "update_engine/payload_generator/filesystem_interface.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace chromeos_update_engine {
+
+class BootImgFilesystem : public FilesystemInterface {
+ public:
+  // Creates an BootImgFilesystem from an Android boot.img file.
+  static std::unique_ptr<BootImgFilesystem> CreateFromFile(
+      const std::string& filename);
+  ~BootImgFilesystem() override = default;
+
+  // FilesystemInterface overrides.
+  size_t GetBlockSize() const override;
+  size_t GetBlockCount() const override;
+
+  // GetFiles will return one FilesystemInterface::File for kernel and one for
+  // ramdisk.
+  bool GetFiles(std::vector<File>* files) const override;
+
+  bool LoadSettings(brillo::KeyValueStore* store) const override;
+
+ private:
+  friend class BootImgFilesystemTest;
+
+  BootImgFilesystem() = default;
+
+  File GetFile(const std::string& name, uint64_t offset, uint64_t size) const;
+
+  // The boot.img file path.
+  std::string filename_;
+
+// https://android.googlesource.com/platform/system/core/+/master/mkbootimg/include/bootimg/bootimg.h
+#define BOOT_MAGIC "ANDROID!"
+#define BOOT_MAGIC_SIZE 8
+  struct boot_img_hdr {
+    // Must be BOOT_MAGIC.
+    uint8_t magic[BOOT_MAGIC_SIZE];
+    uint32_t kernel_size;  /* size in bytes */
+    uint32_t kernel_addr;  /* physical load addr */
+    uint32_t ramdisk_size; /* size in bytes */
+    uint32_t ramdisk_addr; /* physical load addr */
+    uint32_t second_size;  /* size in bytes */
+    uint32_t second_addr;  /* physical load addr */
+    uint32_t tags_addr;    /* physical addr for kernel tags */
+    uint32_t page_size;    /* flash page size we assume */
+  } __attribute__((packed));
+  // The boot image header.
+  boot_img_hdr hdr_;
+
+  DISALLOW_COPY_AND_ASSIGN(BootImgFilesystem);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_BOOT_IMG_FILESYSTEM_H_
diff --git a/payload_generator/boot_img_filesystem_unittest.cc b/payload_generator/boot_img_filesystem_unittest.cc
new file mode 100644
index 0000000..b1e0d99
--- /dev/null
+++ b/payload_generator/boot_img_filesystem_unittest.cc
@@ -0,0 +1,117 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/payload_generator/boot_img_filesystem.h"
+
+#include <vector>
+
+#include <brillo/secure_blob.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+
+namespace chromeos_update_engine {
+
+using std::unique_ptr;
+using std::vector;
+
+class BootImgFilesystemTest : public ::testing::Test {
+ protected:
+  brillo::Blob GetBootImg(const brillo::Blob& kernel,
+                          const brillo::Blob& ramdisk) {
+    brillo::Blob boot_img(16 * 1024);
+    BootImgFilesystem::boot_img_hdr hdr;
+    memcpy(hdr.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
+    hdr.kernel_size = kernel.size();
+    hdr.ramdisk_size = ramdisk.size();
+    hdr.page_size = 4096;
+    size_t offset = 0;
+    memcpy(boot_img.data() + offset, &hdr, sizeof(hdr));
+    offset += utils::RoundUp(sizeof(hdr), hdr.page_size);
+    memcpy(boot_img.data() + offset, kernel.data(), kernel.size());
+    offset += utils::RoundUp(kernel.size(), hdr.page_size);
+    memcpy(boot_img.data() + offset, ramdisk.data(), ramdisk.size());
+    return boot_img;
+  }
+
+  test_utils::ScopedTempFile boot_file_;
+};
+
+TEST_F(BootImgFilesystemTest, SimpleTest) {
+  test_utils::WriteFileVector(
+      boot_file_.path(),
+      GetBootImg(brillo::Blob(1234, 'k'), brillo::Blob(5678, 'r')));
+  unique_ptr<BootImgFilesystem> fs =
+      BootImgFilesystem::CreateFromFile(boot_file_.path());
+  EXPECT_NE(nullptr, fs);
+
+  vector<FilesystemInterface::File> files;
+  EXPECT_TRUE(fs->GetFiles(&files));
+  ASSERT_EQ(2u, files.size());
+
+  EXPECT_EQ("<kernel>", files[0].name);
+  EXPECT_EQ(1u, files[0].extents.size());
+  EXPECT_EQ(1u, files[0].extents[0].start_block());
+  EXPECT_EQ(1u, files[0].extents[0].num_blocks());
+  EXPECT_TRUE(files[0].deflates.empty());
+
+  EXPECT_EQ("<ramdisk>", files[1].name);
+  EXPECT_EQ(1u, files[1].extents.size());
+  EXPECT_EQ(2u, files[1].extents[0].start_block());
+  EXPECT_EQ(2u, files[1].extents[0].num_blocks());
+  EXPECT_TRUE(files[1].deflates.empty());
+}
+
+TEST_F(BootImgFilesystemTest, BadImageTest) {
+  brillo::Blob boot_img = GetBootImg({}, {});
+  boot_img[7] = '?';
+  test_utils::WriteFileVector(boot_file_.path(), boot_img);
+  unique_ptr<BootImgFilesystem> fs =
+      BootImgFilesystem::CreateFromFile(boot_file_.path());
+  EXPECT_EQ(nullptr, fs);
+}
+
+TEST_F(BootImgFilesystemTest, GZipRamdiskTest) {
+  // echo ramdisk | gzip | hexdump -v -e '/1 "0x%02x, "'
+  const brillo::Blob ramdisk = {0x1f, 0x8b, 0x08, 0x00, 0x3a, 0x83, 0x35,
+                                0x5b, 0x00, 0x03, 0x2b, 0x4a, 0xcc, 0x4d,
+                                0xc9, 0x2c, 0xce, 0xe6, 0x02, 0x00, 0x2e,
+                                0xf6, 0x0b, 0x08, 0x08, 0x00, 0x00, 0x00};
+  test_utils::WriteFileVector(boot_file_.path(),
+                              GetBootImg(brillo::Blob(5678, 'k'), ramdisk));
+  unique_ptr<BootImgFilesystem> fs =
+      BootImgFilesystem::CreateFromFile(boot_file_.path());
+  EXPECT_NE(nullptr, fs);
+
+  vector<FilesystemInterface::File> files;
+  EXPECT_TRUE(fs->GetFiles(&files));
+  ASSERT_EQ(2u, files.size());
+
+  EXPECT_EQ("<kernel>", files[0].name);
+  EXPECT_EQ(1u, files[0].extents.size());
+  EXPECT_EQ(1u, files[0].extents[0].start_block());
+  EXPECT_EQ(2u, files[0].extents[0].num_blocks());
+  EXPECT_TRUE(files[0].deflates.empty());
+
+  EXPECT_EQ("<ramdisk>", files[1].name);
+  EXPECT_EQ(1u, files[1].extents.size());
+  EXPECT_EQ(3u, files[1].extents[0].start_block());
+  EXPECT_EQ(1u, files[1].extents[0].num_blocks());
+  EXPECT_EQ(1u, files[1].deflates.size());
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/extent_ranges.cc b/payload_generator/extent_ranges.cc
index c1d3d63..41e8f76 100644
--- a/payload_generator/extent_ranges.cc
+++ b/payload_generator/extent_ranges.cc
@@ -227,6 +227,14 @@
   return ret;
 }
 
+Extent ExtentForBytes(uint64_t block_size,
+                      uint64_t start_bytes,
+                      uint64_t size_bytes) {
+  uint64_t start_block = start_bytes / block_size;
+  uint64_t end_block = utils::DivRoundUp(start_bytes + size_bytes, block_size);
+  return ExtentForRange(start_block, end_block - start_block);
+}
+
 vector<Extent> ExtentRanges::GetExtentsForBlockCount(
     uint64_t count) const {
   vector<Extent> out;
diff --git a/payload_generator/extent_ranges.h b/payload_generator/extent_ranges.h
index 198c834..02cf8fc 100644
--- a/payload_generator/extent_ranges.h
+++ b/payload_generator/extent_ranges.h
@@ -41,6 +41,9 @@
 };
 
 Extent ExtentForRange(uint64_t start_block, uint64_t num_blocks);
+Extent ExtentForBytes(uint64_t block_size,
+                      uint64_t start_bytes,
+                      uint64_t size_bytes);
 
 class ExtentRanges {
  public:
diff --git a/payload_generator/full_update_generator.cc b/payload_generator/full_update_generator.cc
index 482a789..98bb0f3 100644
--- a/payload_generator/full_update_generator.cc
+++ b/payload_generator/full_update_generator.cc
@@ -152,7 +152,7 @@
   // 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;
+  size_t num_chunks = utils::DivRoundUp(partition_blocks, chunk_blocks);
   aops->resize(num_chunks);
   vector<ChunkProcessor> chunk_processors;
   chunk_processors.reserve(num_chunks);
diff --git a/payload_generator/full_update_generator_unittest.cc b/payload_generator/full_update_generator_unittest.cc
index 6da4d10..e398125 100644
--- a/payload_generator/full_update_generator_unittest.cc
+++ b/payload_generator/full_update_generator_unittest.cc
@@ -40,15 +40,11 @@
     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_));
+    new_part_conf.path = part_file_.path();
+    EXPECT_TRUE(utils::MakeTempFile(
+        "FullUpdateTest_blobs.XXXXXX", &out_blobs_path_, &out_blobs_fd_));
 
     blob_file_.reset(new BlobFileWriter(out_blobs_fd_, &out_blobs_length_));
-    part_path_unlinker_.reset(new ScopedPathUnlinker(new_part_conf.path));
     out_blobs_unlinker_.reset(new ScopedPathUnlinker(out_blobs_path_));
   }
 
@@ -62,9 +58,9 @@
   int out_blobs_fd_{-1};
   off_t out_blobs_length_{0};
   ScopedFdCloser out_blobs_fd_closer_{&out_blobs_fd_};
+  test_utils::ScopedTempFile part_file_{"FullUpdateTest_partition.XXXXXX"};
 
   std::unique_ptr<BlobFileWriter> blob_file_;
-  std::unique_ptr<ScopedPathUnlinker> part_path_unlinker_;
   std::unique_ptr<ScopedPathUnlinker> out_blobs_unlinker_;
 
   // FullUpdateGenerator under test.
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc
index cd08118..b842f33 100644
--- a/payload_generator/generate_delta_main.cc
+++ b/payload_generator/generate_delta_main.cc
@@ -339,6 +339,10 @@
   DEFINE_string(properties_file, "",
                 "If passed, dumps the payload properties of the payload passed "
                 "in --in_file and exits.");
+  DEFINE_int64(max_timestamp,
+               0,
+               "The maximum timestamp of the OS allowed to apply this "
+               "payload.");
 
   DEFINE_string(old_channel, "",
                 "The channel for the old image. 'dev-channel', 'npo-channel', "
@@ -583,6 +587,8 @@
     LOG(INFO) << "Using provided minor_version=" << FLAGS_minor_version;
   }
 
+  payload_config.max_timestamp = FLAGS_max_timestamp;
+
   LOG(INFO) << "Generating " << (payload_config.is_delta ? "delta" : "full")
             << " update";
 
diff --git a/payload_generator/payload_file.cc b/payload_generator/payload_file.cc
index a9e32a3..d5c59ce 100644
--- a/payload_generator/payload_file.cc
+++ b/payload_generator/payload_file.cc
@@ -73,6 +73,7 @@
     *(manifest_.mutable_new_image_info()) = config.target.image_info;
 
   manifest_.set_block_size(config.block_size);
+  manifest_.set_max_timestamp(config.max_timestamp);
   return true;
 }
 
diff --git a/payload_generator/payload_file_unittest.cc b/payload_generator/payload_file_unittest.cc
index e8e7e14..45faebb9 100644
--- a/payload_generator/payload_file_unittest.cc
+++ b/payload_generator/payload_file_unittest.cc
@@ -36,23 +36,16 @@
 };
 
 TEST_F(PayloadFileTest, ReorderBlobsTest) {
-  string orig_blobs;
-  EXPECT_TRUE(utils::MakeTempFile("ReorderBlobsTest.orig.XXXXXX", &orig_blobs,
-                                  nullptr));
-  ScopedPathUnlinker orig_blobs_unlinker(orig_blobs);
+  test_utils::ScopedTempFile orig_blobs("ReorderBlobsTest.orig.XXXXXX");
 
   // 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()));
+  EXPECT_TRUE(test_utils::WriteFileString(orig_blobs.path(), orig_data));
 
-  string new_blobs;
-  EXPECT_TRUE(
-      utils::MakeTempFile("ReorderBlobsTest.new.XXXXXX", &new_blobs, nullptr));
-  ScopedPathUnlinker new_blobs_unlinker(new_blobs);
+  test_utils::ScopedTempFile new_blobs("ReorderBlobsTest.new.XXXXXX");
 
   payload_.part_vec_.resize(2);
 
@@ -71,12 +64,12 @@
   aop.op.set_data_length(6);
   payload_.part_vec_[1].aops = {aop};
 
-  EXPECT_TRUE(payload_.ReorderDataBlobs(orig_blobs, new_blobs));
+  EXPECT_TRUE(payload_.ReorderDataBlobs(orig_blobs.path(), new_blobs.path()));
 
   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));
+  EXPECT_TRUE(utils::ReadFile(new_blobs.path(), &new_data));
   // Kernel blobs should appear at the end.
   EXPECT_EQ("bcdakernel", new_data);
 
diff --git a/payload_generator/payload_generation_config.cc b/payload_generator/payload_generation_config.cc
index 1f65b24..5fe56b5 100644
--- a/payload_generator/payload_generation_config.cc
+++ b/payload_generator/payload_generation_config.cc
@@ -20,6 +20,7 @@
 
 #include "update_engine/common/utils.h"
 #include "update_engine/payload_consumer/delta_performer.h"
+#include "update_engine/payload_generator/boot_img_filesystem.h"
 #include "update_engine/payload_generator/delta_diff_generator.h"
 #include "update_engine/payload_generator/delta_diff_utils.h"
 #include "update_engine/payload_generator/ext2_filesystem.h"
@@ -64,6 +65,12 @@
     }
   }
 
+  fs_interface = BootImgFilesystem::CreateFromFile(path);
+  if (fs_interface) {
+    TEST_AND_RETURN_FALSE(fs_interface->GetBlockSize() == kBlockSize);
+    return true;
+  }
+
   // Fall back to a RAW filesystem.
   TEST_AND_RETURN_FALSE(size % kBlockSize == 0);
   fs_interface = RawFilesystem::Create(
diff --git a/payload_generator/payload_generation_config.h b/payload_generator/payload_generation_config.h
index d64bf35..358a76d 100644
--- a/payload_generator/payload_generation_config.h
+++ b/payload_generator/payload_generation_config.h
@@ -186,6 +186,9 @@
 
   // The block size used for all the operations in the manifest.
   size_t block_size = 4096;
+
+  // The maximum timestamp of the OS allowed to apply this payload.
+  int64_t max_timestamp = 0;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/payload_signer.cc b/payload_generator/payload_signer.cc
index 0b47dd4..8eba2dc 100644
--- a/payload_generator/payload_signer.cc
+++ b/payload_generator/payload_signer.cc
@@ -231,8 +231,8 @@
     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);
+    dummy_extent->set_num_blocks(
+        utils::DivRoundUp(signature_blob_length, kBlockSize));
   }
 }
 
diff --git a/payload_generator/payload_signer_unittest.cc b/payload_generator/payload_signer_unittest.cc
index 62b6e7a..fc0925a 100644
--- a/payload_generator/payload_signer_unittest.cc
+++ b/payload_generator/payload_signer_unittest.cc
@@ -127,16 +127,14 @@
   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);
+    test_utils::ScopedTempFile payload_file("payload.XXXXXX");
     uint64_t metadata_size;
-    EXPECT_TRUE(
-        payload.WritePayload(payload_path, "/dev/null", "", &metadata_size));
+    EXPECT_TRUE(payload.WritePayload(
+        payload_file.path(), "/dev/null", "", &metadata_size));
     brillo::Blob payload_metadata_blob;
     DeltaArchiveManifest manifest;
     uint64_t load_metadata_size, load_major_version;
-    EXPECT_TRUE(PayloadSigner::LoadPayloadMetadata(payload_path,
+    EXPECT_TRUE(PayloadSigner::LoadPayloadMetadata(payload_file.path(),
                                                    &payload_metadata_blob,
                                                    &manifest,
                                                    &load_major_version,
@@ -215,50 +213,46 @@
 }
 
 TEST_F(PayloadSignerTest, SkipMetadataSignatureTest) {
-  string payload_path;
-  EXPECT_TRUE(utils::MakeTempFile("payload.XXXXXX", &payload_path, nullptr));
-  ScopedPathUnlinker payload_path_unlinker(payload_path);
-
+  test_utils::ScopedTempFile payload_file("payload.XXXXXX");
   PayloadGenerationConfig config;
   config.version.major = kBrilloMajorPayloadVersion;
   PayloadFile payload;
   EXPECT_TRUE(payload.Init(config));
   uint64_t metadata_size;
-  EXPECT_TRUE(
-      payload.WritePayload(payload_path, "/dev/null", "", &metadata_size));
+  EXPECT_TRUE(payload.WritePayload(
+      payload_file.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(PayloadSigner::HashPayloadForSigning(payload_file.path(),
+                                                   sizes,
+                                                   &unsigned_payload_hash,
+                                                   &unsigned_metadata_hash));
   EXPECT_TRUE(
-      payload.WritePayload(payload_path,
+      payload.WritePayload(payload_file.path(),
                            "/dev/null",
                            GetBuildArtifactsPath(kUnittestPrivateKeyPath),
                            &metadata_size));
   brillo::Blob signed_payload_hash, signed_metadata_hash;
   EXPECT_TRUE(PayloadSigner::HashPayloadForSigning(
-      payload_path, sizes, &signed_payload_hash, &signed_metadata_hash));
+      payload_file.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);
-
+  test_utils::ScopedTempFile payload_file("payload.XXXXXX");
   PayloadGenerationConfig config;
   config.version.major = kBrilloMajorPayloadVersion;
   PayloadFile payload;
   EXPECT_TRUE(payload.Init(config));
   uint64_t metadata_size;
   EXPECT_TRUE(
-      payload.WritePayload(payload_path,
+      payload.WritePayload(payload_file.path(),
                            "/dev/null",
                            GetBuildArtifactsPath(kUnittestPrivateKeyPath),
                            &metadata_size));
   EXPECT_TRUE(PayloadSigner::VerifySignedPayload(
-      payload_path, GetBuildArtifactsPath(kUnittestPublicKeyPath)));
+      payload_file.path(), GetBuildArtifactsPath(kUnittestPublicKeyPath)));
 }
 
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/squashfs_filesystem.cc b/payload_generator/squashfs_filesystem.cc
index c98ad12..6c892f5 100644
--- a/payload_generator/squashfs_filesystem.cc
+++ b/payload_generator/squashfs_filesystem.cc
@@ -44,14 +44,6 @@
 
 namespace {
 
-Extent ExtentForBytes(uint64_t block_size,
-                      uint64_t start_bytes,
-                      uint64_t size_bytes) {
-  uint64_t start_block = start_bytes / block_size;
-  uint64_t end_block = (start_bytes + size_bytes + block_size - 1) / block_size;
-  return ExtentForRange(start_block, end_block - start_block);
-}
-
 // The size of the squashfs super block.
 constexpr size_t kSquashfsSuperBlockSize = 96;
 constexpr uint64_t kSquashfsCompressedBit = 1 << 24;
@@ -192,8 +184,7 @@
   for (const auto& file : files_) {
     file_extents.AddExtents(file.extents);
   }
-  vector<Extent> full = {
-      ExtentForRange(0, (size_ + kBlockSize - 1) / kBlockSize)};
+  vector<Extent> full = {ExtentForBytes(kBlockSize, 0, size_)};
   auto metadata_extents = FilterExtentRanges(full, file_extents);
   // For now there should be at most two extents. One for superblock and one for
   // metadata at the end. Just create appropriate files with <metadata-i> name.
diff --git a/payload_generator/xz_android.cc b/payload_generator/xz_android.cc
index b2b74b1..41c55f7 100644
--- a/payload_generator/xz_android.cc
+++ b/payload_generator/xz_android.cc
@@ -16,12 +16,14 @@
 
 #include "update_engine/payload_generator/xz.h"
 
-#include <7zCrc.h>
-#include <Xz.h>
-#include <XzEnc.h>
+#include <elf.h>
+#include <endian.h>
 
 #include <algorithm>
 
+#include <7zCrc.h>
+#include <Xz.h>
+#include <XzEnc.h>
 #include <base/logging.h>
 
 namespace {
@@ -67,6 +69,37 @@
   brillo::Blob* data_;
 };
 
+// Returns the filter id to be used to compress |data|.
+// Only BCJ filter for x86 and ARM ELF file are supported, returns 0 otherwise.
+int GetFilterID(const brillo::Blob& data) {
+  if (data.size() < sizeof(Elf32_Ehdr) ||
+      memcmp(data.data(), ELFMAG, SELFMAG) != 0)
+    return 0;
+
+  const Elf32_Ehdr* header = reinterpret_cast<const Elf32_Ehdr*>(data.data());
+
+  // Only little-endian is supported.
+  if (header->e_ident[EI_DATA] != ELFDATA2LSB)
+    return 0;
+
+  switch (le16toh(header->e_machine)) {
+    case EM_386:
+    case EM_X86_64:
+      return XZ_ID_X86;
+    case EM_ARM:
+      // Both ARM and ARM Thumb instructions could be found in the same ARM ELF
+      // file. We choose to use the ARM Thumb filter here because testing shows
+      // that it usually works better than the ARM filter.
+      return XZ_ID_ARMT;
+#ifdef EM_AARCH64
+    case EM_AARCH64:
+      // Neither the ARM nor the ARM Thumb filter works well with AArch64.
+      return 0;
+#endif
+  }
+  return 0;
+}
+
 }  // namespace
 
 namespace chromeos_update_engine {
@@ -107,6 +140,8 @@
   Lzma2EncProps_Normalize(&lzma2Props);
   props.lzma2Props = lzma2Props;
 
+  props.filterProps.id = GetFilterID(in);
+
   BlobWriterStream out_writer(out);
   BlobReaderStream in_reader(in);
   SRes res = Xz_Encode(&out_writer, &in_reader, &props, nullptr /* progress */);
diff --git a/payload_generator/zip_unittest.cc b/payload_generator/zip_unittest.cc
index 5b0d5da..29f16d3 100644
--- a/payload_generator/zip_unittest.cc
+++ b/payload_generator/zip_unittest.cc
@@ -135,7 +135,7 @@
   brillo::Blob decompressed;
   EXPECT_TRUE(this->ZipDecompress(out, &decompressed));
   EXPECT_EQ(in.size(), decompressed.size());
-  EXPECT_TRUE(!memcmp(in.data(), decompressed.data(), in.size()));
+  EXPECT_EQ(0, memcmp(in.data(), decompressed.data(), in.size()));
 }
 
 TYPED_TEST(ZipTest, PoorCompressionTest) {
@@ -165,4 +165,18 @@
   EXPECT_EQ(0U, out.size());
 }
 
+TYPED_TEST(ZipTest, CompressELFTest) {
+  string path = test_utils::GetBuildArtifactsPath("delta_generator");
+  brillo::Blob in;
+  utils::ReadFile(path, &in);
+  brillo::Blob out;
+  EXPECT_TRUE(this->ZipCompress(in, &out));
+  EXPECT_LT(out.size(), in.size());
+  EXPECT_GT(out.size(), 0U);
+  brillo::Blob decompressed;
+  EXPECT_TRUE(this->ZipDecompress(out, &decompressed));
+  EXPECT_EQ(in.size(), decompressed.size());
+  EXPECT_EQ(0, memcmp(in.data(), decompressed.data(), in.size()));
+}
+
 }  // namespace chromeos_update_engine
diff --git a/payload_state.cc b/payload_state.cc
index 4670b14..c4eb950 100644
--- a/payload_state.cc
+++ b/payload_state.cc
@@ -304,6 +304,7 @@
     case ErrorCode::kPayloadMismatchedType:
     case ErrorCode::kUnsupportedMajorPayloadVersion:
     case ErrorCode::kUnsupportedMinorPayloadVersion:
+    case ErrorCode::kPayloadTimestampError:
       IncrementUrlIndex();
       break;
 
diff --git a/proxy_resolver_unittest.cc b/proxy_resolver_unittest.cc
index 070b361..484aae1 100644
--- a/proxy_resolver_unittest.cc
+++ b/proxy_resolver_unittest.cc
@@ -22,7 +22,6 @@
 #include <gtest/gtest.h>
 
 #include <base/bind.h>
-#include <brillo/bind_lambda.h>
 #include <brillo/message_loops/fake_message_loop.h>
 
 using std::deque;
diff --git a/scripts/brillo_update_payload b/scripts/brillo_update_payload
index b256b36..531a1bd 100755
--- a/scripts/brillo_update_payload
+++ b/scripts/brillo_update_payload
@@ -178,6 +178,10 @@
     "Optional: Path to a source image. If specified, this makes a delta update."
   DEFINE_string metadata_size_file "" \
     "Optional: Path to output metadata size."
+  DEFINE_string max_timestamp "" \
+    "Optional: The maximum unix timestamp of the OS allowed to apply this \
+payload, should be set to a number higher than the build timestamp of the \
+system running on the device, 0 if not specified."
 fi
 if [[ "${COMMAND}" == "hash" || "${COMMAND}" == "sign" ]]; then
   DEFINE_string unsigned_payload "" "Path to the input unsigned payload."
@@ -604,6 +608,10 @@
     GENERATOR_ARGS+=( --out_metadata_size_file="${FLAGS_metadata_size_file}" )
   fi
 
+  if [[ -n "${FLAGS_max_timestamp}" ]]; then
+    GENERATOR_ARGS+=( --max_timestamp="${FLAGS_max_timestamp}" )
+  fi
+
   if [[ -n "${POSTINSTALL_CONFIG_FILE}" ]]; then
     GENERATOR_ARGS+=(
       --new_postinstall_config_file="${POSTINSTALL_CONFIG_FILE}"
diff --git a/test_http_server.cc b/test_http_server.cc
index 39a12ed..cf15672 100644
--- a/test_http_server.cc
+++ b/test_http_server.cc
@@ -98,9 +98,11 @@
   request->raw_headers = headers;
 
   // Break header into lines.
-  vector<string> lines;
-  base::SplitStringUsingSubstr(
-      headers.substr(0, headers.length() - strlen(EOL EOL)), EOL, &lines);
+  vector<string> lines = base::SplitStringUsingSubstr(
+      headers.substr(0, headers.length() - strlen(EOL EOL)),
+      EOL,
+      base::TRIM_WHITESPACE,
+      base::SPLIT_WANT_ALL);
 
   // Decode URL line.
   vector<string> terms = base::SplitString(lines[0], base::kWhitespaceASCII,
diff --git a/update_attempter.cc b/update_attempter.cc
index 71a4ae1..db373ab 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -31,7 +31,6 @@
 #include <base/rand_util.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
-#include <brillo/bind_lambda.h>
 #include <brillo/data_encoding.h>
 #include <brillo/errors/error_codes.h>
 #include <brillo/message_loops/message_loop.h>
diff --git a/update_attempter_android.cc b/update_attempter_android.cc
index 9b0b42a..9443869 100644
--- a/update_attempter_android.cc
+++ b/update_attempter_android.cc
@@ -29,6 +29,7 @@
 #include <brillo/data_encoding.h>
 #include <brillo/message_loops/message_loop.h>
 #include <brillo/strings/string_utils.h>
+#include <log/log_safetynet.h>
 
 #include "update_engine/common/constants.h"
 #include "update_engine/common/error_code_utils.h"
@@ -138,7 +139,7 @@
     return LogAndSetError(
         error, FROM_HERE, "An update already applied, waiting for reboot");
   }
-  if (ongoing_update_) {
+  if (processor_->IsRunning()) {
     return LogAndSetError(
         error, FROM_HERE, "Already processing an update, cancel it first.");
   }
@@ -269,7 +270,6 @@
   BuildUpdateActions(fetcher);
 
   SetStatusAndNotify(UpdateStatus::UPDATE_AVAILABLE);
-  ongoing_update_ = true;
 
   UpdatePrefsOnUpdateStart(install_plan_.is_resume);
   // TODO(xunchang) report the metrics for unresumable updates
@@ -279,21 +279,21 @@
 }
 
 bool UpdateAttempterAndroid::SuspendUpdate(brillo::ErrorPtr* error) {
-  if (!ongoing_update_)
+  if (!processor_->IsRunning())
     return LogAndSetError(error, FROM_HERE, "No ongoing update to suspend.");
   processor_->SuspendProcessing();
   return true;
 }
 
 bool UpdateAttempterAndroid::ResumeUpdate(brillo::ErrorPtr* error) {
-  if (!ongoing_update_)
+  if (!processor_->IsRunning())
     return LogAndSetError(error, FROM_HERE, "No ongoing update to resume.");
   processor_->ResumeProcessing();
   return true;
 }
 
 bool UpdateAttempterAndroid::CancelUpdate(brillo::ErrorPtr* error) {
-  if (!ongoing_update_)
+  if (!processor_->IsRunning())
     return LogAndSetError(error, FROM_HERE, "No ongoing update to cancel.");
   processor_->StopProcessing();
   return true;
@@ -457,6 +457,11 @@
       LOG(INFO) << "Resetting update progress.";
       break;
 
+    case ErrorCode::kPayloadTimestampError:
+      // SafetyNet logging, b/36232423
+      android_errorWriteLog(0x534e4554, "36232423");
+      break;
+
     default:
       // Ignore all other error codes.
       break;
@@ -558,7 +563,6 @@
       (error_code == ErrorCode::kSuccess ? UpdateStatus::UPDATED_NEED_REBOOT
                                          : UpdateStatus::IDLE);
   SetStatusAndNotify(new_status);
-  ongoing_update_ = false;
 
   // The network id is only applicable to one download attempt and once it's
   // done the network id should not be re-used anymore.
diff --git a/update_attempter_android.h b/update_attempter_android.h
index cad89dc..3faeac9 100644
--- a/update_attempter_android.h
+++ b/update_attempter_android.h
@@ -175,11 +175,6 @@
   // The processor for running Actions.
   std::unique_ptr<ActionProcessor> processor_;
 
-  // Whether there is an ongoing update. This implies that an update was started
-  // but not finished yet. This value will be true even if the update was
-  // suspended.
-  bool ongoing_update_{false};
-
   // The InstallPlan used during the ongoing update.
   InstallPlan install_plan_;
 
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 93fcef1..fb9f7bc 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -22,7 +22,6 @@
 
 #include <base/files/file_util.h>
 #include <base/message_loop/message_loop.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>
diff --git a/update_engine.gyp b/update_engine.gyp
index cbdce2b..ee2471a 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -395,6 +395,7 @@
         'payload_generator/annotated_operation.cc',
         'payload_generator/blob_file_writer.cc',
         'payload_generator/block_mapping.cc',
+        'payload_generator/boot_img_filesystem.cc',
         'payload_generator/bzip.cc',
         'payload_generator/cycle_breaker.cc',
         'payload_generator/deflate_utils.cc',
@@ -548,6 +549,7 @@
             'payload_generator/ab_generator_unittest.cc',
             'payload_generator/blob_file_writer_unittest.cc',
             'payload_generator/block_mapping_unittest.cc',
+            'payload_generator/boot_img_filesystem_unittest.cc',
             'payload_generator/cycle_breaker_unittest.cc',
             'payload_generator/deflate_utils_unittest.cc',
             'payload_generator/delta_diff_utils_unittest.cc',
diff --git a/update_manager/chromeos_policy.cc b/update_manager/chromeos_policy.cc
index abb06c7..71fec40 100644
--- a/update_manager/chromeos_policy.cc
+++ b/update_manager/chromeos_policy.cc
@@ -86,6 +86,7 @@
     case ErrorCode::kPayloadMismatchedType:
     case ErrorCode::kUnsupportedMajorPayloadVersion:
     case ErrorCode::kUnsupportedMinorPayloadVersion:
+    case ErrorCode::kPayloadTimestampError:
       LOG(INFO) << "Advancing download URL due to error "
                 << chromeos_update_engine::utils::ErrorCodeToString(err_code)
                 << " (" << static_cast<int>(err_code) << ")";
diff --git a/update_manager/real_shill_provider_unittest.cc b/update_manager/real_shill_provider_unittest.cc
index af674d0..6506923 100644
--- a/update_manager/real_shill_provider_unittest.cc
+++ b/update_manager/real_shill_provider_unittest.cc
@@ -94,7 +94,9 @@
     now_exp.minute = 5;
     now_exp.second = 33;
     now_exp.millisecond = 675;
-    return Time::FromLocalExploded(now_exp);
+    Time time;
+    ignore_result(Time::FromLocalExploded(now_exp, &time));
+    return time;
   }
 
   Time ConnChangedTime() {
diff --git a/update_manager/real_time_provider.cc b/update_manager/real_time_provider.cc
index 92f985f..baa8ae3 100644
--- a/update_manager/real_time_provider.cc
+++ b/update_manager/real_time_provider.cc
@@ -43,7 +43,10 @@
     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));
+    Time* now = new Time();
+    bool success = Time::FromLocalExploded(now_exp, now);
+    DCHECK(success);
+    return now;
   }
 
  private:
diff --git a/update_manager/real_time_provider_unittest.cc b/update_manager/real_time_provider_unittest.cc
index 093b158..ce2a718 100644
--- a/update_manager/real_time_provider_unittest.cc
+++ b/update_manager/real_time_provider_unittest.cc
@@ -51,7 +51,9 @@
     now_exp.minute = 5;
     now_exp.second = 33;
     now_exp.millisecond = 675;
-    return Time::FromLocalExploded(now_exp);
+    Time time;
+    ignore_result(Time::FromLocalExploded(now_exp, &time));
+    return time;
   }
 
   FakeClock fake_clock_;
@@ -66,7 +68,8 @@
   exploded.minute = 0;
   exploded.second = 0;
   exploded.millisecond = 0;
-  const Time expected = Time::FromLocalExploded(exploded);
+  Time expected;
+  ignore_result(Time::FromLocalExploded(exploded, &expected));
 
   fake_clock_.SetWallclockTime(now);
   UmTestUtils::ExpectVariableHasValue(expected, provider_->var_curr_date());
diff --git a/update_manager/real_updater_provider_unittest.cc b/update_manager/real_updater_provider_unittest.cc
index efe042c..b653885 100644
--- a/update_manager/real_updater_provider_unittest.cc
+++ b/update_manager/real_updater_provider_unittest.cc
@@ -57,7 +57,9 @@
   now_exp.minute = 5;
   now_exp.second = 33;
   now_exp.millisecond = 675;
-  return Time::FromLocalExploded(now_exp);
+  Time time;
+  ignore_result(Time::FromLocalExploded(now_exp, &time));
+  return time;
 }
 
 // Rounds down a timestamp to the nearest second. This is useful when faking
@@ -66,7 +68,9 @@
   Time::Exploded exp;
   time.LocalExplode(&exp);
   exp.millisecond = 0;
-  return Time::FromLocalExploded(exp);
+  Time rounded_time;
+  ignore_result(Time::FromLocalExploded(exp, &rounded_time));
+  return rounded_time;
 }
 
 ACTION_P(ActionSetUpdateEngineStatusLastCheckedTime, time) {
diff --git a/update_manager/update_manager_unittest.cc b/update_manager/update_manager_unittest.cc
index 9625b53..125a60c 100644
--- a/update_manager/update_manager_unittest.cc
+++ b/update_manager/update_manager_unittest.cc
@@ -67,7 +67,9 @@
   now_exp.minute = 5;
   now_exp.second = 33;
   now_exp.millisecond = 675;
-  return Time::FromLocalExploded(now_exp);
+  Time time;
+  ignore_result(Time::FromLocalExploded(now_exp, &time));
+  return time;
 }
 
 }  // namespace
diff --git a/update_manager/update_time_restrictions_policy_impl_unittest.cc b/update_manager/update_time_restrictions_policy_impl_unittest.cc
index ac4e0e9..74e7f3c 100644
--- a/update_manager/update_time_restrictions_policy_impl_unittest.cc
+++ b/update_manager/update_time_restrictions_policy_impl_unittest.cc
@@ -57,7 +57,10 @@
       fake_state_.device_policy_provider()
           ->var_auto_launched_kiosk_app_id()
           ->reset(new string("myapp"));
-    fake_clock_.SetWallclockTime(Time::FromLocalExploded(exploded));
+
+    Time time;
+    EXPECT_TRUE(Time::FromLocalExploded(exploded, &time));
+    fake_clock_.SetWallclockTime(time);
     SetUpDefaultTimeProvider();
     fake_state_.device_policy_provider()
         ->var_disallowed_time_intervals()
diff --git a/update_metadata.proto b/update_metadata.proto
index fe81efb..a0f278b 100644
--- a/update_metadata.proto
+++ b/update_metadata.proto
@@ -290,4 +290,8 @@
   // array can have more than two partitions if needed, and they are identified
   // by the partition name.
   repeated PartitionUpdate partitions = 13;
+
+  // The maximum timestamp of the OS allowed to apply this payload.
+  // Can be used to prevent downgrading the OS.
+  optional int64 max_timestamp = 14;
 }