Merge "update_engine: Install payload signing public key"
diff --git a/delta_performer.cc b/delta_performer.cc
index 2538329..0482a8f 100644
--- a/delta_performer.cc
+++ b/delta_performer.cc
@@ -59,7 +59,6 @@
 const uint64_t DeltaPerformer::kDeltaManifestSizeSize = 8;
 const uint64_t DeltaPerformer::kSupportedMajorPayloadVersion = 1;
 const uint64_t DeltaPerformer::kSupportedMinorPayloadVersion = 2;
-const uint64_t DeltaPerformer::kFullPayloadMinorVersion = 0;
 
 const char DeltaPerformer::kUpdatePayloadPublicKeyPath[] =
     "/usr/share/update_engine/update-payload-key.pub.pem";
@@ -68,11 +67,6 @@
 const unsigned DeltaPerformer::kProgressDownloadWeight = 50;
 const unsigned DeltaPerformer::kProgressOperationsWeight = 50;
 
-const uint64_t kChromeOSMajorPayloadVersion = 1;
-const uint64_t kBrilloMajorPayloadVersion = 2;
-const uint32_t kInPlaceMinorPayloadVersion = 1;
-const uint32_t kSourceMinorPayloadVersion = 2;
-
 namespace {
 const int kUpdateStateOperationInvalid = -1;
 const int kMaxResumedUpdateFailures = 10;
@@ -283,7 +277,7 @@
 
 int DeltaPerformer::Close() {
   int err = 0;
-  if (!kernel_fd_->Close()) {
+  if (kernel_fd_ && !kernel_fd_->Close()) {
     err = errno;
     PLOG(ERROR) << "Unable to close kernel fd:";
   }
@@ -449,7 +443,6 @@
                  << install_plan_->metadata_size
                  << ") in Omaha response as validation is not mandatory. "
                  << "Trusting metadata size in payload = " << metadata_size_;
-    SendUmaStat(ErrorCode::kDownloadInvalidMetadataSize);
   }
 
   // We have the full metadata in |payload|. Verify its integrity
@@ -465,7 +458,6 @@
 
     // For non-mandatory cases, just send a UMA stat.
     LOG(WARNING) << "Ignoring metadata signature validation failures";
-    SendUmaStat(*error);
     *error = ErrorCode::kSuccess;
   }
 
@@ -595,7 +587,6 @@
 
         // For non-mandatory cases, just send a UMA stat.
         LOG(WARNING) << "Ignoring operation validation errors";
-        SendUmaStat(*error);
         *error = ErrorCode::kSuccess;
       }
     }
@@ -1036,9 +1027,7 @@
       return ErrorCode::kDownloadMetadataSignatureMissingError;
     }
 
-    // For non-mandatory cases, just send a UMA stat.
     LOG(WARNING) << "Cannot validate metadata as the signature is empty";
-    SendUmaStat(ErrorCode::kDownloadMetadataSignatureMissingError);
     return ErrorCode::kSuccess;
   }
 
@@ -1171,10 +1160,8 @@
         return ErrorCode::kDownloadOperationHashMissingError;
       }
 
-      // For non-mandatory cases, just send a UMA stat.
       LOG(WARNING) << "Cannot validate operation " << next_operation_num_ + 1
                    << " as there's no operation hash in manifest";
-      SendUmaStat(ErrorCode::kDownloadOperationHashMissingError);
     }
     return ErrorCode::kSuccess;
   }
@@ -1540,8 +1527,4 @@
   return true;
 }
 
-void DeltaPerformer::SendUmaStat(ErrorCode code) {
-  utils::SendErrorCodeToUma(system_state_, code);
-}
-
 }  // namespace chromeos_update_engine
