Merge Android 24Q1 Release (ab/11220357)

Bug: 319669529
Merged-In: Ief2fe8856f452edf0315ecdd831b14685b3f5af5
Change-Id: Ic11a20507c643f627614f15165271a9bcf60b937
diff --git a/Android.bp b/Android.bp
index 6d9b14b..62afe09 100644
--- a/Android.bp
+++ b/Android.bp
@@ -457,7 +457,7 @@
         "libbrillo-binder",
         "libcurl",
         "libcutils",
-        "libupdate_engine_stable-V1-cpp",
+        "libupdate_engine_stable-V2-cpp",
         "liblog",
         "libssl",
         "libstatssocket",
diff --git a/aosp/cleanup_previous_update_action.cc b/aosp/cleanup_previous_update_action.cc
index b070ecd..3b54f80 100644
--- a/aosp/cleanup_previous_update_action.cc
+++ b/aosp/cleanup_previous_update_action.cc
@@ -16,7 +16,7 @@
 #include "update_engine/aosp/cleanup_previous_update_action.h"
 
 #include <algorithm>
-#include <chrono>  // NOLINT(build/c++11) -- for merge times
+#include <chrono>
 #include <functional>
 #include <string>
 #include <type_traits>
@@ -24,6 +24,7 @@
 #include <android-base/chrono_utils.h>
 #include <android-base/properties.h>
 #include <base/bind.h>
+#include <libsnapshot/snapshot.h>
 
 #ifndef __ANDROID_RECOVERY__
 #include <statslog_ue.h>
