Add a skeleton class for VABCPartitionWriter

Bug: 168554689
Test: treehugger
Change-Id: Ib73084b779620db47d2ff4fbb6d87f38047e9211
diff --git a/Android.bp b/Android.bp
index 9187e67..27ba172 100644
--- a/Android.bp
+++ b/Android.bp
@@ -124,6 +124,7 @@
         "libfec_rs",
         "libpuffpatch",
         "libverity_tree",
+        "libsnapshot_cow",
     ],
     shared_libs: [
         "libbase",
@@ -179,6 +180,8 @@
         "payload_consumer/payload_metadata.cc",
         "payload_consumer/payload_verifier.cc",
         "payload_consumer/partition_writer.cc",
+        "payload_consumer/partition_writer_factory_android.cc",
+        "payload_consumer/vabc_partition_writer.cc",
         "payload_consumer/postinstall_runner_action.cc",
         "payload_consumer/verity_writer_android.cc",
         "payload_consumer/xz_extent_writer.cc",
diff --git a/BUILD.gn b/BUILD.gn
index b7de9fc..9575fab 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -160,6 +160,8 @@
     "payload_consumer/install_plan.cc",
     "payload_consumer/mount_history.cc",
     "payload_consumer/partition_update_generator_stub.cc",
+    "payload_consumer/partition_writer_factory_chromeos.cc",
+    "payload_consumer/partition_writer.cc",
     "payload_consumer/payload_constants.cc",
     "payload_consumer/payload_metadata.cc",
     "payload_consumer/payload_verifier.cc",
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 87fc4cf..9bf6d7e 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -241,13 +241,14 @@
       install_plan_->partitions.size() - partitions_.size();
   const InstallPlan::Partition& install_part =
       install_plan_->partitions[num_previous_partitions + current_partition_];
-  partition_writer_ = std::make_unique<PartitionWriter>(
+  auto dynamic_control = boot_control_->GetDynamicPartitionControl();
+  partition_writer_ = partition_writer::CreatePartitionWriter(
       partition,
       install_part,
-      boot_control_->GetDynamicPartitionControl(),
+      dynamic_control,
       block_size_,
-      interactive_);
-
+      interactive_,
+      IsDynamicPartition(install_part.name));
   // Open source fds if we have a delta payload, or for partitions in the
   // partial update.
   bool source_may_exist = manifest_.partial_update() ||
@@ -642,6 +643,11 @@
     }
   }
 
+  auto dynamic_control = boot_control_->GetDynamicPartitionControl();
+  CHECK_NE(dynamic_control, nullptr);
+  TEST_AND_RETURN_FALSE(dynamic_control->ListDynamicPartitionsForSlot(
+      install_plan_->target_slot, &dynamic_partitions_));
+
   // Partitions in manifest are no longer needed after preparing partitions.
   manifest_.clear_partitions();
   // TODO(xunchang) TBD: allow partial update only on devices with dynamic
@@ -1503,4 +1509,10 @@
   return true;
 }
 
+bool DeltaPerformer::IsDynamicPartition(const std::string& part_name) {
+  return std::find(dynamic_partitions_.begin(),
+                   dynamic_partitions_.end(),
+                   part_name) != dynamic_partitions_.end();
+}
+
 }  // namespace chromeos_update_engine
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index d44f6c2..96bc849 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -299,6 +299,8 @@
   //   a generic error on the device.
   ErrorCode CheckTimestampError() const;
 
+  // Check if partition `part_name` is a dynamic partition.
+  bool IsDynamicPartition(const std::string& part_name);
   // Update Engine preference store.
   PrefsInterface* prefs_;
 
@@ -336,22 +338,22 @@
   // otherwise 0.
   size_t num_total_operations_{0};
 
-  // The list of partitions to update as found in the manifest major version 2.
-  // When parsing an older manifest format, the information is converted over to
-  // this format instead.
+  // The list of partitions to update as found in the manifest major
+  // version 2. When parsing an older manifest format, the information is
+  // converted over to this format instead.
   std::vector<PartitionUpdate> partitions_;
 
   // Index in the list of partitions (|partitions_| member) of the current
   // partition being processed.
   size_t current_partition_{0};
 