diff --git a/delta_performer.h b/delta_performer.h
index 38a4151..d088796 100644
--- a/delta_performer.h
+++ b/delta_performer.h
@@ -36,18 +36,6 @@
 
 namespace chromeos_update_engine {
 
-// The major version used by Chrome OS.
-extern const uint64_t kChromeOSMajorPayloadVersion;
-
-// The major version used by Brillo.
-extern const uint64_t kBrilloMajorPayloadVersion;
-
-// The minor version used by the in-place delta generator algorithm.
-extern const uint32_t kInPlaceMinorPayloadVersion;
-
-// The minor version used by the A to B delta generator algorithm.
-extern const uint32_t kSourceMinorPayloadVersion;
-
 class PrefsInterface;
 
 // This class performs the actions in a delta update synchronously. The delta
@@ -65,7 +53,6 @@
   static const uint64_t kDeltaManifestSizeSize;
   static const uint64_t kSupportedMajorPayloadVersion;
   static const uint64_t kSupportedMinorPayloadVersion;
-  static const uint64_t kFullPayloadMinorVersion;
   static const char kUpdatePayloadPublicKeyPath[];
 
   // Defines the granularity of progress logging in terms of how many "completed
@@ -225,7 +212,7 @@
 
  private:
   friend class DeltaPerformerTest;
-  FRIEND_TEST(DeltaPerformerTest, IsIdempotentOperationTest);
+  friend class DeltaPerformerIntegrationTest;
   FRIEND_TEST(DeltaPerformerTest, UsePublicKeyFromResponse);
 
   // Appends up to |*count_p| bytes from |*bytes_p| to |buffer_|, but only to
@@ -308,9 +295,6 @@
   // update. Returns false otherwise.
   bool PrimeUpdateState();
 
-  // Sends UMA statistics for the given error code.
-  void SendUmaStat(ErrorCode code);
-
   // If the Omaha response contains a public RSA key and we're allowed
   // to use it (e.g. if we're in developer mode), extract the key from
   // the response and store it in a temporary file and return true. In
diff --git a/delta_performer_integration_test.cc b/delta_performer_integration_test.cc
new file mode 100644
index 0000000..1e3dea8
--- /dev/null
+++ b/delta_performer_integration_test.cc
@@ -0,0 +1,1005 @@
+//
+// Copyright (C) 2012 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/delta_performer.h"
+
+#include <inttypes.h>
+#include <sys/mount.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/strings/stringprintf.h>
+#include <base/strings/string_util.h>
+#include <google/protobuf/repeated_field.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/constants.h"
+#include "update_engine/fake_hardware.h"
+#include "update_engine/fake_system_state.h"
+#include "update_engine/mock_prefs.h"
+#include "update_engine/payload_constants.h"
+#include "update_engine/payload_generator/delta_diff_generator.h"
+#include "update_engine/payload_generator/payload_signer.h"
+#include "update_engine/payload_verifier.h"
+#include "update_engine/test_utils.h"
+#include "update_engine/update_metadata.pb.h"
+#include "update_engine/utils.h"
+
+namespace chromeos_update_engine {
+
+using std::string;
+using std::vector;
+using testing::Return;
+using testing::_;
+using test_utils::kRandomString;
+using test_utils::ScopedLoopMounter;
+using test_utils::System;
+
+extern const char* kUnittestPrivateKeyPath;
+extern const char* kUnittestPublicKeyPath;
+extern const char* kUnittestPrivateKey2Path;
+extern const char* kUnittestPublicKey2Path;
+
+static const int kDefaultKernelSize = 4096;  // Something small for a test
+static const uint8_t kNewData[] = {'T', 'h', 'i', 's', ' ', 'i', 's', ' ',
+                                   'n', 'e', 'w', ' ', 'd', 'a', 't', 'a', '.'};
+
+namespace {
+struct DeltaState {
+  string a_img;
+  string b_img;
+  string result_img;
+  size_t image_size;
+
+  string delta_path;
+  uint64_t metadata_size;
+
+  string old_kernel;
+  chromeos::Blob old_kernel_data;
+
+  string new_kernel;
+  chromeos::Blob new_kernel_data;
+
+  string result_kernel;
+  chromeos::Blob result_kernel_data;
+  size_t kernel_size;
+
+  // The in-memory copy of delta file.
+  chromeos::Blob delta;
+
+  // The mock system state object with which we initialize the
+  // delta performer.
+  FakeSystemState fake_system_state;
+};
+
+enum SignatureTest {
+  kSignatureNone,  // No payload signing.
+  kSignatureGenerator,  // Sign the payload at generation time.
+  kSignatureGenerated,  // Sign the payload after it's generated.
+  kSignatureGeneratedPlaceholder,  // Insert placeholder signatures, then real.
+  kSignatureGeneratedPlaceholderMismatch,  // Insert a wrong sized placeholder.
+  kSignatureGeneratedShell,  // Sign the generated payload through shell cmds.
+  kSignatureGeneratedShellBadKey,  // Sign with a bad key through shell cmds.
+  kSignatureGeneratedShellRotateCl1,  // Rotate key, test client v1
+  kSignatureGeneratedShellRotateCl2,  // Rotate key, test client v2
+};
+
+enum OperationHashTest {
+  kInvalidOperationData,
+  kValidOperationData,
+};
+
+}  // namespace
+
+class DeltaPerformerIntegrationTest : public ::testing::Test {
+ public:
+  static void SetSupportedVersion(DeltaPerformer* performer,
+                                  uint64_t minor_version) {
+    performer->supported_minor_version_ = minor_version;
+  }
+};
+
+static void CompareFilesByBlock(const string& a_file, const string& b_file,
+                                size_t image_size) {
+  EXPECT_EQ(0, image_size % kBlockSize);
+
+  chromeos::Blob a_data, b_data;
+  EXPECT_TRUE(utils::ReadFile(a_file, &a_data)) << "file failed: " << a_file;
+  EXPECT_TRUE(utils::ReadFile(b_file, &b_data)) << "file failed: " << b_file;
+
+  EXPECT_GE(a_data.size(), image_size);
+  EXPECT_GE(b_data.size(), image_size);
+  for (size_t i = 0; i < image_size; i += kBlockSize) {
+    EXPECT_EQ(0, i % kBlockSize);
+    chromeos::Blob a_sub(&a_data[i], &a_data[i + kBlockSize]);
+    chromeos::Blob b_sub(&b_data[i], &b_data[i + kBlockSize]);
+    EXPECT_TRUE(a_sub == b_sub) << "Block " << (i/kBlockSize) << " differs";
+  }
+  if (::testing::Test::HasNonfatalFailure()) {
+    LOG(INFO) << "Compared filesystems with size " << image_size
+              << ", partition A " << a_file << " size: " << a_data.size()
+              << ", partition B " << b_file << " size: " << b_data.size();
+  }
+}
+
+static bool WriteSparseFile(const string& path, off_t size) {
+  int fd = open(path.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644);
+  TEST_AND_RETURN_FALSE_ERRNO(fd >= 0);
+  ScopedFdCloser fd_closer(&fd);
+  off_t rc = lseek(fd, size + 1, SEEK_SET);
+  TEST_AND_RETURN_FALSE_ERRNO(rc != static_cast<off_t>(-1));
+  int return_code = ftruncate(fd, size);
+  TEST_AND_RETURN_FALSE_ERRNO(return_code == 0);
+  return true;
+}
+
+static size_t GetSignatureSize(const string& private_key_path) {
+  const chromeos::Blob data(1, 'x');
+  chromeos::Blob hash;
+  EXPECT_TRUE(OmahaHashCalculator::RawHashOfData(data, &hash));
+  chromeos::Blob signature;
+  EXPECT_TRUE(PayloadSigner::SignHash(hash,
+                                      private_key_path,
+                                      &signature));
+  return signature.size();
+}
+
+static bool InsertSignaturePlaceholder(int signature_size,
+                                       const string& payload_path,
+                                       uint64_t* out_metadata_size) {
+  vector<chromeos::Blob> signatures;
+  signatures.push_back(chromeos::Blob(signature_size, 0));
+
+  return PayloadSigner::AddSignatureToPayload(
+      payload_path,
+      signatures,
+      payload_path,
+      out_metadata_size);
+}
+
+static void SignGeneratedPayload(const string& payload_path,
+                                 uint64_t* out_metadata_size) {
+  int signature_size = GetSignatureSize(kUnittestPrivateKeyPath);
+  chromeos::Blob hash;
+  ASSERT_TRUE(PayloadSigner::HashPayloadForSigning(
+      payload_path,
+      vector<int>(1, signature_size),
+      &hash));
+  chromeos::Blob signature;
+  ASSERT_TRUE(PayloadSigner::SignHash(hash,
+                                      kUnittestPrivateKeyPath,
+                                      &signature));
+  ASSERT_TRUE(PayloadSigner::AddSignatureToPayload(
+      payload_path,
+      vector<chromeos::Blob>(1, signature),
+      payload_path,
+      out_metadata_size));
+  EXPECT_TRUE(PayloadVerifier::VerifySignedPayload(
+      payload_path,
+      kUnittestPublicKeyPath,
+      kSignatureMessageOriginalVersion));
+}
+
+static void SignGeneratedShellPayload(SignatureTest signature_test,
+                                      const string& payload_path) {
+  string private_key_path = kUnittestPrivateKeyPath;
+  if (signature_test == kSignatureGeneratedShellBadKey) {
+    ASSERT_TRUE(utils::MakeTempFile("key.XXXXXX",
+                                    &private_key_path,
+                                    nullptr));
+  } else {
+    ASSERT_TRUE(signature_test == kSignatureGeneratedShell ||
+                signature_test == kSignatureGeneratedShellRotateCl1 ||
+                signature_test == kSignatureGeneratedShellRotateCl2);
+  }
+  ScopedPathUnlinker key_unlinker(private_key_path);
+  key_unlinker.set_should_remove(signature_test ==
+                                 kSignatureGeneratedShellBadKey);
+  // Generates a new private key that will not match the public key.
+  if (signature_test == kSignatureGeneratedShellBadKey) {
+    LOG(INFO) << "Generating a mismatched private key.";
+    ASSERT_EQ(0, System(base::StringPrintf(
+        "openssl genrsa -out %s 2048", private_key_path.c_str())));
+  }
+  int signature_size = GetSignatureSize(private_key_path);
+  string hash_file;
+  ASSERT_TRUE(utils::MakeTempFile("hash.XXXXXX", &hash_file, nullptr));
+  ScopedPathUnlinker hash_unlinker(hash_file);
+  string signature_size_string;
+  if (signature_test == kSignatureGeneratedShellRotateCl1 ||
+      signature_test == kSignatureGeneratedShellRotateCl2)
+    signature_size_string = base::StringPrintf("%d:%d",
+                                               signature_size, signature_size);
+  else
+    signature_size_string = base::StringPrintf("%d", signature_size);
+  ASSERT_EQ(0,
+            System(base::StringPrintf(
+                "./delta_generator -in_file=%s -signature_size=%s "
+                "-out_hash_file=%s",
+                payload_path.c_str(),
+                signature_size_string.c_str(),
+                hash_file.c_str())));
+
+  // Pad the hash
+  chromeos::Blob hash;
+  ASSERT_TRUE(utils::ReadFile(hash_file, &hash));
+  ASSERT_TRUE(PayloadVerifier::PadRSA2048SHA256Hash(&hash));
+  ASSERT_TRUE(test_utils::WriteFileVector(hash_file, hash));
+
+  string sig_file;
+  ASSERT_TRUE(utils::MakeTempFile("signature.XXXXXX", &sig_file, nullptr));
+  ScopedPathUnlinker sig_unlinker(sig_file);
+  ASSERT_EQ(0,
+            System(base::StringPrintf(
+                "openssl rsautl -raw -sign -inkey %s -in %s -out %s",
+                private_key_path.c_str(),
+                hash_file.c_str(),
+                sig_file.c_str())));
+  string sig_file2;
+  ASSERT_TRUE(utils::MakeTempFile("signature.XXXXXX", &sig_file2, nullptr));
+  ScopedPathUnlinker sig2_unlinker(sig_file2);
+  if (signature_test == kSignatureGeneratedShellRotateCl1 ||
+      signature_test == kSignatureGeneratedShellRotateCl2) {
+    ASSERT_EQ(0,
+              System(base::StringPrintf(
+                  "openssl rsautl -raw -sign -inkey %s -in %s -out %s",
+                  kUnittestPrivateKey2Path,
+                  hash_file.c_str(),
+                  sig_file2.c_str())));
+    // Append second sig file to first path
+    sig_file += ":" + sig_file2;
+  }
+
+  ASSERT_EQ(0,
+            System(base::StringPrintf(
+                "./delta_generator -in_file=%s -signature_file=%s "
+                "-out_file=%s",
+                payload_path.c_str(),
+                sig_file.c_str(),
+                payload_path.c_str())));
+  int verify_result =
+      System(base::StringPrintf(
+          "./delta_generator -in_file=%s -public_key=%s -public_key_version=%d",
+          payload_path.c_str(),
+          signature_test == kSignatureGeneratedShellRotateCl2 ?
+          kUnittestPublicKey2Path : kUnittestPublicKeyPath,
+          signature_test == kSignatureGeneratedShellRotateCl2 ? 2 : 1));
+  if (signature_test == kSignatureGeneratedShellBadKey) {
+    ASSERT_NE(0, verify_result);
+  } else {
+    ASSERT_EQ(0, verify_result);
+  }
+}
+
+static void GenerateDeltaFile(bool full_kernel,
+                              bool full_rootfs,
+                              bool noop,
+                              ssize_t chunk_size,
+                              SignatureTest signature_test,
+                              DeltaState *state,
+                              uint32_t minor_version) {
+  EXPECT_TRUE(utils::MakeTempFile("a_img.XXXXXX", &state->a_img, nullptr));
+  EXPECT_TRUE(utils::MakeTempFile("b_img.XXXXXX", &state->b_img, nullptr));
+
+  // result_img is used in minor version 2. Instead of applying the update
+  // in-place on A, we apply it to a new image, result_img.
+  EXPECT_TRUE(
+      utils::MakeTempFile("result_img.XXXXXX", &state->result_img, nullptr));
+  test_utils::CreateExtImageAtPath(state->a_img, nullptr);
+
+  state->image_size = utils::FileSize(state->a_img);
+
+  // Extend the "partitions" holding the file system a bit.
+  EXPECT_EQ(0, HANDLE_EINTR(truncate(state->a_img.c_str(),
+                                     state->image_size + 1024 * 1024)));
+  EXPECT_EQ(state->image_size + 1024 * 1024, utils::FileSize(state->a_img));
+
+  // Create ImageInfo A & B
+  ImageInfo old_image_info;
+  ImageInfo new_image_info;
+
+  if (!full_rootfs) {
+    old_image_info.set_channel("src-channel");
+    old_image_info.set_board("src-board");
+    old_image_info.set_version("src-version");
+    old_image_info.set_key("src-key");
+    old_image_info.set_build_channel("src-build-channel");
+    old_image_info.set_build_version("src-build-version");
+  }
+
+  new_image_info.set_channel("test-channel");
+  new_image_info.set_board("test-board");
+  new_image_info.set_version("test-version");
+  new_image_info.set_key("test-key");
+  new_image_info.set_build_channel("test-build-channel");
+  new_image_info.set_build_version("test-build-version");
+
+  // Make some changes to the A image.
+  {
+    string a_mnt;
+    ScopedLoopMounter b_mounter(state->a_img, &a_mnt, 0);
+
+    chromeos::Blob hardtocompress;
+    while (hardtocompress.size() < 3 * kBlockSize) {
+      hardtocompress.insert(hardtocompress.end(),
+                            std::begin(kRandomString), std::end(kRandomString));
+    }
+    EXPECT_TRUE(utils::WriteFile(base::StringPrintf("%s/hardtocompress",
+                                                    a_mnt.c_str()).c_str(),
+                                 hardtocompress.data(),
+                                 hardtocompress.size()));
+
+    chromeos::Blob zeros(16 * 1024, 0);
+    EXPECT_EQ(zeros.size(),
+              base::WriteFile(base::FilePath(base::StringPrintf(
+                                  "%s/move-to-sparse", a_mnt.c_str())),
+                              reinterpret_cast<const char*>(zeros.data()),
+                              zeros.size()));
+
+    EXPECT_TRUE(
+        WriteSparseFile(base::StringPrintf("%s/move-from-sparse",
+                                           a_mnt.c_str()), 16 * 1024));
+
+    EXPECT_EQ(0,
+              System(base::StringPrintf("dd if=/dev/zero of=%s/move-semi-sparse"
+                                        " bs=1 seek=4096 count=1 status=none",
+                                        a_mnt.c_str()).c_str()));
+
+    // Write 1 MiB of 0xff to try to catch the case where writing a bsdiff
+    // patch fails to zero out the final block.
+    chromeos::Blob ones(1024 * 1024, 0xff);
+    EXPECT_TRUE(utils::WriteFile(base::StringPrintf("%s/ones",
+                                                    a_mnt.c_str()).c_str(),
+                                 ones.data(),
+                                 ones.size()));
+  }
+
+  if (noop) {
+    EXPECT_TRUE(base::CopyFile(base::FilePath(state->a_img),
+                               base::FilePath(state->b_img)));
+    old_image_info = new_image_info;
+  } else {
+    if (minor_version == kSourceMinorPayloadVersion) {
+      // Create a result image with image_size bytes of garbage, followed by
+      // zeroes after the rootfs, like image A and B have.
+      chromeos::Blob ones(state->image_size, 0xff);
+      ones.insert(ones.end(), 1024 * 1024, 0);
+      EXPECT_TRUE(utils::WriteFile(state->result_img.c_str(),
+                                   ones.data(),
+                                   ones.size()));
+      EXPECT_EQ(utils::FileSize(state->a_img),
+                utils::FileSize(state->result_img));
+    }
+
+    test_utils::CreateExtImageAtPath(state->b_img, nullptr);
+    EXPECT_EQ(0, HANDLE_EINTR(truncate(state->b_img.c_str(),
+                                       state->image_size + 1024 * 1024)));
+    EXPECT_EQ(state->image_size + 1024 * 1024, utils::FileSize(state->b_img));
+
+    // Make some changes to the B image.
+    string b_mnt;
+    ScopedLoopMounter b_mounter(state->b_img, &b_mnt, 0);
+
+    EXPECT_EQ(0, System(base::StringPrintf("cp %s/hello %s/hello2",
+                                           b_mnt.c_str(),
+                                           b_mnt.c_str()).c_str()));
+    EXPECT_EQ(0, System(base::StringPrintf("rm %s/hello",
+                                           b_mnt.c_str()).c_str()));
+    EXPECT_EQ(0, System(base::StringPrintf("mv %s/hello2 %s/hello",
+                                           b_mnt.c_str(),
+                                           b_mnt.c_str()).c_str()));
+    EXPECT_EQ(0, System(base::StringPrintf("echo foo > %s/foo",
+                                           b_mnt.c_str()).c_str()));
+    EXPECT_EQ(0, System(base::StringPrintf("touch %s/emptyfile",
+                                           b_mnt.c_str()).c_str()));
+    EXPECT_TRUE(WriteSparseFile(base::StringPrintf("%s/fullsparse",
+                                                   b_mnt.c_str()),
+                                                   1024 * 1024));
+
+    EXPECT_TRUE(
+        WriteSparseFile(base::StringPrintf("%s/move-to-sparse", b_mnt.c_str()),
+                        16 * 1024));
+
+    chromeos::Blob zeros(16 * 1024, 0);
+    EXPECT_EQ(zeros.size(),
+              base::WriteFile(base::FilePath(base::StringPrintf(
+                                  "%s/move-from-sparse", b_mnt.c_str())),
+                              reinterpret_cast<const char*>(zeros.data()),
+                              zeros.size()));
+
+    EXPECT_EQ(0, System(base::StringPrintf("dd if=/dev/zero "
+                                           "of=%s/move-semi-sparse "
+                                           "bs=1 seek=4096 count=1 status=none",
+                                           b_mnt.c_str()).c_str()));
+
+    EXPECT_EQ(0, System(base::StringPrintf("dd if=/dev/zero "
+                                           "of=%s/partsparse bs=1 "
+                                           "seek=4096 count=1 status=none",
+                                           b_mnt.c_str()).c_str()));
+    EXPECT_EQ(0, System(base::StringPrintf("cp %s/srchardlink0 %s/tmp && "
+                                           "mv %s/tmp %s/srchardlink1",
+                                           b_mnt.c_str(),
+                                           b_mnt.c_str(),
+                                           b_mnt.c_str(),
+                                           b_mnt.c_str()).c_str()));
+    EXPECT_EQ(0, System(
+        base::StringPrintf("rm %s/boguslink && echo foobar > %s/boguslink",
+                           b_mnt.c_str(), b_mnt.c_str()).c_str()));
+
+    chromeos::Blob hardtocompress;
+    while (hardtocompress.size() < 3 * kBlockSize) {
+      hardtocompress.insert(hardtocompress.end(),
+                            std::begin(kRandomString), std::end(kRandomString));
+    }
+    EXPECT_TRUE(utils::WriteFile(base::StringPrintf("%s/hardtocompress",
+                                              b_mnt.c_str()).c_str(),
+                                 hardtocompress.data(),
+                                 hardtocompress.size()));
+  }
+
+  string old_kernel;
+  EXPECT_TRUE(utils::MakeTempFile("old_kernel.XXXXXX",
+                                  &state->old_kernel,
+                                  nullptr));
+
+  string new_kernel;
+  EXPECT_TRUE(utils::MakeTempFile("new_kernel.XXXXXX",
+                                  &state->new_kernel,
+                                  nullptr));
+
+  string result_kernel;
+  EXPECT_TRUE(utils::MakeTempFile("result_kernel.XXXXXX",
+                                  &state->result_kernel,
+                                  nullptr));
+
+  state->kernel_size = kDefaultKernelSize;
+  state->old_kernel_data.resize(kDefaultKernelSize);
+  state->new_kernel_data.resize(state->old_kernel_data.size());
+  state->result_kernel_data.resize(state->old_kernel_data.size());
+  test_utils::FillWithData(&state->old_kernel_data);
+  test_utils::FillWithData(&state->new_kernel_data);
+  test_utils::FillWithData(&state->result_kernel_data);
+
+  // change the new kernel data
+  std::copy(std::begin(kNewData), std::end(kNewData),
+            state->new_kernel_data.begin());
+
+  if (noop) {
+    state->old_kernel_data = state->new_kernel_data;
+  }
+
+  // Write kernels to disk
+  EXPECT_TRUE(utils::WriteFile(state->old_kernel.c_str(),
+                               state->old_kernel_data.data(),
+                               state->old_kernel_data.size()));
+  EXPECT_TRUE(utils::WriteFile(state->new_kernel.c_str(),
+                               state->new_kernel_data.data(),
+                               state->new_kernel_data.size()));
+  EXPECT_TRUE(utils::WriteFile(state->result_kernel.c_str(),
+                               state->result_kernel_data.data(),
+                               state->result_kernel_data.size()));
+
+  EXPECT_TRUE(utils::MakeTempFile("delta.XXXXXX",
+                                  &state->delta_path,
+                                  nullptr));
+  LOG(INFO) << "delta path: " << state->delta_path;
+  {
+    const string private_key =
+        signature_test == kSignatureGenerator ? kUnittestPrivateKeyPath : "";
+
+    PayloadGenerationConfig payload_config;
+    payload_config.is_delta = !full_rootfs;
+    payload_config.hard_chunk_size = chunk_size;
+    payload_config.rootfs_partition_size = kRootFSPartitionSize;
+    payload_config.major_version = kChromeOSMajorPayloadVersion;
+    payload_config.minor_version = minor_version;
+    if (!full_rootfs) {
+      payload_config.source.rootfs.path = state->a_img;
+      if (!full_kernel)
+        payload_config.source.kernel.path = state->old_kernel;
+      payload_config.source.image_info = old_image_info;
+      EXPECT_TRUE(payload_config.source.LoadImageSize());
+      EXPECT_TRUE(payload_config.source.rootfs.OpenFilesystem());
+      EXPECT_TRUE(payload_config.source.kernel.OpenFilesystem());
+    } else {
+      if (payload_config.hard_chunk_size == -1)
+        // Use 1 MiB chunk size for the full unittests.
+        payload_config.hard_chunk_size = 1024 * 1024;
+    }
+    payload_config.target.rootfs.path = state->b_img;
+    payload_config.target.kernel.path = state->new_kernel;
+    payload_config.target.image_info = new_image_info;
+    EXPECT_TRUE(payload_config.target.LoadImageSize());
+    EXPECT_TRUE(payload_config.target.rootfs.OpenFilesystem());
+    EXPECT_TRUE(payload_config.target.kernel.OpenFilesystem());
+
+    EXPECT_TRUE(payload_config.Validate());
+    EXPECT_TRUE(
+        GenerateUpdatePayloadFile(
+            payload_config,
+            state->delta_path,
+            private_key,
+            &state->metadata_size));
+  }
+
+  if (signature_test == kSignatureGeneratedPlaceholder ||
+      signature_test == kSignatureGeneratedPlaceholderMismatch) {
+    int signature_size = GetSignatureSize(kUnittestPrivateKeyPath);
+    LOG(INFO) << "Inserting placeholder signature.";
+    ASSERT_TRUE(InsertSignaturePlaceholder(signature_size, state->delta_path,
+                                           &state->metadata_size));
+
+    if (signature_test == kSignatureGeneratedPlaceholderMismatch) {
+      signature_size -= 1;
+      LOG(INFO) << "Inserting mismatched placeholder signature.";
+      ASSERT_FALSE(InsertSignaturePlaceholder(signature_size, state->delta_path,
+                                              &state->metadata_size));
+      return;
+    }
+  }
+
+  if (signature_test == kSignatureGenerated ||
+      signature_test == kSignatureGeneratedPlaceholder ||
+      signature_test == kSignatureGeneratedPlaceholderMismatch) {
+    // Generate the signed payload and update the metadata size in state to
+    // reflect the new size after adding the signature operation to the
+    // manifest.
+    LOG(INFO) << "Signing payload.";
+    SignGeneratedPayload(state->delta_path, &state->metadata_size);
+  } else if (signature_test == kSignatureGeneratedShell ||
+             signature_test == kSignatureGeneratedShellBadKey ||
+             signature_test == kSignatureGeneratedShellRotateCl1 ||
+             signature_test == kSignatureGeneratedShellRotateCl2) {
+    SignGeneratedShellPayload(signature_test, state->delta_path);
+  }
+}
+
+static void ApplyDeltaFile(bool full_kernel, bool full_rootfs, bool noop,
+                           SignatureTest signature_test, DeltaState* state,
+                           bool hash_checks_mandatory,
+                           OperationHashTest op_hash_test,
+                           DeltaPerformer** performer,
+                           uint32_t minor_version) {
+  // Check the metadata.
+  {
+    DeltaArchiveManifest manifest;
+    EXPECT_TRUE(PayloadVerifier::LoadPayload(state->delta_path,
+                                             &state->delta,
+                                             &manifest,
+                                             &state->metadata_size));
+    LOG(INFO) << "Metadata size: " << state->metadata_size;
+
+
+
+    if (signature_test == kSignatureNone) {
+      EXPECT_FALSE(manifest.has_signatures_offset());
+      EXPECT_FALSE(manifest.has_signatures_size());
+    } else {
+      EXPECT_TRUE(manifest.has_signatures_offset());
+      EXPECT_TRUE(manifest.has_signatures_size());
+      Signatures sigs_message;
+      EXPECT_TRUE(sigs_message.ParseFromArray(
+          &state->delta[state->metadata_size + manifest.signatures_offset()],
+          manifest.signatures_size()));
+      if (signature_test == kSignatureGeneratedShellRotateCl1 ||
+          signature_test == kSignatureGeneratedShellRotateCl2)
+        EXPECT_EQ(2, sigs_message.signatures_size());
+      else
+        EXPECT_EQ(1, sigs_message.signatures_size());
+      const Signatures_Signature& signature = sigs_message.signatures(0);
+      EXPECT_EQ(1, signature.version());
+
+      uint64_t expected_sig_data_length = 0;
+      vector<string> key_paths{kUnittestPrivateKeyPath};
+      if (signature_test == kSignatureGeneratedShellRotateCl1 ||
+          signature_test == kSignatureGeneratedShellRotateCl2) {
+        key_paths.push_back(kUnittestPrivateKey2Path);
+      }
+      EXPECT_TRUE(PayloadSigner::SignatureBlobLength(
+          key_paths,
+          &expected_sig_data_length));
+      EXPECT_EQ(expected_sig_data_length, manifest.signatures_size());
+      EXPECT_FALSE(signature.data().empty());
+    }
+
+    if (noop) {
+      EXPECT_EQ(0, manifest.install_operations_size());
+      EXPECT_EQ(1, manifest.kernel_install_operations_size());
+    }
+
+    if (full_kernel) {
+      EXPECT_FALSE(manifest.has_old_kernel_info());
+    } else {
+      EXPECT_EQ(state->old_kernel_data.size(),
+                manifest.old_kernel_info().size());
+      EXPECT_FALSE(manifest.old_kernel_info().hash().empty());
+    }
+
+    EXPECT_EQ(manifest.new_image_info().channel(), "test-channel");
+    EXPECT_EQ(manifest.new_image_info().board(), "test-board");
+    EXPECT_EQ(manifest.new_image_info().version(), "test-version");
+    EXPECT_EQ(manifest.new_image_info().key(), "test-key");
+    EXPECT_EQ(manifest.new_image_info().build_channel(), "test-build-channel");
+    EXPECT_EQ(manifest.new_image_info().build_version(), "test-build-version");
+
+    if (!full_rootfs) {
+      if (noop) {
+        EXPECT_EQ(manifest.old_image_info().channel(), "test-channel");
+        EXPECT_EQ(manifest.old_image_info().board(), "test-board");
+        EXPECT_EQ(manifest.old_image_info().version(), "test-version");
+        EXPECT_EQ(manifest.old_image_info().key(), "test-key");
+        EXPECT_EQ(manifest.old_image_info().build_channel(),
+                  "test-build-channel");
+        EXPECT_EQ(manifest.old_image_info().build_version(),
+                  "test-build-version");
+      } else {
+        EXPECT_EQ(manifest.old_image_info().channel(), "src-channel");
+        EXPECT_EQ(manifest.old_image_info().board(), "src-board");
+        EXPECT_EQ(manifest.old_image_info().version(), "src-version");
+        EXPECT_EQ(manifest.old_image_info().key(), "src-key");
+        EXPECT_EQ(manifest.old_image_info().build_channel(),
+                  "src-build-channel");
+        EXPECT_EQ(manifest.old_image_info().build_version(),
+                  "src-build-version");
+      }
+    }
+
+
+    if (full_rootfs) {
+      EXPECT_FALSE(manifest.has_old_rootfs_info());
+      EXPECT_FALSE(manifest.has_old_image_info());
+      EXPECT_TRUE(manifest.has_new_image_info());
+    } else {
+      EXPECT_EQ(state->image_size, manifest.old_rootfs_info().size());
+      EXPECT_FALSE(manifest.old_rootfs_info().hash().empty());
+    }
+
+    EXPECT_EQ(state->new_kernel_data.size(), manifest.new_kernel_info().size());
+    EXPECT_EQ(state->image_size, manifest.new_rootfs_info().size());
+
+    EXPECT_FALSE(manifest.new_kernel_info().hash().empty());
+    EXPECT_FALSE(manifest.new_rootfs_info().hash().empty());
+  }
+
+  MockPrefs prefs;
+  EXPECT_CALL(prefs, SetInt64(kPrefsManifestMetadataSize,
+                              state->metadata_size)).WillOnce(Return(true));
+  EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextOperation, _))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(prefs, GetInt64(kPrefsUpdateStateNextOperation, _))
+      .WillOnce(Return(false));
+  EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextDataOffset, _))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextDataLength, _))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSHA256Context, _))
+      .WillRepeatedly(Return(true));
+  if (op_hash_test == kValidOperationData && signature_test != kSignatureNone) {
+    EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignedSHA256Context, _))
+        .WillOnce(Return(true));
+    EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignatureBlob, _))
+        .WillOnce(Return(true));
+  }
+
+  // Update the A image in place.
+  InstallPlan install_plan;
+  install_plan.hash_checks_mandatory = hash_checks_mandatory;
+  install_plan.metadata_size = state->metadata_size;
+  install_plan.is_full_update = full_kernel && full_rootfs;
+  install_plan.source_path = state->a_img.c_str();
+  install_plan.kernel_source_path = state->old_kernel.c_str();
+
+  LOG(INFO) << "Setting payload metadata size in Omaha  = "
+            << state->metadata_size;
+  ASSERT_TRUE(PayloadSigner::GetMetadataSignature(
+      state->delta.data(),
+      state->metadata_size,
+      kUnittestPrivateKeyPath,
+      &install_plan.metadata_signature));
+  EXPECT_FALSE(install_plan.metadata_signature.empty());
+
+  *performer = new DeltaPerformer(&prefs,
+                                  &state->fake_system_state,
+                                  &install_plan);
+  EXPECT_TRUE(utils::FileExists(kUnittestPublicKeyPath));
+  (*performer)->set_public_key_path(kUnittestPublicKeyPath);
+  DeltaPerformerIntegrationTest::SetSupportedVersion(*performer, minor_version);
+
+  EXPECT_EQ(state->image_size,
+            OmahaHashCalculator::RawHashOfFile(
+                state->a_img,
+                state->image_size,
+                &install_plan.source_rootfs_hash));
+  EXPECT_TRUE(OmahaHashCalculator::RawHashOfData(
+                  state->old_kernel_data,
+                  &install_plan.source_kernel_hash));
+
+  // With minor version 2, we want the target to be the new image, result_img,
+  // but with version 1, we want to update A in place.
+  if (minor_version == kSourceMinorPayloadVersion) {
+    EXPECT_EQ(0, (*performer)->Open(state->result_img.c_str(), 0, 0));
+    EXPECT_TRUE((*performer)->OpenKernel(state->result_kernel.c_str()));
+  } else {
+    EXPECT_EQ(0, (*performer)->Open(state->a_img.c_str(), 0, 0));
+    EXPECT_TRUE((*performer)->OpenKernel(state->old_kernel.c_str()));
+  }
+
+
+  ErrorCode expected_error, actual_error;
+  bool continue_writing;
+  switch (op_hash_test) {
+    case kInvalidOperationData: {
+      // Muck with some random offset post the metadata size so that
+      // some operation hash will result in a mismatch.
+      int some_offset = state->metadata_size + 300;
+      LOG(INFO) << "Tampered value at offset: " << some_offset;
+      state->delta[some_offset]++;
+      expected_error = ErrorCode::kDownloadOperationHashMismatch;
+      continue_writing = false;
+      break;
+    }
+
+    case kValidOperationData:
+    default:
+      // no change.
+      expected_error = ErrorCode::kSuccess;
+      continue_writing = true;
+      break;
+  }
+
+  // Write at some number of bytes per operation. Arbitrarily chose 5.
+  const size_t kBytesPerWrite = 5;
+  for (size_t i = 0; i < state->delta.size(); i += kBytesPerWrite) {
+    size_t count = std::min(state->delta.size() - i, kBytesPerWrite);
+    bool write_succeeded = ((*performer)->Write(&state->delta[i],
+                                                count,
+                                                &actual_error));
+    // Normally write_succeeded should be true every time and
+    // actual_error should be ErrorCode::kSuccess. If so, continue the loop.
+    // But if we seeded an operation hash error above, then write_succeeded
+    // will be false. The failure may happen at any operation n. So, all
+    // Writes until n-1 should succeed and the nth operation will fail with
+    // actual_error. In this case, we should bail out of the loop because
+    // we cannot proceed applying the delta.
+    if (!write_succeeded) {
+      LOG(INFO) << "Write failed. Checking if it failed with expected error";
+      EXPECT_EQ(expected_error, actual_error);
+      if (!continue_writing) {
+        LOG(INFO) << "Cannot continue writing. Bailing out.";
+        break;
+      }
+    }
+
+    EXPECT_EQ(ErrorCode::kSuccess, actual_error);
+  }
+
+  // If we had continued all the way through, Close should succeed.
+  // Otherwise, it should fail. Check appropriately.
+  bool close_result = (*performer)->Close();
+  if (continue_writing)
+    EXPECT_EQ(0, close_result);
+  else
+    EXPECT_LE(0, close_result);
+}
+
+void VerifyPayloadResult(DeltaPerformer* performer,
+                         DeltaState* state,
+                         ErrorCode expected_result,
+                         uint32_t minor_version) {
+  if (!performer) {
+    EXPECT_TRUE(!"Skipping payload verification since performer is null.");
+    return;
+  }
+
+  int expected_times = (expected_result == ErrorCode::kSuccess) ? 1 : 0;
+  EXPECT_CALL(*(state->fake_system_state.mock_payload_state()),
+              DownloadComplete()).Times(expected_times);
+
+  LOG(INFO) << "Verifying payload for expected result "
+            << expected_result;
+  EXPECT_EQ(expected_result, performer->VerifyPayload(
+      OmahaHashCalculator::OmahaHashOfData(state->delta),
+      state->delta.size()));
+  LOG(INFO) << "Verified payload.";
+
+  if (expected_result != ErrorCode::kSuccess) {
+    // no need to verify new partition if VerifyPayload failed.
+    return;
+  }
+
+  chromeos::Blob updated_kernel_partition;
+  if (minor_version == kSourceMinorPayloadVersion) {
+    CompareFilesByBlock(state->result_kernel, state->new_kernel,
+                        state->kernel_size);
+    CompareFilesByBlock(state->result_img, state->b_img,
+                        state->image_size);
+    EXPECT_TRUE(utils::ReadFile(state->result_kernel,
+                                &updated_kernel_partition));
+  } else {
+    CompareFilesByBlock(state->old_kernel, state->new_kernel,
+                        state->kernel_size);
+    CompareFilesByBlock(state->a_img, state->b_img,
+                        state->image_size);
+    EXPECT_TRUE(utils::ReadFile(state->old_kernel, &updated_kernel_partition));
+  }
+
+  ASSERT_GE(updated_kernel_partition.size(), arraysize(kNewData));
+  EXPECT_TRUE(std::equal(std::begin(kNewData), std::end(kNewData),
+                         updated_kernel_partition.begin()));
+
+  uint64_t new_kernel_size;
+  chromeos::Blob new_kernel_hash;
+  uint64_t new_rootfs_size;
+  chromeos::Blob new_rootfs_hash;
+  EXPECT_TRUE(performer->GetNewPartitionInfo(&new_kernel_size,
+                                             &new_kernel_hash,
+                                             &new_rootfs_size,
+                                             &new_rootfs_hash));
+  EXPECT_EQ(kDefaultKernelSize, new_kernel_size);
+  chromeos::Blob expected_new_kernel_hash;
+  EXPECT_TRUE(OmahaHashCalculator::RawHashOfData(state->new_kernel_data,
+                                                 &expected_new_kernel_hash));
+  EXPECT_TRUE(expected_new_kernel_hash == new_kernel_hash);
+  EXPECT_EQ(state->image_size, new_rootfs_size);
+  chromeos::Blob expected_new_rootfs_hash;
+  EXPECT_EQ(state->image_size,
+            OmahaHashCalculator::RawHashOfFile(state->b_img,
+                                               state->image_size,
+                                               &expected_new_rootfs_hash));
+  EXPECT_TRUE(expected_new_rootfs_hash == new_rootfs_hash);
+}
+
+void VerifyPayload(DeltaPerformer* performer,
+                   DeltaState* state,
+                   SignatureTest signature_test,
+                   uint32_t minor_version) {
+  ErrorCode expected_result = ErrorCode::kSuccess;
+  switch (signature_test) {
+    case kSignatureNone:
+      expected_result = ErrorCode::kSignedDeltaPayloadExpectedError;
+      break;
+    case kSignatureGeneratedShellBadKey:
+      expected_result = ErrorCode::kDownloadPayloadPubKeyVerificationError;
+      break;
+    default: break;  // appease gcc
+  }
+
+  VerifyPayloadResult(performer, state, expected_result, minor_version);
+}
+
+void DoSmallImageTest(bool full_kernel, bool full_rootfs, bool noop,
+                      ssize_t chunk_size,
+                      SignatureTest signature_test,
+                      bool hash_checks_mandatory, uint32_t minor_version) {
+  DeltaState state;
+  DeltaPerformer *performer = nullptr;
+  GenerateDeltaFile(full_kernel, full_rootfs, noop, chunk_size,
+                    signature_test, &state, minor_version);
+
+  ScopedPathUnlinker a_img_unlinker(state.a_img);
+  ScopedPathUnlinker b_img_unlinker(state.b_img);
+  ScopedPathUnlinker new_img_unlinker(state.result_img);
+  ScopedPathUnlinker delta_unlinker(state.delta_path);
+  ScopedPathUnlinker old_kernel_unlinker(state.old_kernel);
+  ScopedPathUnlinker new_kernel_unlinker(state.new_kernel);
+  ScopedPathUnlinker result_kernel_unlinker(state.result_kernel);
+  ApplyDeltaFile(full_kernel, full_rootfs, noop, signature_test,
+                 &state, hash_checks_mandatory, kValidOperationData,
+                 &performer, minor_version);
+  VerifyPayload(performer, &state, signature_test, minor_version);
+  delete performer;
+}
+
+void DoOperationHashMismatchTest(OperationHashTest op_hash_test,
+                                 bool hash_checks_mandatory) {
+  DeltaState state;
+  uint64_t minor_version = kFullPayloadMinorVersion;
+  GenerateDeltaFile(true, true, false, -1, kSignatureGenerated, &state,
+                    minor_version);
+  ScopedPathUnlinker a_img_unlinker(state.a_img);
+  ScopedPathUnlinker b_img_unlinker(state.b_img);
+  ScopedPathUnlinker delta_unlinker(state.delta_path);
+  ScopedPathUnlinker old_kernel_unlinker(state.old_kernel);
+  ScopedPathUnlinker new_kernel_unlinker(state.new_kernel);
+  DeltaPerformer *performer = nullptr;
+  ApplyDeltaFile(true, true, false, kSignatureGenerated, &state,
+                 hash_checks_mandatory, op_hash_test, &performer,
+                 minor_version);
+  delete performer;
+}
+
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageTest) {
+  DoSmallImageTest(false, false, false, -1, kSignatureGenerator,
+                   false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignaturePlaceholderTest) {
+  DoSmallImageTest(false, false, false, -1, kSignatureGeneratedPlaceholder,
+                   false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignaturePlaceholderMismatchTest) {
+  DeltaState state;
+  GenerateDeltaFile(false, false, false, -1,
+                    kSignatureGeneratedPlaceholderMismatch, &state,
+                    kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageChunksTest) {
+  DoSmallImageTest(false, false, false, kBlockSize, kSignatureGenerator,
+                   false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootFullKernelSmallImageTest) {
+  DoSmallImageTest(true, false, false, -1, kSignatureGenerator,
+                   false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootFullSmallImageTest) {
+  DoSmallImageTest(true, true, false, -1, kSignatureGenerator,
+                   true, kFullPayloadMinorVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootNoopSmallImageTest) {
+  DoSmallImageTest(false, false, true, -1, kSignatureGenerator,
+                   false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignNoneTest) {
+  DoSmallImageTest(false, false, false, -1, kSignatureNone,
+                   false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedTest) {
+  DoSmallImageTest(false, false, false, -1, kSignatureGenerated,
+                   true, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedShellTest) {
+  DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShell,
+                   false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedShellBadKeyTest) {
+  DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShellBadKey,
+                   false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedShellRotateCl1Test) {
+  DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShellRotateCl1,
+                   false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedShellRotateCl2Test) {
+  DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShellRotateCl2,
+                   false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSourceOpsTest) {
+  DoSmallImageTest(false, false, false, -1, kSignatureGenerator,
+                   false, kSourceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootMandatoryOperationHashMismatchTest) {
+  DoOperationHashMismatchTest(kInvalidOperationData, true);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/delta_performer_unittest.cc b/delta_performer_unittest.cc
index f0f399d..cc3d6e2 100644
--- a/delta_performer_unittest.cc
+++ b/delta_performer_unittest.cc
@@ -17,27 +17,27 @@
 #include "update_engine/delta_performer.h"
 
 #include <inttypes.h>
-#include <sys/mount.h>
 
-#include <algorithm>
 #include <string>
 #include <vector>
 
 #include <base/files/file_path.h>
 #include <base/files/file_util.h>
 #include <base/strings/stringprintf.h>
+#include <base/strings/string_number_conversions.h>
 #include <base/strings/string_util.h>
 #include <google/protobuf/repeated_field.h>
 #include <gtest/gtest.h>
 
+#include "update_engine/bzip.h"
 #include "update_engine/constants.h"
 #include "update_engine/fake_hardware.h"
+#include "update_engine/fake_prefs.h"
 #include "update_engine/fake_system_state.h"
-#include "update_engine/mock_prefs.h"
 #include "update_engine/payload_constants.h"
-#include "update_engine/payload_generator/delta_diff_generator.h"
+#include "update_engine/payload_generator/extent_ranges.h"
+#include "update_engine/payload_generator/payload_file.h"
 #include "update_engine/payload_generator/payload_signer.h"
-#include "update_engine/payload_verifier.h"
 #include "update_engine/test_utils.h"
 #include "update_engine/update_metadata.pb.h"
 #include "update_engine/utils.h"
@@ -49,13 +49,10 @@
 using testing::Return;
 using testing::_;
 using test_utils::kRandomString;
-using test_utils::ScopedLoopMounter;
 using test_utils::System;
 
 extern const char* kUnittestPrivateKeyPath;
 extern const char* kUnittestPublicKeyPath;
-extern const char* kUnittestPrivateKey2Path;
-extern const char* kUnittestPublicKey2Path;
 
 static const char* kBogusMetadataSignature1 =
     "awSFIUdUZz2VWFiR+ku0Pj00V7bPQPQFYQSXjEXr3vaw3TE4xHV5CraY3/YrZpBv"
@@ -65,50 +62,7 @@
     "fjoTeLYZpt+WN65Vu7jJ0cQN8e1y+2yka5112wpRf/LLtPgiAjEZnsoYpLUd7CoV"
     "pLRtClp97kN2+tXGNBQqkA==";
 
-static const int kDefaultKernelSize = 4096;  // Something small for a test
-static const uint8_t kNewData[] = {'T', 'h', 'i', 's', ' ', 'i', 's', ' ',
-                                   'n', 'e', 'w', ' ', 'd', 'a', 't', 'a', '.'};
-
 namespace {
-struct DeltaState {
-  string a_img;
-  string b_img;
-  string result_img;
-  size_t image_size;
-
-  string delta_path;
-  uint64_t metadata_size;
-
-  string old_kernel;
-  chromeos::Blob old_kernel_data;
-
-  string new_kernel;
-  chromeos::Blob new_kernel_data;
-
-  string result_kernel;
-  chromeos::Blob result_kernel_data;
-  size_t kernel_size;
-
-  // The in-memory copy of delta file.
-  chromeos::Blob delta;
-
-  // The mock system state object with which we initialize the
-  // delta performer.
-  FakeSystemState fake_system_state;
-};
-
-enum SignatureTest {
-  kSignatureNone,  // No payload signing.
-  kSignatureGenerator,  // Sign the payload at generation time.
-  kSignatureGenerated,  // Sign the payload after it's generated.
-  kSignatureGeneratedPlaceholder,  // Insert placeholder signatures, then real.
-  kSignatureGeneratedPlaceholderMismatch,  // Insert a wrong sized placeholder.
-  kSignatureGeneratedShell,  // Sign the generated payload through shell cmds.
-  kSignatureGeneratedShellBadKey,  // Sign with a bad key through shell cmds.
-  kSignatureGeneratedShellRotateCl1,  // Rotate key, test client v1
-  kSignatureGeneratedShellRotateCl2,  // Rotate key, test client v2
-};
-
 // Different options that determine what we should fill into the
 // install_plan.metadata_signature to simulate the contents received in the
 // Omaha response.
@@ -118,978 +72,274 @@
   kValidMetadataSignature,
 };
 
-enum OperationHashTest {
-  kInvalidOperationData,
-  kValidOperationData,
-};
-
 }  // namespace
 
 class DeltaPerformerTest : public ::testing::Test {
- public:
-  // Test helper placed where it can easily be friended from DeltaPerformer.
-  static void RunManifestValidation(const DeltaArchiveManifest& manifest,
-                                    bool full_payload,
-                                    ErrorCode expected) {
-    MockPrefs prefs;
-    InstallPlan install_plan;
-    FakeSystemState fake_system_state;
-    DeltaPerformer performer(&prefs, &fake_system_state, &install_plan);
+ protected:
 
+  // Test helper placed where it can easily be friended from DeltaPerformer.
+  void RunManifestValidation(const DeltaArchiveManifest& manifest,
+                             bool full_payload,
+                             ErrorCode expected) {
     // The install plan is for Full or Delta.
-    install_plan.is_full_update = full_payload;
+    install_plan_.is_full_update = full_payload;
 
     // The Manifest we are validating.
-    performer.manifest_.CopyFrom(manifest);
+    performer_.manifest_.CopyFrom(manifest);
 
-    EXPECT_EQ(expected, performer.ValidateManifest());
+    EXPECT_EQ(expected, performer_.ValidateManifest());
   }
 
-  static void SetSupportedVersion(DeltaPerformer* performer,
-                                  uint64_t minor_version) {
-    performer->supported_minor_version_ = minor_version;
-  }
-};
+  chromeos::Blob GeneratePayload(const chromeos::Blob& blob_data,
+                                 const vector<AnnotatedOperation>& aops,
+                                 bool sign_payload,
+                                 int32_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()));
 
-static void CompareFilesByBlock(const string& a_file, const string& b_file,
-                                size_t image_size) {
-  EXPECT_EQ(0, image_size % kBlockSize);
+    PayloadGenerationConfig config;
+    config.major_version = kChromeOSMajorPayloadVersion;
+    config.minor_version = minor_version;
+    config.target.rootfs.path = blob_path;
+    config.target.rootfs.size = blob_data.size();
+    config.target.kernel.path = blob_path;
+    config.target.kernel.size = blob_data.size();
 
-  chromeos::Blob a_data, b_data;
-  EXPECT_TRUE(utils::ReadFile(a_file, &a_data)) << "file failed: " << a_file;
-  EXPECT_TRUE(utils::ReadFile(b_file, &b_data)) << "file failed: " << b_file;
+    PayloadFile payload;
+    EXPECT_TRUE(payload.Init(config));
 
-  EXPECT_GE(a_data.size(), image_size);
-  EXPECT_GE(b_data.size(), image_size);
-  for (size_t i = 0; i < image_size; i += kBlockSize) {
-    EXPECT_EQ(0, i % kBlockSize);
-    chromeos::Blob a_sub(&a_data[i], &a_data[i + kBlockSize]);
-    chromeos::Blob b_sub(&b_data[i], &b_data[i + kBlockSize]);
-    EXPECT_TRUE(a_sub == b_sub) << "Block " << (i/kBlockSize) << " differs";
-  }
-  if (::testing::Test::HasNonfatalFailure()) {
-    LOG(INFO) << "Compared filesystems with size " << image_size
-              << ", partition A " << a_file << " size: " << a_data.size()
-              << ", partition B " << b_file << " size: " << b_data.size();
-  }
-}
+    payload.AddPartition(config.source.rootfs, config.target.rootfs, aops);
 
-static bool WriteSparseFile(const string& path, off_t size) {
-  int fd = open(path.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644);
-  TEST_AND_RETURN_FALSE_ERRNO(fd >= 0);
-  ScopedFdCloser fd_closer(&fd);
-  off_t rc = lseek(fd, size + 1, SEEK_SET);
-  TEST_AND_RETURN_FALSE_ERRNO(rc != static_cast<off_t>(-1));
-  int return_code = ftruncate(fd, size);
-  TEST_AND_RETURN_FALSE_ERRNO(return_code == 0);
-  return true;
-}
+    string payload_path;
+    EXPECT_TRUE(utils::MakeTempFile("Payload-XXXXXX", &payload_path, nullptr));
+    ScopedPathUnlinker payload_unlinker(payload_path);
+    EXPECT_TRUE(payload.WritePayload(payload_path, blob_path,
+        sign_payload ? kUnittestPrivateKeyPath : "",
+        &install_plan_.metadata_size));
 
-static size_t GetSignatureSize(const string& private_key_path) {
-  const chromeos::Blob data(1, 'x');
-  chromeos::Blob hash;
-  EXPECT_TRUE(OmahaHashCalculator::RawHashOfData(data, &hash));
-  chromeos::Blob signature;
-  EXPECT_TRUE(PayloadSigner::SignHash(hash,
-                                      private_key_path,
-                                      &signature));
-  return signature.size();
-}
-
-static bool InsertSignaturePlaceholder(int signature_size,
-                                       const string& payload_path,
-                                       uint64_t* out_metadata_size) {
-  vector<chromeos::Blob> signatures;
-  signatures.push_back(chromeos::Blob(signature_size, 0));
-
-  return PayloadSigner::AddSignatureToPayload(
-      payload_path,
-      signatures,
-      payload_path,
-      out_metadata_size);
-}
-
-static void SignGeneratedPayload(const string& payload_path,
-                                 uint64_t* out_metadata_size) {
-  int signature_size = GetSignatureSize(kUnittestPrivateKeyPath);
-  chromeos::Blob hash;
-  ASSERT_TRUE(PayloadSigner::HashPayloadForSigning(
-      payload_path,
-      vector<int>(1, signature_size),
-      &hash));
-  chromeos::Blob signature;
-  ASSERT_TRUE(PayloadSigner::SignHash(hash,
-                                      kUnittestPrivateKeyPath,
-                                      &signature));
-  ASSERT_TRUE(PayloadSigner::AddSignatureToPayload(
-      payload_path,
-      vector<chromeos::Blob>(1, signature),
-      payload_path,
-      out_metadata_size));
-  EXPECT_TRUE(PayloadVerifier::VerifySignedPayload(
-      payload_path,
-      kUnittestPublicKeyPath,
-      kSignatureMessageOriginalVersion));
-}
-
-static void SignGeneratedShellPayload(SignatureTest signature_test,
-                                      const string& payload_path) {
-  string private_key_path = kUnittestPrivateKeyPath;
-  if (signature_test == kSignatureGeneratedShellBadKey) {
-    ASSERT_TRUE(utils::MakeTempFile("key.XXXXXX",
-                                    &private_key_path,
-                                    nullptr));
-  } else {
-    ASSERT_TRUE(signature_test == kSignatureGeneratedShell ||
-                signature_test == kSignatureGeneratedShellRotateCl1 ||
-                signature_test == kSignatureGeneratedShellRotateCl2);
-  }
-  ScopedPathUnlinker key_unlinker(private_key_path);
-  key_unlinker.set_should_remove(signature_test ==
-                                 kSignatureGeneratedShellBadKey);
-  // Generates a new private key that will not match the public key.
-  if (signature_test == kSignatureGeneratedShellBadKey) {
-    LOG(INFO) << "Generating a mismatched private key.";
-    ASSERT_EQ(0, System(base::StringPrintf(
-        "openssl genrsa -out %s 2048", private_key_path.c_str())));
-  }
-  int signature_size = GetSignatureSize(private_key_path);
-  string hash_file;
-  ASSERT_TRUE(utils::MakeTempFile("hash.XXXXXX", &hash_file, nullptr));
-  ScopedPathUnlinker hash_unlinker(hash_file);
-  string signature_size_string;
-  if (signature_test == kSignatureGeneratedShellRotateCl1 ||
-      signature_test == kSignatureGeneratedShellRotateCl2)
-    signature_size_string = base::StringPrintf("%d:%d",
-                                               signature_size, signature_size);
-  else
-    signature_size_string = base::StringPrintf("%d", signature_size);
-  ASSERT_EQ(0,
-            System(base::StringPrintf(
-                "./delta_generator -in_file=%s -signature_size=%s "
-                "-out_hash_file=%s",
-                payload_path.c_str(),
-                signature_size_string.c_str(),
-                hash_file.c_str())));
-
-  // Pad the hash
-  chromeos::Blob hash;
-  ASSERT_TRUE(utils::ReadFile(hash_file, &hash));
-  ASSERT_TRUE(PayloadVerifier::PadRSA2048SHA256Hash(&hash));
-  ASSERT_TRUE(test_utils::WriteFileVector(hash_file, hash));
-
-  string sig_file;
-  ASSERT_TRUE(utils::MakeTempFile("signature.XXXXXX", &sig_file, nullptr));
-  ScopedPathUnlinker sig_unlinker(sig_file);
-  ASSERT_EQ(0,
-            System(base::StringPrintf(
-                "openssl rsautl -raw -sign -inkey %s -in %s -out %s",
-                private_key_path.c_str(),
-                hash_file.c_str(),
-                sig_file.c_str())));
-  string sig_file2;
-  ASSERT_TRUE(utils::MakeTempFile("signature.XXXXXX", &sig_file2, nullptr));
-  ScopedPathUnlinker sig2_unlinker(sig_file2);
-  if (signature_test == kSignatureGeneratedShellRotateCl1 ||
-      signature_test == kSignatureGeneratedShellRotateCl2) {
-    ASSERT_EQ(0,
-              System(base::StringPrintf(
-                  "openssl rsautl -raw -sign -inkey %s -in %s -out %s",
-                  kUnittestPrivateKey2Path,
-                  hash_file.c_str(),
-                  sig_file2.c_str())));
-    // Append second sig file to first path
-    sig_file += ":" + sig_file2;
+    chromeos::Blob payload_data;
+    EXPECT_TRUE(utils::ReadFile(payload_path, &payload_data));
+    return payload_data;
   }
 
-  ASSERT_EQ(0,
-            System(base::StringPrintf(
-                "./delta_generator -in_file=%s -signature_file=%s "
-                "-out_file=%s",
-                payload_path.c_str(),
-                sig_file.c_str(),
-                payload_path.c_str())));
-  int verify_result =
-      System(base::StringPrintf(
-          "./delta_generator -in_file=%s -public_key=%s -public_key_version=%d",
-          payload_path.c_str(),
-          signature_test == kSignatureGeneratedShellRotateCl2 ?
-          kUnittestPublicKey2Path : kUnittestPublicKeyPath,
-          signature_test == kSignatureGeneratedShellRotateCl2 ? 2 : 1));
-  if (signature_test == kSignatureGeneratedShellBadKey) {
-    ASSERT_NE(0, verify_result);
-  } else {
-    ASSERT_EQ(0, verify_result);
-  }
-}
+  // Apply |payload_data| on partition specified in |source_path|.
+  chromeos::Blob ApplyPayload(const chromeos::Blob& payload_data,
+                              const string& source_path) {
+    install_plan_.source_path = source_path;
+    install_plan_.kernel_source_path = "/dev/null";
 
-static void GenerateDeltaFile(bool full_kernel,
-                              bool full_rootfs,
-                              bool noop,
-                              ssize_t chunk_size,
-                              SignatureTest signature_test,
-                              DeltaState *state,
-                              uint32_t minor_version) {
-  EXPECT_TRUE(utils::MakeTempFile("a_img.XXXXXX", &state->a_img, nullptr));
-  EXPECT_TRUE(utils::MakeTempFile("b_img.XXXXXX", &state->b_img, nullptr));
+    string new_part;
+    EXPECT_TRUE(utils::MakeTempFile("Partition-XXXXXX", &new_part, nullptr));
+    ScopedPathUnlinker partition_unlinker(new_part);
 
-  // result_img is used in minor version 2. Instead of applying the update
-  // in-place on A, we apply it to a new image, result_img.
-  EXPECT_TRUE(
-      utils::MakeTempFile("result_img.XXXXXX", &state->result_img, nullptr));
-  test_utils::CreateExtImageAtPath(state->a_img, nullptr);
+    EXPECT_EQ(0, performer_.Open(new_part.c_str(), 0, 0));
+    EXPECT_TRUE(performer_.Write(payload_data.data(), payload_data.size()));
+    EXPECT_EQ(0, performer_.Close());
 
-  state->image_size = utils::FileSize(state->a_img);
-
-  // Extend the "partitions" holding the file system a bit.
-  EXPECT_EQ(0, HANDLE_EINTR(truncate(state->a_img.c_str(),
-                                     state->image_size + 1024 * 1024)));
-  EXPECT_EQ(state->image_size + 1024 * 1024, utils::FileSize(state->a_img));
-
-  // Create ImageInfo A & B
-  ImageInfo old_image_info;
-  ImageInfo new_image_info;
-
-  if (!full_rootfs) {
-    old_image_info.set_channel("src-channel");
-    old_image_info.set_board("src-board");
-    old_image_info.set_version("src-version");
-    old_image_info.set_key("src-key");
-    old_image_info.set_build_channel("src-build-channel");
-    old_image_info.set_build_version("src-build-version");
+    chromeos::Blob partition_data;
+    EXPECT_TRUE(utils::ReadFile(new_part, &partition_data));
+    return partition_data;
   }
 
-  new_image_info.set_channel("test-channel");
-  new_image_info.set_board("test-board");
-  new_image_info.set_version("test-version");
-  new_image_info.set_key("test-key");
-  new_image_info.set_build_channel("test-build-channel");
-  new_image_info.set_build_version("test-build-version");
+  // Calls delta performer's Write method by pretending to pass in bytes from a
+  // delta file whose metadata size is actual_metadata_size and tests if all
+  // checks are correctly performed if the install plan contains
+  // expected_metadata_size and that the result of the parsing are as per
+  // hash_checks_mandatory flag.
+  void DoMetadataSizeTest(uint64_t expected_metadata_size,
+                          uint64_t actual_metadata_size,
+                          bool hash_checks_mandatory) {
+    install_plan_.hash_checks_mandatory = hash_checks_mandatory;
+    EXPECT_EQ(0, performer_.Open("/dev/null", 0, 0));
+    EXPECT_TRUE(performer_.OpenKernel("/dev/null"));
 
-  // Make some changes to the A image.
-  {
-    string a_mnt;
-    ScopedLoopMounter b_mounter(state->a_img, &a_mnt, 0);
+    // Set a valid magic string and version number 1.
+    EXPECT_TRUE(performer_.Write("CrAU", 4));
+    uint64_t version = htobe64(kChromeOSMajorPayloadVersion);
+    EXPECT_TRUE(performer_.Write(&version, 8));
 
-    chromeos::Blob hardtocompress;
-    while (hardtocompress.size() < 3 * kBlockSize) {
-      hardtocompress.insert(hardtocompress.end(),
-                            std::begin(kRandomString), std::end(kRandomString));
-    }
-    EXPECT_TRUE(utils::WriteFile(base::StringPrintf("%s/hardtocompress",
-                                                    a_mnt.c_str()).c_str(),
-                                 hardtocompress.data(),
-                                 hardtocompress.size()));
-
-    chromeos::Blob zeros(16 * 1024, 0);
-    EXPECT_EQ(zeros.size(),
-              base::WriteFile(base::FilePath(base::StringPrintf(
-                                  "%s/move-to-sparse", a_mnt.c_str())),
-                              reinterpret_cast<const char*>(zeros.data()),
-                              zeros.size()));
-
-    EXPECT_TRUE(
-        WriteSparseFile(base::StringPrintf("%s/move-from-sparse",
-                                           a_mnt.c_str()), 16 * 1024));
-
-    EXPECT_EQ(0,
-              System(base::StringPrintf("dd if=/dev/zero of=%s/move-semi-sparse"
-                                        " bs=1 seek=4096 count=1 status=none",
-                                        a_mnt.c_str()).c_str()));
-
-    // Write 1 MiB of 0xff to try to catch the case where writing a bsdiff
-    // patch fails to zero out the final block.
-    chromeos::Blob ones(1024 * 1024, 0xff);
-    EXPECT_TRUE(utils::WriteFile(base::StringPrintf("%s/ones",
-                                                    a_mnt.c_str()).c_str(),
-                                 ones.data(),
-                                 ones.size()));
-  }
-
-  if (noop) {
-    EXPECT_TRUE(base::CopyFile(base::FilePath(state->a_img),
-                               base::FilePath(state->b_img)));
-    old_image_info = new_image_info;
-  } else {
-    if (minor_version == kSourceMinorPayloadVersion) {
-      // Create a result image with image_size bytes of garbage, followed by
-      // zeroes after the rootfs, like image A and B have.
-      chromeos::Blob ones(state->image_size, 0xff);
-      ones.insert(ones.end(), 1024 * 1024, 0);
-      EXPECT_TRUE(utils::WriteFile(state->result_img.c_str(),
-                                   ones.data(),
-                                   ones.size()));
-      EXPECT_EQ(utils::FileSize(state->a_img),
-                utils::FileSize(state->result_img));
-    }
-
-    test_utils::CreateExtImageAtPath(state->b_img, nullptr);
-    EXPECT_EQ(0, HANDLE_EINTR(truncate(state->b_img.c_str(),
-                                       state->image_size + 1024 * 1024)));
-    EXPECT_EQ(state->image_size + 1024 * 1024, utils::FileSize(state->b_img));
-
-    // Make some changes to the B image.
-    string b_mnt;
-    ScopedLoopMounter b_mounter(state->b_img, &b_mnt, 0);
-
-    EXPECT_EQ(0, System(base::StringPrintf("cp %s/hello %s/hello2",
-                                           b_mnt.c_str(),
-                                           b_mnt.c_str()).c_str()));
-    EXPECT_EQ(0, System(base::StringPrintf("rm %s/hello",
-                                           b_mnt.c_str()).c_str()));
-    EXPECT_EQ(0, System(base::StringPrintf("mv %s/hello2 %s/hello",
-                                           b_mnt.c_str(),
-                                           b_mnt.c_str()).c_str()));
-    EXPECT_EQ(0, System(base::StringPrintf("echo foo > %s/foo",
-                                           b_mnt.c_str()).c_str()));
-    EXPECT_EQ(0, System(base::StringPrintf("touch %s/emptyfile",
-                                           b_mnt.c_str()).c_str()));
-    EXPECT_TRUE(WriteSparseFile(base::StringPrintf("%s/fullsparse",
-                                                   b_mnt.c_str()),
-                                                   1024 * 1024));
-
-    EXPECT_TRUE(
-        WriteSparseFile(base::StringPrintf("%s/move-to-sparse", b_mnt.c_str()),
-                        16 * 1024));
-
-    chromeos::Blob zeros(16 * 1024, 0);
-    EXPECT_EQ(zeros.size(),
-              base::WriteFile(base::FilePath(base::StringPrintf(
-                                  "%s/move-from-sparse", b_mnt.c_str())),
-                              reinterpret_cast<const char*>(zeros.data()),
-                              zeros.size()));
-
-    EXPECT_EQ(0, System(base::StringPrintf("dd if=/dev/zero "
-                                           "of=%s/move-semi-sparse "
-                                           "bs=1 seek=4096 count=1 status=none",
-                                           b_mnt.c_str()).c_str()));
-
-    EXPECT_EQ(0, System(base::StringPrintf("dd if=/dev/zero "
-                                           "of=%s/partsparse bs=1 "
-                                           "seek=4096 count=1 status=none",
-                                           b_mnt.c_str()).c_str()));
-    EXPECT_EQ(0, System(base::StringPrintf("cp %s/srchardlink0 %s/tmp && "
-                                           "mv %s/tmp %s/srchardlink1",
-                                           b_mnt.c_str(),
-                                           b_mnt.c_str(),
-                                           b_mnt.c_str(),
-                                           b_mnt.c_str()).c_str()));
-    EXPECT_EQ(0, System(
-        base::StringPrintf("rm %s/boguslink && echo foobar > %s/boguslink",
-                           b_mnt.c_str(), b_mnt.c_str()).c_str()));
-
-    chromeos::Blob hardtocompress;
-    while (hardtocompress.size() < 3 * kBlockSize) {
-      hardtocompress.insert(hardtocompress.end(),
-                            std::begin(kRandomString), std::end(kRandomString));
-    }
-    EXPECT_TRUE(utils::WriteFile(base::StringPrintf("%s/hardtocompress",
-                                              b_mnt.c_str()).c_str(),
-                                 hardtocompress.data(),
-                                 hardtocompress.size()));
-  }
-
-  string old_kernel;
-  EXPECT_TRUE(utils::MakeTempFile("old_kernel.XXXXXX",
-                                  &state->old_kernel,
-                                  nullptr));
-
-  string new_kernel;
-  EXPECT_TRUE(utils::MakeTempFile("new_kernel.XXXXXX",
-                                  &state->new_kernel,
-                                  nullptr));
-
-  string result_kernel;
-  EXPECT_TRUE(utils::MakeTempFile("result_kernel.XXXXXX",
-                                  &state->result_kernel,
-                                  nullptr));
-
-  state->kernel_size = kDefaultKernelSize;
-  state->old_kernel_data.resize(kDefaultKernelSize);
-  state->new_kernel_data.resize(state->old_kernel_data.size());
-  state->result_kernel_data.resize(state->old_kernel_data.size());
-  test_utils::FillWithData(&state->old_kernel_data);
-  test_utils::FillWithData(&state->new_kernel_data);
-  test_utils::FillWithData(&state->result_kernel_data);
-
-  // change the new kernel data
-  std::copy(std::begin(kNewData), std::end(kNewData),
-            state->new_kernel_data.begin());
-
-  if (noop) {
-    state->old_kernel_data = state->new_kernel_data;
-  }
-
-  // Write kernels to disk
-  EXPECT_TRUE(utils::WriteFile(state->old_kernel.c_str(),
-                               state->old_kernel_data.data(),
-                               state->old_kernel_data.size()));
-  EXPECT_TRUE(utils::WriteFile(state->new_kernel.c_str(),
-                               state->new_kernel_data.data(),
-                               state->new_kernel_data.size()));
-  EXPECT_TRUE(utils::WriteFile(state->result_kernel.c_str(),
-                               state->result_kernel_data.data(),
-                               state->result_kernel_data.size()));
-
-  EXPECT_TRUE(utils::MakeTempFile("delta.XXXXXX",
-                                  &state->delta_path,
-                                  nullptr));
-  LOG(INFO) << "delta path: " << state->delta_path;
-  {
-    const string private_key =
-        signature_test == kSignatureGenerator ? kUnittestPrivateKeyPath : "";
-
-    PayloadGenerationConfig payload_config;
-    payload_config.is_delta = !full_rootfs;
-    payload_config.hard_chunk_size = chunk_size;
-    payload_config.rootfs_partition_size = kRootFSPartitionSize;
-    payload_config.major_version = kChromeOSMajorPayloadVersion;
-    payload_config.minor_version = minor_version;
-    if (!full_rootfs) {
-      payload_config.source.rootfs.path = state->a_img;
-      if (!full_kernel)
-        payload_config.source.kernel.path = state->old_kernel;
-      payload_config.source.image_info = old_image_info;
-      EXPECT_TRUE(payload_config.source.LoadImageSize());
-      EXPECT_TRUE(payload_config.source.rootfs.OpenFilesystem());
-      EXPECT_TRUE(payload_config.source.kernel.OpenFilesystem());
+    install_plan_.metadata_size = expected_metadata_size;
+    ErrorCode error_code;
+    // When filling in size in manifest, exclude the size of the 20-byte header.
+    uint64_t size_in_manifest = htobe64(actual_metadata_size - 20);
+    bool result = performer_.Write(&size_in_manifest, 8, &error_code);
+    if (expected_metadata_size == actual_metadata_size ||
+        !hash_checks_mandatory) {
+      EXPECT_TRUE(result);
     } else {
-      if (payload_config.hard_chunk_size == -1)
-        // Use 1 MiB chunk size for the full unittests.
-        payload_config.hard_chunk_size = 1024 * 1024;
+      EXPECT_FALSE(result);
+      EXPECT_EQ(ErrorCode::kDownloadInvalidMetadataSize, error_code);
     }
-    payload_config.target.rootfs.path = state->b_img;
-    payload_config.target.kernel.path = state->new_kernel;
-    payload_config.target.image_info = new_image_info;
-    EXPECT_TRUE(payload_config.target.LoadImageSize());
-    EXPECT_TRUE(payload_config.target.rootfs.OpenFilesystem());
-    EXPECT_TRUE(payload_config.target.kernel.OpenFilesystem());
 
-    EXPECT_TRUE(payload_config.Validate());
-    EXPECT_TRUE(
-        GenerateUpdatePayloadFile(
-            payload_config,
-            state->delta_path,
-            private_key,
-            &state->metadata_size));
+    EXPECT_LT(performer_.Close(), 0);
   }
 
-  if (signature_test == kSignatureGeneratedPlaceholder ||
-      signature_test == kSignatureGeneratedPlaceholderMismatch) {
-    int signature_size = GetSignatureSize(kUnittestPrivateKeyPath);
-    LOG(INFO) << "Inserting placeholder signature.";
-    ASSERT_TRUE(InsertSignaturePlaceholder(signature_size, state->delta_path,
-                                           &state->metadata_size));
+  // Generates a valid delta file but tests the delta performer by suppling
+  // different metadata signatures as per metadata_signature_test flag and
+  // sees if the result of the parsing are as per hash_checks_mandatory flag.
+  void DoMetadataSignatureTest(MetadataSignatureTest metadata_signature_test,
+                               bool sign_payload,
+                               bool hash_checks_mandatory) {
 
-    if (signature_test == kSignatureGeneratedPlaceholderMismatch) {
-      signature_size -= 1;
-      LOG(INFO) << "Inserting mismatched placeholder signature.";
-      ASSERT_FALSE(InsertSignaturePlaceholder(signature_size, state->delta_path,
-                                              &state->metadata_size));
-      return;
-    }
-  }
+    // Loads the payload and parses the manifest.
+    chromeos::Blob payload = GeneratePayload(chromeos::Blob(),
+        vector<AnnotatedOperation>(), sign_payload,
+        kFullPayloadMinorVersion);
 
-  if (signature_test == kSignatureGenerated ||
-      signature_test == kSignatureGeneratedPlaceholder ||
-      signature_test == kSignatureGeneratedPlaceholderMismatch) {
-    // Generate the signed payload and update the metadata size in state to
-    // reflect the new size after adding the signature operation to the
-    // manifest.
-    LOG(INFO) << "Signing payload.";
-    SignGeneratedPayload(state->delta_path, &state->metadata_size);
-  } else if (signature_test == kSignatureGeneratedShell ||
-             signature_test == kSignatureGeneratedShellBadKey ||
-             signature_test == kSignatureGeneratedShellRotateCl1 ||
-             signature_test == kSignatureGeneratedShellRotateCl2) {
-    SignGeneratedShellPayload(signature_test, state->delta_path);
-  }
-}
+    LOG(INFO) << "Payload size: " << payload.size();
 
-static void ApplyDeltaFile(bool full_kernel, bool full_rootfs, bool noop,
-                           SignatureTest signature_test, DeltaState* state,
-                           bool hash_checks_mandatory,
-                           OperationHashTest op_hash_test,
-                           DeltaPerformer** performer,
-                           uint32_t minor_version) {
-  // Check the metadata.
-  {
-    DeltaArchiveManifest manifest;
-    EXPECT_TRUE(PayloadVerifier::LoadPayload(state->delta_path,
-                                             &state->delta,
-                                             &manifest,
-                                             &state->metadata_size));
-    LOG(INFO) << "Metadata size: " << state->metadata_size;
+    install_plan_.hash_checks_mandatory = hash_checks_mandatory;
 
+    DeltaPerformer::MetadataParseResult expected_result, actual_result;
+    ErrorCode expected_error, actual_error;
 
-
-    if (signature_test == kSignatureNone) {
-      EXPECT_FALSE(manifest.has_signatures_offset());
-      EXPECT_FALSE(manifest.has_signatures_size());
-    } else {
-      EXPECT_TRUE(manifest.has_signatures_offset());
-      EXPECT_TRUE(manifest.has_signatures_size());
-      Signatures sigs_message;
-      EXPECT_TRUE(sigs_message.ParseFromArray(
-          &state->delta[state->metadata_size + manifest.signatures_offset()],
-          manifest.signatures_size()));
-      if (signature_test == kSignatureGeneratedShellRotateCl1 ||
-          signature_test == kSignatureGeneratedShellRotateCl2)
-        EXPECT_EQ(2, sigs_message.signatures_size());
-      else
-        EXPECT_EQ(1, sigs_message.signatures_size());
-      const Signatures_Signature& signature = sigs_message.signatures(0);
-      EXPECT_EQ(1, signature.version());
-
-      uint64_t expected_sig_data_length = 0;
-      vector<string> key_paths{kUnittestPrivateKeyPath};
-      if (signature_test == kSignatureGeneratedShellRotateCl1 ||
-          signature_test == kSignatureGeneratedShellRotateCl2) {
-        key_paths.push_back(kUnittestPrivateKey2Path);
-      }
-      EXPECT_TRUE(PayloadSigner::SignatureBlobLength(
-          key_paths,
-          &expected_sig_data_length));
-      EXPECT_EQ(expected_sig_data_length, manifest.signatures_size());
-      EXPECT_FALSE(signature.data().empty());
-    }
-
-    if (noop) {
-      EXPECT_EQ(0, manifest.install_operations_size());
-      EXPECT_EQ(1, manifest.kernel_install_operations_size());
-    }
-
-    if (full_kernel) {
-      EXPECT_FALSE(manifest.has_old_kernel_info());
-    } else {
-      EXPECT_EQ(state->old_kernel_data.size(),
-                manifest.old_kernel_info().size());
-      EXPECT_FALSE(manifest.old_kernel_info().hash().empty());
-    }
-
-    EXPECT_EQ(manifest.new_image_info().channel(), "test-channel");
-    EXPECT_EQ(manifest.new_image_info().board(), "test-board");
-    EXPECT_EQ(manifest.new_image_info().version(), "test-version");
-    EXPECT_EQ(manifest.new_image_info().key(), "test-key");
-    EXPECT_EQ(manifest.new_image_info().build_channel(), "test-build-channel");
-    EXPECT_EQ(manifest.new_image_info().build_version(), "test-build-version");
-
-    if (!full_rootfs) {
-      if (noop) {
-        EXPECT_EQ(manifest.old_image_info().channel(), "test-channel");
-        EXPECT_EQ(manifest.old_image_info().board(), "test-board");
-        EXPECT_EQ(manifest.old_image_info().version(), "test-version");
-        EXPECT_EQ(manifest.old_image_info().key(), "test-key");
-        EXPECT_EQ(manifest.old_image_info().build_channel(),
-                  "test-build-channel");
-        EXPECT_EQ(manifest.old_image_info().build_version(),
-                  "test-build-version");
-      } else {
-        EXPECT_EQ(manifest.old_image_info().channel(), "src-channel");
-        EXPECT_EQ(manifest.old_image_info().board(), "src-board");
-        EXPECT_EQ(manifest.old_image_info().version(), "src-version");
-        EXPECT_EQ(manifest.old_image_info().key(), "src-key");
-        EXPECT_EQ(manifest.old_image_info().build_channel(),
-                  "src-build-channel");
-        EXPECT_EQ(manifest.old_image_info().build_version(),
-                  "src-build-version");
-      }
-    }
-
-
-    if (full_rootfs) {
-      EXPECT_FALSE(manifest.has_old_rootfs_info());
-      EXPECT_FALSE(manifest.has_old_image_info());
-      EXPECT_TRUE(manifest.has_new_image_info());
-    } else {
-      EXPECT_EQ(state->image_size, manifest.old_rootfs_info().size());
-      EXPECT_FALSE(manifest.old_rootfs_info().hash().empty());
-    }
-
-    EXPECT_EQ(state->new_kernel_data.size(), manifest.new_kernel_info().size());
-    EXPECT_EQ(state->image_size, manifest.new_rootfs_info().size());
-
-    EXPECT_FALSE(manifest.new_kernel_info().hash().empty());
-    EXPECT_FALSE(manifest.new_rootfs_info().hash().empty());
-  }
-
-  MockPrefs prefs;
-  EXPECT_CALL(prefs, SetInt64(kPrefsManifestMetadataSize,
-                              state->metadata_size)).WillOnce(Return(true));
-  EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextOperation, _))
-      .WillRepeatedly(Return(true));
-  EXPECT_CALL(prefs, GetInt64(kPrefsUpdateStateNextOperation, _))
-      .WillOnce(Return(false));
-  EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextDataOffset, _))
-      .WillRepeatedly(Return(true));
-  EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextDataLength, _))
-      .WillRepeatedly(Return(true));
-  EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSHA256Context, _))
-      .WillRepeatedly(Return(true));
-  if (op_hash_test == kValidOperationData && signature_test != kSignatureNone) {
-    EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignedSHA256Context, _))
-        .WillOnce(Return(true));
-    EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignatureBlob, _))
-        .WillOnce(Return(true));
-  }
-
-  // Update the A image in place.
-  InstallPlan install_plan;
-  install_plan.hash_checks_mandatory = hash_checks_mandatory;
-  install_plan.metadata_size = state->metadata_size;
-  install_plan.is_full_update = full_kernel && full_rootfs;
-  install_plan.source_path = state->a_img.c_str();
-  install_plan.kernel_source_path = state->old_kernel.c_str();
-
-  LOG(INFO) << "Setting payload metadata size in Omaha  = "
-            << state->metadata_size;
-  ASSERT_TRUE(PayloadSigner::GetMetadataSignature(
-      state->delta.data(),
-      state->metadata_size,
-      kUnittestPrivateKeyPath,
-      &install_plan.metadata_signature));
-  EXPECT_FALSE(install_plan.metadata_signature.empty());
-
-  *performer = new DeltaPerformer(&prefs,
-                                  &state->fake_system_state,
-                                  &install_plan);
-  EXPECT_TRUE(utils::FileExists(kUnittestPublicKeyPath));
-  (*performer)->set_public_key_path(kUnittestPublicKeyPath);
-  DeltaPerformerTest::SetSupportedVersion(*performer, minor_version);
-
-  EXPECT_EQ(state->image_size,
-            OmahaHashCalculator::RawHashOfFile(
-                state->a_img,
-                state->image_size,
-                &install_plan.source_rootfs_hash));
-  EXPECT_TRUE(OmahaHashCalculator::RawHashOfData(
-                  state->old_kernel_data,
-                  &install_plan.source_kernel_hash));
-
-  // With minor version 2, we want the target to be the new image, result_img,
-  // but with version 1, we want to update A in place.
-  if (minor_version == kSourceMinorPayloadVersion) {
-    EXPECT_EQ(0, (*performer)->Open(state->result_img.c_str(), 0, 0));
-    EXPECT_TRUE((*performer)->OpenKernel(state->result_kernel.c_str()));
-  } else {
-    EXPECT_EQ(0, (*performer)->Open(state->a_img.c_str(), 0, 0));
-    EXPECT_TRUE((*performer)->OpenKernel(state->old_kernel.c_str()));
-  }
-
-
-  ErrorCode expected_error, actual_error;
-  bool continue_writing;
-  switch (op_hash_test) {
-    case kInvalidOperationData: {
-      // Muck with some random offset post the metadata size so that
-      // some operation hash will result in a mismatch.
-      int some_offset = state->metadata_size + 300;
-      LOG(INFO) << "Tampered value at offset: " << some_offset;
-      state->delta[some_offset]++;
-      expected_error = ErrorCode::kDownloadOperationHashMismatch;
-      continue_writing = false;
-      break;
-    }
-
-    case kValidOperationData:
-    default:
-      // no change.
-      expected_error = ErrorCode::kSuccess;
-      continue_writing = true;
-      break;
-  }
-
-  // Write at some number of bytes per operation. Arbitrarily chose 5.
-  const size_t kBytesPerWrite = 5;
-  for (size_t i = 0; i < state->delta.size(); i += kBytesPerWrite) {
-    size_t count = std::min(state->delta.size() - i, kBytesPerWrite);
-    bool write_succeeded = ((*performer)->Write(&state->delta[i],
-                                                count,
-                                                &actual_error));
-    // Normally write_succeeded should be true every time and
-    // actual_error should be ErrorCode::kSuccess. If so, continue the loop.
-    // But if we seeded an operation hash error above, then write_succeeded
-    // will be false. The failure may happen at any operation n. So, all
-    // Writes until n-1 should succeed and the nth operation will fail with
-    // actual_error. In this case, we should bail out of the loop because
-    // we cannot proceed applying the delta.
-    if (!write_succeeded) {
-      LOG(INFO) << "Write failed. Checking if it failed with expected error";
-      EXPECT_EQ(expected_error, actual_error);
-      if (!continue_writing) {
-        LOG(INFO) << "Cannot continue writing. Bailing out.";
+    // Fill up the metadata signature in install plan according to the test.
+    switch (metadata_signature_test) {
+      case kEmptyMetadataSignature:
+        install_plan_.metadata_signature.clear();
+        expected_result = DeltaPerformer::kMetadataParseError;
+        expected_error = ErrorCode::kDownloadMetadataSignatureMissingError;
         break;
-      }
+
+      case kInvalidMetadataSignature:
+        install_plan_.metadata_signature = kBogusMetadataSignature1;
+        expected_result = DeltaPerformer::kMetadataParseError;
+        expected_error = ErrorCode::kDownloadMetadataSignatureMismatch;
+        break;
+
+      case kValidMetadataSignature:
+      default:
+        // Set the install plan's metadata size to be the same as the one
+        // in the manifest so that we pass the metadata size checks. Only
+        // then we can get to manifest signature checks.
+        ASSERT_TRUE(PayloadSigner::GetMetadataSignature(
+            payload.data(),
+            install_plan_.metadata_size,
+            kUnittestPrivateKeyPath,
+            &install_plan_.metadata_signature));
+        EXPECT_FALSE(install_plan_.metadata_signature.empty());
+        expected_result = DeltaPerformer::kMetadataParseSuccess;
+        expected_error = ErrorCode::kSuccess;
+        break;
     }
 
-    EXPECT_EQ(ErrorCode::kSuccess, actual_error);
-  }
-
-  // If we had continued all the way through, Close should succeed.
-  // Otherwise, it should fail. Check appropriately.
-  bool close_result = (*performer)->Close();
-  if (continue_writing)
-    EXPECT_EQ(0, close_result);
-  else
-    EXPECT_LE(0, close_result);
-}
-
-void VerifyPayloadResult(DeltaPerformer* performer,
-                         DeltaState* state,
-                         ErrorCode expected_result,
-                         uint32_t minor_version) {
-  if (!performer) {
-    EXPECT_TRUE(!"Skipping payload verification since performer is null.");
-    return;
-  }
-
-  int expected_times = (expected_result == ErrorCode::kSuccess) ? 1 : 0;
-  EXPECT_CALL(*(state->fake_system_state.mock_payload_state()),
-              DownloadComplete()).Times(expected_times);
-
-  LOG(INFO) << "Verifying payload for expected result "
-            << expected_result;
-  EXPECT_EQ(expected_result, performer->VerifyPayload(
-      OmahaHashCalculator::OmahaHashOfData(state->delta),
-      state->delta.size()));
-  LOG(INFO) << "Verified payload.";
-
-  if (expected_result != ErrorCode::kSuccess) {
-    // no need to verify new partition if VerifyPayload failed.
-    return;
-  }
-
-  chromeos::Blob updated_kernel_partition;
-  if (minor_version == kSourceMinorPayloadVersion) {
-    CompareFilesByBlock(state->result_kernel, state->new_kernel,
-                        state->kernel_size);
-    CompareFilesByBlock(state->result_img, state->b_img,
-                        state->image_size);
-    EXPECT_TRUE(utils::ReadFile(state->result_kernel,
-                                &updated_kernel_partition));
-  } else {
-    CompareFilesByBlock(state->old_kernel, state->new_kernel,
-                        state->kernel_size);
-    CompareFilesByBlock(state->a_img, state->b_img,
-                        state->image_size);
-    EXPECT_TRUE(utils::ReadFile(state->old_kernel, &updated_kernel_partition));
-  }
-
-  ASSERT_GE(updated_kernel_partition.size(), arraysize(kNewData));
-  EXPECT_TRUE(std::equal(std::begin(kNewData), std::end(kNewData),
-                         updated_kernel_partition.begin()));
-
-  uint64_t new_kernel_size;
-  chromeos::Blob new_kernel_hash;
-  uint64_t new_rootfs_size;
-  chromeos::Blob new_rootfs_hash;
-  EXPECT_TRUE(performer->GetNewPartitionInfo(&new_kernel_size,
-                                             &new_kernel_hash,
-                                             &new_rootfs_size,
-                                             &new_rootfs_hash));
-  EXPECT_EQ(kDefaultKernelSize, new_kernel_size);
-  chromeos::Blob expected_new_kernel_hash;
-  EXPECT_TRUE(OmahaHashCalculator::RawHashOfData(state->new_kernel_data,
-                                                 &expected_new_kernel_hash));
-  EXPECT_TRUE(expected_new_kernel_hash == new_kernel_hash);
-  EXPECT_EQ(state->image_size, new_rootfs_size);
-  chromeos::Blob expected_new_rootfs_hash;
-  EXPECT_EQ(state->image_size,
-            OmahaHashCalculator::RawHashOfFile(state->b_img,
-                                               state->image_size,
-                                               &expected_new_rootfs_hash));
-  EXPECT_TRUE(expected_new_rootfs_hash == new_rootfs_hash);
-}
-
-void VerifyPayload(DeltaPerformer* performer,
-                   DeltaState* state,
-                   SignatureTest signature_test,
-                   uint32_t minor_version) {
-  ErrorCode expected_result = ErrorCode::kSuccess;
-  switch (signature_test) {
-    case kSignatureNone:
-      expected_result = ErrorCode::kSignedDeltaPayloadExpectedError;
-      break;
-    case kSignatureGeneratedShellBadKey:
-      expected_result = ErrorCode::kDownloadPayloadPubKeyVerificationError;
-      break;
-    default: break;  // appease gcc
-  }
-
-  VerifyPayloadResult(performer, state, expected_result, minor_version);
-}
-
-void DoSmallImageTest(bool full_kernel, bool full_rootfs, bool noop,
-                      ssize_t chunk_size,
-                      SignatureTest signature_test,
-                      bool hash_checks_mandatory, uint32_t minor_version) {
-  DeltaState state;
-  DeltaPerformer *performer = nullptr;
-  GenerateDeltaFile(full_kernel, full_rootfs, noop, chunk_size,
-                    signature_test, &state, minor_version);
-
-  ScopedPathUnlinker a_img_unlinker(state.a_img);
-  ScopedPathUnlinker b_img_unlinker(state.b_img);
-  ScopedPathUnlinker new_img_unlinker(state.result_img);
-  ScopedPathUnlinker delta_unlinker(state.delta_path);
-  ScopedPathUnlinker old_kernel_unlinker(state.old_kernel);
-  ScopedPathUnlinker new_kernel_unlinker(state.new_kernel);
-  ScopedPathUnlinker result_kernel_unlinker(state.result_kernel);
-  ApplyDeltaFile(full_kernel, full_rootfs, noop, signature_test,
-                 &state, hash_checks_mandatory, kValidOperationData,
-                 &performer, minor_version);
-  VerifyPayload(performer, &state, signature_test, minor_version);
-  delete performer;
-}
-
-// Calls delta performer's Write method by pretending to pass in bytes from a
-// delta file whose metadata size is actual_metadata_size and tests if all
-// checks are correctly performed if the install plan contains
-// expected_metadata_size and that the result of the parsing are as per
-// hash_checks_mandatory flag.
-void DoMetadataSizeTest(uint64_t expected_metadata_size,
-                        uint64_t actual_metadata_size,
-                        bool hash_checks_mandatory) {
-  MockPrefs prefs;
-  InstallPlan install_plan;
-  install_plan.hash_checks_mandatory = hash_checks_mandatory;
-  FakeSystemState fake_system_state;
-  DeltaPerformer performer(&prefs, &fake_system_state, &install_plan);
-  EXPECT_EQ(0, performer.Open("/dev/null", 0, 0));
-  EXPECT_TRUE(performer.OpenKernel("/dev/null"));
-
-  // Set a valid magic string and version number 1.
-  EXPECT_TRUE(performer.Write("CrAU", 4));
-  uint64_t version = htobe64(1);
-  EXPECT_TRUE(performer.Write(&version, 8));
-
-  install_plan.metadata_size = expected_metadata_size;
-  ErrorCode error_code;
-  // When filling in size in manifest, exclude the size of the 20-byte header.
-  uint64_t size_in_manifest = htobe64(actual_metadata_size - 20);
-  bool result = performer.Write(&size_in_manifest, 8, &error_code);
-  if (expected_metadata_size == actual_metadata_size ||
-      !hash_checks_mandatory) {
-    EXPECT_TRUE(result);
-  } else {
-    EXPECT_FALSE(result);
-    EXPECT_EQ(ErrorCode::kDownloadInvalidMetadataSize, error_code);
-  }
-
-  EXPECT_LT(performer.Close(), 0);
-}
-
-// Generates a valid delta file but tests the delta performer by suppling
-// different metadata signatures as per omaha_metadata_signature flag and
-// sees if the result of the parsing are as per hash_checks_mandatory flag.
-void DoMetadataSignatureTest(MetadataSignatureTest metadata_signature_test,
-                             SignatureTest signature_test,
-                             bool hash_checks_mandatory) {
-  DeltaState state;
-
-  // Using kSignatureNone since it doesn't affect the results of our test.
-  // If we've to use other signature options, then we'd have to get the
-  // metadata size again after adding the signing operation to the manifest.
-  GenerateDeltaFile(true, true, false, -1, signature_test, &state,
-                    DeltaPerformer::kFullPayloadMinorVersion);
-
-  ScopedPathUnlinker a_img_unlinker(state.a_img);
-  ScopedPathUnlinker b_img_unlinker(state.b_img);
-  ScopedPathUnlinker delta_unlinker(state.delta_path);
-  ScopedPathUnlinker old_kernel_unlinker(state.old_kernel);
-  ScopedPathUnlinker new_kernel_unlinker(state.new_kernel);
-
-  // Loads the payload and parses the manifest.
-  chromeos::Blob payload;
-  EXPECT_TRUE(utils::ReadFile(state.delta_path, &payload));
-  LOG(INFO) << "Payload size: " << payload.size();
-
-  InstallPlan install_plan;
-  install_plan.hash_checks_mandatory = hash_checks_mandatory;
-  install_plan.metadata_size = state.metadata_size;
-
-  DeltaPerformer::MetadataParseResult expected_result, actual_result;
-  ErrorCode expected_error, actual_error;
-
-  // Fill up the metadata signature in install plan according to the test.
-  switch (metadata_signature_test) {
-    case kEmptyMetadataSignature:
-      install_plan.metadata_signature.clear();
-      expected_result = DeltaPerformer::kMetadataParseError;
-      expected_error = ErrorCode::kDownloadMetadataSignatureMissingError;
-      break;
-
-    case kInvalidMetadataSignature:
-      install_plan.metadata_signature = kBogusMetadataSignature1;
-      expected_result = DeltaPerformer::kMetadataParseError;
-      expected_error = ErrorCode::kDownloadMetadataSignatureMismatch;
-      break;
-
-    case kValidMetadataSignature:
-    default:
-      // Set the install plan's metadata size to be the same as the one
-      // in the manifest so that we pass the metadata size checks. Only
-      // then we can get to manifest signature checks.
-      ASSERT_TRUE(PayloadSigner::GetMetadataSignature(
-          payload.data(),
-          state.metadata_size,
-          kUnittestPrivateKeyPath,
-          &install_plan.metadata_signature));
-      EXPECT_FALSE(install_plan.metadata_signature.empty());
+    // Ignore the expected result/error if hash checks are not mandatory.
+    if (!hash_checks_mandatory) {
       expected_result = DeltaPerformer::kMetadataParseSuccess;
       expected_error = ErrorCode::kSuccess;
-      break;
+    }
+
+    // Use the public key corresponding to the private key used above to
+    // sign the metadata.
+    EXPECT_TRUE(utils::FileExists(kUnittestPublicKeyPath));
+    performer_.set_public_key_path(kUnittestPublicKeyPath);
+
+    // Init actual_error with an invalid value so that we make sure
+    // ParsePayloadMetadata properly populates it in all cases.
+    actual_error = ErrorCode::kUmaReportedMax;
+    actual_result = performer_.ParsePayloadMetadata(payload, &actual_error);
+
+    EXPECT_EQ(expected_result, actual_result);
+    EXPECT_EQ(expected_error, actual_error);
+
+    // Check that the parsed metadata size is what's expected. This test
+    // implicitly confirms that the metadata signature is valid, if required.
+    EXPECT_EQ(install_plan_.metadata_size, performer_.GetMetadataSize());
   }
 
-  // Ignore the expected result/error if hash checks are not mandatory.
-  if (!hash_checks_mandatory) {
-    expected_result = DeltaPerformer::kMetadataParseSuccess;
-    expected_error = ErrorCode::kSuccess;
-  }
+  FakePrefs prefs_;
+  InstallPlan install_plan_;
+  FakeSystemState fake_system_state_;
+  DeltaPerformer performer_{&prefs_, &fake_system_state_, &install_plan_};
+};
 
-  // Create the delta performer object.
-  MockPrefs prefs;
-  DeltaPerformer delta_performer(&prefs,
-                                 &state.fake_system_state,
-                                 &install_plan);
+TEST_F(DeltaPerformerTest, FullPayloadWriteTest) {
+  install_plan_.is_full_update = true;
+  chromeos::Blob expected_data = chromeos::Blob(std::begin(kRandomString),
+                                                std::end(kRandomString));
+  expected_data.resize(4096);  // block size
+  vector<AnnotatedOperation> aops;
+  AnnotatedOperation aop;
+  *(aop.op.add_dst_extents()) = ExtentForRange(0, 1);
+  aop.op.set_data_offset(0);
+  aop.op.set_data_length(expected_data.size());
+  aop.op.set_type(InstallOperation::REPLACE);
+  aops.push_back(aop);
 
-  // Use the public key corresponding to the private key used above to
-  // sign the metadata.
-  EXPECT_TRUE(utils::FileExists(kUnittestPublicKeyPath));
-  delta_performer.set_public_key_path(kUnittestPublicKeyPath);
+  chromeos::Blob payload_data = GeneratePayload(expected_data, aops, false,
+      kFullPayloadMinorVersion);
 
-  // Init actual_error with an invalid value so that we make sure
-  // ParsePayloadMetadata properly populates it in all cases.
-  actual_error = ErrorCode::kUmaReportedMax;
-  actual_result = delta_performer.ParsePayloadMetadata(payload, &actual_error);
-
-  EXPECT_EQ(expected_result, actual_result);
-  EXPECT_EQ(expected_error, actual_error);
-
-  // Check that the parsed metadata size is what's expected. This test
-  // implicitly confirms that the metadata signature is valid, if required.
-  EXPECT_EQ(state.metadata_size, delta_performer.GetMetadataSize());
+  EXPECT_EQ(expected_data, ApplyPayload(payload_data, ""));
 }
 
-void DoOperationHashMismatchTest(OperationHashTest op_hash_test,
-                                 bool hash_checks_mandatory) {
-  DeltaState state;
-  uint64_t minor_version = DeltaPerformer::kFullPayloadMinorVersion;
-  GenerateDeltaFile(true, true, false, -1, kSignatureGenerated, &state,
-                    minor_version);
-  ScopedPathUnlinker a_img_unlinker(state.a_img);
-  ScopedPathUnlinker b_img_unlinker(state.b_img);
-  ScopedPathUnlinker delta_unlinker(state.delta_path);
-  ScopedPathUnlinker old_kernel_unlinker(state.old_kernel);
-  ScopedPathUnlinker new_kernel_unlinker(state.new_kernel);
-  DeltaPerformer *performer = nullptr;
-  ApplyDeltaFile(true, true, false, kSignatureGenerated, &state,
-                 hash_checks_mandatory, op_hash_test, &performer,
-                 minor_version);
-  delete performer;
+TEST_F(DeltaPerformerTest, ReplaceOperationTest) {
+  chromeos::Blob expected_data = chromeos::Blob(std::begin(kRandomString),
+                                                std::end(kRandomString));
+  expected_data.resize(4096);  // block size
+  vector<AnnotatedOperation> aops;
+  AnnotatedOperation aop;
+  *(aop.op.add_dst_extents()) = ExtentForRange(0, 1);
+  aop.op.set_data_offset(0);
+  aop.op.set_data_length(expected_data.size());
+  aop.op.set_type(InstallOperation::REPLACE);
+  aops.push_back(aop);
+
+  chromeos::Blob payload_data = GeneratePayload(expected_data, aops, false,
+                                                kSourceMinorPayloadVersion);
+
+  EXPECT_EQ(expected_data, ApplyPayload(payload_data, "/dev/null"));
 }
 
-TEST(DeltaPerformerTest, ExtentsToByteStringTest) {
+TEST_F(DeltaPerformerTest, ReplaceBzOperationTest) {
+  chromeos::Blob expected_data = chromeos::Blob(std::begin(kRandomString),
+                                                std::end(kRandomString));
+  expected_data.resize(4096);  // block size
+  chromeos::Blob bz_data;
+  EXPECT_TRUE(BzipCompress(expected_data, &bz_data));
+
+  vector<AnnotatedOperation> aops;
+  AnnotatedOperation aop;
+  *(aop.op.add_dst_extents()) = ExtentForRange(0, 1);
+  aop.op.set_data_offset(0);
+  aop.op.set_data_length(bz_data.size());
+  aop.op.set_type(InstallOperation::REPLACE_BZ);
+  aops.push_back(aop);
+
+  chromeos::Blob payload_data = GeneratePayload(bz_data, aops, false,
+                                                kSourceMinorPayloadVersion);
+
+  EXPECT_EQ(expected_data, ApplyPayload(payload_data, "/dev/null"));
+}
+
+TEST_F(DeltaPerformerTest, SourceCopyOperationTest) {
+  chromeos::Blob expected_data = chromeos::Blob(std::begin(kRandomString),
+                                                std::end(kRandomString));
+  expected_data.resize(4096);  // block size
+  vector<AnnotatedOperation> aops;
+  AnnotatedOperation aop;
+  *(aop.op.add_src_extents()) = ExtentForRange(0, 1);
+  *(aop.op.add_dst_extents()) = ExtentForRange(0, 1);
+  aop.op.set_type(InstallOperation::SOURCE_COPY);
+  aops.push_back(aop);
+
+  chromeos::Blob payload_data = GeneratePayload(chromeos::Blob(), aops, false,
+                                                kSourceMinorPayloadVersion);
+  string source_path;
+  EXPECT_TRUE(utils::MakeTempFile("Source-XXXXXX",
+                                  &source_path, nullptr));
+  ScopedPathUnlinker path_unlinker(source_path);
+  EXPECT_TRUE(utils::WriteFile(source_path.c_str(),
+                               expected_data.data(),
+                               expected_data.size()));
+
+  EXPECT_EQ(expected_data, ApplyPayload(payload_data, source_path));
+}
+
+TEST_F(DeltaPerformerTest, ExtentsToByteStringTest) {
   uint64_t test[] = {1, 1, 4, 2, 0, 1};
   COMPILE_ASSERT(arraysize(test) % 2 == 0, array_size_uneven);
   const uint64_t block_size = 4096;
@@ -1097,9 +347,7 @@
 
   google::protobuf::RepeatedPtrField<Extent> extents;
   for (size_t i = 0; i < arraysize(test); i += 2) {
-    Extent* extent = extents.Add();
-    extent->set_start_block(test[i]);
-    extent->set_num_blocks(test[i + 1]);
+    *(extents.Add()) = ExtentForRange(test[i], test[i + 1]);
   }
 
   string expected_output = "4096:4096,16384:8192,0:4083";
@@ -1111,18 +359,17 @@
   EXPECT_EQ(expected_output, actual_output);
 }
 
-TEST(DeltaPerformerTest, ValidateManifestFullGoodTest) {
+TEST_F(DeltaPerformerTest, ValidateManifestFullGoodTest) {
   // The Manifest we are validating.
   DeltaArchiveManifest manifest;
   manifest.mutable_new_kernel_info();
   manifest.mutable_new_rootfs_info();
-  manifest.set_minor_version(DeltaPerformer::kFullPayloadMinorVersion);
+  manifest.set_minor_version(kFullPayloadMinorVersion);
 
-  DeltaPerformerTest::RunManifestValidation(manifest, true,
-                                            ErrorCode::kSuccess);
+  RunManifestValidation(manifest, true, ErrorCode::kSuccess);
 }
 
-TEST(DeltaPerformerTest, ValidateManifestDeltaGoodTest) {
+TEST_F(DeltaPerformerTest, ValidateManifestDeltaGoodTest) {
   // The Manifest we are validating.
   DeltaArchiveManifest manifest;
   manifest.mutable_old_kernel_info();
@@ -1131,28 +378,25 @@
   manifest.mutable_new_rootfs_info();
   manifest.set_minor_version(DeltaPerformer::kSupportedMinorPayloadVersion);
 
-  DeltaPerformerTest::RunManifestValidation(manifest, false,
-                                            ErrorCode::kSuccess);
+  RunManifestValidation(manifest, false, ErrorCode::kSuccess);
 }
 
-TEST(DeltaPerformerTest, ValidateManifestFullUnsetMinorVersion) {
+TEST_F(DeltaPerformerTest, ValidateManifestFullUnsetMinorVersion) {
   // The Manifest we are validating.
   DeltaArchiveManifest manifest;
 
-  DeltaPerformerTest::RunManifestValidation(manifest, true,
-                                            ErrorCode::kSuccess);
+  RunManifestValidation(manifest, true, ErrorCode::kSuccess);
 }
 
-TEST(DeltaPerformerTest, ValidateManifestDeltaUnsetMinorVersion) {
+TEST_F(DeltaPerformerTest, ValidateManifestDeltaUnsetMinorVersion) {
   // The Manifest we are validating.
   DeltaArchiveManifest manifest;
 
-  DeltaPerformerTest::RunManifestValidation(
-      manifest, false,
-      ErrorCode::kUnsupportedMinorPayloadVersion);
+  RunManifestValidation(manifest, false,
+                        ErrorCode::kUnsupportedMinorPayloadVersion);
 }
 
-TEST(DeltaPerformerTest, ValidateManifestFullOldKernelTest) {
+TEST_F(DeltaPerformerTest, ValidateManifestFullOldKernelTest) {
   // The Manifest we are validating.
   DeltaArchiveManifest manifest;
   manifest.mutable_old_kernel_info();
@@ -1160,12 +404,10 @@
   manifest.mutable_new_rootfs_info();
   manifest.set_minor_version(DeltaPerformer::kSupportedMinorPayloadVersion);
 
-  DeltaPerformerTest::RunManifestValidation(
-      manifest, true,
-      ErrorCode::kPayloadMismatchedType);
+  RunManifestValidation(manifest, true, ErrorCode::kPayloadMismatchedType);
 }
 
-TEST(DeltaPerformerTest, ValidateManifestFullOldRootfsTest) {
+TEST_F(DeltaPerformerTest, ValidateManifestFullOldRootfsTest) {
   // The Manifest we are validating.
   DeltaArchiveManifest manifest;
   manifest.mutable_old_rootfs_info();
@@ -1173,12 +415,10 @@
   manifest.mutable_new_rootfs_info();
   manifest.set_minor_version(DeltaPerformer::kSupportedMinorPayloadVersion);
 
-  DeltaPerformerTest::RunManifestValidation(
-      manifest, true,
-      ErrorCode::kPayloadMismatchedType);
+  RunManifestValidation(manifest, true, ErrorCode::kPayloadMismatchedType);
 }
 
-TEST(DeltaPerformerTest, ValidateManifestBadMinorVersion) {
+TEST_F(DeltaPerformerTest, ValidateManifestBadMinorVersion) {
   // The Manifest we are validating.
   DeltaArchiveManifest manifest;
 
@@ -1186,172 +426,83 @@
   manifest.set_minor_version(DeltaPerformer::kSupportedMinorPayloadVersion +
                              10000);
 
-  DeltaPerformerTest::RunManifestValidation(
-      manifest, false,
-      ErrorCode::kUnsupportedMinorPayloadVersion);
+  RunManifestValidation(manifest, false,
+                        ErrorCode::kUnsupportedMinorPayloadVersion);
 }
 
-TEST(DeltaPerformerTest, RunAsRootSmallImageTest) {
-  DoSmallImageTest(false, false, false, -1, kSignatureGenerator,
-                   false, kInPlaceMinorPayloadVersion);
+TEST_F(DeltaPerformerTest, BadDeltaMagicTest) {
+  EXPECT_EQ(0, performer_.Open("/dev/null", 0, 0));
+  EXPECT_TRUE(performer_.OpenKernel("/dev/null"));
+  EXPECT_TRUE(performer_.Write("junk", 4));
+  EXPECT_TRUE(performer_.Write("morejunk", 8));
+  EXPECT_FALSE(performer_.Write("morejunk", 8));
+  EXPECT_LT(performer_.Close(), 0);
 }
 
-TEST(DeltaPerformerTest, RunAsRootSmallImageSignaturePlaceholderTest) {
-  DoSmallImageTest(false, false, false, -1, kSignatureGeneratedPlaceholder,
-                   false, kInPlaceMinorPayloadVersion);
-}
+TEST_F(DeltaPerformerTest, WriteUpdatesPayloadState) {
+  EXPECT_EQ(0, performer_.Open("/dev/null", 0, 0));
+  EXPECT_TRUE(performer_.OpenKernel("/dev/null"));
 
-TEST(DeltaPerformerTest, RunAsRootSmallImageSignaturePlaceholderMismatchTest) {
-  DeltaState state;
-  GenerateDeltaFile(false, false, false, -1,
-                    kSignatureGeneratedPlaceholderMismatch, &state,
-                    kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootSmallImageChunksTest) {
-  DoSmallImageTest(false, false, false, kBlockSize, kSignatureGenerator,
-                   false, kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootFullKernelSmallImageTest) {
-  DoSmallImageTest(true, false, false, -1, kSignatureGenerator,
-                   false, kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootFullSmallImageTest) {
-  DoSmallImageTest(true, true, false, -1, kSignatureGenerator,
-                   true, DeltaPerformer::kFullPayloadMinorVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootNoopSmallImageTest) {
-  DoSmallImageTest(false, false, true, -1, kSignatureGenerator,
-                   false, kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootSmallImageSignNoneTest) {
-  DoSmallImageTest(false, false, false, -1, kSignatureNone,
-                   false, kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedTest) {
-  DoSmallImageTest(false, false, false, -1, kSignatureGenerated,
-                   true, kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedShellTest) {
-  DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShell,
-                   false, kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedShellBadKeyTest) {
-  DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShellBadKey,
-                   false, kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedShellRotateCl1Test) {
-  DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShellRotateCl1,
-                   false, kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedShellRotateCl2Test) {
-  DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShellRotateCl2,
-                   false, kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootSmallImageSourceOpsTest) {
-  DoSmallImageTest(false, false, false, -1, kSignatureGenerator,
-                   false, kSourceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, BadDeltaMagicTest) {
-  MockPrefs prefs;
-  InstallPlan install_plan;
-  FakeSystemState fake_system_state;
-  DeltaPerformer performer(&prefs, &fake_system_state, &install_plan);
-  EXPECT_EQ(0, performer.Open("/dev/null", 0, 0));
-  EXPECT_TRUE(performer.OpenKernel("/dev/null"));
-  EXPECT_TRUE(performer.Write("junk", 4));
-  EXPECT_TRUE(performer.Write("morejunk", 8));
-  EXPECT_FALSE(performer.Write("morejunk", 8));
-  EXPECT_LT(performer.Close(), 0);
-}
-
-TEST(DeltaPerformerTest, WriteUpdatesPayloadState) {
-  MockPrefs prefs;
-  InstallPlan install_plan;
-  FakeSystemState fake_system_state;
-  DeltaPerformer performer(&prefs, &fake_system_state, &install_plan);
-  EXPECT_EQ(0, performer.Open("/dev/null", 0, 0));
-  EXPECT_TRUE(performer.OpenKernel("/dev/null"));
-
-  EXPECT_CALL(*(fake_system_state.mock_payload_state()),
+  EXPECT_CALL(*(fake_system_state_.mock_payload_state()),
               DownloadProgress(4)).Times(1);
-  EXPECT_CALL(*(fake_system_state.mock_payload_state()),
+  EXPECT_CALL(*(fake_system_state_.mock_payload_state()),
               DownloadProgress(8)).Times(2);
 
-  EXPECT_TRUE(performer.Write("junk", 4));
-  EXPECT_TRUE(performer.Write("morejunk", 8));
-  EXPECT_FALSE(performer.Write("morejunk", 8));
-  EXPECT_LT(performer.Close(), 0);
+  EXPECT_TRUE(performer_.Write("junk", 4));
+  EXPECT_TRUE(performer_.Write("morejunk", 8));
+  EXPECT_FALSE(performer_.Write("morejunk", 8));
+  EXPECT_LT(performer_.Close(), 0);
 }
 
-TEST(DeltaPerformerTest, MissingMandatoryMetadataSizeTest) {
+TEST_F(DeltaPerformerTest, MissingMandatoryMetadataSizeTest) {
   DoMetadataSizeTest(0, 75456, true);
 }
 
-TEST(DeltaPerformerTest, MissingNonMandatoryMetadataSizeTest) {
+TEST_F(DeltaPerformerTest, MissingNonMandatoryMetadataSizeTest) {
   DoMetadataSizeTest(0, 123456, false);
 }
 
-TEST(DeltaPerformerTest, InvalidMandatoryMetadataSizeTest) {
+TEST_F(DeltaPerformerTest, InvalidMandatoryMetadataSizeTest) {
   DoMetadataSizeTest(13000, 140000, true);
 }
 
-TEST(DeltaPerformerTest, InvalidNonMandatoryMetadataSizeTest) {
+TEST_F(DeltaPerformerTest, InvalidNonMandatoryMetadataSizeTest) {
   DoMetadataSizeTest(40000, 50000, false);
 }
 
-TEST(DeltaPerformerTest, ValidMandatoryMetadataSizeTest) {
+TEST_F(DeltaPerformerTest, ValidMandatoryMetadataSizeTest) {
   DoMetadataSizeTest(85376, 85376, true);
 }
 
-TEST(DeltaPerformerTest, RunAsRootMandatoryEmptyMetadataSignatureTest) {
-  DoMetadataSignatureTest(kEmptyMetadataSignature, kSignatureGenerated, true);
+TEST_F(DeltaPerformerTest, MandatoryEmptyMetadataSignatureTest) {
+  DoMetadataSignatureTest(kEmptyMetadataSignature, true, true);
 }
 
-TEST(DeltaPerformerTest, RunAsRootNonMandatoryEmptyMetadataSignatureTest) {
-  DoMetadataSignatureTest(kEmptyMetadataSignature, kSignatureGenerated, false);
+TEST_F(DeltaPerformerTest, NonMandatoryEmptyMetadataSignatureTest) {
+  DoMetadataSignatureTest(kEmptyMetadataSignature, true, false);
 }
 
-TEST(DeltaPerformerTest, RunAsRootMandatoryInvalidMetadataSignatureTest) {
-  DoMetadataSignatureTest(kInvalidMetadataSignature, kSignatureGenerated, true);
+TEST_F(DeltaPerformerTest, MandatoryInvalidMetadataSignatureTest) {
+  DoMetadataSignatureTest(kInvalidMetadataSignature, true, true);
 }
 
-TEST(DeltaPerformerTest, RunAsRootNonMandatoryInvalidMetadataSignatureTest) {
-  DoMetadataSignatureTest(kInvalidMetadataSignature, kSignatureGenerated,
-                          false);
+TEST_F(DeltaPerformerTest, NonMandatoryInvalidMetadataSignatureTest) {
+  DoMetadataSignatureTest(kInvalidMetadataSignature, true, false);
 }
 
-TEST(DeltaPerformerTest, RunAsRootMandatoryValidMetadataSignature1Test) {
-  DoMetadataSignatureTest(kValidMetadataSignature, kSignatureNone, true);
+TEST_F(DeltaPerformerTest, MandatoryValidMetadataSignature1Test) {
+  DoMetadataSignatureTest(kValidMetadataSignature, false, true);
 }
 
-TEST(DeltaPerformerTest, RunAsRootMandatoryValidMetadataSignature2Test) {
-  DoMetadataSignatureTest(kValidMetadataSignature, kSignatureGenerated, true);
+TEST_F(DeltaPerformerTest, MandatoryValidMetadataSignature2Test) {
+  DoMetadataSignatureTest(kValidMetadataSignature, true, true);
 }
 
-TEST(DeltaPerformerTest, RunAsRootNonMandatoryValidMetadataSignatureTest) {
-  DoMetadataSignatureTest(kValidMetadataSignature, kSignatureGenerated, false);
+TEST_F(DeltaPerformerTest, NonMandatoryValidMetadataSignatureTest) {
+  DoMetadataSignatureTest(kValidMetadataSignature, true, false);
 }
 
-TEST(DeltaPerformerTest, RunAsRootMandatoryOperationHashMismatchTest) {
-  DoOperationHashMismatchTest(kInvalidOperationData, true);
-}
-
-TEST(DeltaPerformerTest, UsePublicKeyFromResponse) {
-  MockPrefs prefs;
-  FakeSystemState fake_system_state;
-  InstallPlan install_plan;
+TEST_F(DeltaPerformerTest, UsePublicKeyFromResponse) {
   base::FilePath key_path;
 
   // The result of the GetPublicKeyResponse() method is based on three things
@@ -1366,10 +517,7 @@
   //  a. it's not an official build; and
   //  b. there is no key in the root filesystem.
 
-  DeltaPerformer *performer = new DeltaPerformer(&prefs,
-                                                 &fake_system_state,
-                                                 &install_plan);
-  FakeHardware* fake_hardware = fake_system_state.fake_hardware();
+  FakeHardware* fake_hardware = fake_system_state_.fake_hardware();
 
   string temp_dir;
   EXPECT_TRUE(utils::MakeTempDirectory("PublicKeyFromResponseTests.XXXXXX",
@@ -1380,61 +528,66 @@
 
   // Non-official build, non-existing public-key, key in response -> true
   fake_hardware->SetIsOfficialBuild(false);
-  performer->public_key_path_ = non_existing_file;
-  install_plan.public_key_rsa = "VGVzdAo=";  // result of 'echo "Test" | base64'
-  EXPECT_TRUE(performer->GetPublicKeyFromResponse(&key_path));
+  performer_.public_key_path_ = non_existing_file;
+  install_plan_.public_key_rsa = "VGVzdAo="; // result of 'echo "Test" | base64'
+  EXPECT_TRUE(performer_.GetPublicKeyFromResponse(&key_path));
   EXPECT_FALSE(key_path.empty());
   EXPECT_EQ(unlink(key_path.value().c_str()), 0);
   // Same with official build -> false
   fake_hardware->SetIsOfficialBuild(true);
-  EXPECT_FALSE(performer->GetPublicKeyFromResponse(&key_path));
+  EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
 
   // Non-official build, existing public-key, key in response -> false
   fake_hardware->SetIsOfficialBuild(false);
-  performer->public_key_path_ = existing_file;
-  install_plan.public_key_rsa = "VGVzdAo=";  // result of 'echo "Test" | base64'
-  EXPECT_FALSE(performer->GetPublicKeyFromResponse(&key_path));
+  performer_.public_key_path_ = existing_file;
+  install_plan_.public_key_rsa = "VGVzdAo="; // result of 'echo "Test" | base64'
+  EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
   // Same with official build -> false
   fake_hardware->SetIsOfficialBuild(true);
-  EXPECT_FALSE(performer->GetPublicKeyFromResponse(&key_path));
+  EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
 
   // Non-official build, non-existing public-key, no key in response -> false
   fake_hardware->SetIsOfficialBuild(false);
-  performer->public_key_path_ = non_existing_file;
-  install_plan.public_key_rsa = "";
-  EXPECT_FALSE(performer->GetPublicKeyFromResponse(&key_path));
+  performer_.public_key_path_ = non_existing_file;
+  install_plan_.public_key_rsa = "";
+  EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
   // Same with official build -> false
   fake_hardware->SetIsOfficialBuild(true);
-  EXPECT_FALSE(performer->GetPublicKeyFromResponse(&key_path));
+  EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
 
   // Non-official build, existing public-key, no key in response -> false
   fake_hardware->SetIsOfficialBuild(false);
-  performer->public_key_path_ = existing_file;
-  install_plan.public_key_rsa = "";
-  EXPECT_FALSE(performer->GetPublicKeyFromResponse(&key_path));
+  performer_.public_key_path_ = existing_file;
+  install_plan_.public_key_rsa = "";
+  EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
   // Same with official build -> false
   fake_hardware->SetIsOfficialBuild(true);
-  EXPECT_FALSE(performer->GetPublicKeyFromResponse(&key_path));
+  EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
 
   // Non-official build, non-existing public-key, key in response
   // but invalid base64 -> false
   fake_hardware->SetIsOfficialBuild(false);
-  performer->public_key_path_ = non_existing_file;
-  install_plan.public_key_rsa = "not-valid-base64";
-  EXPECT_FALSE(performer->GetPublicKeyFromResponse(&key_path));
+  performer_.public_key_path_ = non_existing_file;
+  install_plan_.public_key_rsa = "not-valid-base64";
+  EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
 
-  delete performer;
   EXPECT_TRUE(test_utils::RecursiveUnlinkDir(temp_dir));
 }
 
-TEST(DeltaPerformerTest, MinorVersionsMatch) {
-  // Test that the minor version in update_engine.conf that is installed to
-  // the image matches the supported delta minor version in the update engine.
+TEST_F(DeltaPerformerTest, ConfVersionsMatch) {
+  // Test that the versions in update_engine.conf that is installed to the
+  // image match the supported delta versions in the update engine.
   uint32_t minor_version;
   chromeos::KeyValueStore store;
   EXPECT_TRUE(store.Load(base::FilePath("update_engine.conf")));
   EXPECT_TRUE(utils::GetMinorVersion(store, &minor_version));
   EXPECT_EQ(DeltaPerformer::kSupportedMinorPayloadVersion, minor_version);
+
+  string major_version_str;
+  uint64_t major_version;
+  EXPECT_TRUE(store.GetString("PAYLOAD_MAJOR_VERSION", &major_version_str));
+  EXPECT_TRUE(base::StringToUint64(major_version_str, &major_version));
+  EXPECT_EQ(DeltaPerformer::kSupportedMajorPayloadVersion, major_version);
 }
 
 }  // namespace chromeos_update_engine
diff --git a/error_code.h b/error_code.h
index 417e585..fdef895 100644
--- a/error_code.h
+++ b/error_code.h
@@ -90,7 +90,7 @@
   // particular value for this enum so that it's just one more than the last
   // one above and thus always represents the correct count of UMA metrics
   // buckets, even when new enums are added above this line in future. See
-  // utils::SendErrorCodeToUma on how this enum is used.
+  // metrics::ReportUpdateAttemptMetrics() on how this enum is used.
   kUmaReportedMax,
 
   // use the 2xxx range to encode HTTP errors. These errors are available in
diff --git a/http_fetcher_unittest.cc b/http_fetcher_unittest.cc
index 33cedf2..f3a9c3e 100644
--- a/http_fetcher_unittest.cc
+++ b/http_fetcher_unittest.cc
@@ -711,6 +711,42 @@
   }
 }
 
+TYPED_TEST(HttpFetcherTest, NoResponseTest) {
+  // This test starts a new http server but the server doesn't respond and just
+  // closes the connection.
+  if (this->test_.IsMock())
+    return;
+
+  PythonHttpServer* server = new PythonHttpServer();
+  int port = server->GetPort();
+  ASSERT_TRUE(server->started_);
+
+  // Handles destruction and claims ownership.
+  FailureHttpFetcherTestDelegate delegate(server);
+  unique_ptr<HttpFetcher> fetcher(this->test_.NewSmallFetcher());
+  fetcher->set_delegate(&delegate);
+  // The server will not reply at all, so we can limit the execution time of the
+  // test by reducing the low-speed timeout to something small. The test will
+  // finish once the TimeoutCallback() triggers (every second) and the timeout
+  // expired.
+  fetcher->set_low_speed_limit(kDownloadLowSpeedLimitBps, 1);
+
+  this->loop_.PostTask(FROM_HERE, base::Bind(
+      StartTransfer,
+      fetcher.get(),
+      LocalServerUrlForPath(port, "/hang")));
+  this->loop_.Run();
+
+  // Check that no other callback runs in the next two seconds. That would
+  // indicate a leaked callback.
+  bool timeout = false;
+  auto callback = base::Bind([&timeout]{ timeout = true;});
+  this->loop_.PostDelayedTask(FROM_HERE, callback,
+                              base::TimeDelta::FromSeconds(2));
+  EXPECT_TRUE(this->loop_.RunOnce(true));
+  EXPECT_TRUE(timeout);
+}
+
 TYPED_TEST(HttpFetcherTest, ServerDiesTest) {
   // This test starts a new http server and kills it after receiving its first
   // set of bytes. It test whether or not our fetcher eventually gives up on
diff --git a/libcurl_http_fetcher.cc b/libcurl_http_fetcher.cc
index e8a8c4b..5b6c605 100644
--- a/libcurl_http_fetcher.cc
+++ b/libcurl_http_fetcher.cc
@@ -514,9 +514,6 @@
 }
 
 void LibcurlHttpFetcher::TimeoutCallback() {
-  if (transfer_in_progress_)
-    CurlPerformOnce();
-
   // We always re-schedule the callback, even if we don't want to be called
   // anymore. We will remove the event source separately if we don't want to
   // be called back.
@@ -524,6 +521,11 @@
       FROM_HERE,
       base::Bind(&LibcurlHttpFetcher::TimeoutCallback, base::Unretained(this)),
       TimeDelta::FromSeconds(idle_seconds_));
+
+  // CurlPerformOnce() may call CleanUp(), so we need to schedule our callback
+  // first, since it could be canceled by this call.
+  if (transfer_in_progress_)
+    CurlPerformOnce();
 }
 
 void LibcurlHttpFetcher::CleanUp() {
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index 05d950f..8967762 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -1343,13 +1343,7 @@
   if (!prefs->SetInt64(kPrefsInstallDateDays, install_date_days))
     return false;
 
-  string metric_name = "Installer.InstallDateProvisioningSource";
-  system_state->metrics_lib()->SendEnumToUMA(
-      metric_name,
-      static_cast<int>(source),  // Sample.
-      kProvisionedMax);          // Maximum.
-
-  metric_name = metrics::kMetricInstallDateProvisioningSource;
+  string metric_name = metrics::kMetricInstallDateProvisioningSource;
   system_state->metrics_lib()->SendEnumToUMA(
       metric_name,
       static_cast<int>(source),  // Sample.
diff --git a/payload_constants.cc b/payload_constants.cc
index 9d6fb3d..aeddf11 100644
--- a/payload_constants.cc
+++ b/payload_constants.cc
@@ -18,6 +18,13 @@
 
 namespace chromeos_update_engine {
 
+const uint64_t kChromeOSMajorPayloadVersion = 1;
+const uint64_t kBrilloMajorPayloadVersion = 2;
+
+const uint32_t kFullPayloadMinorVersion = 0;
+const uint32_t kInPlaceMinorPayloadVersion = 1;
+const uint32_t kSourceMinorPayloadVersion = 2;
+
 const char kLegacyPartitionNameKernel[] = "boot";
 const char kLegacyPartitionNameRoot[] = "system";
 
diff --git a/payload_constants.h b/payload_constants.h
index 188ea84..21dab00 100644
--- a/payload_constants.h
+++ b/payload_constants.h
@@ -23,6 +23,22 @@
 
 namespace chromeos_update_engine {
 
+// The major version used by Chrome OS.
+extern const uint64_t kChromeOSMajorPayloadVersion;
+
+// The major version used by Brillo.
+extern const uint64_t kBrilloMajorPayloadVersion;
+
+// The minor version used for all full payloads.
+extern const uint32_t kFullPayloadMinorVersion;
+
+// The minor version used by the in-place delta generator algorithm.
+extern const uint32_t kInPlaceMinorPayloadVersion;
+
+// The minor version used by the A to B delta generator algorithm.
+extern const uint32_t kSourceMinorPayloadVersion;
+
+
 // The kernel and rootfs partition names used by the BootControlInterface when
 // handling update payloads with a major version 1. The names of the updated
 // partitions are include in the payload itself for major version 2.
diff --git a/payload_generator/full_update_generator_unittest.cc b/payload_generator/full_update_generator_unittest.cc
index 2f39126..6c1047c 100644
--- a/payload_generator/full_update_generator_unittest.cc
+++ b/payload_generator/full_update_generator_unittest.cc
@@ -21,7 +21,7 @@
 
 #include <gtest/gtest.h>
 
-#include "update_engine/delta_performer.h"
+#include "update_engine/payload_constants.h"
 #include "update_engine/payload_generator/extent_utils.h"
 #include "update_engine/test_utils.h"
 
@@ -35,7 +35,7 @@
  protected:
   void SetUp() override {
     config_.is_delta = false;
-    config_.minor_version = DeltaPerformer::kFullPayloadMinorVersion;
+    config_.minor_version = kFullPayloadMinorVersion;
     config_.hard_chunk_size = 128 * 1024;
     config_.block_size = 4096;
 
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc
index e122fb9..d38c5c5 100644
--- a/payload_generator/generate_delta_main.cc
+++ b/payload_generator/generate_delta_main.cc
@@ -30,6 +30,7 @@
 #include <chromeos/flag_helper.h>
 
 #include "update_engine/delta_performer.h"
+#include "update_engine/payload_constants.h"
 #include "update_engine/payload_generator/delta_diff_generator.h"
 #include "update_engine/payload_generator/delta_diff_utils.h"
 #include "update_engine/payload_generator/payload_generation_config.h"
@@ -424,7 +425,7 @@
         payload_config.minor_version = kInPlaceMinorPayloadVersion;
       }
     } else {
-      payload_config.minor_version = DeltaPerformer::kFullPayloadMinorVersion;
+      payload_config.minor_version = kFullPayloadMinorVersion;
     }
     LOG(INFO) << "Auto-detected minor_version=" << payload_config.minor_version;
   } else {
diff --git a/payload_generator/payload_generation_config.cc b/payload_generator/payload_generation_config.cc
index a2d9311..786c4e2 100644
--- a/payload_generator/payload_generation_config.cc
+++ b/payload_generator/payload_generation_config.cc
@@ -115,8 +115,7 @@
   } else {
     // All the "source" image fields must be empty for full payloads.
     TEST_AND_RETURN_FALSE(source.ValidateIsEmpty());
-    TEST_AND_RETURN_FALSE(minor_version ==
-                          DeltaPerformer::kFullPayloadMinorVersion);
+    TEST_AND_RETURN_FALSE(minor_version == kFullPayloadMinorVersion);
   }
 
   // In all cases, the target image must exists.
diff --git a/payload_state.cc b/payload_state.cc
index 78c048b..9a1c740 100644
--- a/payload_state.cc
+++ b/payload_state.cc
@@ -114,7 +114,6 @@
     SetNumResponsesSeen(num_responses_seen_ + 1);
     SetResponseSignature(new_response_signature);
     ResetPersistedState();
-    ReportUpdatesAbandonedEventCountMetric();
     return;
   }
 
@@ -420,18 +419,6 @@
 void PayloadState::IncrementPayloadAttemptNumber() {
   // Update the payload attempt number for both payload types: full and delta.
   SetPayloadAttemptNumber(GetPayloadAttemptNumber() + 1);
-
-  // Report the metric every time the value is incremented.
-  string metric = "Installer.PayloadAttemptNumber";
-  int value = GetPayloadAttemptNumber();
-
-  LOG(INFO) << "Uploading " << value << " (count) for metric " <<  metric;
-  system_state_->metrics_lib()->SendToUMA(
-       metric,
-       value,
-       1,    // min value
-       50,   // max value
-       kNumDefaultUmaBuckets);
 }
 
 void PayloadState::IncrementFullPayloadAttemptNumber() {
@@ -444,18 +431,6 @@
   LOG(INFO) << "Incrementing the full payload attempt number";
   SetFullPayloadAttemptNumber(GetFullPayloadAttemptNumber() + 1);
   UpdateBackoffExpiryTime();
-
-  // Report the metric every time the value is incremented.
-  string metric = "Installer.FullPayloadAttemptNumber";
-  int value = GetFullPayloadAttemptNumber();
-
-  LOG(INFO) << "Uploading " << value << " (count) for metric " <<  metric;
-  system_state_->metrics_lib()->SendToUMA(
-       metric,
-       value,
-       1,    // min value
-       50,   // max value
-       kNumDefaultUmaBuckets);
 }
 
 void PayloadState::IncrementUrlIndex() {
@@ -679,7 +654,6 @@
   string metric;
 
   // Report metrics collected from all known download sources to UMA.
-  int64_t successful_bytes_by_source[kNumDownloadSources];
   int64_t total_bytes_by_source[kNumDownloadSources];
   int64_t successful_bytes = 0;
   int64_t total_bytes = 0;
@@ -697,7 +671,6 @@
     // zero-byte events in the histogram.
 
     bytes = GetCurrentBytesDownloaded(source);
-    successful_bytes_by_source[i] = bytes;
     successful_bytes += bytes;
     successful_mbs += bytes / kNumBytesInOneMiB;
     SetCurrentBytesDownloaded(source, 0, true);
@@ -722,7 +695,6 @@
   SetNumReboots(0);
 
   TimeDelta duration = GetUpdateDuration();
-  TimeDelta duration_uptime = GetUpdateDurationUptime();
 
   prefs_->Delete(kPrefsUpdateTimestampStart);
   prefs_->Delete(kPrefsUpdateDurationUptime);
@@ -745,142 +717,6 @@
                                          duration,
                                          reboot_count,
                                          url_switch_count);
-
-  // TODO(zeuthen): This is the old metric reporting code which is
-  // slated for removal soon. See http://crbug.com/355745 for details.
-
-  // The old metrics code is using MiB's instead of bytes to calculate
-  // the overhead which due to rounding makes the numbers slightly
-  // different.
-  download_overhead_percentage = 0;
-  if (successful_mbs > 0) {
-    download_overhead_percentage = (total_mbs - successful_mbs) * 100ULL /
-                                   successful_mbs;
-  }
-
-  int download_sources_used = 0;
-  for (int i = 0; i < kNumDownloadSources; i++) {
-    DownloadSource source = static_cast<DownloadSource>(i);
-    const int kMaxMiBs = 10240;  // Anything above 10GB goes in the last bucket.
-    int64_t mbs;
-
-    // Only consider this download source (and send byte counts) as
-    // having been used if we downloaded a non-trivial amount of bytes
-    // (e.g. at least 1 MiB) that contributed to the final success of
-    // the update. Otherwise we're going to end up with a lot of
-    // zero-byte events in the histogram.
-
-    mbs = successful_bytes_by_source[i] / kNumBytesInOneMiB;
-    if (mbs > 0) {
-      metric = "Installer.SuccessfulMBsDownloadedFrom" +
-          utils::ToString(source);
-      LOG(INFO) << "Uploading " << mbs << " (MBs) for metric " << metric;
-      system_state_->metrics_lib()->SendToUMA(metric,
-                                              mbs,
-                                              0,  // min
-                                              kMaxMiBs,
-                                              kNumDefaultUmaBuckets);
-    }
-
-    mbs = total_bytes_by_source[i] / kNumBytesInOneMiB;
-    if (mbs > 0) {
-      metric = "Installer.TotalMBsDownloadedFrom" + utils::ToString(source);
-      LOG(INFO) << "Uploading " << mbs << " (MBs) for metric " << metric;
-      system_state_->metrics_lib()->SendToUMA(metric,
-                                              mbs,
-                                              0,  // min
-                                              kMaxMiBs,
-                                              kNumDefaultUmaBuckets);
-      download_sources_used |= (1 << i);
-    }
-  }
-
-  metric = "Installer.DownloadSourcesUsed";
-  LOG(INFO) << "Uploading 0x" << std::hex << download_sources_used
-            << " (bit flags) for metric " << metric;
-  int num_buckets = min(1 << kNumDownloadSources, kNumDefaultUmaBuckets);
-  system_state_->metrics_lib()->SendToUMA(metric,
-                                          download_sources_used,
-                                          0,  // min
-                                          1 << kNumDownloadSources,
-                                          num_buckets);
-
-  metric = "Installer.DownloadOverheadPercentage";
-  LOG(INFO) << "Uploading " << download_overhead_percentage
-            << "% for metric " << metric;
-  system_state_->metrics_lib()->SendToUMA(metric,
-                                          download_overhead_percentage,
-                                          0,     // min: 0% overhead
-                                          1000,  // max: 1000% overhead
-                                          kNumDefaultUmaBuckets);
-
-  metric = "Installer.UpdateURLSwitches";
-  LOG(INFO) << "Uploading " << url_switch_count
-            << " (count) for metric " << metric;
-  system_state_->metrics_lib()->SendToUMA(
-       metric,
-       url_switch_count,
-       0,    // min value
-       100,  // max value
-       kNumDefaultUmaBuckets);
-
-  metric = "Installer.UpdateNumReboots";
-  LOG(INFO) << "Uploading reboot count of " << reboot_count << " for metric "
-            <<  metric;
-  system_state_->metrics_lib()->SendToUMA(
-      metric,
-      reboot_count,  // sample
-      0,    // min = 0.
-      50,   // max
-      25);  // buckets
-
-  metric = "Installer.UpdateDurationMinutes";
-  system_state_->metrics_lib()->SendToUMA(
-       metric,
-       static_cast<int>(duration.InMinutes()),
-       1,             // min: 1 minute
-       365*24*60,     // max: 1 year (approx)
-       kNumDefaultUmaBuckets);
-  LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration)
-            << " for metric " <<  metric;
-
-  metric = "Installer.UpdateDurationUptimeMinutes";
-  system_state_->metrics_lib()->SendToUMA(
-       metric,
-       static_cast<int>(duration_uptime.InMinutes()),
-       1,             // min: 1 minute
-       30*24*60,      // max: 1 month (approx)
-       kNumDefaultUmaBuckets);
-  LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration_uptime)
-            << " for metric " <<  metric;
-
-  metric = "Installer.PayloadFormat";
-  system_state_->metrics_lib()->SendEnumToUMA(
-      metric,
-      payload_type,
-      kNumPayloadTypes);
-  LOG(INFO) << "Uploading " << utils::ToString(payload_type)
-            << " for metric " <<  metric;
-
-  metric = "Installer.AttemptsCount.Total";
-  system_state_->metrics_lib()->SendToUMA(
-       metric,
-       attempt_count,
-       1,      // min
-       50,     // max
-       kNumDefaultUmaBuckets);
-  LOG(INFO) << "Uploading " << attempt_count
-            << " for metric " <<  metric;
-
-  metric = "Installer.UpdatesAbandonedCount";
-  LOG(INFO) << "Uploading " << updates_abandoned_count
-            << " (count) for metric " <<  metric;
-  system_state_->metrics_lib()->SendToUMA(
-       metric,
-       updates_abandoned_count,
-       0,    // min value
-       100,  // max value
-       kNumDefaultUmaBuckets);
 }
 
 void PayloadState::UpdateNumReboots() {
@@ -1309,24 +1145,6 @@
   prefs_->SetInt64(kPrefsNumResponsesSeen, num_responses_seen_);
 }
 
-void PayloadState::ReportUpdatesAbandonedEventCountMetric() {
-  string metric = "Installer.UpdatesAbandonedEventCount";
-  int value = num_responses_seen_ - 1;
-
-  // Do not send an "abandoned" event when 0 payloads were abandoned since the
-  // last successful update.
-  if (value == 0)
-    return;
-
-  LOG(INFO) << "Uploading " << value << " (count) for metric " <<  metric;
-  system_state_->metrics_lib()->SendToUMA(
-       metric,
-       value,
-       0,    // min value
-       100,  // max value
-       kNumDefaultUmaBuckets);
-}
-
 void PayloadState::ComputeCandidateUrls() {
   bool http_url_ok = true;
 
@@ -1361,16 +1179,7 @@
 
 void PayloadState::BootedIntoUpdate(TimeDelta time_to_reboot) {
   // Send |time_to_reboot| as a UMA stat.
-  string metric = "Installer.TimeToRebootMinutes";
-  system_state_->metrics_lib()->SendToUMA(metric,
-                                          time_to_reboot.InMinutes(),
-                                          0,         // min: 0 minute
-                                          30*24*60,  // max: 1 month (approx)
-                                          kNumDefaultUmaBuckets);
-  LOG(INFO) << "Uploading " << utils::FormatTimeDelta(time_to_reboot)
-            << " for metric " <<  metric;
-
-  metric = metrics::kMetricTimeToRebootMinutes;
+  string metric = metrics::kMetricTimeToRebootMinutes;
   system_state_->metrics_lib()->SendToUMA(metric,
                                           time_to_reboot.InMinutes(),
                                           0,         // min: 0 minute
@@ -1440,18 +1249,7 @@
       }
 
       // Report the UMA metric of the current boot failure.
-      string metric = "Installer.RebootToNewPartitionAttempt";
-
-      LOG(INFO) << "Uploading " << target_attempt
-                << " (count) for metric " <<  metric;
-      system_state_->metrics_lib()->SendToUMA(
-           metric,
-           target_attempt,
-           1,    // min value
-           50,   // max value
-           kNumDefaultUmaBuckets);
-
-      metric = metrics::kMetricFailedUpdateCount;
+      string metric = metrics::kMetricFailedUpdateCount;
       LOG(INFO) << "Uploading " << target_attempt
                 << " (count) for metric " <<  metric;
       system_state_->metrics_lib()->SendToUMA(
diff --git a/payload_state.h b/payload_state.h
index 23c01a1..3616826 100644
--- a/payload_state.h
+++ b/payload_state.h
@@ -377,13 +377,6 @@
   // Initializes |num_responses_seen_| from persisted state.
   void LoadNumResponsesSeen();
 
-  // Reports metric conveying how many times updates were abandoned since
-  // the last update was applied. The difference between this metric and the
-  // previous ReportUpdatesAbandonedCountMetric() one is that this metric is
-  // reported every time an update is abandoned, as oposed to the mentioned
-  // metric that is reported once the new update was applied.
-  void ReportUpdatesAbandonedEventCountMetric();
-
   // Initializes |num_reboots_| from the persisted state.
   void LoadNumReboots();
 
diff --git a/payload_state_unittest.cc b/payload_state_unittest.cc
index e4eca89..012a3fc 100644
--- a/payload_state_unittest.cc
+++ b/payload_state_unittest.cc
@@ -324,10 +324,6 @@
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
     .Times(AnyNumber());
 
-  // The first response doesn't send an abandoned event.
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.UpdatesAbandonedEventCount", 0, _, _, _)).Times(0);
-
   // Set the first response.
   SetupPayloadStateWith2Urls("Hash5823", true, &payload_state, &response);
   EXPECT_EQ(1, payload_state.GetNumResponsesSeen());
@@ -338,9 +334,6 @@
   EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(1, payload_state.GetUrlSwitchCount());
 
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.UpdatesAbandonedEventCount", 1, _, _, _));
-
   // Now, slightly change the response and set it again.
   SetupPayloadStateWith2Urls("Hash8225", true, &payload_state, &response);
   EXPECT_EQ(2, payload_state.GetNumResponsesSeen());
@@ -350,9 +343,6 @@
   EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(1, payload_state.GetUrlSwitchCount());
 
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.UpdatesAbandonedEventCount", 2, _, _, _));
-
   // Return a third different response.
   SetupPayloadStateWith2Urls("Hash9999", true, &payload_state, &response);
   EXPECT_EQ(3, payload_state.GetNumResponsesSeen());
@@ -546,11 +536,6 @@
 
   SetupPayloadStateWith2Urls("Hash8593", true, &payload_state, &response);
 
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.PayloadAttemptNumber", 1, _, _, _));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.FullPayloadAttemptNumber", 1, _, _, _));
-
   // This should just advance the payload attempt number;
   EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
   EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
@@ -591,13 +576,6 @@
 
   SetupPayloadStateWith2Urls("Hash8593", true, &payload_state, &response);
 
-  // Metrics for Full payload attempt number is not sent with Delta payloads.
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.PayloadAttemptNumber", 1, _, _, _));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.FullPayloadAttemptNumber", _, _, _, _))
-      .Times(0);
-
   // This should just advance the payload attempt number;
   EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
   EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
@@ -873,7 +851,6 @@
   int third_chunk = 32345678;
   int http_chunk = first_chunk + third_chunk;
   http_total += third_chunk;
-  int https_chunk = second_chunk;
   payload_state.DownloadProgress(third_chunk);
 
   // Test that third chunk is again back on HTTP. HTTPS remains on second chunk.
@@ -901,58 +878,20 @@
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
     .Times(AnyNumber());
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.SuccessfulMBsDownloadedFromHttpServer",
-      http_chunk / kNumBytesInOneMiB, _, _, _));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.TotalMBsDownloadedFromHttpServer",
-      http_total / kNumBytesInOneMiB, _, _, _));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.SuccessfulMBsDownloadedFromHttpsServer",
-      https_chunk / kNumBytesInOneMiB, _, _, _));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.TotalMBsDownloadedFromHttpsServer",
-      https_total / kNumBytesInOneMiB, _, _, _));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.SuccessfulMBsDownloadedFromHttpPeer",
-      p2p_total / kNumBytesInOneMiB, _, _, _));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.TotalMBsDownloadedFromHttpPeer",
-      p2p_total / kNumBytesInOneMiB, _, _, _));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.UpdateURLSwitches",
-      3, _, _, _));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
       metrics::kMetricSuccessfulUpdateUrlSwitchCount,
       3, _, _, _));
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.UpdateDurationMinutes",
-      _, _, _, _));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
       metrics::kMetricSuccessfulUpdateTotalDurationMinutes,
       _, _, _, _));
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.UpdateDurationUptimeMinutes",
-      _, _, _, _));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.DownloadSourcesUsed",
-      (1 << kDownloadSourceHttpsServer) | (1 << kDownloadSourceHttpServer) |
-      (1 << kDownloadSourceHttpPeer),
-      _, _, _));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.DownloadOverheadPercentage", 318, _, _, _));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
       metrics::kMetricSuccessfulUpdateDownloadOverheadPercentage,
       314, _, _, _));
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
-      "Installer.PayloadFormat", kPayloadTypeFull, kNumPayloadTypes));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
       metrics::kMetricAttemptPayloadType, kPayloadTypeFull, kNumPayloadTypes));
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
       metrics::kMetricSuccessfulUpdatePayloadType, kPayloadTypeFull,
       kNumPayloadTypes));
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.AttemptsCount.Total", 1, _, _, _));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
       metrics::kMetricSuccessfulUpdateAttemptCount, 1, _, _, _));
 
   payload_state.UpdateSucceeded();