@@ -205,6 +206,10 @@
 }
 
 void CleanupPreviousUpdateAction::CheckForMergeDelay() {
+  if (!android::snapshot::SnapshotManager::IsSnapshotManagerNeeded()) {
+    StartMerge();
+    return;
+  }
   const auto merge_delay_seconds =
       std::clamp<int>(android::base::GetIntProperty(kMergeDelaySecondsProp, 0),
                       0,
diff --git a/aosp/cleanup_previous_update_action.h b/aosp/cleanup_previous_update_action.h
index 1d701b1..6708a5e 100644
--- a/aosp/cleanup_previous_update_action.h
+++ b/aosp/cleanup_previous_update_action.h
@@ -17,7 +17,6 @@
 #ifndef UPDATE_ENGINE_AOSP_CLEANUP_PREVIOUS_UPDATE_ACTION_H_
 #define UPDATE_ENGINE_AOSP_CLEANUP_PREVIOUS_UPDATE_ACTION_H_
 
-#include <chrono>  // NOLINT(build/c++11) -- for merge times
 #include <memory>
 #include <string>
 #include <string_view>
diff --git a/aosp/cow_converter.cc b/aosp/cow_converter.cc
index 1ec3540..3e8e5fd 100644
--- a/aosp/cow_converter.cc
+++ b/aosp/cow_converter.cc
@@ -39,6 +39,10 @@
               "",
               "Comma separated list of partitions to extract, leave empty for "
               "extracting all partitions");
+DEFINE_int32(cow_version,
+             0,
+             "VABC Cow version to use. Default is to use what's specified in "
+             "the OTA manifest");
 DEFINE_string(vabc_compression_param,
               "",
               "Compression parameter for VABC. Default is use what's specified "
@@ -69,12 +73,20 @@
 
   android::snapshot::CowOptions options{
       .block_size = static_cast<uint32_t>(manifest.block_size()),
-      .compression = dap.vabc_compression_param()};
+      .compression = dap.vabc_compression_param(),
+      .batch_write = true,
+      .op_count_max = static_cast<uint32_t>(
+          partition.new_partition_info().size() / manifest.block_size())};
   if (!FLAGS_vabc_compression_param.empty()) {
     options.compression = FLAGS_vabc_compression_param;
   }
+  auto cow_version = dap.cow_version();
+  if (FLAGS_cow_version > 0) {
+    cow_version = FLAGS_cow_version;
+    LOG(INFO) << "Using user specified COW version " << cow_version;
+  }
   auto cow_writer = android::snapshot::CreateCowWriter(
-      dap.cow_version(), options, std::move(output_fd));
+      cow_version, options, std::move(output_fd));
   TEST_AND_RETURN_FALSE(cow_writer);
   TEST_AND_RETURN_FALSE(CowDryRun(nullptr,
                                   target_img_fd,
diff --git a/aosp/logging_android.cc b/aosp/logging_android.cc
index 5940f78..1a0fa9a 100644
--- a/aosp/logging_android.cc
+++ b/aosp/logging_android.cc
@@ -124,7 +124,7 @@
   explicit FileLogger(const string& path) {
     fd_.reset(TEMP_FAILURE_RETRY(
         open(path.c_str(),
-             O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_SYNC,
+             O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
              0644)));
     if (fd_ == -1) {
       // Use ALOGE that logs to logd before __android_log_set_logger.
@@ -155,6 +155,7 @@
     WriteToFd(GetPrefix(log_message));
     WriteToFd(message_str);
     WriteToFd("\n");
+    fsync(fd_);
   }
 
  private:
diff --git a/aosp/update_attempter_android.cc b/aosp/update_attempter_android.cc
index 1adaabc..e41531c 100644
--- a/aosp/update_attempter_android.cc
+++ b/aosp/update_attempter_android.cc
@@ -1325,21 +1325,22 @@
   CHECK_NE(install_plan_.source_slot, UINT32_MAX);
   CHECK_NE(install_plan_.target_slot, UINT32_MAX);
 
-  auto install_plan_action = std::make_unique<InstallPlanAction>(install_plan_);
   auto postinstall_runner_action =
       std::make_unique<PostinstallRunnerAction>(boot_control_, hardware_);
-  SetStatusAndNotify(UpdateStatus::VERIFYING);
   postinstall_runner_action->set_delegate(this);
-  ErrorCode error_code{};
 
   // If last error code is kUpdatedButNotActive, we know that we reached this
   // state by calling applyPayload() with switch_slot=false. That applyPayload()
   // call would have already performed filesystem verification, therefore, we
   // can safely skip the verification to save time.
   if (last_error_ == ErrorCode::kUpdatedButNotActive) {
+    auto install_plan_action =
+        std::make_unique<InstallPlanAction>(install_plan_);
     BondActions(install_plan_action.get(), postinstall_runner_action.get());
     processor_->EnqueueAction(std::move(install_plan_action));
+    SetStatusAndNotify(UpdateStatus::FINALIZING);
   } else {
+    ErrorCode error_code{};
     if (!boot_control_->GetDynamicPartitionControl()
              ->PreparePartitionsForUpdate(GetCurrentSlot(),
                                           GetTargetSlot(),
@@ -1361,7 +1362,8 @@
                                 utils::ErrorCodeToString(error_code),
                             error_code);
     }
-
+    auto install_plan_action =
+        std::make_unique<InstallPlanAction>(install_plan_);
     auto filesystem_verifier_action =
         std::make_unique<FilesystemVerifierAction>(
             boot_control_->GetDynamicPartitionControl());
@@ -1371,6 +1373,7 @@
                 postinstall_runner_action.get());
     processor_->EnqueueAction(std::move(install_plan_action));
     processor_->EnqueueAction(std::move(filesystem_verifier_action));
+    SetStatusAndNotify(UpdateStatus::VERIFYING);
   }
 
   processor_->EnqueueAction(std::move(postinstall_runner_action));
diff --git a/aosp/update_engine_client_android.cc b/aosp/update_engine_client_android.cc
index 81736a7..4f42a59 100644
--- a/aosp/update_engine_client_android.cc
+++ b/aosp/update_engine_client_android.cc
@@ -17,6 +17,7 @@
 #include <sysexits.h>
 #include <unistd.h>
 
+#include <chrono>
 #include <string>
 #include <vector>
 
@@ -344,7 +345,15 @@
 }  // namespace chromeos_update_engine
 
 int main(int argc, char** argv) {
+  const auto start = std::chrono::system_clock::now();
   chromeos_update_engine::internal::UpdateEngineClientAndroid client(argc,
                                                                      argv);
-  return client.Run();
+  const auto ret = client.Run();
+  const auto end = std::chrono::system_clock::now();
+  const auto duration = end - start;
+  LOG(INFO)
+      << "Command took "
+      << std::chrono::duration_cast<std::chrono::milliseconds>(duration).count()
+      << " ms";
+  return ret;
 }
diff --git a/download_action_android_unittest.cc b/download_action_android_unittest.cc
index 968f875..426d7fd 100644
--- a/download_action_android_unittest.cc
+++ b/download_action_android_unittest.cc
@@ -102,8 +102,8 @@
   ftruncate(partition_file.fd(), 4096);
   partition_config.size = 4096;
   partition_config.path = partition_file.path();
-  ASSERT_TRUE(
-      payload_file.AddPartition(partition_config, partition_config, {}, {}, 0));
+  ASSERT_TRUE(payload_file.AddPartition(
+      partition_config, partition_config, {}, {}, {}));
   ScopedTempFile blob_file("Blob-XXXXXX");
   ScopedTempFile manifest_file("Manifest-XXXXXX");
   uint64_t metadata_size;
diff --git a/payload_consumer/block_extent_writer.cc b/payload_consumer/block_extent_writer.cc
index 456c491..1379277 100644
--- a/payload_consumer/block_extent_writer.cc
+++ b/payload_consumer/block_extent_writer.cc
@@ -69,8 +69,7 @@
   if (buffer_.empty() && count >= write_size) {
     if (!WriteExtent(data, write_size)) {
       LOG(ERROR) << "WriteExtent(" << cur_extent.start_block() << ", "
-                 << static_cast<const void*>(data) << ", " << write_size
-                 << ") failed.";
+                 << write_size << ") failed.";
       // return value is expected to be greater than 0. Return 0 to signal error
       // condition
       return 0;
@@ -92,9 +91,8 @@
 
   if (buffer_.size() == write_size) {
     if (!WriteExtent(buffer_.data(), write_size)) {
-      LOG(ERROR) << "WriteExtent(" << buffer_.data() << ", "
-                 << cur_extent.start_block() << ", " << cur_extent.num_blocks()
-                 << ") failed.";
+      LOG(ERROR) << "WriteExtent(" << cur_extent.start_block() << ", "
+                 << cur_extent.num_blocks() << ") failed.";
       return 0;
     }
     buffer_.clear();
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 8a820f9..dfce92a 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -16,12 +16,11 @@
 
 #include "update_engine/payload_consumer/delta_performer.h"
 
-#include <errno.h>
 #include <linux/fs.h>
 
 #include <algorithm>
+#include <chrono>
 #include <cstring>
-#include <map>
 #include <memory>
 #include <set>
 #include <string>
@@ -494,10 +493,15 @@
     // new_cow_size per partition = partition_size - (#blocks in Copy
     // operations part of the partition)
     if (install_plan_->vabc_none) {
-      LOG(INFO) << "Setting Virtual AB Compression algorithm to none";
+      LOG(INFO) << "Setting Virtual AB Compression algorithm to none. This "
+                   "would also disable VABC XOR as XOR only saves space if "
+                   "compression is enabled.";
       manifest_.mutable_dynamic_partition_metadata()
           ->set_vabc_compression_param("none");
       for (auto& partition : *manifest_.mutable_partitions()) {
+        if (!partition.has_estimate_cow_size()) {
+          continue;
+        }
         auto new_cow_size = partition.new_partition_info().size();
         for (const auto& operation : partition.merge_operations()) {
           if (operation.type() == CowMergeOperation::COW_COPY) {
@@ -505,6 +509,17 @@
                 operation.dst_extent().num_blocks() * manifest_.block_size();
           }
         }
+        // Remove all COW_XOR merge ops, as XOR without compression is useless.
+        // It increases CPU usage but does not reduce space usage at all.
+        auto&& merge_ops = *partition.mutable_merge_operations();
+        merge_ops.erase(std::remove_if(merge_ops.begin(),
+                                       merge_ops.end(),
+                                       [](const auto& op) {
+                                         return op.type() ==
+                                                CowMergeOperation::COW_XOR;
+                                       }),
+                        merge_ops.end());
+
         // Every block written to COW device will come with a header which
         // stores src/dst block info along with other data.
         const auto cow_metadata_size = partition.new_partition_info().size() /
@@ -531,7 +546,10 @@
       manifest_.mutable_dynamic_partition_metadata()
           ->mutable_vabc_feature_set()
           ->set_threaded(install_plan_->enable_threading.value());
-      LOG(INFO) << "Attempting to enable multi-threaded compression for VABC";
+      LOG(INFO) << "Attempting to "
+                << (install_plan_->enable_threading.value() ? "enable"
+                                                            : "disable")
+                << " multi-threaded compression for VABC";
     }
     if (install_plan_->batched_writes) {
       manifest_.mutable_dynamic_partition_metadata()
@@ -599,7 +617,13 @@
           return false;
         }
       }
-      CloseCurrentPartition();
+      const auto err = CloseCurrentPartition();
+      if (err < 0) {
+        LOG(ERROR) << "Failed to close partition "
+                   << partitions_[current_partition_].partition_name() << " "
+                   << strerror(-err);
+        return false;
+      }
       // Skip until there are operations for current_partition_.
       while (next_operation_num_ >= acc_num_operations_[current_partition_]) {
         current_partition_++;
@@ -791,10 +815,17 @@
     }
   }
 
+  const auto start = std::chrono::system_clock::now();
   if (!install_plan_->ParsePartitions(
           partitions_, boot_control_, block_size_, error)) {
     return false;
   }
+  const auto duration = std::chrono::system_clock::now() - start;
+  LOG(INFO)
+      << "ParsePartitions done. took "
+      << std::chrono::duration_cast<std::chrono::milliseconds>(duration).count()
+      << " ms";
+
   auto&& has_verity = [](const auto& part) {
     return part.fec_extent().num_blocks() > 0 ||
            part.hash_tree_extent().num_blocks() > 0;
@@ -848,6 +879,7 @@
     ResetUpdateProgress(prefs, false);
   }
 
+  const auto start = std::chrono::system_clock::now();
   if (!boot_control->GetDynamicPartitionControl()->PreparePartitionsForUpdate(
           boot_control->GetCurrentSlot(),
           target_slot,
@@ -860,10 +892,14 @@
                << utils::ErrorCodeToString(*error);
     return false;
   }
+  const auto duration = std::chrono::system_clock::now() - start;
 
   TEST_AND_RETURN_FALSE(prefs->SetString(kPrefsDynamicPartitionMetadataUpdated,
                                          update_check_response_hash));
-  LOG(INFO) << "PreparePartitionsForUpdate done.";
+  LOG(INFO)
+      << "PreparePartitionsForUpdate done. took "
+      << std::chrono::duration_cast<std::chrono::milliseconds>(duration).count()
+      << " ms";
 
   return true;
 }
@@ -1342,7 +1378,8 @@
   if (prefs->GetInt64(kPrefsResumedUpdateFailures, &resumed_update_failures) &&
       resumed_update_failures > kMaxResumedUpdateFailures) {
     LOG(WARNING) << "Failed to resume update " << kPrefsResumedUpdateFailures
-                 << " invalid: " << resumed_update_failures;
+                 << " has value " << resumed_update_failures
+                 << " is over the limit " << kMaxResumedUpdateFailures;
     return false;
   }
 
diff --git a/payload_consumer/delta_performer_unittest.cc b/payload_consumer/delta_performer_unittest.cc
index e90bb7c..04cfaad 100644
--- a/payload_consumer/delta_performer_unittest.cc
+++ b/payload_consumer/delta_performer_unittest.cc
@@ -228,13 +228,14 @@
     new_part.path = "/dev/zero";
     new_part.size = 1234;
 
-    payload.AddPartition(*old_part, new_part, aops, {}, 0);
+    payload.AddPartition(*old_part, new_part, aops, {}, {});
 
     // We include a kernel partition without operations.
     old_part->name = kPartitionNameKernel;
     new_part.name = kPartitionNameKernel;
     new_part.size = 0;
-    payload.AddPartition(*old_part, new_part, {}, {}, 0);
+
+    payload.AddPartition(*old_part, new_part, {}, {}, {});
 
     ScopedTempFile payload_file("Payload-XXXXXX");
     string private_key =
diff --git a/payload_consumer/extent_map.h b/payload_consumer/extent_map.h
index a83bf0f..27c67ec 100644
--- a/payload_consumer/extent_map.h
+++ b/payload_consumer/extent_map.h
@@ -17,7 +17,6 @@
 #ifndef UPDATE_ENGINE_PAYLOAD_CONSUMER_EXTENT_MAP_H_
 #define UPDATE_ENGINE_PAYLOAD_CONSUMER_EXTENT_MAP_H_
 
-#include <functional>
 #include <map>
 #include <utility>
 #include <vector>
diff --git a/payload_consumer/filesystem_verifier_action.cc b/payload_consumer/filesystem_verifier_action.cc
index 5342e6f..5345085 100644
--- a/payload_consumer/filesystem_verifier_action.cc
+++ b/payload_consumer/filesystem_verifier_action.cc
@@ -136,13 +136,6 @@
   partition_fd_.reset();
   // This memory is not used anymore.
   buffer_.clear();
-  if (code == ErrorCode::kSuccess && !cancelled_) {
-    if (!dynamic_control_->FinishUpdate(install_plan_.powerwash_required)) {
-      LOG(ERROR) << "Failed to FinishUpdate("
-                 << install_plan_.powerwash_required << ")";
-      code = ErrorCode::kFilesystemVerifierError;
-    }
-  }
 
   // If we didn't write verity, partitions were maped. Releaase resource now.
   if (!install_plan_.write_verity &&
diff --git a/payload_consumer/filesystem_verifier_action_unittest.cc b/payload_consumer/filesystem_verifier_action_unittest.cc
index 565976a..f2967db 100644
--- a/payload_consumer/filesystem_verifier_action_unittest.cc
+++ b/payload_consumer/filesystem_verifier_action_unittest.cc
@@ -32,7 +32,6 @@
 #include <libsnapshot/cow_writer.h>
 #include <sys/stat.h>
 
-#include "gmock/gmock-spec-builders.h"
 #include "update_engine/common/dynamic_partition_control_stub.h"
 #include "update_engine/common/hash_calculator.h"
 #include "update_engine/common/mock_dynamic_partition_control.h"
@@ -557,13 +556,12 @@
     install_plan_.write_verity = true;
     ASSERT_NO_FATAL_FAILURE(SetHashWithVerity(&part));
   }
-  NiceMock<MockDynamicPartitionControl> dynamic_control;
   if (clear_target_hash) {
     part.target_hash.clear();
-  } else {
-    EXPECT_CALL(dynamic_control, FinishUpdate(0)).WillOnce(Return(true));
   }
 
+  NiceMock<MockDynamicPartitionControl> dynamic_control;
+
   EnableVABC(&dynamic_control, part.name);
   auto open_cow = [part]() {
     auto cow_fd = std::make_unique<EintrSafeFileDescriptor>();
@@ -663,7 +661,6 @@
   part.readonly_target_path = target_part_.path();
   NiceMock<MockDynamicPartitionControl> dynamic_control;
   EnableVABC(&dynamic_control, part.name);
-  ON_CALL(dynamic_control, FinishUpdate(_)).WillByDefault(Return(true));
 
   // b/186196758 is only visible if we repeatedely run FS verification w/o
   // writing verity
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index fa4654d..2cfd3c6 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -109,12 +109,11 @@
   auto dynamic_control = boot_control_->GetDynamicPartitionControl();
   CHECK(dynamic_control);
 
-  // Mount snapshot partitions for Virtual AB Compression
+  // Mount snapshot partitions for Virtual AB Compression Compression.
   if (dynamic_control->UpdateUsesSnapshotCompression()) {
     // Before calling MapAllPartitions to map snapshot devices, all CowWriters
     // must be closed, and MapAllPartitions() should be called.
     if (!install_plan_.partitions.empty()) {
-      dynamic_control->UnmapAllPartitions();
       if (!dynamic_control->MapAllPartitions()) {
         return CompletePostinstall(ErrorCode::kPostInstallMountError);
       }
@@ -429,13 +428,39 @@
   PerformPartitionPostinstall();
 }
 
+PostinstallRunnerAction::~PostinstallRunnerAction() {
+  if (!install_plan_.partitions.empty()) {
+    auto dynamic_control = boot_control_->GetDynamicPartitionControl();
+    CHECK(dynamic_control);
+    dynamic_control->UnmapAllPartitions();
+    LOG(INFO) << "Unmapped all partitions.";
+  }
+}
+
 void PostinstallRunnerAction::CompletePostinstall(ErrorCode error_code) {
   // We only attempt to mark the new slot as active if all the postinstall
   // steps succeeded.
+  DEFER {
+    if (error_code != ErrorCode::kSuccess &&
+        error_code != ErrorCode::kUpdatedButNotActive) {
+      LOG(ERROR) << "Postinstall action failed.";
+
+      // Undo any changes done to trigger Powerwash.
+      if (powerwash_scheduled_)
+        hardware_->CancelPowerwash();
+    }
+    processor_->ActionComplete(this, error_code);
+  };
   if (error_code == ErrorCode::kSuccess) {
     if (install_plan_.switch_slot_on_reboot) {
-      if (!boot_control_->SetActiveBootSlot(install_plan_.target_slot)) {
-        LOG(ERROR) << "Failed to switch slot to " << install_plan_.target_slot;
+      if (!boot_control_->GetDynamicPartitionControl()->MapAllPartitions()) {
+        LOG(ERROR) << "Failed to map all partitions before marking snapshot as "
+                      "ready for slot switch.";
+        return;
+      }
+      if (!boot_control_->GetDynamicPartitionControl()->FinishUpdate(
+              install_plan_.powerwash_required) ||
+          !boot_control_->SetActiveBootSlot(install_plan_.target_slot)) {
         error_code = ErrorCode::kPostinstallRunnerError;
       } else {
         // Schedules warm reset on next reboot, ignores the error.
@@ -447,26 +472,6 @@
       error_code = ErrorCode::kUpdatedButNotActive;
     }
   }
-  if (!install_plan_.partitions.empty()) {
-    auto dynamic_control = boot_control_->GetDynamicPartitionControl();
-    CHECK(dynamic_control);
-    dynamic_control->UnmapAllPartitions();
-    LOG(INFO) << "Unmapped all partitions.";
-  }
-
-  ScopedActionCompleter completer(processor_, this);
-  completer.set_code(error_code);
-
-  if (error_code != ErrorCode::kSuccess &&
-      error_code != ErrorCode::kUpdatedButNotActive) {
-    LOG(ERROR) << "Postinstall action failed.";
-
-    // Undo any changes done to trigger Powerwash.
-    if (powerwash_scheduled_)
-      hardware_->CancelPowerwash();
-
-    return;
-  }
 
   LOG(INFO) << "All post-install commands succeeded";
   if (HasOutputPipe()) {
diff --git a/payload_consumer/postinstall_runner_action.h b/payload_consumer/postinstall_runner_action.h
index 66721af..6017069 100644
--- a/payload_consumer/postinstall_runner_action.h
+++ b/payload_consumer/postinstall_runner_action.h
@@ -42,6 +42,7 @@
  public:
   PostinstallRunnerAction(BootControlInterface* boot_control,
                           HardwareInterface* hardware);
+  ~PostinstallRunnerAction();
 
   // InstallPlanAction overrides.
   void PerformAction() override;
diff --git a/payload_consumer/snapshot_extent_writer_unittest.cc b/payload_consumer/snapshot_extent_writer_unittest.cc
index 057dbda..31ec3cb 100644
--- a/payload_consumer/snapshot_extent_writer_unittest.cc
+++ b/payload_consumer/snapshot_extent_writer_unittest.cc
@@ -100,12 +100,15 @@
   }
 
   // Return number of bytes the cow image occupies on disk.
-  uint64_t GetCowSize() override {
+  uint64_t GetCowSize() const {
     return std::accumulate(
         operations_.begin(), operations_.end(), 0, [](auto&& acc, auto&& op) {
           return acc + op.second.data.size();
         });
   }
+  android::snapshot::CowSizeInfo GetCowSizeInfo() const override {
+    return android::snapshot::CowSizeInfo{GetCowSize(), 0};
+  }
   bool Contains(size_t block) {
     return operations_.find(block) != operations_.end();
   }
diff --git a/payload_consumer/vabc_partition_writer.cc b/payload_consumer/vabc_partition_writer.cc
index bcaccce..1a7a049 100644
--- a/payload_consumer/vabc_partition_writer.cc
+++ b/payload_consumer/vabc_partition_writer.cc
@@ -16,8 +16,6 @@
 
 #include "update_engine/payload_consumer/vabc_partition_writer.h"
 
-#include <algorithm>
-#include <map>
 #include <memory>
 #include <string>
 #include <utility>
@@ -401,7 +399,9 @@
   if (cow_writer_) {
     LOG(INFO) << "Finalizing " << partition_update_.partition_name()
               << " COW image";
-    cow_writer_->Finalize();
+    if (!cow_writer_->Finalize()) {
+      return -errno;
+    }
     cow_writer_ = nullptr;
   }
   return 0;
diff --git a/payload_generator/cow_size_estimator.cc b/payload_generator/cow_size_estimator.cc
index 704aeaa..9ab2b87 100644
--- a/payload_generator/cow_size_estimator.cc
+++ b/payload_generator/cow_size_estimator.cc
@@ -24,8 +24,8 @@
 
 #include <android-base/unique_fd.h>
 #include <libsnapshot/cow_writer.h>
+#include <libsnapshot/cow_format.h>
 
-#include "update_engine/common/cow_operation_convert.h"
 #include "update_engine/common/utils.h"
 #include "update_engine/payload_consumer/vabc_partition_writer.h"
 #include "update_engine/payload_generator/extent_ranges.h"
@@ -136,7 +136,7 @@
   return cow_writer->Finalize();
 }
 
-size_t EstimateCowSize(
+android::snapshot::CowSizeInfo EstimateCowSizeInfo(
     FileDescriptorPtr source_fd,
     FileDescriptorPtr target_fd,
     const google::protobuf::RepeatedPtrField<InstallOperation>& operations,
@@ -145,12 +145,13 @@
     const size_t block_size,
     std::string compression,
     const size_t partition_size,
-    const bool xor_enabled) {
+    const bool xor_enabled,
+    uint32_t cow_version) {
   android::snapshot::CowOptions options{
       .block_size = static_cast<uint32_t>(block_size),
-      .compression = std::move(compression)};
-  auto cow_writer =
-      CreateCowEstimator(android::snapshot::kCowVersionManifest, options);
+      .compression = std::move(compression),
+      .max_blocks = (partition_size / block_size)};
+  auto cow_writer = CreateCowEstimator(cow_version, options);
   CHECK_NE(cow_writer, nullptr) << "Could not create cow estimator";
   CHECK(CowDryRun(source_fd,
                   target_fd,
@@ -160,7 +161,7 @@
                   cow_writer.get(),
                   partition_size,
                   xor_enabled));
-  return cow_writer->GetCowSize();
+  return cow_writer->GetCowSizeInfo();
 }
 
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/cow_size_estimator.h b/payload_generator/cow_size_estimator.h
index 3bf6880..7cb3485 100644
--- a/payload_generator/cow_size_estimator.h
+++ b/payload_generator/cow_size_estimator.h
@@ -28,7 +28,7 @@
 // generators to put an estimate cow size in OTA payload. When installing an OTA
 // update, libsnapshot will take this estimate as a hint to allocate spaces.
 // If |xor_enabled| is true, then |source_fd| must be non-null.
-size_t EstimateCowSize(
+android::snapshot::CowSizeInfo EstimateCowSizeInfo(
     FileDescriptorPtr source_fd,
     FileDescriptorPtr target_fd,
     const google::protobuf::RepeatedPtrField<InstallOperation>& operations,
@@ -37,7 +37,8 @@
     const size_t block_size,
     std::string compression,
     const size_t partition_size,
-    bool xor_enabled);
+    bool xor_enabled,
+    uint32_t cow_version);
 
 // Convert InstallOps to CowOps and apply the converted cow op to |cow_writer|
 bool CowDryRun(
diff --git a/payload_generator/delta_diff_generator.cc b/payload_generator/delta_diff_generator.cc
index cfec1b9..0da929e 100644
--- a/payload_generator/delta_diff_generator.cc
+++ b/payload_generator/delta_diff_generator.cc
@@ -75,7 +75,7 @@
       BlobFileWriter* file_writer,
       std::vector<AnnotatedOperation>* aops,
       std::vector<CowMergeOperation>* cow_merge_sequence,
-      size_t* cow_size,
+      android::snapshot::CowSizeInfo* cow_info,
       std::unique_ptr<chromeos_update_engine::OperationsGenerator> strategy)
       : config_(config),
         old_part_(old_part),
@@ -83,7 +83,7 @@
         file_writer_(file_writer),
         aops_(aops),
         cow_merge_sequence_(cow_merge_sequence),
-        cow_size_(cow_size),
+        cow_info_(cow_info),
         strategy_(std::move(strategy)) {}
   PartitionProcessor(PartitionProcessor&&) noexcept = default;
 
@@ -134,7 +134,7 @@
       source_fd->Open(old_part_.path.c_str(), O_RDONLY);
     }
 
-    *cow_size_ = EstimateCowSize(
+    *cow_info_ = EstimateCowSizeInfo(
         std::move(source_fd),
         std::move(target_fd),
         std::move(operations),
@@ -142,9 +142,13 @@
         config_.block_size,
         config_.target.dynamic_partition_metadata->vabc_compression_param(),
         new_part_.size,
-        config_.enable_vabc_xor);
+        config_.enable_vabc_xor,
+        config_.target.dynamic_partition_metadata->cow_version());
+
+    // ops buffer size == 0 for v2 version of cow format
     LOG(INFO) << "Estimated COW size for partition: " << new_part_.name << " "
-              << *cow_size_;
+              << cow_info_->cow_size
+              << " ops buffer size: " << cow_info_->op_count_max;
   }
 
  private:
@@ -154,7 +158,7 @@
   BlobFileWriter* file_writer_;
   std::vector<AnnotatedOperation>* aops_;
   std::vector<CowMergeOperation>* cow_merge_sequence_;
-  size_t* cow_size_;
+  android::snapshot::CowSizeInfo* cow_info_;
   std::unique_ptr<chromeos_update_engine::OperationsGenerator> strategy_;
   DISALLOW_COPY_AND_ASSIGN(PartitionProcessor);
 };
@@ -188,7 +192,8 @@
     std::vector<std::vector<CowMergeOperation>> all_merge_sequences;
     all_merge_sequences.resize(config.target.partitions.size());
 
-    std::vector<size_t> all_cow_sizes(config.target.partitions.size(), 0);
+    std::vector<android::snapshot::CowSizeInfo> all_cow_info(
+        config.target.partitions.size());
 
     std::vector<PartitionProcessor> partition_tasks{};
     auto thread_count = std::min<int>(diff_utils::GetMaxThreads(),
@@ -223,7 +228,7 @@
                                                    &blob_file,
                                                    &all_aops[i],
                                                    &all_merge_sequences[i],
-                                                   &all_cow_sizes[i],
+                                                   &all_cow_info[i],
                                                    std::move(strategy)));
     }
     thread_pool.Start();
@@ -241,7 +246,7 @@
                                new_part,
                                std::move(all_aops[i]),
                                std::move(all_merge_sequences[i]),
-                               all_cow_sizes[i]));
+                               all_cow_info[i]));
     }
   }
   data_file.CloseFd();