-  // Index of the next operation to perform in the manifest. The index is linear
-  // on the total number of operation on the manifest.
+  // Index of the next operation to perform in the manifest. The index is
+  // linear on the total number of operation on the manifest.
   size_t next_operation_num_{0};
 
   // A buffer used for accumulating downloaded data. Initially, it stores the
-  // payload metadata; once that's downloaded and parsed, it stores data for the
-  // next update operation.
+  // payload metadata; once that's downloaded and parsed, it stores data for
+  // the next update operation.
   brillo::Blob buffer_;
   // Offset of buffer_ in the binary blobs section of the update.
   uint64_t buffer_offset_{0};
@@ -393,8 +395,9 @@
   // If |true|, the update is user initiated (vs. periodic update checks).
   bool interactive_{false};
 
-  // The timeout after which we should force emitting a progress log (constant),
-  // and the actual point in time for the next forced log to be emitted.
+  // The timeout after which we should force emitting a progress log
+  // (constant), and the actual point in time for the next forced log to be
+  // emitted.
   const base::TimeDelta forced_progress_log_wait_{
       base::TimeDelta::FromSeconds(kProgressLogTimeoutSeconds)};
   base::TimeTicks forced_progress_log_time_;
@@ -407,6 +410,8 @@
 
   std::unique_ptr<PartitionWriter> partition_writer_;
 
+  // List of dynamic partitions on device.
+  std::vector<std::string> dynamic_partitions_;
   DISALLOW_COPY_AND_ASSIGN(DeltaPerformer);
 };
 
diff --git a/payload_consumer/partition_writer.cc b/payload_consumer/partition_writer.cc
index d47ebee..b4b869c 100644
--- a/payload_consumer/partition_writer.cc
+++ b/payload_consumer/partition_writer.cc
@@ -308,7 +308,7 @@
                                               const void* data,
                                               size_t count) {
   // Setup the ExtentWriter stack based on the operation type.
-  std::unique_ptr<ExtentWriter> writer = std::make_unique<DirectExtentWriter>();
+  std::unique_ptr<ExtentWriter> writer = CreateBaseExtentWriter();
 
   if (operation.type() == InstallOperation::REPLACE_BZ) {
     writer.reset(new BzipExtentWriter(std::move(writer)));
@@ -320,7 +320,7 @@
       writer->Init(target_fd_, operation.dst_extents(), block_size_));
   TEST_AND_RETURN_FALSE(writer->Write(data, operation.data_length()));
 
-  return target_fd_->Flush();
+  return Flush();
 }
 
 bool PartitionWriter::PerformZeroOrDiscardOperation(
@@ -353,7 +353,7 @@
           target_fd_, zeros.data(), chunk_length, start + offset));
     }
   }
-  return target_fd_->Flush();
+  return Flush();
 }
 
 bool PartitionWriter::PerformSourceCopyOperation(
@@ -464,8 +464,9 @@
                                                        block_size_,
                                                        nullptr));
   }
-  return target_fd_->Flush();
+  return Flush();
 }
+
 bool PartitionWriter::PerformSourceBsdiffOperation(
     const InstallOperation& operation,
     ErrorCode* error,
@@ -481,7 +482,7 @@
       std::move(reader),
       utils::BlocksInExtents(operation.src_extents()) * block_size_);
 
-  auto writer = std::make_unique<DirectExtentWriter>();
+  auto writer = CreateBaseExtentWriter();
   TEST_AND_RETURN_FALSE(
       writer->Init(target_fd_, operation.dst_extents(), block_size_));
   auto dst_file = std::make_unique<BsdiffExtentFile>(
@@ -492,7 +493,7 @@
                                         std::move(dst_file),
                                         reinterpret_cast<const uint8_t*>(data),
                                         count) == 0);