@@ -991,10 +930,6 @@
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(_, _, _, _, _))
     .Times(AnyNumber());
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.DownloadSourcesUsed",
-      (1 << kDownloadSourceHttpServer),
-      _, _, _));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
       metrics::kMetricSuccessfulUpdateDownloadSourcesUsed,
       (1 << kDownloadSourceHttpServer),
       _, _, _));
@@ -1199,9 +1134,6 @@
 
   // Expect 500 - 30 seconds = 470 seconds ~= 7 min 50 sec
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.TimeToRebootMinutes",
-      7, _, _, _));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
       metrics::kMetricTimeToRebootMinutes,
       7, _, _, _));
   fake_system_state.set_system_rebooted(true);
@@ -1394,8 +1326,6 @@
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
     .Times(AnyNumber());
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
-      "Installer.PayloadFormat", kPayloadTypeDelta, kNumPayloadTypes));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
       metrics::kMetricAttemptPayloadType, kPayloadTypeDelta, kNumPayloadTypes));
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
       metrics::kMetricSuccessfulUpdatePayloadType, kPayloadTypeDelta,
@@ -1414,8 +1344,6 @@
   payload_state.DownloadComplete();
 
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
-      "Installer.PayloadFormat", kPayloadTypeDelta, kNumPayloadTypes));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
       metrics::kMetricAttemptPayloadType, kPayloadTypeDelta,
       kNumPayloadTypes));
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
@@ -1444,8 +1372,6 @@
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
     .Times(AnyNumber());
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
-      "Installer.PayloadFormat", kPayloadTypeForcedFull, kNumPayloadTypes));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
       metrics::kMetricAttemptPayloadType, kPayloadTypeForcedFull,
       kNumPayloadTypes));
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
@@ -1475,8 +1401,6 @@
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
     .Times(AnyNumber());
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
-      "Installer.PayloadFormat", kPayloadTypeFull, kNumPayloadTypes));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
       metrics::kMetricAttemptPayloadType, kPayloadTypeFull,
       kNumPayloadTypes));
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
@@ -1502,8 +1426,6 @@
 
   // Reboot into the same environment to get an UMA metric with a value of 1.
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.RebootToNewPartitionAttempt", 1, _, _, _));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
       metrics::kMetricFailedUpdateCount, 1, _, _, _));
   payload_state.ReportFailedBootIfNeeded();
   Mock::VerifyAndClearExpectations(fake_system_state.mock_metrics_lib());