diff --git a/payload_generator/extent_ranges.cc b/payload_generator/extent_ranges.cc
index eecc8b3..32580b9 100644
--- a/payload_generator/extent_ranges.cc
+++ b/payload_generator/extent_ranges.cc
@@ -25,7 +25,6 @@
 
 #include "update_engine/common/utils.h"
 #include "update_engine/payload_consumer/payload_constants.h"
-#include "update_engine/payload_generator/extent_utils.h"
 
 using std::vector;
 
@@ -84,9 +83,8 @@
   ExtentSet::iterator begin_del = extent_set_.end();
   ExtentSet::iterator end_del = extent_set_.end();
   uint64_t del_blocks = 0;
-  for (ExtentSet::iterator it = extent_set_.begin(), e = extent_set_.end();
-       it != e;
-       ++it) {
+  const auto range = GetCandidateRange(extent);
+  for (ExtentSet::iterator it = range.begin(), e = range.end(); it != e; ++it) {
     const bool should_merge = merge_touching_extents_
                                   ? ExtentsOverlapOrTouch(*it, extent)
                                   : ExtentsOverlap(*it, extent);
@@ -291,12 +289,20 @@
 Range<ExtentRanges::ExtentSet::const_iterator> ExtentRanges::GetCandidateRange(
     const Extent& extent) const {
   auto lower_it = extent_set_.lower_bound(extent);
-  if (lower_it != extent_set_.begin()) {
+  while (lower_it != extent_set_.begin() &&
+         (lower_it == extent_set_.end() ||
+          lower_it->start_block() + lower_it->num_blocks() >
+              extent.start_block())) {
     --lower_it;
   }
 
-  const auto upper_it = extent_set_.upper_bound(
+  auto upper_it = extent_set_.upper_bound(
       ExtentForRange(extent.start_block() + extent.num_blocks(), 1));
+  while (upper_it != extent_set_.end() &&
+         upper_it->start_block() <=
+             extent.start_block() + extent.num_blocks()) {
+    ++upper_it;
+  }
   return {lower_it, upper_it};
 }
 
diff --git a/payload_generator/extent_ranges.h b/payload_generator/extent_ranges.h
index 08cf5fe..bd468a1 100644
--- a/payload_generator/extent_ranges.h
+++ b/payload_generator/extent_ranges.h
@@ -17,13 +17,13 @@
 #ifndef UPDATE_ENGINE_PAYLOAD_GENERATOR_EXTENT_RANGES_H_
 #define UPDATE_ENGINE_PAYLOAD_GENERATOR_EXTENT_RANGES_H_
 
-#include <map>
 #include <set>
 #include <vector>
 
 #include <base/macros.h>
 
 #include "update_engine/common/utils.h"
+#include "update_engine/payload_generator/extent_utils.h"
 #include "update_engine/update_metadata.pb.h"
 
 // An ExtentRanges object represents an unordered collection of extents (and
@@ -35,15 +35,6 @@
 
 namespace chromeos_update_engine {
 
-struct ExtentLess {
-  bool operator()(const Extent& x, const Extent& y) const {
-    if (x.start_block() == y.start_block()) {
-      return x.num_blocks() < y.num_blocks();
-    }
-    return x.start_block() < y.start_block();
-  }
-};
-
 Extent ExtentForRange(uint64_t start_block, uint64_t num_blocks);
 Extent ExtentForBytes(uint64_t block_size,
                       uint64_t start_bytes,
diff --git a/payload_generator/extent_ranges_unittest.cc b/payload_generator/extent_ranges_unittest.cc
index f7a4cd2..5f36aa3 100644
--- a/payload_generator/extent_ranges_unittest.cc
+++ b/payload_generator/extent_ranges_unittest.cc
@@ -21,11 +21,11 @@
 #include <base/stl_util.h>
 #include <gtest/gtest.h>
 
-#include "update_engine/common/test_utils.h"
-#include "update_engine/payload_consumer/payload_constants.h"
 #include "update_engine/payload_generator/extent_utils.h"
+#include "update_engine/payload_consumer/payload_constants.h"
 
 using std::vector;
+using chromeos_update_engine::operator==;
 
 namespace chromeos_update_engine {
 
@@ -390,4 +390,35 @@
   ASSERT_TRUE(ranges.OverlapsWithExtent(ExtentForRange(19, 1)));
 }
 
+TEST(ExtentRangesTest, AddExtentMergeStressTest) {
+  ExtentRanges ranges(true);
+  for (size_t i = 0; i < 1000000; i++) {
+    ranges.AddExtent(ExtentForRange(i, 1));
+  }
+  ASSERT_EQ(ranges.extent_set().size(), 1UL) << ranges.extent_set();
+}
+
+TEST(ExtentRangesTest, AddExtentNoMergeStressTest) {
+  ExtentRanges ranges(true);
+  for (size_t i = 0; i < 200000; i++) {
+    ranges.AddExtent(ExtentForRange(i * 2, 1));
+  }
+  ASSERT_EQ(ranges.extent_set().size(), 200000UL) << ranges.extent_set();
+}
+
+TEST(ExtentRangesTest, AddExtentTouching) {
+  ExtentRanges ranges(true);
+  ranges.AddExtent(ExtentForRange(5, 5));
+  ranges.AddExtent(ExtentForRange(25, 7));
+  ASSERT_EQ(ranges.extent_set().size(), 2UL) << ranges.extent_set();
+  ranges.AddExtent(ExtentForRange(0, 5));
+  ASSERT_EQ(ranges.extent_set().size(), 2UL) << ranges.extent_set();
+  ranges.AddExtent(ExtentForRange(10, 15));
+  ASSERT_EQ(ranges.extent_set().size(), 1UL) << ranges.extent_set();
+  ranges.AddExtent(ExtentForRange(32, 8));
+  ASSERT_EQ(ranges.extent_set().size(), 1UL) << ranges.extent_set();
+  ranges.AddExtent(ExtentForRange(45, 5));
+  ASSERT_EQ(ranges.extent_set().size(), 2UL) << ranges.extent_set();
+}
+
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/extent_utils.cc b/payload_generator/extent_utils.cc
index 612fd67..851db8a 100644
--- a/payload_generator/extent_utils.cc
+++ b/payload_generator/extent_utils.cc
@@ -19,16 +19,13 @@
 #include <inttypes.h>
 
 #include <string>
-#include <utility>
 #include <vector>
 
 #include <base/logging.h>
 #include <base/macros.h>
 #include <base/strings/stringprintf.h>
 
-#include "update_engine/common/utils.h"
 #include "update_engine/payload_consumer/payload_constants.h"
-#include "update_engine/payload_generator/annotated_operation.h"
 #include "update_engine/payload_generator/extent_ranges.h"
 
 using std::string;
@@ -171,8 +168,7 @@
 }
 
 std::ostream& operator<<(std::ostream& out, const Extent& extent) {
-  out << "[" << extent.start_block() << " - "
-      << extent.start_block() + extent.num_blocks() - 1 << "]";
+  out << "(" << extent.start_block() << " - " << extent.num_blocks() << ")";
   return out;
 }
 
@@ -202,4 +198,19 @@
   return PrintExtents(out, extents);
 }
 
+std::ostream& operator<<(std::ostream& out, const std::set<Extent>& extents) {
+  return PrintExtents(out, extents);
+}
+
+std::ostream& operator<<(std::ostream& out,
+                         const std::set<Extent, ExtentLess>& extents) {
+  return PrintExtents(out, extents);
+}
+
+std::ostream& operator<<(
+    std::ostream& out,
+    Range<std::set<Extent, ExtentLess>::const_iterator> range) {
+  return PrintExtents(out, range);
+}
+
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/extent_utils.h b/payload_generator/extent_utils.h
index f8d36e7..52b6d1e 100644
--- a/payload_generator/extent_utils.h
+++ b/payload_generator/extent_utils.h
@@ -23,13 +23,21 @@
 
 #include <base/logging.h>
 
-#include "google/protobuf/repeated_field.h"
+#include "update_engine/common/utils.h"
 #include "update_engine/payload_consumer/payload_constants.h"
 #include "update_engine/update_metadata.pb.h"
 
 // Utility functions for manipulating Extents and lists of blocks.
 
 namespace chromeos_update_engine {
+struct ExtentLess {
+  constexpr bool operator()(const Extent& x, const Extent& y) const {
+    if (x.start_block() == y.start_block()) {
+      return x.num_blocks() < y.num_blocks();
+    }
+    return x.start_block() < y.start_block();
+  }
+};
 
 // |block| must either be the next block in the last extent or a block
 // in the next extent. This function will not handle inserting block
@@ -130,6 +138,14 @@
 
 std::ostream& operator<<(std::ostream& out, const Extent& extent);
 std::ostream& operator<<(std::ostream& out, const std::vector<Extent>& extent);
+std::ostream& operator<<(std::ostream& out, const std::set<Extent>& extents);
+
+std::ostream& operator<<(std::ostream& out,
+                         const std::set<Extent, ExtentLess>& extents);
+std::ostream& operator<<(
+    std::ostream& out,
+    Range<std::set<Extent, ExtentLess>::const_iterator> range);
+
 std::ostream& operator<<(
     std::ostream& out,
     const google::protobuf::RepeatedPtrField<Extent>& extent);
diff --git a/payload_generator/payload_file.cc b/payload_generator/payload_file.cc
index f404c79..8f5b826 100644
--- a/payload_generator/payload_file.cc
+++ b/payload_generator/payload_file.cc
@@ -98,15 +98,15 @@
                                const PartitionConfig& new_conf,
                                vector<AnnotatedOperation> aops,
                                vector<CowMergeOperation> merge_sequence,
-                               size_t cow_size) {
+                               const android::snapshot::CowSizeInfo& cow_info) {
   Partition part;
-  part.cow_size = cow_size;
   part.name = new_conf.name;
   part.aops = std::move(aops);
   part.cow_merge_sequence = std::move(merge_sequence);
   part.postinstall = new_conf.postinstall;
   part.verity = new_conf.verity;
   part.version = new_conf.version;
+  part.cow_info = cow_info;
   // Initialize the PartitionInfo objects if present.
   if (!old_conf.path.empty())
     TEST_AND_RETURN_FALSE(
@@ -148,8 +148,11 @@
     if (!part.version.empty()) {
       partition->set_version(part.version);
     }
-    if (part.cow_size > 0) {
-      partition->set_estimate_cow_size(part.cow_size);
+    if (part.cow_info.cow_size > 0) {
+      partition->set_estimate_cow_size(part.cow_info.cow_size);
+    }
+    if (part.cow_info.op_count_max > 0) {
+      partition->set_estimate_op_count_max(part.cow_info.op_count_max);
     }
     if (part.postinstall.run) {
       partition->set_run_postinstall(true);
diff --git a/payload_generator/payload_file.h b/payload_generator/payload_file.h
index 86bf243..cb41494 100644
--- a/payload_generator/payload_file.h
+++ b/payload_generator/payload_file.h
@@ -23,6 +23,8 @@
 #include <brillo/secure_blob.h>
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
+#include <libsnapshot/cow_writer.h>
+
 #include "update_engine/payload_generator/annotated_operation.h"
 #include "update_engine/payload_generator/payload_generation_config.h"
 #include "update_engine/update_metadata.pb.h"
@@ -45,7 +47,7 @@
                     const PartitionConfig& new_conf,
                     std::vector<AnnotatedOperation> aops,
                     std::vector<CowMergeOperation> merge_sequence,
-                    size_t cow_size);
+                    const android::snapshot::CowSizeInfo& cow_info);
 
   // Write the payload to the |payload_file| file. The operations reference
   // blobs in the |data_blobs_path| file and the blobs will be reordered in the
@@ -108,7 +110,7 @@
     VerityConfig verity;
     // Per partition timestamp.
     std::string version;
-    size_t cow_size;
+    android::snapshot::CowSizeInfo cow_info;
   };
 
   std::vector<Partition> part_vec_;
diff --git a/payload_generator/payload_properties_unittest.cc b/payload_generator/payload_properties_unittest.cc
index f7ac743..b4bfb81 100644
--- a/payload_generator/payload_properties_unittest.cc
+++ b/payload_generator/payload_properties_unittest.cc
@@ -88,7 +88,7 @@
     EXPECT_TRUE(strategy->GenerateOperations(
         config, old_part, new_part, &blob_file_writer, &aops));
 
-    payload.AddPartition(old_part, new_part, aops, {}, 0);
+    payload.AddPartition(old_part, new_part, aops, {}, {});
 
     uint64_t metadata_size;
     EXPECT_TRUE(payload.WritePayload(
diff --git a/stable/Android.bp b/stable/Android.bp
index 73a9c37..59165a7 100644
--- a/stable/Android.bp
+++ b/stable/Android.bp
@@ -40,7 +40,7 @@
             enabled: true,
         },
         java: {
-            enabled: false,
+            enabled: true,
         },
         ndk: {
             enabled: true,
@@ -49,7 +49,18 @@
             ],
         },
     },
-    versions: ["1"],
+    versions_with_info: [
+        {
+            version: "1",
+            imports: [],
+        },
+        {
+            version: "2",
+            imports: [],
+        },
+    ],
+    frozen: true,
+
 }
 
 // update_engine_stable_client (type: executable)
@@ -68,7 +79,7 @@
     ],
     static_libs: [
         "libgflags",
-        "libupdate_engine_stable-V1-ndk",
+        "libupdate_engine_stable-V2-ndk",
     ],
     srcs: [
         "update_engine_stable_client.cc",
diff --git a/stable/aidl_api/libupdate_engine_stable/2/.hash b/stable/aidl_api/libupdate_engine_stable/2/.hash
new file mode 100644
index 0000000..6661ad2
--- /dev/null
+++ b/stable/aidl_api/libupdate_engine_stable/2/.hash
@@ -0,0 +1 @@
+ee2e6f0bd51391955f79f4d5eeeafc37c668cd40
diff --git a/stable/aidl_api/libupdate_engine_stable/2/android/os/IUpdateEngineStable.aidl b/stable/aidl_api/libupdate_engine_stable/2/android/os/IUpdateEngineStable.aidl
new file mode 100644
index 0000000..43e8dd0
--- /dev/null
+++ b/stable/aidl_api/libupdate_engine_stable/2/android/os/IUpdateEngineStable.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.os;
+/* @hide */
+interface IUpdateEngineStable {
+  /* @hide */
+  void applyPayloadFd(in ParcelFileDescriptor pfd, in long payload_offset, in long payload_size, in String[] headerKeyValuePairs);
+  /* @hide */
+  boolean bind(android.os.IUpdateEngineStableCallback callback);
+  /* @hide */
+  boolean unbind(android.os.IUpdateEngineStableCallback callback);
+}
diff --git a/stable/aidl_api/libupdate_engine_stable/2/android/os/IUpdateEngineStableCallback.aidl b/stable/aidl_api/libupdate_engine_stable/2/android/os/IUpdateEngineStableCallback.aidl
new file mode 100644
index 0000000..c09fa43
--- /dev/null
+++ b/stable/aidl_api/libupdate_engine_stable/2/android/os/IUpdateEngineStableCallback.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.os;
+/* @hide */
+interface IUpdateEngineStableCallback {
+  /* @hide */
+  oneway void onStatusUpdate(int status_code, float percentage);
+  /* @hide */
+  oneway void onPayloadApplicationComplete(int error_code);
+}
diff --git a/stable/aidl_api/libupdate_engine_stable/current/android/os/IUpdateEngineStable.aidl b/stable/aidl_api/libupdate_engine_stable/current/android/os/IUpdateEngineStable.aidl
index 82c3ca5..43e8dd0 100644
--- a/stable/aidl_api/libupdate_engine_stable/current/android/os/IUpdateEngineStable.aidl
+++ b/stable/aidl_api/libupdate_engine_stable/current/android/os/IUpdateEngineStable.aidl
@@ -1,14 +1,30 @@
+/*
+ * 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.
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
@@ -16,8 +32,12 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.os;
+/* @hide */
 interface IUpdateEngineStable {
+  /* @hide */
   void applyPayloadFd(in ParcelFileDescriptor pfd, in long payload_offset, in long payload_size, in String[] headerKeyValuePairs);
+  /* @hide */
   boolean bind(android.os.IUpdateEngineStableCallback callback);
+  /* @hide */
   boolean unbind(android.os.IUpdateEngineStableCallback callback);
 }
diff --git a/stable/aidl_api/libupdate_engine_stable/current/android/os/IUpdateEngineStableCallback.aidl b/stable/aidl_api/libupdate_engine_stable/current/android/os/IUpdateEngineStableCallback.aidl
index 4c72b49..c09fa43 100644
--- a/stable/aidl_api/libupdate_engine_stable/current/android/os/IUpdateEngineStableCallback.aidl
+++ b/stable/aidl_api/libupdate_engine_stable/current/android/os/IUpdateEngineStableCallback.aidl
@@ -1,14 +1,30 @@
+/*
+ * 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.
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
@@ -16,7 +32,10 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.os;
+/* @hide */
 interface IUpdateEngineStableCallback {
+  /* @hide */
   oneway void onStatusUpdate(int status_code, float percentage);
+  /* @hide */
   oneway void onPayloadApplicationComplete(int error_code);
 }
diff --git a/stable/android/os/IUpdateEngineStable.aidl b/stable/android/os/IUpdateEngineStable.aidl
index b3b6674..a38ba89 100644
--- a/stable/android/os/IUpdateEngineStable.aidl
+++ b/stable/android/os/IUpdateEngineStable.aidl
@@ -21,6 +21,11 @@
 
 /**
  * The stable interface exposed by the update engine daemon.
+ *
+ * WARNING: this interface exposes less capabilities than IUpdateEngine,
+ * for instance, not having a cancel method. This is relied on for
+ * security.
+ * @hide
  */
 interface IUpdateEngineStable {
   /**
@@ -35,6 +40,7 @@
    *   detected.
    * @param headerKeyValuePairs additional header key value pairs, in the format of "key=value".
    * @see android.os.UpdateEngine#applyPayload(android.content.res.AssetFileDescriptor, String[])
+   * @hide
    */
   void applyPayloadFd(in ParcelFileDescriptor pfd,
                       in long payload_offset,
@@ -55,6 +61,7 @@
    * @param callback See {@link IUpdateEngineStableCallback}
    * @return true if binding is successful, false otherwise.
    * @see android.os.UpdateEngine#bind(android.os.UpdateEngineCallback)
+   * @hide
    */
   boolean bind(IUpdateEngineStableCallback callback);
 
@@ -70,6 +77,7 @@
    * @param callback The callback to be unbound. See {@link IUpdateEngineStableCallback}.
    * @return true if unbinding is successful, false otherwise.
    * @see android.os.UpdateEngine#unbind(android.os.UpdateEngineCallback)
+   * @hide
    */
   boolean unbind(IUpdateEngineStableCallback callback);
 }
diff --git a/stable/android/os/IUpdateEngineStableCallback.aidl b/stable/android/os/IUpdateEngineStableCallback.aidl
index d8fc333..4f91c15 100644
--- a/stable/android/os/IUpdateEngineStableCallback.aidl
+++ b/stable/android/os/IUpdateEngineStableCallback.aidl
@@ -18,6 +18,7 @@
 
 /**
  * The stable Callback interface for IUpdateEngineStable.
+ * @hide
  */
 oneway interface IUpdateEngineStableCallback {
   /**
@@ -26,6 +27,7 @@
    * @param status_code see {@link android.os.UpdateEngine.UpdateStatusConstants}.
    * @param percentage percentage of progress of the current stage.
    * @see android.os.UpdateEngineCallback#onStatusUpdate(int, float)
+   * @hide
    */
   void onStatusUpdate(int status_code, float percentage);
 
@@ -34,6 +36,7 @@
    *
    * @param error_code see {@link android.os.UpdateEngine.ErrorCodeConstants}
    * @see android.os.UpdateEngineCallback#onPayloadApplicationComplete(int)
+   * @hide
    */
   void onPayloadApplicationComplete(int error_code);
 }
diff --git a/update_metadata.proto b/update_metadata.proto
index 3881464..535f386 100644
--- a/update_metadata.proto
+++ b/update_metadata.proto
@@ -316,6 +316,10 @@
   // as a hint. If set to 0, libsnapshot should use alternative
   // methods for estimating size.
   optional uint64 estimate_cow_size = 19;
+
+  // Information about the cow used by Cow Writer to specify
+  // number of cow operations to be written
+  optional uint64 estimate_op_count_max = 20;
 }
 
 message DynamicPartitionGroup {