-  return target_fd_->Flush();
+  return Flush();
 }
 
 bool PartitionWriter::PerformPuffDiffOperation(
@@ -510,7 +511,7 @@
       std::move(reader),
       utils::BlocksInExtents(operation.src_extents()) * block_size_));
 
-  auto writer = std::make_unique<DirectExtentWriter>();
+  auto writer = CreateBaseExtentWriter();
   TEST_AND_RETURN_FALSE(
       writer->Init(target_fd_, operation.dst_extents(), block_size_));
   puffin::UniqueStreamPtr dst_stream(new PuffinExtentStream(
@@ -524,7 +525,7 @@
                         reinterpret_cast<const uint8_t*>(data),
                         count,
                         kMaxCacheSize));
-  return target_fd_->Flush();
+  return Flush();
 }
 
 FileDescriptorPtr PartitionWriter::ChooseSourceFD(
@@ -641,4 +642,13 @@
   source_ecc_open_failure_ = false;
   return -err;
 }
+
+std::unique_ptr<ExtentWriter> PartitionWriter::CreateBaseExtentWriter() {
+  return std::make_unique<DirectExtentWriter>();
+}
+
+bool PartitionWriter::Flush() {
+  return target_fd_->Flush();
+}
+
 }  // namespace chromeos_update_engine
diff --git a/payload_consumer/partition_writer.h b/payload_consumer/partition_writer.h
index 624a411..1acbddc 100644
--- a/payload_consumer/partition_writer.h
+++ b/payload_consumer/partition_writer.h
@@ -18,12 +18,14 @@
 #define UPDATE_ENGINE_PARTITION_WRITER_H_
 
 #include <cstdint>
+#include <memory>
 #include <string>
 
 #include <brillo/secure_blob.h>
 #include <gtest/gtest_prod.h>
 
 #include "update_engine/common/dynamic_partition_control_interface.h"
+#include "update_engine/payload_consumer/extent_writer.h"
 #include "update_engine/payload_consumer/file_descriptor.h"
 #include "update_engine/payload_consumer/install_plan.h"
 #include "update_engine/update_metadata.pb.h"
@@ -35,7 +37,7 @@
                   DynamicPartitionControlInterface* dynamic_control,
                   size_t block_size,
                   bool is_interactive);