@@ -1513,8 +1435,6 @@
   payload_state.ExpectRebootInNewVersion("Version:12345678");
 
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.RebootToNewPartitionAttempt", 2, _, _, _));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
       metrics::kMetricFailedUpdateCount, 2, _, _, _));
   payload_state.ReportFailedBootIfNeeded();
   Mock::VerifyAndClearExpectations(fake_system_state.mock_metrics_lib());
@@ -1523,8 +1443,6 @@
   // different payload. This should send a value of 1 this time.
   payload_state.ExpectRebootInNewVersion("Version:3141592");
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.RebootToNewPartitionAttempt", 1, _, _, _));
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
       metrics::kMetricFailedUpdateCount, 1, _, _, _));
   payload_state.ReportFailedBootIfNeeded();
   Mock::VerifyAndClearExpectations(fake_system_state.mock_metrics_lib());
@@ -1552,9 +1470,6 @@
   fake_boot_control->SetCurrentSlot(1);
 
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.RebootToNewPartitionAttempt", _, _, _, _))
-      .Times(0);
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
       metrics::kMetricFailedUpdateCount, _, _, _, _))
       .Times(0);
   payload_state.ReportFailedBootIfNeeded();
@@ -1581,9 +1496,6 @@
   payload_state.ExpectRebootInNewVersion("Version:12345678");
 
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.RebootToNewPartitionAttempt", _, _, _, _))
-      .Times(0);
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
       metrics::kMetricFailedUpdateCount, _, _, _, _))
       .Times(0);
 
@@ -1603,9 +1515,6 @@
   EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
 
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
-      "Installer.RebootToNewPartitionAttempt", _, _, _, _))
-      .Times(0);
-  EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
       metrics::kMetricFailedUpdateCount, _, _, _, _))
       .Times(0);
 
diff --git a/test_http_server.cc b/test_http_server.cc
index f4190c3..7326eb5 100644
--- a/test_http_server.cc
+++ b/test_http_server.cc
@@ -41,6 +41,7 @@
 #include <vector>
 
 #include <base/logging.h>
+#include <base/posix/eintr_wrapper.h>
 #include <base/strings/string_split.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
@@ -450,6 +451,12 @@
   }
 }
 
+void HandleHang(int fd) {
+  LOG(INFO) << "Hanging until the other side of the connection is closed.";
+  char c;
+  while (HANDLE_EINTR(read(fd, &c, 1)) > 0) {}
+}
+
 void HandleDefault(int fd, const HttpRequest& request) {
   const off_t start_offset = request.start_offset;
   const string data("unhandled path");
@@ -517,6 +524,8 @@
   } else if (base::StartsWithASCII(url, "/error-if-offset/", true)) {
     const UrlTerms terms(url, 3);
     HandleErrorIfOffset(fd, request, terms.GetSizeT(1), terms.GetInt(2));
+  } else if (url == "/hang") {
+    HandleHang(fd);
   } else {
     HandleDefault(fd, request);
   }