-  ~PartitionWriter();
+  virtual ~PartitionWriter();
   static bool ValidateSourceHash(const brillo::Blob& calculated_hash,
                                  const InstallOperation& operation,
                                  const FileDescriptorPtr source_fd,
@@ -43,33 +45,34 @@
 
   // Perform necessary initialization work before InstallOperation can be
   // applied to this partition
-  [[nodiscard]] bool Init(const InstallPlan* install_plan,
-                          bool source_may_exist);
+  [[nodiscard]] virtual bool Init(const InstallPlan* install_plan,
+                                  bool source_may_exist);
 
   int Close();
 
   // These perform a specific type of operation and return true on success.
   // |error| will be set if source hash mismatch, otherwise |error| might not be
   // set even if it fails.
-  [[nodiscard]] bool PerformReplaceOperation(const InstallOperation& operation,
-                                             const void* data,
-                                             size_t count);
-  [[nodiscard]] bool PerformZeroOrDiscardOperation(
+  [[nodiscard]] virtual bool PerformReplaceOperation(
+      const InstallOperation& operation, const void* data, size_t count);
+  [[nodiscard]] virtual bool PerformZeroOrDiscardOperation(
       const InstallOperation& operation);
 
-  [[nodiscard]] bool PerformSourceCopyOperation(
+  [[nodiscard]] virtual bool PerformSourceCopyOperation(
       const InstallOperation& operation, ErrorCode* error);
-  [[nodiscard]] bool PerformSourceBsdiffOperation(
+  [[nodiscard]] virtual bool PerformSourceBsdiffOperation(
       const InstallOperation& operation,
       ErrorCode* error,
       const void* data,
       size_t count);
-  [[nodiscard]] bool PerformPuffDiffOperation(const InstallOperation& operation,
-                                              ErrorCode* error,
-                                              const void* data,
-                                              size_t count);
+  [[nodiscard]] virtual bool PerformPuffDiffOperation(
+      const InstallOperation& operation,
+      ErrorCode* error,
+      const void* data,
+      size_t count);
+  [[nodiscard]] virtual bool Flush();
 
- private:
+ protected:
   friend class PartitionWriterTest;
   FRIEND_TEST(PartitionWriterTest, ChooseSourceFDTest);
 
@@ -80,6 +83,7 @@
   // the |error| accordingly.
   FileDescriptorPtr ChooseSourceFD(const InstallOperation& operation,
                                    ErrorCode* error);
+  [[nodiscard]] virtual std::unique_ptr<ExtentWriter> CreateBaseExtentWriter();
 
   const PartitionUpdate& partition_update_;
   const InstallPlan::Partition& install_part_;
@@ -108,6 +112,18 @@
   // error corrected.
   bool source_ecc_open_failure_{false};
 };
+
+namespace partition_writer {
+// Return a PartitionWriter instance for perform InstallOps on this partition.
+// Uses VABCPartitionWriter for Virtual AB Compression
+std::unique_ptr<PartitionWriter> CreatePartitionWriter(
+    const PartitionUpdate& partition_update,
+    const InstallPlan::Partition& install_part,
+    DynamicPartitionControlInterface* dynamic_control,
+    size_t block_size,
+    bool is_interactive,
+    bool is_dynamic_partition);
+}  // namespace partition_writer
 }  // namespace chromeos_update_engine
 
 #endif
diff --git a/payload_consumer/partition_writer_factory_android.cc b/payload_consumer/partition_writer_factory_android.cc
new file mode 100644
index 0000000..0c9f7ea
--- /dev/null
+++ b/payload_consumer/partition_writer_factory_android.cc
@@ -0,0 +1,54 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <cstddef>
+#include <memory>
+
+#include <base/logging.h>
+
+#include "update_engine/payload_consumer/vabc_partition_writer.h"
+
+namespace chromeos_update_engine::partition_writer {
+
+std::unique_ptr<PartitionWriter> CreatePartitionWriter(
+    const PartitionUpdate& partition_update,
+    const InstallPlan::Partition& install_part,
+    DynamicPartitionControlInterface* dynamic_control,
+    size_t block_size,
+    bool is_interactive,
+    bool is_dynamic_partition) {
+  if (dynamic_control &&
+      dynamic_control->GetVirtualAbCompressionFeatureFlag().IsEnabled() &&
+      is_dynamic_partition) {
+    LOG(INFO)
+        << "Virtual AB Compression Enabled, using VABC Partition Writer for `"
+        << install_part.name << '`';
+    return std::make_unique<VABCPartitionWriter>(partition_update,
+                                                 install_part,
+                                                 dynamic_control,
+                                                 block_size,
+                                                 is_interactive);
+  } else {
+    LOG(INFO) << "Virtual AB Compression disabled, using Partition Writer for `"
+              << install_part.name << '`';
+    return std::make_unique<PartitionWriter>(partition_update,
+                                             install_part,
+                                             dynamic_control,
+                                             block_size,
+                                             is_interactive);
+  }
+}
+}  // namespace chromeos_update_engine::partition_writer
diff --git a/payload_consumer/partition_writer_factory_chromeos.cc b/payload_consumer/partition_writer_factory_chromeos.cc
new file mode 100644
index 0000000..609f043
--- /dev/null
+++ b/payload_consumer/partition_writer_factory_chromeos.cc
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <cstddef>
+#include <memory>
+
+#include <base/logging.h>
+
+#include "update_engine/payload_consumer/partition_writer.h"
+
+namespace chromeos_update_engine::partition_writer {
+std::unique_ptr<PartitionWriter> CreatePartitionWriter(
+    const PartitionUpdate& partition_update,
+    const InstallPlan::Partition& install_part,
+    DynamicPartitionControlInterface* dynamic_control,
+    size_t block_size,
+    bool is_interactive,
+    bool is_dynamic_partition) {
+  return std::make_unique<PartitionWriter>(partition_update,
+                                           install_part,
+                                           dynamic_control,
+                                           block_size,
+                                           is_interactive);
+}
+}  // namespace chromeos_update_engine::partition_writer
diff --git a/payload_consumer/vabc_partition_writer.cc b/payload_consumer/vabc_partition_writer.cc
new file mode 100644
index 0000000..ab4897f
--- /dev/null
+++ b/payload_consumer/vabc_partition_writer.cc
@@ -0,0 +1,58 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/payload_consumer/vabc_partition_writer.h"
+
+#include <memory>
+
+#include <libsnapshot/cow_writer.h>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/extent_writer.h"
+#include "update_engine/payload_consumer/install_plan.h"
+#include "update_engine/payload_consumer/partition_writer.h"
+
+namespace chromeos_update_engine {
+bool VABCPartitionWriter::Init(const InstallPlan* install_plan,
+                               bool source_may_exist) {
+  TEST_AND_RETURN_FALSE(PartitionWriter::Init(install_plan, source_may_exist));
+
+  // TODO(zhangkelvin) Add code specific to VABC. E.x. Convert InstallOps to
+  // CowOps, perform all SOURCE_COPY upfront according to merge sequence.
+  return true;
+}
+
+std::unique_ptr<ExtentWriter> VABCPartitionWriter::CreateBaseExtentWriter() {
+  // TODO(zhangkelvin) Return a SnapshotExtentWriter
+  return std::make_unique<DirectExtentWriter>();
+}
+
+[[nodiscard]] bool VABCPartitionWriter::PerformZeroOrDiscardOperation(
+    const InstallOperation& operation) {
+  // TODO(zhangkelvin) Create a COW_ZERO operation and send it to CowWriter
+  return PartitionWriter::PerformZeroOrDiscardOperation(operation);
+}
+
+[[nodiscard]] bool VABCPartitionWriter::PerformSourceCopyOperation(
+    const InstallOperation& operation, ErrorCode* error) {
+  // TODO(zhangkelvin) Probably just ignore SOURCE_COPY? They should be taken
+  // care of during Init();
+  return true;
+}
+
+VABCPartitionWriter::~VABCPartitionWriter() = default;
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/vabc_partition_writer.h b/payload_consumer/vabc_partition_writer.h
new file mode 100644
index 0000000..034fb57
--- /dev/null
+++ b/payload_consumer/vabc_partition_writer.h
@@ -0,0 +1,51 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_VABC_PARTITION_WRITER_H_
+#define UPDATE_ENGINE_VABC_PARTITION_WRITER_H_
+
+#include <memory>
+
+#include <libsnapshot/cow_writer.h>
+
+#include "update_engine/payload_consumer/install_plan.h"
+#include "update_engine/payload_consumer/partition_writer.h"
+
+namespace chromeos_update_engine {
+class VABCPartitionWriter final : public PartitionWriter {
+ public:
+  using PartitionWriter::PartitionWriter;
+  [[nodiscard]] bool Init(const InstallPlan* install_plan,
+                          bool source_may_exist) override;
+  ~VABCPartitionWriter() override;
+
+  [[nodiscard]] std::unique_ptr<ExtentWriter> CreateBaseExtentWriter() override;
+
+  // Only ZERO and SOURCE_COPY InstallOperations are treated special by VABC
+  // Partition Writer. These operations correspond to COW_ZERO and COW_COPY. All
+  // other operations just get converted to COW_REPLACE.
+  [[nodiscard]] bool PerformZeroOrDiscardOperation(
+      const InstallOperation& operation) override;
+  [[nodiscard]] bool PerformSourceCopyOperation(
+      const InstallOperation& operation, ErrorCode* error) override;
+
+ private:
+  std::unique_ptr<android::snapshot::ICowWriter> cow_writer_;
+};
+
+}  // namespace chromeos_update_engine
+
+#endif