diff --git a/update_attempter.cc b/update_attempter.cc
index cda244d..d4e1654 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -257,16 +257,6 @@
     return;
   }
 
-  string metric = "Installer.OSAgeDays";
-  LOG(INFO) << "Uploading " << utils::FormatTimeDelta(age)
-            << " for metric " <<  metric;
-  system_state_->metrics_lib()->SendToUMA(
-       metric,
-       static_cast<int>(age.InDays()),
-       0,             // min: 0 days
-       6*30,          // max: 6 months (approx)
-       kNumDefaultUmaBuckets);
-
   metrics::ReportDailyMetrics(system_state_, age);
 }
 
@@ -1001,10 +991,6 @@
       // next boot.
       system_state_->payload_state()->ExpectRebootInNewVersion(
           target_version_uid);
-
-      // Also report the success code so that the percentiles can be
-      // interpreted properly for the remaining error codes in UMA.
-      utils::SendErrorCodeToUma(system_state_, code);
     } else {
       // If we just finished a rollback, then we expect to have no Omaha
       // response. Otherwise, it's an error.
@@ -1314,11 +1300,8 @@
   LOG(ERROR) << "Update failed.";
   system_state_->payload_state()->UpdateFailed(error_event_->error_code);
 
-  // Send it to Uma.
-  LOG(INFO) << "Reporting the error event";
-  utils::SendErrorCodeToUma(system_state_, error_event_->error_code);
-
   // Send it to Omaha.
+  LOG(INFO) << "Reporting the error event";
   shared_ptr<OmahaRequestAction> error_event_action(
       new OmahaRequestAction(system_state_,
                              error_event_.release(),  // Pass ownership.
diff --git a/update_engine.conf b/update_engine.conf
index dc65ebe..dea31e4 100644
--- a/update_engine.conf
+++ b/update_engine.conf
@@ -1 +1,2 @@
+PAYLOAD_MAJOR_VERSION=1
 PAYLOAD_MINOR_VERSION=2
diff --git a/update_engine.gyp b/update_engine.gyp
index 6138b87..05bb6b4 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -390,6 +390,7 @@
             'chrome_browser_proxy_resolver_unittest.cc',
             'connection_manager_unittest.cc',
             'dbus_service_unittest.cc',
+            'delta_performer_integration_test.cc',
             'delta_performer_unittest.cc',
             'download_action_unittest.cc',
             'extent_writer_unittest.cc',
diff --git a/utils.cc b/utils.cc
index 4018b37..98f3576 100644
--- a/utils.cc
+++ b/utils.cc
@@ -1246,56 +1246,6 @@
   return metrics::ConnectionType::kUnknown;
 }
 
-// Returns a printable version of the various flags denoted in the higher order
-// bits of the given code. Returns an empty string if none of those bits are
-// set.
-string GetFlagNames(uint32_t code) {
-  uint32_t flags = (static_cast<uint32_t>(code) &
-                    static_cast<uint32_t>(ErrorCode::kSpecialFlags));
-  string flag_names;
-  string separator = "";
-  for (size_t i = 0; i < sizeof(flags) * 8; i++) {
-    uint32_t flag = flags & (1 << i);
-    if (flag) {
-      flag_names += separator + CodeToString(static_cast<ErrorCode>(flag));
-      separator = ", ";
-    }
-  }
-
-  return flag_names;
-}
-
-void SendErrorCodeToUma(SystemState* system_state, ErrorCode code) {
-  if (!system_state)
-    return;
-
-  ErrorCode uma_error_code = GetBaseErrorCode(code);
-
-  // If the code doesn't have flags computed already, compute them now based on
-  // the state of the current update attempt.
-  uint32_t flags =
-      static_cast<int>(code) & static_cast<int>(ErrorCode::kSpecialFlags);
-  if (!flags)
-    flags = system_state->update_attempter()->GetErrorCodeFlags();
-
-  // Determine the UMA bucket depending on the flags. But, ignore the resumed
-  // flag, as it's perfectly normal for production devices to resume their
-  // downloads and so we want to record those cases also in NormalErrorCodes
-  // bucket.
-  string metric =
-      flags & ~static_cast<uint32_t>(ErrorCode::kResumedFlag) ?
-      "Installer.DevModeErrorCodes" : "Installer.NormalErrorCodes";
-
-  LOG(INFO) << "Sending error code " << uma_error_code
-            << " (" << CodeToString(uma_error_code) << ")"
-            << " to UMA metric: " << metric
-            << ". Flags = " << (flags ? GetFlagNames(flags) : "None");
-
-  system_state->metrics_lib()->SendEnumToUMA(
-      metric, static_cast<int>(uma_error_code),
-      static_cast<int>(ErrorCode::kUmaReportedMax));
-}
-
 string CodeToString(ErrorCode code) {
   // If the given code has both parts (i.e. the error code part and the flags
   // part) then strip off the flags part since the switch statement below
diff --git a/utils.h b/utils.h
index 252feab..c1ee5a9 100644
--- a/utils.h
+++ b/utils.h
@@ -334,11 +334,6 @@
 metrics::ConnectionType GetConnectionType(NetworkConnectionType type,
                                           NetworkTethering tethering);
 
-// Sends the error code to UMA using the metrics interface object in the given
-// system state. It also uses the system state to determine the right UMA
-// bucket for the error code.
-void SendErrorCodeToUma(SystemState* system_state, ErrorCode code);
-
 // Returns a string representation of the ErrorCodes (either the base
 // error codes or the bit flags) for logging purposes.
 std::string CodeToString(ErrorCode code);