Merge ab/7061308 into stage.

Bug: 180401296
Merged-In: Ib1d0f8080bc35a9af330dc768b78f3385b50a47c
Change-Id: I9992904c3a85090ba0c352520c51d5f7650751cd
diff --git a/Android.bp b/Android.bp
index 020fd6e..4ec17f2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -15,6 +15,23 @@
 //
 
 // AIDL interface between libupdate_engine and framework.jar
+package {
+    default_applicable_licenses: ["system_update_engine_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+    name: "system_update_engine_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+    ],
+    license_text: [
+        "NOTICE",
+    ],
+}
+
 filegroup {
     name: "libupdate_engine_aidl",
     srcs: [
@@ -307,9 +324,12 @@
     ],
 
     static_libs: [
+        "libavb",
+        "libavb_user",
         "gkiprops",
         "libpayload_consumer",
         "libupdate_engine_boot_control",
+        "PlatformProperties",
     ],
     shared_libs: [
         "libandroid_net",
@@ -320,7 +340,7 @@
         "libbrillo-binder",
         "libcurl",
         "libcutils",
-        "libupdate_engine_stable-cpp",
+        "libupdate_engine_stable-V1-cpp",
         "liblog",
         "libssl",
         "libstatslog",
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 40ddcd1..85fd5ec 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -2,3 +2,6 @@
 clang_format = true
 cpplint = true
 pylint = true
+
+[Hook Scripts]
+protobuflint = ./protobuflint.py ${PREUPLOAD_COMMIT} ${PREUPLOAD_FILES}
diff --git a/aosp/cleanup_previous_update_action.cc b/aosp/cleanup_previous_update_action.cc
index 16cb9fe..55c5a73 100644
--- a/aosp/cleanup_previous_update_action.cc
+++ b/aosp/cleanup_previous_update_action.cc
@@ -270,10 +270,11 @@
 void CleanupPreviousUpdateAction::WaitForMergeOrSchedule() {
   AcknowledgeTaskExecuted();
   TEST_AND_RETURN(running_);
+  auto update_uses_compression = snapshot_->UpdateUsesCompression();
   auto state = snapshot_->ProcessUpdateState(
       std::bind(&CleanupPreviousUpdateAction::OnMergePercentageUpdate, this),
       std::bind(&CleanupPreviousUpdateAction::BeforeCancel, this));
-  merge_stats_->set_state(state);
+  merge_stats_->set_state(state, update_uses_compression);
 
   switch (state) {
     case UpdateState::None: {
@@ -403,7 +404,7 @@
 
   LOG(WARNING) << "InitiateMerge failed.";
   auto state = snapshot_->GetUpdateState();
-  merge_stats_->set_state(state);
+  merge_stats_->set_state(state, snapshot_->UpdateUsesCompression());
   if (state == UpdateState::Unverified) {
     // We are stuck at unverified state. This can happen if the update has
     // been applied, but it has not even been attempted yet (in libsnapshot,
@@ -456,6 +457,13 @@
   bool vab_retrofit = boot_control_->GetDynamicPartitionControl()
                           ->GetVirtualAbFeatureFlag()
                           .IsRetrofit();
+  bool vab_compression_enabled = boot_control_->GetDynamicPartitionControl()
+                                     ->GetVirtualAbCompressionFeatureFlag()
+                                     .IsEnabled();
+  // The snapshot has been merged, so we can no longer call
+  // DynamicPartitionControlInterface::UpdateUsesSnapshotCompression.
+  // However, we have saved the flag in the snapshot report.
+  bool vab_compression_used = report.compression_enabled();
 
   LOG(INFO) << "Reporting merge stats: "
             << android::snapshot::UpdateState_Name(report.state()) << " in "
@@ -467,7 +475,9 @@
                              static_cast<int64_t>(passed_ms.count()),
                              static_cast<int32_t>(report.resume_count()),
                              vab_retrofit,
-                             static_cast<int64_t>(report.cow_file_size()));
+                             static_cast<int64_t>(report.cow_file_size()),
+                             vab_compression_enabled,
+                             vab_compression_used);
 #endif
 }
 
diff --git a/aosp/dynamic_partition_control_android.cc b/aosp/dynamic_partition_control_android.cc
index 1575796..657eec9 100644
--- a/aosp/dynamic_partition_control_android.cc
+++ b/aosp/dynamic_partition_control_android.cc
@@ -46,6 +46,8 @@
 #include "update_engine/aosp/cleanup_previous_update_action.h"
 #include "update_engine/aosp/dynamic_partition_utils.h"
 #include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/dynamic_partition_control_interface.h"
+#include "update_engine/common/platform_constants.h"
 #include "update_engine/common/utils.h"
 #include "update_engine/payload_consumer/delta_performer.h"
 
@@ -138,6 +140,10 @@
 
 FeatureFlag
 DynamicPartitionControlAndroid::GetVirtualAbCompressionFeatureFlag() {
+  if constexpr (constants::kIsRecovery) {
+    // Don't attempt VABC in recovery
+    return FeatureFlag(FeatureFlag::Value::NONE);
+  }
   return virtual_ab_compression_;
 }
 
@@ -961,7 +967,8 @@
     bool not_in_payload,
     std::string* device,
     bool* is_dynamic) {
-  auto partition_dev = GetPartitionDevice(partition_name, slot, current_slot);
+  auto partition_dev =
+      GetPartitionDevice(partition_name, slot, current_slot, not_in_payload);
   if (!partition_dev.has_value()) {
     return false;
   }
@@ -1006,8 +1013,8 @@
   // target slot.
   const auto& partition_name_suffix =
       partition_name + SlotSuffixForSlotNumber(slot);
-  if (GetVirtualAbCompressionFeatureFlag().IsEnabled() &&
-      IsDynamicPartition(partition_name) && slot != current_slot) {
+  if (UpdateUsesSnapshotCompression() && IsDynamicPartition(partition_name) &&
+      slot != current_slot) {
     return {{.mountable_device_path =
                  GetStaticDevicePath(device_dir, partition_name_suffix),
              .is_dynamic = true}};
@@ -1193,22 +1200,32 @@
 }
 
 bool DynamicPartitionControlAndroid::ListDynamicPartitionsForSlot(
-    uint32_t current_slot, std::vector<std::string>* partitions) {
-  if (!GetDynamicPartitionsFeatureFlag().IsEnabled()) {
-    LOG(ERROR) << "Dynamic partition is not enabled";
-    return false;
+    uint32_t slot,
+    uint32_t current_slot,
+    std::vector<std::string>* partitions) {
+  bool slot_enables_dynamic_partitions =
+      GetDynamicPartitionsFeatureFlag().IsEnabled();
+  // Check if the target slot has dynamic partitions, this may happen when
+  // applying a retrofit package.
+  if (slot != current_slot) {
+    slot_enables_dynamic_partitions =
+        slot_enables_dynamic_partitions && is_target_dynamic_;
+  }
+
+  if (!slot_enables_dynamic_partitions) {
+    LOG(INFO) << "Dynamic partition is not enabled for slot " << slot;
+    return true;
   }
 
   std::string device_dir_str;
   TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
   base::FilePath device_dir(device_dir_str);
-  auto super_device =
-      device_dir.Append(GetSuperPartitionName(current_slot)).value();
-  auto builder = LoadMetadataBuilder(super_device, current_slot);
+  auto super_device = device_dir.Append(GetSuperPartitionName(slot)).value();
+  auto builder = LoadMetadataBuilder(super_device, slot);
   TEST_AND_RETURN_FALSE(builder != nullptr);
 
   std::vector<std::string> result;
-  auto suffix = SlotSuffixForSlotNumber(current_slot);
+  auto suffix = SlotSuffixForSlotNumber(slot);
   for (const auto& group : builder->ListGroups()) {
     for (const auto& partition : builder->ListPartitionsInGroup(group)) {
       std::string_view partition_name = partition->name();
@@ -1325,11 +1342,17 @@
     const std::string& partition_name) {
   if (dynamic_partition_list_.empty() &&
       GetDynamicPartitionsFeatureFlag().IsEnabled()) {
-    CHECK(ListDynamicPartitionsForSlot(source_slot_, &dynamic_partition_list_));
+    // Use the DAP config of the target slot.
+    CHECK(ListDynamicPartitionsForSlot(
+        target_slot_, source_slot_, &dynamic_partition_list_));
   }
   return std::find(dynamic_partition_list_.begin(),
                    dynamic_partition_list_.end(),
                    partition_name) != dynamic_partition_list_.end();
 }
 
+bool DynamicPartitionControlAndroid::UpdateUsesSnapshotCompression() {
+  return snapshot_->UpdateUsesCompression();
+}
+
 }  // namespace chromeos_update_engine
diff --git a/aosp/dynamic_partition_control_android.h b/aosp/dynamic_partition_control_android.h
index ecab6fa..d7c8781 100644
--- a/aosp/dynamic_partition_control_android.h
+++ b/aosp/dynamic_partition_control_android.h
@@ -58,7 +58,9 @@
   bool ResetUpdate(PrefsInterface* prefs) override;
 
   bool ListDynamicPartitionsForSlot(
-      uint32_t current_slot, std::vector<std::string>* partitions) override;
+      uint32_t slot,
+      uint32_t current_slot,
+      std::vector<std::string>* partitions) override;
 
   bool VerifyExtentsForUntouchedPartitions(
       uint32_t source_slot,
@@ -76,7 +78,7 @@
       const std::string& partition_name,
       uint32_t slot,
       uint32_t current_slot,
-      bool not_in_payload = false);
+      bool not_in_payload);
   // Deprecated, please use GetPartitionDevice(string, uint32_t, uint32_t);
   // TODO(zhangkelvin) Remove below deprecated APIs.
   bool GetPartitionDevice(const std::string& partition_name,
@@ -105,6 +107,8 @@
 
   bool IsDynamicPartition(const std::string& part_name) override;
 
+  bool UpdateUsesSnapshotCompression() override;
+
  protected:
   // These functions are exposed for testing.
 
diff --git a/aosp/dynamic_partition_control_android_unittest.cc b/aosp/dynamic_partition_control_android_unittest.cc
index af5ae2c..2f290d7 100644
--- a/aosp/dynamic_partition_control_android_unittest.cc
+++ b/aosp/dynamic_partition_control_android_unittest.cc
@@ -59,7 +59,8 @@
         .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::NONE)));
     ON_CALL(dynamicControl(), GetVirtualAbCompressionFeatureFlag())
         .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::NONE)));
-
+    ON_CALL(dynamicControl(), UpdateUsesSnapshotCompression())
+        .WillByDefault(Return(false));
     ON_CALL(dynamicControl(), GetDeviceDir(_))
         .WillByDefault(Invoke([](auto path) {
           *path = kFakeDevicePath;
@@ -399,6 +400,8 @@
       .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
   ON_CALL(dynamicControl(), GetVirtualAbCompressionFeatureFlag())
       .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::NONE)));
+  ON_CALL(dynamicControl(), UpdateUsesSnapshotCompression())
+      .WillByDefault(Return(false));
   ON_CALL(dynamicControl(), IsDynamicPartition(_)).WillByDefault(Return(true));
 
   EXPECT_CALL(dynamicControl(),
@@ -425,7 +428,7 @@
 
   // If VABC is disabled, mountable device path should be same as device path.
   auto device_info =
-      dynamicControl().GetPartitionDevice("system", target(), source());
+      dynamicControl().GetPartitionDevice("system", target(), source(), false);
   ASSERT_TRUE(device_info.has_value());
   ASSERT_EQ(device_info->mountable_device_path, device);
 }
@@ -437,6 +440,8 @@
       .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
   ON_CALL(dynamicControl(), GetVirtualAbCompressionFeatureFlag())
       .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+  ON_CALL(dynamicControl(), UpdateUsesSnapshotCompression())
+      .WillByDefault(Return(true));
   EXPECT_CALL(dynamicControl(), IsDynamicPartition(_))
       .Times(AtLeast(1))
       .WillRepeatedly(Return(true));
@@ -465,7 +470,7 @@
   ASSERT_EQ("", device);
 
   auto device_info =
-      dynamicControl().GetPartitionDevice("system", target(), source());
+      dynamicControl().GetPartitionDevice("system", target(), source(), false);
   ASSERT_TRUE(device_info.has_value());
   ASSERT_EQ(device_info->mountable_device_path, GetDevice(T("system")));
 }
diff --git a/aosp/hardware_android.cc b/aosp/hardware_android.cc
index 6f884d4..0ac82d6 100644
--- a/aosp/hardware_android.cc
+++ b/aosp/hardware_android.cc
@@ -23,16 +23,24 @@
 #include <string_view>
 
 #include <android/sysprop/GkiProperties.sysprop.h>
-#include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <base/files/file_util.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
 #include <bootloader_message/bootloader_message.h>
+#include <fstab/fstab.h>
+#include <libavb/libavb.h>
+#include <libavb_user/avb_ops_user.h>
 
 #include "update_engine/common/error_code_utils.h"
 #include "update_engine/common/hardware.h"
 #include "update_engine/common/platform_constants.h"
 #include "update_engine/common/utils.h"
 
+#ifndef __ANDROID_RECOVERY__
+#include <android/sysprop/OtaProperties.sysprop.h>
+#endif
+
 using android::base::GetBoolProperty;
 using android::base::GetIntProperty;
 using android::base::GetProperty;
@@ -68,6 +76,40 @@
   return error_code;
 }
 
+void SetVbmetaDigestProp(const std::string& value) {
+#ifndef __ANDROID_RECOVERY__
+  if (!android::sysprop::OtaProperties::other_vbmeta_digest(value)) {
+    LOG(WARNING) << "Failed to set other vbmeta digest to " << value;
+  }
+#endif
+}
+
+std::string CalculateVbmetaDigestForInactiveSlot() {
+  AvbSlotVerifyData* avb_slot_data;
+
+  auto suffix = fs_mgr_get_other_slot_suffix();
+  const char* requested_partitions[] = {nullptr};
+  auto avb_ops = avb_ops_user_new();
+  auto verify_result = avb_slot_verify(avb_ops,
+                                       requested_partitions,
+                                       suffix.c_str(),
+                                       AVB_SLOT_VERIFY_FLAGS_NONE,
+                                       AVB_HASHTREE_ERROR_MODE_EIO,
+                                       &avb_slot_data);
+  if (verify_result != AVB_SLOT_VERIFY_RESULT_OK) {
+    LOG(WARNING) << "Failed to verify avb slot data: " << verify_result;
+    return "";
+  }
+
+  uint8_t vbmeta_digest[AVB_SHA256_DIGEST_SIZE];
+  avb_slot_verify_data_calculate_vbmeta_digest(
+      avb_slot_data, AVB_DIGEST_TYPE_SHA256, vbmeta_digest);
+
+  std::string encoded_digest =
+      base::HexEncode(vbmeta_digest, AVB_SHA256_DIGEST_SIZE);
+  return base::ToLowerASCII(encoded_digest);
+}
+
 }  // namespace
 
 namespace hardware {
@@ -239,6 +281,30 @@
   }
 }
 
+void HardwareAndroid::SetVbmetaDigestForInactiveSlot(bool reset) {
+  if constexpr (constants::kIsRecovery) {
+    return;
+  }
+
+  if (android::base::GetProperty("ro.boot.avb_version", "").empty() &&
+      android::base::GetProperty("ro.boot.vbmeta.avb_version", "").empty()) {
+    LOG(INFO) << "Device doesn't use avb, skipping setting vbmeta digest";
+    return;
+  }
+
+  if (reset) {
+    SetVbmetaDigestProp("");
+    return;
+  }
+
+  std::string digest = CalculateVbmetaDigestForInactiveSlot();
+  if (digest.empty()) {
+    LOG(WARNING) << "Failed to calculate the vbmeta digest for the other slot";
+    return;
+  }
+  SetVbmetaDigestProp(digest);
+}
+
 string HardwareAndroid::GetVersionForLogging(
     const string& partition_name) const {
   if (partition_name == "boot") {
diff --git a/aosp/hardware_android.h b/aosp/hardware_android.h
index 5ffd7c5..78f056e 100644
--- a/aosp/hardware_android.h
+++ b/aosp/hardware_android.h
@@ -58,6 +58,7 @@
   bool GetFirstActiveOmahaPingSent() const override;
   bool SetFirstActiveOmahaPingSent() override;
   void SetWarmReset(bool warm_reset) override;
+  void SetVbmetaDigestForInactiveSlot(bool reset) override;
   [[nodiscard]] std::string GetVersionForLogging(
       const std::string& partition_name) const override;
   [[nodiscard]] ErrorCode IsPartitionUpdateValid(
diff --git a/aosp/metrics_reporter_android.cc b/aosp/metrics_reporter_android.cc
index 22ebf0d..1f8f45a 100644
--- a/aosp/metrics_reporter_android.cc
+++ b/aosp/metrics_reporter_android.cc
@@ -54,8 +54,9 @@
 
 namespace metrics {
 
-std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter() {
-  return std::make_unique<MetricsReporterAndroid>();
+std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter(
+    DynamicPartitionControlInterface* dynamic_partition_control) {
+  return std::make_unique<MetricsReporterAndroid>(dynamic_partition_control);
 }
 
 }  // namespace metrics
@@ -105,6 +106,11 @@
     }
   }
 
+  bool vab_compression_enabled = android::base::GetBoolProperty(
+      "ro.virtual_ab.compression.enabled", false);
+  bool vab_compression_used =
+      dynamic_partition_control_->UpdateUsesSnapshotCompression();
+
   android::util::stats_write(
       android::util::UPDATE_ENGINE_UPDATE_ATTEMPT_REPORTED,
       attempt_number,
@@ -117,7 +123,9 @@
       android::base::GetProperty("ro.build.fingerprint", "").c_str(),
       super_partition_size_bytes,
       slot_size_bytes,
-      super_free_space);
+      super_free_space,
+      vab_compression_enabled,
+      vab_compression_used);
 }
 
 void MetricsReporterAndroid::ReportUpdateAttemptDownloadMetrics(
diff --git a/aosp/metrics_reporter_android.h b/aosp/metrics_reporter_android.h
index 729542e..abe7c27 100644
--- a/aosp/metrics_reporter_android.h
+++ b/aosp/metrics_reporter_android.h
@@ -27,7 +27,9 @@
 
 class MetricsReporterAndroid : public MetricsReporterInterface {
  public:
-  MetricsReporterAndroid() = default;
+  explicit MetricsReporterAndroid(
+      DynamicPartitionControlInterface* dynamic_partition_control)
+      : dynamic_partition_control_(dynamic_partition_control) {}
 
   ~MetricsReporterAndroid() override = default;
 
@@ -91,6 +93,8 @@
       bool has_time_restriction_policy, int time_to_update_days) override {}
 
  private:
+  DynamicPartitionControlInterface* dynamic_partition_control_;
+
   DISALLOW_COPY_AND_ASSIGN(MetricsReporterAndroid);
 };
 
diff --git a/aosp/mock_dynamic_partition_control_android.h b/aosp/mock_dynamic_partition_control_android.h
index 1d4bb14..682ddfd 100644
--- a/aosp/mock_dynamic_partition_control_android.h
+++ b/aosp/mock_dynamic_partition_control_android.h
@@ -101,6 +101,7 @@
   MOCK_METHOD(bool, MapAllPartitions, (), (override));
   MOCK_METHOD(bool, UnmapAllPartitions, (), (override));
   MOCK_METHOD(bool, IsDynamicPartition, (const std::string&), (override));
+  MOCK_METHOD(bool, UpdateUsesSnapshotCompression, (), (override));
 
   void set_fake_mapped_devices(const std::set<std::string>& fake) override {
     DynamicPartitionControlAndroid::set_fake_mapped_devices(fake);
diff --git a/aosp/update_attempter_android.cc b/aosp/update_attempter_android.cc
index eb1ebe0..79840e8 100644
--- a/aosp/update_attempter_android.cc
+++ b/aosp/update_attempter_android.cc
@@ -140,7 +140,8 @@
       hardware_(hardware),
       processor_(new ActionProcessor()),
       clock_(new Clock()) {
-  metrics_reporter_ = metrics::CreateMetricsReporter();
+  metrics_reporter_ = metrics::CreateMetricsReporter(
+      boot_control_->GetDynamicPartitionControl());
   network_selector_ = network::CreateNetworkSelector();
 }
 
@@ -181,7 +182,7 @@
     return LogAndSetError(
         error, FROM_HERE, "Already processing an update, cancel it first.");
   }
-  DCHECK(status_ == UpdateStatus::IDLE);
+  DCHECK_EQ(status_, UpdateStatus::IDLE);
 
   std::map<string, string> headers;
   if (!ParseKeyValuePairHeaders(key_value_pair_headers, &headers, error)) {
@@ -317,6 +318,18 @@
     int64_t payload_size,
     const vector<string>& key_value_pair_headers,
     brillo::ErrorPtr* error) {
+  // update_engine state must be checked before modifying payload_fd_ otherwise
+  // already running update will be terminated (existing file descriptor will be closed)
+  if (status_ == UpdateStatus::UPDATED_NEED_REBOOT) {
+    return LogAndSetError(
+        error, FROM_HERE, "An update already applied, waiting for reboot");
+  }
+  if (processor_->IsRunning()) {
+    return LogAndSetError(
+        error, FROM_HERE, "Already processing an update, cancel it first.");
+  }
+  DCHECK_EQ(status_, UpdateStatus::IDLE);
+
   payload_fd_.reset(dup(fd));
   const string payload_url = "fd://" + std::to_string(payload_fd_.get());
 
@@ -374,6 +387,9 @@
       // Resets the warm reset property since we won't switch the slot.
       hardware_->SetWarmReset(false);
 
+      // Resets the vbmeta digest.
+      hardware_->SetVbmetaDigestForInactiveSlot(true /* reset */);
+
       // Remove update progress for DeltaPerformer and remove snapshots.
       if (!boot_control_->GetDynamicPartitionControl()->ResetUpdate(prefs_))
         ret_value = false;
diff --git a/aosp/update_attempter_android_unittest.cc b/aosp/update_attempter_android_unittest.cc
index fc30268..173e943 100644
--- a/aosp/update_attempter_android_unittest.cc
+++ b/aosp/update_attempter_android_unittest.cc
@@ -63,14 +63,14 @@
         std::move(payload));
   }
 
-  UpdateAttempterAndroid update_attempter_android_{
-      &daemon_state_, &prefs_, &boot_control_, &hardware_};
-
   DaemonStateAndroid daemon_state_;
   FakePrefs prefs_;
   FakeBootControl boot_control_;
   FakeHardware hardware_;
 
+  UpdateAttempterAndroid update_attempter_android_{
+      &daemon_state_, &prefs_, &boot_control_, &hardware_};
+
   FakeClock* clock_;
   testing::NiceMock<MockMetricsReporter>* metrics_reporter_;
 };
diff --git a/common/boot_control_interface.h b/common/boot_control_interface.h
index 3b61add..321174e 100644
--- a/common/boot_control_interface.h
+++ b/common/boot_control_interface.h
@@ -103,7 +103,7 @@
   // Check if |slot| is marked boot successfully.
   virtual bool IsSlotMarkedSuccessful(Slot slot) const = 0;
 
-  // Return the dynamic partition control interface.
+  // Return the dynamic partition control interface. Never null.
   virtual DynamicPartitionControlInterface* GetDynamicPartitionControl() = 0;
 
   // Return a human-readable slot name used for logging.
diff --git a/common/download_action.h b/common/download_action.h
index caa5a7b..7b496b1 100644
--- a/common/download_action.h
+++ b/common/download_action.h
@@ -23,6 +23,7 @@
 
 #include <memory>
 #include <string>
+#include <utility>
 
 #include "update_engine/common/action.h"
 #include "update_engine/common/boot_control_interface.h"
@@ -87,7 +88,9 @@
   std::string Type() const override { return StaticType(); }
 
   // Testing
-  void SetTestFileWriter(DeltaPerformer* writer) { writer_ = writer; }
+  void SetTestFileWriter(std::unique_ptr<DeltaPerformer> writer) {
+    delta_performer_ = std::move(writer);
+  }
 
   int GetHTTPResponseCode() { return http_fetcher_->http_response_code(); }
 
@@ -130,10 +133,6 @@
   // update.
   bool interactive_;
 
-  // The FileWriter that downloaded data should be written to. It will
-  // either point to a writer for unittest or *delta_performer_.
-  DeltaPerformer* writer_;
-
   std::unique_ptr<DeltaPerformer> delta_performer_;
 
   // Used by TransferTerminated to figure if this action terminated itself or
diff --git a/common/dynamic_partition_control_interface.h b/common/dynamic_partition_control_interface.h
index 4f46f74..8b29c3b 100644
--- a/common/dynamic_partition_control_interface.h
+++ b/common/dynamic_partition_control_interface.h
@@ -69,6 +69,9 @@
   // Return the feature flags of Virtual A/B on this device.
   virtual FeatureFlag GetVirtualAbFeatureFlag() = 0;
   // Return the feature flags of Virtual A/B Compression on this device.
+  // This function will tell you if current device supports VABC. However, it
+  // DOES NOT tell you if VABC is used for current OTA update. For that, use
+  // UpdateUsesSnapshotCompression.
   virtual FeatureFlag GetVirtualAbCompressionFeatureFlag() = 0;
 
   // Attempt to optimize |operation|.
@@ -134,11 +137,13 @@
   // allocated space for snapshot updates.
   virtual bool ResetUpdate(PrefsInterface* prefs) = 0;
 
-  // Reads the dynamic partitions metadata from the current slot, and puts the
+  // Reads the dynamic partitions metadata from the given slot, and puts the
   // name of the dynamic partitions with the current suffix to |partitions|.
   // Returns true on success.
   virtual bool ListDynamicPartitionsForSlot(
-      uint32_t current_slot, std::vector<std::string>* partitions) = 0;
+      uint32_t slot,
+      uint32_t current_slot,
+      std::vector<std::string>* partitions) = 0;
 
   // Finds a possible location that list all block devices by name; and puts
   // the result in |path|. Returns true on success.
@@ -171,6 +176,16 @@
   virtual bool MapAllPartitions() = 0;
   // Unmap virtual block devices for all partitions.
   virtual bool UnmapAllPartitions() = 0;
+
+  // Return if snapshot compression is enabled for this update.
+  // This function should only be called after preparing for an update
+  // (PreparePartitionsForUpdate), and before merging
+  // (see GetCleanupPreviousUpdateAction and CleanupPreviousUpdateAction) or
+  // resetting it (ResetUpdate).
+  //
+  // To know if the device supports snapshot compression by itself, use
+  // GetVirtualAbCompressionFeatureFlag
+  virtual bool UpdateUsesSnapshotCompression() = 0;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/common/dynamic_partition_control_stub.cc b/common/dynamic_partition_control_stub.cc
index 2c6bb1b..7d0ef18 100644
--- a/common/dynamic_partition_control_stub.cc
+++ b/common/dynamic_partition_control_stub.cc
@@ -73,7 +73,9 @@
 }
 
 bool DynamicPartitionControlStub::ListDynamicPartitionsForSlot(
-    uint32_t current_slot, std::vector<std::string>* partitions) {
+    uint32_t slot,
+    uint32_t current_slot,
+    std::vector<std::string>* partitions) {
   return true;
 }
 
@@ -116,4 +118,8 @@
   return false;
 }
 
+bool DynamicPartitionControlStub::UpdateUsesSnapshotCompression() {
+  return false;
+}
+
 }  // namespace chromeos_update_engine
diff --git a/common/dynamic_partition_control_stub.h b/common/dynamic_partition_control_stub.h
index 0f428ab..5ab82f5 100644
--- a/common/dynamic_partition_control_stub.h
+++ b/common/dynamic_partition_control_stub.h
@@ -50,7 +50,9 @@
   bool ResetUpdate(PrefsInterface* prefs) override;
 
   bool ListDynamicPartitionsForSlot(
-      uint32_t current_slot, std::vector<std::string>* partitions) override;
+      uint32_t slot,
+      uint32_t current_slot,
+      std::vector<std::string>* partitions) override;
   bool GetDeviceDir(std::string* path) override;
 
   bool VerifyExtentsForUntouchedPartitions(
@@ -70,6 +72,7 @@
   bool UnmapAllPartitions() override;
 
   bool IsDynamicPartition(const std::string& part_name) override;
+  bool UpdateUsesSnapshotCompression() override;
 };
 }  // namespace chromeos_update_engine
 
diff --git a/common/fake_hardware.h b/common/fake_hardware.h
index 00a212e..29ba607 100644
--- a/common/fake_hardware.h
+++ b/common/fake_hardware.h
@@ -194,6 +194,8 @@
 
   void SetWarmReset(bool warm_reset) override { warm_reset_ = warm_reset; }
 
+  void SetVbmetaDigestForInactiveSlot(bool reset) override {}
+
   // Getters to verify state.
   int GetMaxKernelKeyRollforward() const { return kernel_max_rollforward_; }
 
diff --git a/common/fake_prefs.cc b/common/fake_prefs.cc
index 275667e..ea6ea60 100644
--- a/common/fake_prefs.cc
+++ b/common/fake_prefs.cc
@@ -17,6 +17,7 @@
 #include "update_engine/common/fake_prefs.h"
 
 #include <algorithm>
+#include <utility>
 
 #include <gtest/gtest.h>
 
@@ -66,8 +67,8 @@
   return GetValue(key, value);
 }
 
-bool FakePrefs::SetString(const string& key, const string& value) {
-  SetValue(key, value);
+bool FakePrefs::SetString(const string& key, std::string_view value) {
+  SetValue(key, std::string(value));
   return true;
 }
 
@@ -149,10 +150,10 @@
 }
 
 template <typename T>
-void FakePrefs::SetValue(const string& key, const T& value) {
+void FakePrefs::SetValue(const string& key, T value) {
   CheckKeyType(key, PrefConsts<T>::type);
   values_[key].type = PrefConsts<T>::type;
-  values_[key].value.*(PrefConsts<T>::member) = value;
+  values_[key].value.*(PrefConsts<T>::member) = std::move(value);
   const auto observers_for_key = observers_.find(key);
   if (observers_for_key != observers_.end()) {
     std::vector<ObserverInterface*> copy_observers(observers_for_key->second);
diff --git a/common/fake_prefs.h b/common/fake_prefs.h
index 9af2550..430c291 100644
--- a/common/fake_prefs.h
+++ b/common/fake_prefs.h
@@ -19,6 +19,7 @@
 
 #include <map>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include <base/macros.h>
@@ -40,7 +41,7 @@
 
   // PrefsInterface methods.
   bool GetString(const std::string& key, std::string* value) const override;
-  bool SetString(const std::string& key, const std::string& value) override;
+  bool SetString(const std::string& key, std::string_view value) override;
   bool GetInt64(const std::string& key, int64_t* value) const override;
   bool SetInt64(const std::string& key, const int64_t value) override;
   bool GetBoolean(const std::string& key, bool* value) const override;
@@ -96,7 +97,7 @@
   // Helper function to set a value of the passed |key|. It sets the type based
   // on the template parameter T.
   template <typename T>
-  void SetValue(const std::string& key, const T& value);
+  void SetValue(const std::string& key, T value);
 
   // Helper function to get a value from the map checking for invalid calls.
   // The function fails the test if you attempt to read a value  defined as a
diff --git a/common/hardware_interface.h b/common/hardware_interface.h
index cad32fc..7460097 100644
--- a/common/hardware_interface.h
+++ b/common/hardware_interface.h
@@ -137,6 +137,10 @@
   // needed on the next reboot. Otherwise, clears the flag.
   virtual void SetWarmReset(bool warm_reset) = 0;
 
+  // If not reset, sets the vbmeta digest of the inactive slot as a sysprop.
+  // Otherwise, clears the sysprop.
+  virtual void SetVbmetaDigestForInactiveSlot(bool reset) = 0;
+
   // Return the version/timestamp for partition `partition_name`.
   // Don't make any assumption about the formatting of returned string.
   // Only used for logging/debugging purposes.
diff --git a/common/metrics_reporter_interface.h b/common/metrics_reporter_interface.h
index 08636e3..29d13fa 100644
--- a/common/metrics_reporter_interface.h
+++ b/common/metrics_reporter_interface.h
@@ -23,6 +23,7 @@
 #include <base/time/time.h>
 
 #include "update_engine/common/constants.h"
+#include "update_engine/common/dynamic_partition_control_interface.h"
 #include "update_engine/common/error_code.h"
 #include "update_engine/common/metrics_constants.h"
 
@@ -235,7 +236,8 @@
 
 namespace metrics {
 
-std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter();
+std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter(
+    DynamicPartitionControlInterface* dynamic_partition_control);
 
 }  // namespace metrics
 
diff --git a/common/metrics_reporter_stub.cc b/common/metrics_reporter_stub.cc
index dcb4e8c..96b519b 100644
--- a/common/metrics_reporter_stub.cc
+++ b/common/metrics_reporter_stub.cc
@@ -22,7 +22,8 @@
 
 namespace metrics {
 
-std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter() {
+std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter(
+    DynamicPartitionControlInterface* dynamic_partition_control) {
   return std::make_unique<MetricsReporterStub>();
 }
 
diff --git a/common/mock_dynamic_partition_control.h b/common/mock_dynamic_partition_control.h
index 3cbf9be..74f4efc 100644
--- a/common/mock_dynamic_partition_control.h
+++ b/common/mock_dynamic_partition_control.h
@@ -72,13 +72,15 @@
               (override));
   MOCK_METHOD(bool,
               ListDynamicPartitionsForSlot,
-              (uint32_t, std::vector<std::string>*),
+              (uint32_t, uint32_t, std::vector<std::string>*),
               (override));
+
   MOCK_METHOD(bool,
               VerifyExtentsForUntouchedPartitions,
               (uint32_t, uint32_t, const std::vector<std::string>&),
               (override));
   MOCK_METHOD(bool, IsDynamicPartition, (const std::string&), (override));
+  MOCK_METHOD(bool, UpdateUsesSnapshotCompression, (), (override));
 };
 
 }  // namespace chromeos_update_engine
diff --git a/common/mock_prefs.h b/common/mock_prefs.h
index c91664e..49431fb 100644
--- a/common/mock_prefs.h
+++ b/common/mock_prefs.h
@@ -31,8 +31,7 @@
  public:
   MOCK_CONST_METHOD2(GetString,
                      bool(const std::string& key, std::string* value));
-  MOCK_METHOD2(SetString,
-               bool(const std::string& key, const std::string& value));
+  MOCK_METHOD2(SetString, bool(const std::string& key, std::string_view value));
   MOCK_CONST_METHOD2(GetInt64, bool(const std::string& key, int64_t* value));
   MOCK_METHOD2(SetInt64, bool(const std::string& key, const int64_t value));
 
diff --git a/common/prefs.cc b/common/prefs.cc
index 84fe536..1e06be4 100644
--- a/common/prefs.cc
+++ b/common/prefs.cc
@@ -55,7 +55,7 @@
   return storage_->GetKey(key, value);
 }
 
-bool PrefsBase::SetString(const string& key, const string& value) {
+bool PrefsBase::SetString(const string& key, std::string_view value) {
   TEST_AND_RETURN_FALSE(storage_->SetKey(key, value));
   const auto observers_for_key = observers_.find(key);
   if (observers_for_key != observers_.end()) {
@@ -192,7 +192,7 @@
   return true;
 }
 
-bool Prefs::FileStorage::SetKey(const string& key, const string& value) {
+bool Prefs::FileStorage::SetKey(const string& key, std::string_view value) {
   base::FilePath filename;
   TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
   if (!base::DirectoryExists(filename.DirName())) {
@@ -263,7 +263,7 @@
 }
 
 bool MemoryPrefs::MemoryStorage::SetKey(const string& key,
-                                        const string& value) {
+                                        std::string_view value) {
   values_[key] = value;
   return true;
 }
diff --git a/common/prefs.h b/common/prefs.h
index d6ef668..93477dd 100644
--- a/common/prefs.h
+++ b/common/prefs.h
@@ -19,6 +19,7 @@
 
 #include <map>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include <base/files/file_path.h>
@@ -49,7 +50,7 @@
 
     // Set the value of the key named |key| to |value| regardless of the
     // previous value. Returns whether the operation succeeded.
-    virtual bool SetKey(const std::string& key, const std::string& value) = 0;
+    virtual bool SetKey(const std::string& key, std::string_view value) = 0;
 
     // Returns whether the key named |key| exists.
     virtual bool KeyExists(const std::string& key) const = 0;
@@ -66,7 +67,7 @@
 
   // PrefsInterface methods.
   bool GetString(const std::string& key, std::string* value) const override;
-  bool SetString(const std::string& key, const std::string& value) override;
+  bool SetString(const std::string& key, std::string_view value) override;
   bool GetInt64(const std::string& key, int64_t* value) const override;
   bool SetInt64(const std::string& key, const int64_t value) override;
   bool GetBoolean(const std::string& key, bool* value) const override;
@@ -123,7 +124,7 @@
     bool GetKey(const std::string& key, std::string* value) const override;
     bool GetSubKeys(const std::string& ns,
                     std::vector<std::string>* keys) const override;
-    bool SetKey(const std::string& key, const std::string& value) override;
+    bool SetKey(const std::string& key, std::string_view value) override;
     bool KeyExists(const std::string& key) const override;
     bool DeleteKey(const std::string& key) override;
 
@@ -163,7 +164,7 @@
     bool GetKey(const std::string& key, std::string* value) const override;
     bool GetSubKeys(const std::string& ns,
                     std::vector<std::string>* keys) const override;
-    bool SetKey(const std::string& key, const std::string& value) override;
+    bool SetKey(const std::string& key, std::string_view value) override;
     bool KeyExists(const std::string& key) const override;
     bool DeleteKey(const std::string& key) override;
 
diff --git a/common/prefs_interface.h b/common/prefs_interface.h
index 866d0ca..e773a35 100644
--- a/common/prefs_interface.h
+++ b/common/prefs_interface.h
@@ -52,7 +52,7 @@
 
   // Associates |key| with a string |value|. Returns true on success,
   // false otherwise.
-  virtual bool SetString(const std::string& key, const std::string& value) = 0;
+  virtual bool SetString(const std::string& key, std::string_view value) = 0;
 
   // Gets an int64_t |value| associated with |key|. Returns true on
   // success, false on failure (including when the |key| is not
diff --git a/common/utils.cc b/common/utils.cc
index 3a89c2a..0f3b6c6 100644
--- a/common/utils.cc
+++ b/common/utils.cc
@@ -492,6 +492,11 @@
   return lstat(path, &stbuf) == 0 && S_ISLNK(stbuf.st_mode) != 0;
 }
 
+bool IsRegFile(const char* path) {
+  struct stat stbuf;
+  return lstat(path, &stbuf) == 0 && S_ISREG(stbuf.st_mode) != 0;
+}
+
 bool MakeTempFile(const string& base_filename_template,
                   string* filename,
                   int* fd) {
diff --git a/common/utils.h b/common/utils.h
index 616de06..5f6e475 100644
--- a/common/utils.h
+++ b/common/utils.h
@@ -144,6 +144,9 @@
 // Returns true if |path| exists and is a symbolic link.
 bool IsSymlink(const char* path);
 
+// Return true iff |path| exists and is a regular file
+bool IsRegFile(const char* path);
+
 // If |base_filename_template| is neither absolute (starts with "/") nor
 // explicitly relative to the current working directory (starts with "./" or
 // "../"), then it is prepended the system's temporary directory. On success,
diff --git a/cros/hardware_chromeos.cc b/cros/hardware_chromeos.cc
index 14f2497..a57cd78 100644
--- a/cros/hardware_chromeos.cc
+++ b/cros/hardware_chromeos.cc
@@ -349,6 +349,8 @@
 
 void HardwareChromeOS::SetWarmReset(bool warm_reset) {}
 
+void HardwareChromeOS::SetVbmetaDigestForInactiveSlot(bool reset) {}
+
 std::string HardwareChromeOS::GetVersionForLogging(
     const std::string& partition_name) const {
   // TODO(zhangkelvin) Implement per-partition timestamp for Chrome OS.
diff --git a/cros/hardware_chromeos.h b/cros/hardware_chromeos.h
index de84d78..8a920ef 100644
--- a/cros/hardware_chromeos.h
+++ b/cros/hardware_chromeos.h
@@ -62,6 +62,7 @@
   bool GetFirstActiveOmahaPingSent() const override;
   bool SetFirstActiveOmahaPingSent() override;
   void SetWarmReset(bool warm_reset) override;
+  void SetVbmetaDigestForInactiveSlot(bool reset) override;
   std::string GetVersionForLogging(
       const std::string& partition_name) const override;
   ErrorCode IsPartitionUpdateValid(
diff --git a/cros/metrics_reporter_omaha.cc b/cros/metrics_reporter_omaha.cc
index 22c0aa9..69cdb19 100644
--- a/cros/metrics_reporter_omaha.cc
+++ b/cros/metrics_reporter_omaha.cc
@@ -135,7 +135,8 @@
     "UpdateEngine.InstallDateProvisioningSource";
 const char kMetricTimeToRebootMinutes[] = "UpdateEngine.TimeToRebootMinutes";
 
-std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter() {
+std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter(
+    DynamicPartitionControlInterface* dynamic_partition_control) {
   return std::make_unique<MetricsReporterOmaha>();
 }
 
diff --git a/download_action.cc b/download_action.cc
index 0456298..62a8423 100644
--- a/download_action.cc
+++ b/download_action.cc
@@ -47,7 +47,6 @@
       hardware_(hardware),
       http_fetcher_(new MultiRangeHttpFetcher(http_fetcher)),
       interactive_(interactive),
-      writer_(nullptr),
       code_(ErrorCode::kSuccess),
       delegate_(nullptr) {}
 
@@ -105,14 +104,11 @@
     return false;
   }
 
-  if (writer_ && writer_ != delta_performer_.get()) {
-    LOG(INFO) << "Using writer for test.";
-  }
   ErrorCode error;
   const bool success =
-      writer_->Write(
+      delta_performer_->Write(
           cached_manifest_bytes.data(), cached_manifest_bytes.size(), &error) &&
-      writer_->IsManifestValid();
+      delta_performer_->IsManifestValid();
   if (success) {
     LOG(INFO) << "Successfully parsed cached manifest";
   } else {
@@ -127,7 +123,7 @@
   download_active_ = true;
   http_fetcher_->ClearRanges();
 
-  if (writer_ && writer_ != delta_performer_.get()) {
+  if (delta_performer_ != nullptr) {
     LOG(INFO) << "Using writer for test.";
   } else {
     delta_performer_.reset(new DeltaPerformer(prefs_,
@@ -137,7 +133,6 @@
                                               &install_plan_,
                                               payload_,
                                               interactive_));
-    writer_ = delta_performer_.get();
   }
 
   if (install_plan_.is_resume &&
@@ -159,7 +154,6 @@
                                                             &install_plan_,
                                                             payload_,
                                                             interactive_);
-        writer_ = delta_performer_.get();
       }
       http_fetcher_->AddRange(base_offset_,
                               manifest_metadata_size + manifest_signature_size);
@@ -200,9 +194,9 @@
 }
 
 void DownloadAction::TerminateProcessing() {
-  if (writer_) {
-    writer_->Close();
-    writer_ = nullptr;
+  if (delta_performer_) {
+    delta_performer_->Close();
+    delta_performer_.reset();
   }
   download_active_ = false;
   // Terminates the transfer. The action is terminated, if necessary, when the
@@ -223,7 +217,7 @@
   if (delegate_ && download_active_) {
     delegate_->BytesReceived(length, bytes_downloaded_total, bytes_total_);
   }
-  if (writer_ && !writer_->Write(bytes, length, &code_)) {
+  if (delta_performer_ && !delta_performer_->Write(bytes, length, &code_)) {
     if (code_ != ErrorCode::kSuccess) {
       LOG(ERROR) << "Error " << utils::ErrorCodeToString(code_) << " (" << code_
                  << ") in DeltaPerformer's Write method when "
@@ -240,12 +234,9 @@
 }
 
 void DownloadAction::TransferComplete(HttpFetcher* fetcher, bool successful) {
-  if (writer_) {
-    LOG_IF(WARNING, writer_->Close() != 0) << "Error closing the writer.";
-    if (delta_performer_.get() == writer_) {
-      // no delta_performer_ in tests, so leave the test writer in place
-      writer_ = nullptr;
-    }
+  if (delta_performer_) {
+    LOG_IF(WARNING, delta_performer_->Close() != 0)
+        << "Error closing the writer.";
   }
   download_active_ = false;
   ErrorCode code =
diff --git a/download_action_android_unittest.cc b/download_action_android_unittest.cc
index c1ad9c2..b17550b 100644
--- a/download_action_android_unittest.cc
+++ b/download_action_android_unittest.cc
@@ -156,15 +156,15 @@
       &prefs, &boot_control, nullptr, http_fetcher, false /* interactive */);
 
   FakeHardware hardware;
-  DeltaPerformer delta_performer(&prefs,
-                                 &boot_control,
-                                 &hardware,
-                                 nullptr,
-                                 &install_plan,
-                                 &payload,
-                                 false);
-  delta_performer.set_public_key_path(kUnittestPublicKeyPath);
-  download_action->SetTestFileWriter(&delta_performer);
+  auto delta_performer = std::make_unique<DeltaPerformer>(&prefs,
+                                                          &boot_control,
+                                                          &hardware,
+                                                          nullptr,
+                                                          &install_plan,
+                                                          &payload,
+                                                          false);
+  delta_performer->set_public_key_path(kUnittestPublicKeyPath);
+  download_action->SetTestFileWriter(std::move(delta_performer));
   download_action->set_in_pipe(action_pipe);
   MockActionProcessor mock_processor;
   download_action->SetProcessor(&mock_processor);
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index a9461ac..c6d6343 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -436,7 +436,8 @@
       return false;
     manifest_valid_ = true;
     if (!install_plan_->is_resume) {
-      prefs_->SetString(kPrefsManifestBytes, {buffer_.begin(), buffer_.end()});
+      auto begin = reinterpret_cast<const char*>(buffer_.data());
+      prefs_->SetString(kPrefsManifestBytes, {begin, buffer_.size()});
     }
 
     // Clear the download buffer.
@@ -640,11 +641,6 @@
     }
   }
 
-  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
@@ -672,6 +668,7 @@
     std::vector<std::string> dynamic_partitions;
     if (!boot_control_->GetDynamicPartitionControl()
              ->ListDynamicPartitionsForSlot(install_plan_->source_slot,
+                                            boot_control_->GetCurrentSlot(),
                                             &dynamic_partitions)) {
       LOG(ERROR) << "Failed to load dynamic partitions from slot "
                  << install_plan_->source_slot;
@@ -975,20 +972,6 @@
   signatures_message_data_.assign(
       buffer_.begin(), buffer_.begin() + manifest_.signatures_size());
 
-  // Save the signature blob because if the update is interrupted after the
-  // download phase we don't go through this path anymore. Some alternatives
-  // to consider:
-  //
-  // 1. On resume, re-download the signature blob from the server and
-  // re-verify it.
-  //
-  // 2. Verify the signature as soon as it's received and don't checkpoint the
-  // blob and the signed sha-256 context.
-  LOG_IF(WARNING,
-         !prefs_->SetString(kPrefsUpdateStateSignatureBlob,
-                            signatures_message_data_))
-      << "Unable to store the signature blob.";
-
   LOG(INFO) << "Extracted signature data of size "
             << manifest_.signatures_size() << " at "
             << manifest_.signatures_offset();
@@ -1421,6 +1404,21 @@
   if (last_updated_operation_num_ != next_operation_num_ || force) {
     // Resets the progress in case we die in the middle of the state update.
     ResetUpdateProgress(prefs_, true);
+    if (!signatures_message_data_.empty()) {
+      // Save the signature blob because if the update is interrupted after the
+      // download phase we don't go through this path anymore. Some alternatives
+      // to consider:
+      //
+      // 1. On resume, re-download the signature blob from the server and
+      // re-verify it.
+      //
+      // 2. Verify the signature as soon as it's received and don't checkpoint
+      // the blob and the signed sha-256 context.
+      LOG_IF(WARNING,
+             !prefs_->SetString(kPrefsUpdateStateSignatureBlob,
+                                signatures_message_data_))
+          << "Unable to store the signature blob.";
+    }
     TEST_AND_RETURN_FALSE(prefs_->SetString(
         kPrefsUpdateStateSHA256Context, payload_hash_calculator_.GetContext()));
     TEST_AND_RETURN_FALSE(
@@ -1522,9 +1520,8 @@
 }
 
 bool DeltaPerformer::IsDynamicPartition(const std::string& part_name) {
-  return std::find(dynamic_partitions_.begin(),
-                   dynamic_partitions_.end(),
-                   part_name) != dynamic_partitions_.end();
+  return boot_control_->GetDynamicPartitionControl()->IsDynamicPartition(
+      part_name);
 }
 
 std::unique_ptr<PartitionWriter> DeltaPerformer::CreatePartitionWriter(
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index ec23997..31fa6b2 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -436,8 +436,6 @@
 
   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/filesystem_verifier_action.cc b/payload_consumer/filesystem_verifier_action.cc
index 634f03f..e3a3e34 100644
--- a/payload_consumer/filesystem_verifier_action.cc
+++ b/payload_consumer/filesystem_verifier_action.cc
@@ -167,7 +167,7 @@
   LOG(INFO) << "Hashing partition " << partition_index_ << " ("
             << partition.name << ") on device " << part_path;
   auto success = false;
-  if (dynamic_control_->GetVirtualAbCompressionFeatureFlag().IsEnabled() &&
+  if (dynamic_control_->UpdateUsesSnapshotCompression() &&
       dynamic_control_->IsDynamicPartition(partition.name) &&
       verifier_step_ == VerifierStep::kVerifyTargetHash) {
     success = InitializeFdVABC();
diff --git a/payload_consumer/filesystem_verifier_action_unittest.cc b/payload_consumer/filesystem_verifier_action_unittest.cc
index e9560f0..d163ec2 100644
--- a/payload_consumer/filesystem_verifier_action_unittest.cc
+++ b/payload_consumer/filesystem_verifier_action_unittest.cc
@@ -415,20 +415,20 @@
 
   ON_CALL(dynamic_control, GetDynamicPartitionsFeatureFlag())
       .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
-  ON_CALL(dynamic_control, GetVirtualAbCompressionFeatureFlag())
-      .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+  ON_CALL(dynamic_control, UpdateUsesSnapshotCompression())
+      .WillByDefault(Return(true));
   ON_CALL(dynamic_control, OpenCowReader(_, _, _))
       .WillByDefault(Return(nullptr));
   ON_CALL(dynamic_control, IsDynamicPartition(part.name))
       .WillByDefault(Return(true));
 
-  EXPECT_CALL(dynamic_control, GetVirtualAbCompressionFeatureFlag())
+  EXPECT_CALL(dynamic_control, UpdateUsesSnapshotCompression())
       .Times(AtLeast(1));
   EXPECT_CALL(dynamic_control, OpenCowReader(part.name, {part.source_path}, _))
       .Times(1);
-  EXPECT_CALL(dynamic_control, ListDynamicPartitionsForSlot(_, _))
+  EXPECT_CALL(dynamic_control, ListDynamicPartitionsForSlot(_, _, _))
       .WillRepeatedly(
-          DoAll(SetArgPointee<1, std::vector<std::string>>({part.name}),
+          DoAll(SetArgPointee<2, std::vector<std::string>>({part.name}),
                 Return(true)));
 
   BuildActions(install_plan, &dynamic_control);
diff --git a/payload_consumer/partition_writer_factory_android.cc b/payload_consumer/partition_writer_factory_android.cc
index 0c9f7ea..184e2d5 100644
--- a/payload_consumer/partition_writer_factory_android.cc
+++ b/payload_consumer/partition_writer_factory_android.cc
@@ -30,8 +30,7 @@
     size_t block_size,
     bool is_interactive,
     bool is_dynamic_partition) {
-  if (dynamic_control &&
-      dynamic_control->GetVirtualAbCompressionFeatureFlag().IsEnabled() &&
+  if (dynamic_control && dynamic_control->UpdateUsesSnapshotCompression() &&
       is_dynamic_partition) {
     LOG(INFO)
         << "Virtual AB Compression Enabled, using VABC Partition Writer for `"
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index dc444c7..e3e305b 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -76,7 +76,7 @@
   CHECK(dynamic_control);
 
   // Mount snapshot partitions for Virtual AB Compression Compression.
-  if (dynamic_control->GetVirtualAbCompressionFeatureFlag().IsEnabled()) {
+  if (dynamic_control->UpdateUsesSnapshotCompression()) {
     // Before calling MapAllPartitions to map snapshot devices, all CowWriters
     // must be closed, and MapAllPartitions() should be called.
     dynamic_control->UnmapAllPartitions();
@@ -374,6 +374,8 @@
       } else {
         // Schedules warm reset on next reboot, ignores the error.
         hardware_->SetWarmReset(true);
+        // Sets the vbmeta digest for the other slot to boot into.
+        hardware_->SetVbmetaDigestForInactiveSlot(false);
       }
     } else {
       error_code = ErrorCode::kUpdatedButNotActive;
diff --git a/payload_consumer/vabc_partition_writer.cc b/payload_consumer/vabc_partition_writer.cc
index 2479132..aa8c3ce 100644
--- a/payload_consumer/vabc_partition_writer.cc
+++ b/payload_consumer/vabc_partition_writer.cc
@@ -153,17 +153,23 @@
 void VABCPartitionWriter::CheckpointUpdateProgress(size_t next_op_index) {
   // No need to call fsync/sync, as CowWriter flushes after a label is added
   // added.
+  // if cow_writer_ failed, that means Init() failed. This function shouldn't be
+  // called if Init() fails.
+  TEST_AND_RETURN(cow_writer_ != nullptr);
   cow_writer_->AddLabel(next_op_index);
 }
 
 [[nodiscard]] bool VABCPartitionWriter::FinishedInstallOps() {
   // Add a hardcoded magic label to indicate end of all install ops. This label
   // is needed by filesystem verification, don't remove.
+  TEST_AND_RETURN_FALSE(cow_writer_ != nullptr);
   return cow_writer_->AddLabel(kEndOfInstallLabel);
 }
 
 VABCPartitionWriter::~VABCPartitionWriter() {
-  cow_writer_->Finalize();
+  if (cow_writer_) {
+    cow_writer_->Finalize();
+  }
 }
 
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/delta_diff_generator.cc b/payload_generator/delta_diff_generator.cc
index 74d43fd..74014d9 100644
--- a/payload_generator/delta_diff_generator.cc
+++ b/payload_generator/delta_diff_generator.cc
@@ -106,6 +106,10 @@
     if (!snapshot_enabled || !IsDynamicPartition(new_part_.name)) {
       return;
     }
+    // Skip cow size estimation if VABC isn't enabled
+    if (!config_.target.dynamic_partition_metadata->vabc_enabled()) {
+      return;
+    }
     if (!old_part_.path.empty()) {
       auto generator = MergeSequenceGenerator::Create(*aops_);
       if (!generator || !generator->Generate(cow_merge_sequence_)) {
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc
index a187b32..7288eca 100644
--- a/payload_generator/generate_delta_main.cc
+++ b/payload_generator/generate_delta_main.cc
@@ -421,6 +421,12 @@
               false,
               "The payload only targets a subset of partitions on the device,"
               "e.g. generic kernel image update.");
+  DEFINE_bool(
+      disable_vabc,
+      false,
+      "Whether to disable Virtual AB Compression when installing the OTA");
+  DEFINE_string(
+      apex_info_file, "", "Path to META/apex_info.pb found in target build");
 
   brillo::FlagHelper::Init(
       argc,
@@ -528,6 +534,15 @@
     return 1;
   }
 
+  if (!FLAGS_apex_info_file.empty()) {
+    // apex_info_file should point to a regular file(or symlink to a regular
+    // file)
+    CHECK(utils::FileExists(FLAGS_apex_info_file.c_str()));
+    CHECK(utils::IsRegFile(FLAGS_apex_info_file.c_str()) ||
+          utils::IsSymlink(FLAGS_apex_info_file.c_str()));
+    payload_config.apex_info_file = FLAGS_apex_info_file;
+  }
+
   if (!FLAGS_new_partitions.empty()) {
     LOG_IF(FATAL, !FLAGS_new_image.empty() || !FLAGS_new_kernel.empty())
         << "--new_image and --new_kernel are deprecated, please use "
@@ -584,6 +599,7 @@
   if (FLAGS_is_partial_update) {
     payload_config.is_partial_update = true;
   }
+  payload_config.disable_vabc = FLAGS_disable_vabc;
 
   if (!FLAGS_in_file.empty()) {
     return ApplyPayload(FLAGS_in_file, payload_config) ? 0 : 1;
diff --git a/payload_generator/payload_file.cc b/payload_generator/payload_file.cc
index 33c0749..4334066 100644
--- a/payload_generator/payload_file.cc
+++ b/payload_generator/payload_file.cc
@@ -25,6 +25,7 @@
 #include <base/strings/stringprintf.h>
 
 #include "update_engine/common/hash_calculator.h"
+#include "update_engine/common/utils.h"
 #include "update_engine/payload_consumer/delta_performer.h"
 #include "update_engine/payload_consumer/file_writer.h"
 #include "update_engine/payload_consumer/payload_constants.h"
@@ -71,9 +72,26 @@
     *(manifest_.mutable_dynamic_partition_metadata()) =
         *(config.target.dynamic_partition_metadata);
 
+  if (config.disable_vabc) {
+    manifest_.mutable_dynamic_partition_metadata()->set_vabc_enabled(false);
+  }
   if (config.is_partial_update) {
     manifest_.set_partial_update(true);
   }
+
+  if (!config.apex_info_file.empty()) {
+    ApexMetadata apex_metadata;
+    int fd = open(config.apex_info_file.c_str(), O_RDONLY);
+    if (fd < 0) {
+      PLOG(FATAL) << "Failed to open " << config.apex_info_file << " for read.";
+    }
+    ScopedFdCloser closer{&fd};
+    CHECK(apex_metadata.ParseFromFileDescriptor(fd));
+    if (apex_metadata.apex_info_size() > 0) {
+      *manifest_.mutable_apex_info() =
+          std::move(*apex_metadata.mutable_apex_info());
+    }
+  }
   return true;
 }
 
diff --git a/payload_generator/payload_generation_config.cc b/payload_generator/payload_generation_config.cc
index ef2f240..f5a7062 100644
--- a/payload_generator/payload_generation_config.cc
+++ b/payload_generator/payload_generation_config.cc
@@ -176,6 +176,10 @@
   bool snapshot_enabled = false;
   store.GetBoolean("virtual_ab", &snapshot_enabled);
   metadata->set_snapshot_enabled(snapshot_enabled);
+  bool vabc_enabled = false;
+  if (store.GetBoolean("virtual_ab_compression", &vabc_enabled)) {
+    metadata->set_vabc_enabled(vabc_enabled);
+  }
 
   dynamic_partition_metadata = std::move(metadata);
   return true;
diff --git a/payload_generator/payload_generation_config.h b/payload_generator/payload_generation_config.h
index 1d88101..f7d0b6b 100644
--- a/payload_generator/payload_generation_config.h
+++ b/payload_generator/payload_generation_config.h
@@ -230,6 +230,14 @@
 
   // The maximum timestamp of the OS allowed to apply this payload.
   int64_t max_timestamp = 0;
+
+  // Permit use of VABC by default. Even if this is set to true, the device must
+  // support VABC in order to use it. If this is set to false, device must not
+  // use VABC regardless.
+  bool disable_vabc = false;
+
+  // Path to apex_info.pb, extracted from target_file.zip
+  std::string apex_info_file;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/protobuflint.py b/protobuflint.py
new file mode 100755
index 0000000..02ccdbe
--- /dev/null
+++ b/protobuflint.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2021 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.
+#
+
+"""CLI script for linting .proto files inside update_engine."""
+
+import sys
+import re
+import subprocess
+
+def check_proto_file(commit_hash, filename):
+  """Check if |filename| is consistnet with our protobuf guidelines
+
+    Args:
+      commit_hash: Hash of the git commit to look
+      filename: A filesystem path to a .proto file
+    Returns:
+      True if this file passes linting check, False otherwise
+  """
+  output = subprocess.check_output(
+      ["git", "diff", commit_hash+"~", commit_hash, "--", filename])
+  output = output.decode()
+  p = re.compile(r"^[+]?\s*required .*$", re.M)
+  m = p.search(output)
+  if m:
+    print("File", filename,
+          "contains 'required' keyword. Usage of required",
+          "is strongly discouraged in protobuf", m.group())
+    return False
+  return True
+
+def main():
+  if len(sys.argv) < 2:
+    print("Usage:", sys.argv[0], "commit_hash", "<file1>", "<file2>", "...")
+    sys.exit(1)
+  commit_hash = sys.argv[1]
+  for filename in sys.argv[2:]:
+    if filename.endswith(".proto"):
+      if not check_proto_file(commit_hash, filename):
+        sys.exit(1)
+
+if __name__ == "__main__":
+  main()
diff --git a/scripts/brillo_update_payload b/scripts/brillo_update_payload
index 1e729bd..c510148 100755
--- a/scripts/brillo_update_payload
+++ b/scripts/brillo_update_payload
@@ -200,6 +200,8 @@
     "Optional: True if the payload is for partial update. i.e. it only updates \
 a subset of partitions on device."
   DEFINE_string full_boot "" "Will include full boot image"
+  DEFINE_string disable_vabc "" \
+    "Optional: Disables Virtual AB Compression when installing the OTA"
 fi
 if [[ "${COMMAND}" == "hash" || "${COMMAND}" == "sign" ]]; then
   DEFINE_string unsigned_payload "" "Path to the input unsigned payload."
@@ -282,6 +284,9 @@
 # Path to the dynamic partition info file in target image if exists.
 DYNAMIC_PARTITION_INFO_FILE=""
 
+# Path to the META/apex_info.pb found in target build
+APEX_INFO_FILE=""
+
 # read_option_int <file.txt> <option_key> [default_value]
 #
 # Reads the unsigned integer value associated with |option_key| in a key=value
@@ -565,6 +570,13 @@
         "${dynamic_partitions_info}"
       DYNAMIC_PARTITION_INFO_FILE="${dynamic_partitions_info}"
     fi
+    local apex_info=$(create_tempfile "apex_info.XXXXXX")
+    CLEANUP_FILES+=("${apex_info}")
+    if unzip -l "${image}" "META/apex_info.pb" > /dev/null; then
+      extract_file "${image}" "META/apex_info.pb" \
+        "${apex_info}"
+      APEX_INFO_FILE="${apex_info}"
+    fi
   fi
 
   local part
@@ -694,6 +706,10 @@
       GENERATOR_ARGS+=(
         --disable_verity_computation="${FLAGS_disable_verity_computation}" )
     fi
+    if [[ -n "${FLAGS_disable_vabc}" ]]; then
+      GENERATOR_ARGS+=(
+        --disable_vabc="${FLAGS_disable_vabc}" )
+    fi
   fi
 
   # minor version is set only for delta or partial payload.
@@ -728,6 +744,11 @@
       --dynamic_partition_info_file="${DYNAMIC_PARTITION_INFO_FILE}"
     )
   fi
+  if [[ -n "{APEX_INFO_FILE}" ]]; then
+    GENERATOR_ARGS+=(
+      --apex_info_file="${APEX_INFO_FILE}"
+    )
+  fi
 
   echo "Running delta_generator with args: ${GENERATOR_ARGS[@]}"
   "${GENERATOR}" "${GENERATOR_ARGS[@]}"
diff --git a/scripts/update_payload/update_metadata_pb2.py b/scripts/update_payload/update_metadata_pb2.py
index ea4bc59..1641229 100644
--- a/scripts/update_payload/update_metadata_pb2.py
+++ b/scripts/update_payload/update_metadata_pb2.py
@@ -2,6 +2,8 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: update_metadata.proto
 
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
 from google.protobuf import descriptor as _descriptor
 from google.protobuf import message as _message
 from google.protobuf import reflection as _reflection
@@ -17,8 +19,8 @@
   name='update_metadata.proto',
   package='chromeos_update_engine',
   syntax='proto2',
-  serialized_options=b'H\003',
-  serialized_pb=b'\n\x15update_metadata.proto\x12\x16\x63hromeos_update_engine\"1\n\x06\x45xtent\x12\x13\n\x0bstart_block\x18\x01 \x01(\x04\x12\x12\n\nnum_blocks\x18\x02 \x01(\x04\"\x9f\x01\n\nSignatures\x12@\n\nsignatures\x18\x01 \x03(\x0b\x32,.chromeos_update_engine.Signatures.Signature\x1aO\n\tSignature\x12\x13\n\x07version\x18\x01 \x01(\rB\x02\x18\x01\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x1f\n\x17unpadded_signature_size\x18\x03 \x01(\x07\"+\n\rPartitionInfo\x12\x0c\n\x04size\x18\x01 \x01(\x04\x12\x0c\n\x04hash\x18\x02 \x01(\x0c\"w\n\tImageInfo\x12\r\n\x05\x62oard\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\t\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\t\x12\x0f\n\x07version\x18\x04 \x01(\t\x12\x15\n\rbuild_channel\x18\x05 \x01(\t\x12\x15\n\rbuild_version\x18\x06 \x01(\t\"\xee\x03\n\x10InstallOperation\x12;\n\x04type\x18\x01 \x02(\x0e\x32-.chromeos_update_engine.InstallOperation.Type\x12\x13\n\x0b\x64\x61ta_offset\x18\x02 \x01(\x04\x12\x13\n\x0b\x64\x61ta_length\x18\x03 \x01(\x04\x12\x33\n\x0bsrc_extents\x18\x04 \x03(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x12\n\nsrc_length\x18\x05 \x01(\x04\x12\x33\n\x0b\x64st_extents\x18\x06 \x03(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x12\n\ndst_length\x18\x07 \x01(\x04\x12\x18\n\x10\x64\x61ta_sha256_hash\x18\x08 \x01(\x0c\x12\x17\n\x0fsrc_sha256_hash\x18\t \x01(\x0c\"\xad\x01\n\x04Type\x12\x0b\n\x07REPLACE\x10\x00\x12\x0e\n\nREPLACE_BZ\x10\x01\x12\x0c\n\x04MOVE\x10\x02\x1a\x02\x08\x01\x12\x0e\n\x06\x42SDIFF\x10\x03\x1a\x02\x08\x01\x12\x0f\n\x0bSOURCE_COPY\x10\x04\x12\x11\n\rSOURCE_BSDIFF\x10\x05\x12\x0e\n\nREPLACE_XZ\x10\x08\x12\x08\n\x04ZERO\x10\x06\x12\x0b\n\x07\x44ISCARD\x10\x07\x12\x11\n\rBROTLI_BSDIFF\x10\n\x12\x0c\n\x08PUFFDIFF\x10\t\"\xcf\x01\n\x11\x43owMergeOperation\x12<\n\x04type\x18\x01 \x01(\x0e\x32..chromeos_update_engine.CowMergeOperation.Type\x12\x32\n\nsrc_extent\x18\x02 \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x32\n\ndst_extent\x18\x03 \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\"\x14\n\x04Type\x12\x0c\n\x08\x43OW_COPY\x10\x00\"\xc8\x06\n\x0fPartitionUpdate\x12\x16\n\x0epartition_name\x18\x01 \x02(\t\x12\x17\n\x0frun_postinstall\x18\x02 \x01(\x08\x12\x18\n\x10postinstall_path\x18\x03 \x01(\t\x12\x17\n\x0f\x66ilesystem_type\x18\x04 \x01(\t\x12M\n\x17new_partition_signature\x18\x05 \x03(\x0b\x32,.chromeos_update_engine.Signatures.Signature\x12\x41\n\x12old_partition_info\x18\x06 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12\x41\n\x12new_partition_info\x18\x07 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12<\n\noperations\x18\x08 \x03(\x0b\x32(.chromeos_update_engine.InstallOperation\x12\x1c\n\x14postinstall_optional\x18\t \x01(\x08\x12=\n\x15hash_tree_data_extent\x18\n \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x38\n\x10hash_tree_extent\x18\x0b \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x1b\n\x13hash_tree_algorithm\x18\x0c \x01(\t\x12\x16\n\x0ehash_tree_salt\x18\r \x01(\x0c\x12\x37\n\x0f\x66\x65\x63_data_extent\x18\x0e \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x32\n\nfec_extent\x18\x0f \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x14\n\tfec_roots\x18\x10 \x01(\r:\x01\x32\x12\x0f\n\x07version\x18\x11 \x01(\t\x12\x43\n\x10merge_operations\x18\x12 \x03(\x0b\x32).chromeos_update_engine.CowMergeOperation\x12\x19\n\x11\x65stimate_cow_size\x18\x13 \x01(\x04\"L\n\x15\x44ynamicPartitionGroup\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0c\n\x04size\x18\x02 \x01(\x04\x12\x17\n\x0fpartition_names\x18\x03 \x03(\t\"s\n\x18\x44ynamicPartitionMetadata\x12=\n\x06groups\x18\x01 \x03(\x0b\x32-.chromeos_update_engine.DynamicPartitionGroup\x12\x18\n\x10snapshot_enabled\x18\x02 \x01(\x08\"\xe1\x06\n\x14\x44\x65ltaArchiveManifest\x12H\n\x12install_operations\x18\x01 \x03(\x0b\x32(.chromeos_update_engine.InstallOperationB\x02\x18\x01\x12O\n\x19kernel_install_operations\x18\x02 \x03(\x0b\x32(.chromeos_update_engine.InstallOperationB\x02\x18\x01\x12\x18\n\nblock_size\x18\x03 \x01(\r:\x04\x34\x30\x39\x36\x12\x19\n\x11signatures_offset\x18\x04 \x01(\x04\x12\x17\n\x0fsignatures_size\x18\x05 \x01(\x04\x12\x42\n\x0fold_kernel_info\x18\x06 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12\x42\n\x0fnew_kernel_info\x18\x07 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12\x42\n\x0fold_rootfs_info\x18\x08 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12\x42\n\x0fnew_rootfs_info\x18\t \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12\x39\n\x0eold_image_info\x18\n \x01(\x0b\x32!.chromeos_update_engine.ImageInfo\x12\x39\n\x0enew_image_info\x18\x0b \x01(\x0b\x32!.chromeos_update_engine.ImageInfo\x12\x18\n\rminor_version\x18\x0c \x01(\r:\x01\x30\x12;\n\npartitions\x18\r \x03(\x0b\x32\'.chromeos_update_engine.PartitionUpdate\x12\x15\n\rmax_timestamp\x18\x0e \x01(\x03\x12T\n\x1a\x64ynamic_partition_metadata\x18\x0f \x01(\x0b\x32\x30.chromeos_update_engine.DynamicPartitionMetadata\x12\x16\n\x0epartial_update\x18\x10 \x01(\x08\x42\x02H\x03'
+  serialized_options=_b('H\003'),
+  serialized_pb=_b('\n\x15update_metadata.proto\x12\x16\x63hromeos_update_engine\"1\n\x06\x45xtent\x12\x13\n\x0bstart_block\x18\x01 \x01(\x04\x12\x12\n\nnum_blocks\x18\x02 \x01(\x04\"\x9f\x01\n\nSignatures\x12@\n\nsignatures\x18\x01 \x03(\x0b\x32,.chromeos_update_engine.Signatures.Signature\x1aO\n\tSignature\x12\x13\n\x07version\x18\x01 \x01(\rB\x02\x18\x01\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x1f\n\x17unpadded_signature_size\x18\x03 \x01(\x07\"+\n\rPartitionInfo\x12\x0c\n\x04size\x18\x01 \x01(\x04\x12\x0c\n\x04hash\x18\x02 \x01(\x0c\"\x8f\x01\n\tImageInfo\x12\x11\n\x05\x62oard\x18\x01 \x01(\tB\x02\x18\x01\x12\x0f\n\x03key\x18\x02 \x01(\tB\x02\x18\x01\x12\x13\n\x07\x63hannel\x18\x03 \x01(\tB\x02\x18\x01\x12\x13\n\x07version\x18\x04 \x01(\tB\x02\x18\x01\x12\x19\n\rbuild_channel\x18\x05 \x01(\tB\x02\x18\x01\x12\x19\n\rbuild_version\x18\x06 \x01(\tB\x02\x18\x01\"\xee\x03\n\x10InstallOperation\x12;\n\x04type\x18\x01 \x02(\x0e\x32-.chromeos_update_engine.InstallOperation.Type\x12\x13\n\x0b\x64\x61ta_offset\x18\x02 \x01(\x04\x12\x13\n\x0b\x64\x61ta_length\x18\x03 \x01(\x04\x12\x33\n\x0bsrc_extents\x18\x04 \x03(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x12\n\nsrc_length\x18\x05 \x01(\x04\x12\x33\n\x0b\x64st_extents\x18\x06 \x03(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x12\n\ndst_length\x18\x07 \x01(\x04\x12\x18\n\x10\x64\x61ta_sha256_hash\x18\x08 \x01(\x0c\x12\x17\n\x0fsrc_sha256_hash\x18\t \x01(\x0c\"\xad\x01\n\x04Type\x12\x0b\n\x07REPLACE\x10\x00\x12\x0e\n\nREPLACE_BZ\x10\x01\x12\x0c\n\x04MOVE\x10\x02\x1a\x02\x08\x01\x12\x0e\n\x06\x42SDIFF\x10\x03\x1a\x02\x08\x01\x12\x0f\n\x0bSOURCE_COPY\x10\x04\x12\x11\n\rSOURCE_BSDIFF\x10\x05\x12\x0e\n\nREPLACE_XZ\x10\x08\x12\x08\n\x04ZERO\x10\x06\x12\x0b\n\x07\x44ISCARD\x10\x07\x12\x11\n\rBROTLI_BSDIFF\x10\n\x12\x0c\n\x08PUFFDIFF\x10\t\"\xcf\x01\n\x11\x43owMergeOperation\x12<\n\x04type\x18\x01 \x01(\x0e\x32..chromeos_update_engine.CowMergeOperation.Type\x12\x32\n\nsrc_extent\x18\x02 \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x32\n\ndst_extent\x18\x03 \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\"\x14\n\x04Type\x12\x0c\n\x08\x43OW_COPY\x10\x00\"\xc8\x06\n\x0fPartitionUpdate\x12\x16\n\x0epartition_name\x18\x01 \x02(\t\x12\x17\n\x0frun_postinstall\x18\x02 \x01(\x08\x12\x18\n\x10postinstall_path\x18\x03 \x01(\t\x12\x17\n\x0f\x66ilesystem_type\x18\x04 \x01(\t\x12M\n\x17new_partition_signature\x18\x05 \x03(\x0b\x32,.chromeos_update_engine.Signatures.Signature\x12\x41\n\x12old_partition_info\x18\x06 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12\x41\n\x12new_partition_info\x18\x07 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12<\n\noperations\x18\x08 \x03(\x0b\x32(.chromeos_update_engine.InstallOperation\x12\x1c\n\x14postinstall_optional\x18\t \x01(\x08\x12=\n\x15hash_tree_data_extent\x18\n \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x38\n\x10hash_tree_extent\x18\x0b \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x1b\n\x13hash_tree_algorithm\x18\x0c \x01(\t\x12\x16\n\x0ehash_tree_salt\x18\r \x01(\x0c\x12\x37\n\x0f\x66\x65\x63_data_extent\x18\x0e \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x32\n\nfec_extent\x18\x0f \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x14\n\tfec_roots\x18\x10 \x01(\r:\x01\x32\x12\x0f\n\x07version\x18\x11 \x01(\t\x12\x43\n\x10merge_operations\x18\x12 \x03(\x0b\x32).chromeos_update_engine.CowMergeOperation\x12\x19\n\x11\x65stimate_cow_size\x18\x13 \x01(\x04\"L\n\x15\x44ynamicPartitionGroup\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0c\n\x04size\x18\x02 \x01(\x04\x12\x17\n\x0fpartition_names\x18\x03 \x03(\t\"\x89\x01\n\x18\x44ynamicPartitionMetadata\x12=\n\x06groups\x18\x01 \x03(\x0b\x32-.chromeos_update_engine.DynamicPartitionGroup\x12\x18\n\x10snapshot_enabled\x18\x02 \x01(\x08\x12\x14\n\x0cvabc_enabled\x18\x03 \x01(\x08\"c\n\x08\x41pexInfo\x12\x14\n\x0cpackage_name\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\x03\x12\x15\n\ris_compressed\x18\x03 \x01(\x08\x12\x19\n\x11\x64\x65\x63ompressed_size\x18\x04 \x01(\x03\"C\n\x0c\x41pexMetadata\x12\x33\n\tapex_info\x18\x01 \x03(\x0b\x32 .chromeos_update_engine.ApexInfo\"\x9e\x07\n\x14\x44\x65ltaArchiveManifest\x12H\n\x12install_operations\x18\x01 \x03(\x0b\x32(.chromeos_update_engine.InstallOperationB\x02\x18\x01\x12O\n\x19kernel_install_operations\x18\x02 \x03(\x0b\x32(.chromeos_update_engine.InstallOperationB\x02\x18\x01\x12\x18\n\nblock_size\x18\x03 \x01(\r:\x04\x34\x30\x39\x36\x12\x19\n\x11signatures_offset\x18\x04 \x01(\x04\x12\x17\n\x0fsignatures_size\x18\x05 \x01(\x04\x12\x42\n\x0fold_kernel_info\x18\x06 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12\x42\n\x0fnew_kernel_info\x18\x07 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12\x42\n\x0fold_rootfs_info\x18\x08 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12\x42\n\x0fnew_rootfs_info\x18\t \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12=\n\x0eold_image_info\x18\n \x01(\x0b\x32!.chromeos_update_engine.ImageInfoB\x02\x18\x01\x12=\n\x0enew_image_info\x18\x0b \x01(\x0b\x32!.chromeos_update_engine.ImageInfoB\x02\x18\x01\x12\x18\n\rminor_version\x18\x0c \x01(\r:\x01\x30\x12;\n\npartitions\x18\r \x03(\x0b\x32\'.chromeos_update_engine.PartitionUpdate\x12\x15\n\rmax_timestamp\x18\x0e \x01(\x03\x12T\n\x1a\x64ynamic_partition_metadata\x18\x0f \x01(\x0b\x32\x30.chromeos_update_engine.DynamicPartitionMetadata\x12\x16\n\x0epartial_update\x18\x10 \x01(\x08\x12\x33\n\tapex_info\x18\x11 \x03(\x0b\x32 .chromeos_update_engine.ApexInfoB\x02H\x03')
 )
 
 
@@ -39,11 +41,11 @@
       type=None),
     _descriptor.EnumValueDescriptor(
       name='MOVE', index=2, number=2,
-      serialized_options=b'\010\001',
+      serialized_options=_b('\010\001'),
       type=None),
     _descriptor.EnumValueDescriptor(
       name='BSDIFF', index=3, number=3,
-      serialized_options=b'\010\001',
+      serialized_options=_b('\010\001'),
       type=None),
     _descriptor.EnumValueDescriptor(
       name='SOURCE_COPY', index=4, number=4,
@@ -76,8 +78,8 @@
   ],
   containing_type=None,
   serialized_options=None,
-  serialized_start=750,
-  serialized_end=923,
+  serialized_start=775,
+  serialized_end=948,
 )
 _sym_db.RegisterEnumDescriptor(_INSTALLOPERATION_TYPE)
 
@@ -94,8 +96,8 @@
   ],
   containing_type=None,
   serialized_options=None,
-  serialized_start=1113,
-  serialized_end=1133,
+  serialized_start=1138,
+  serialized_end=1158,
 )
 _sym_db.RegisterEnumDescriptor(_COWMERGEOPERATION_TYPE)
 
@@ -151,11 +153,11 @@
       has_default_value=False, default_value=0,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=b'\030\001', file=DESCRIPTOR),
+      serialized_options=_b('\030\001'), file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='data', full_name='chromeos_update_engine.Signatures.Signature.data', index=1,
       number=2, type=12, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"",
+      has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
@@ -230,7 +232,7 @@
     _descriptor.FieldDescriptor(
       name='hash', full_name='chromeos_update_engine.PartitionInfo.hash', index=1,
       number=2, type=12, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"",
+      has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
@@ -261,45 +263,45 @@
     _descriptor.FieldDescriptor(
       name='board', full_name='chromeos_update_engine.ImageInfo.board', index=0,
       number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
+      serialized_options=_b('\030\001'), file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='key', full_name='chromeos_update_engine.ImageInfo.key', index=1,
       number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
+      serialized_options=_b('\030\001'), file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='channel', full_name='chromeos_update_engine.ImageInfo.channel', index=2,
       number=3, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
+      serialized_options=_b('\030\001'), file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='version', full_name='chromeos_update_engine.ImageInfo.version', index=3,
       number=4, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
+      serialized_options=_b('\030\001'), file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='build_channel', full_name='chromeos_update_engine.ImageInfo.build_channel', index=4,
       number=5, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
+      serialized_options=_b('\030\001'), file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='build_version', full_name='chromeos_update_engine.ImageInfo.build_version', index=5,
       number=6, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
+      serialized_options=_b('\030\001'), file=DESCRIPTOR),
   ],
   extensions=[
   ],
@@ -312,8 +314,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=307,
-  serialized_end=426,
+  serialized_start=308,
+  serialized_end=451,
 )
 
 
@@ -376,14 +378,14 @@
     _descriptor.FieldDescriptor(
       name='data_sha256_hash', full_name='chromeos_update_engine.InstallOperation.data_sha256_hash', index=7,
       number=8, type=12, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"",
+      has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='src_sha256_hash', full_name='chromeos_update_engine.InstallOperation.src_sha256_hash', index=8,
       number=9, type=12, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"",
+      has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
@@ -400,8 +402,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=429,
-  serialized_end=923,
+  serialized_start=454,
+  serialized_end=948,
 )
 
 
@@ -446,8 +448,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=926,
-  serialized_end=1133,
+  serialized_start=951,
+  serialized_end=1158,
 )
 
 
@@ -461,7 +463,7 @@
     _descriptor.FieldDescriptor(
       name='partition_name', full_name='chromeos_update_engine.PartitionUpdate.partition_name', index=0,
       number=1, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=b"".decode('utf-8'),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
@@ -475,14 +477,14 @@
     _descriptor.FieldDescriptor(
       name='postinstall_path', full_name='chromeos_update_engine.PartitionUpdate.postinstall_path', index=2,
       number=3, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='filesystem_type', full_name='chromeos_update_engine.PartitionUpdate.filesystem_type', index=3,
       number=4, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
@@ -538,14 +540,14 @@
     _descriptor.FieldDescriptor(
       name='hash_tree_algorithm', full_name='chromeos_update_engine.PartitionUpdate.hash_tree_algorithm', index=11,
       number=12, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='hash_tree_salt', full_name='chromeos_update_engine.PartitionUpdate.hash_tree_salt', index=12,
       number=13, type=12, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"",
+      has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
@@ -573,7 +575,7 @@
     _descriptor.FieldDescriptor(
       name='version', full_name='chromeos_update_engine.PartitionUpdate.version', index=16,
       number=17, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
@@ -603,8 +605,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1136,
-  serialized_end=1976,
+  serialized_start=1161,
+  serialized_end=2001,
 )
 
 
@@ -618,7 +620,7 @@
     _descriptor.FieldDescriptor(
       name='name', full_name='chromeos_update_engine.DynamicPartitionGroup.name', index=0,
       number=1, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=b"".decode('utf-8'),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
@@ -648,8 +650,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=1978,
-  serialized_end=2054,
+  serialized_start=2003,
+  serialized_end=2079,
 )
 
 
@@ -674,6 +676,13 @@
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='vabc_enabled', full_name='chromeos_update_engine.DynamicPartitionMetadata.vabc_enabled', index=2,
+      number=3, type=8, cpp_type=7, label=1,
+      has_default_value=False, default_value=False,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
   ],
   extensions=[
   ],
@@ -686,8 +695,91 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2056,
-  serialized_end=2171,
+  serialized_start=2082,
+  serialized_end=2219,
+)
+
+
+_APEXINFO = _descriptor.Descriptor(
+  name='ApexInfo',
+  full_name='chromeos_update_engine.ApexInfo',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='package_name', full_name='chromeos_update_engine.ApexInfo.package_name', index=0,
+      number=1, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='version', full_name='chromeos_update_engine.ApexInfo.version', index=1,
+      number=2, type=3, cpp_type=2, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='is_compressed', full_name='chromeos_update_engine.ApexInfo.is_compressed', index=2,
+      number=3, type=8, cpp_type=7, label=1,
+      has_default_value=False, default_value=False,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='decompressed_size', full_name='chromeos_update_engine.ApexInfo.decompressed_size', index=3,
+      number=4, type=3, cpp_type=2, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=2221,
+  serialized_end=2320,
+)
+
+
+_APEXMETADATA = _descriptor.Descriptor(
+  name='ApexMetadata',
+  full_name='chromeos_update_engine.ApexMetadata',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='apex_info', full_name='chromeos_update_engine.ApexMetadata.apex_info', index=0,
+      number=1, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=2322,
+  serialized_end=2389,
 )
 
 
@@ -704,14 +796,14 @@
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=b'\030\001', file=DESCRIPTOR),
+      serialized_options=_b('\030\001'), file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='kernel_install_operations', full_name='chromeos_update_engine.DeltaArchiveManifest.kernel_install_operations', index=1,
       number=2, type=11, cpp_type=10, label=3,
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=b'\030\001', file=DESCRIPTOR),
+      serialized_options=_b('\030\001'), file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='block_size', full_name='chromeos_update_engine.DeltaArchiveManifest.block_size', index=2,
       number=3, type=13, cpp_type=3, label=1,
@@ -739,42 +831,42 @@
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=b'\030\001', file=DESCRIPTOR),
+      serialized_options=_b('\030\001'), file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='new_kernel_info', full_name='chromeos_update_engine.DeltaArchiveManifest.new_kernel_info', index=6,
       number=7, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=b'\030\001', file=DESCRIPTOR),
+      serialized_options=_b('\030\001'), file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='old_rootfs_info', full_name='chromeos_update_engine.DeltaArchiveManifest.old_rootfs_info', index=7,
       number=8, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=b'\030\001', file=DESCRIPTOR),
+      serialized_options=_b('\030\001'), file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='new_rootfs_info', full_name='chromeos_update_engine.DeltaArchiveManifest.new_rootfs_info', index=8,
       number=9, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=b'\030\001', file=DESCRIPTOR),
+      serialized_options=_b('\030\001'), file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='old_image_info', full_name='chromeos_update_engine.DeltaArchiveManifest.old_image_info', index=9,
       number=10, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
+      serialized_options=_b('\030\001'), file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='new_image_info', full_name='chromeos_update_engine.DeltaArchiveManifest.new_image_info', index=10,
       number=11, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
+      serialized_options=_b('\030\001'), file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='minor_version', full_name='chromeos_update_engine.DeltaArchiveManifest.minor_version', index=11,
       number=12, type=13, cpp_type=3, label=1,
@@ -810,6 +902,13 @@
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='apex_info', full_name='chromeos_update_engine.DeltaArchiveManifest.apex_info', index=16,
+      number=17, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
   ],
   extensions=[
   ],
@@ -822,8 +921,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=2174,
-  serialized_end=3039,
+  serialized_start=2392,
+  serialized_end=3318,
 )
 
 _SIGNATURES_SIGNATURE.containing_type = _SIGNATURES
@@ -846,6 +945,7 @@
 _PARTITIONUPDATE.fields_by_name['fec_extent'].message_type = _EXTENT
 _PARTITIONUPDATE.fields_by_name['merge_operations'].message_type = _COWMERGEOPERATION
 _DYNAMICPARTITIONMETADATA.fields_by_name['groups'].message_type = _DYNAMICPARTITIONGROUP
+_APEXMETADATA.fields_by_name['apex_info'].message_type = _APEXINFO
 _DELTAARCHIVEMANIFEST.fields_by_name['install_operations'].message_type = _INSTALLOPERATION
 _DELTAARCHIVEMANIFEST.fields_by_name['kernel_install_operations'].message_type = _INSTALLOPERATION
 _DELTAARCHIVEMANIFEST.fields_by_name['old_kernel_info'].message_type = _PARTITIONINFO
@@ -856,6 +956,7 @@
 _DELTAARCHIVEMANIFEST.fields_by_name['new_image_info'].message_type = _IMAGEINFO
 _DELTAARCHIVEMANIFEST.fields_by_name['partitions'].message_type = _PARTITIONUPDATE
 _DELTAARCHIVEMANIFEST.fields_by_name['dynamic_partition_metadata'].message_type = _DYNAMICPARTITIONMETADATA
+_DELTAARCHIVEMANIFEST.fields_by_name['apex_info'].message_type = _APEXINFO
 DESCRIPTOR.message_types_by_name['Extent'] = _EXTENT
 DESCRIPTOR.message_types_by_name['Signatures'] = _SIGNATURES
 DESCRIPTOR.message_types_by_name['PartitionInfo'] = _PARTITIONINFO
@@ -865,6 +966,8 @@
 DESCRIPTOR.message_types_by_name['PartitionUpdate'] = _PARTITIONUPDATE
 DESCRIPTOR.message_types_by_name['DynamicPartitionGroup'] = _DYNAMICPARTITIONGROUP
 DESCRIPTOR.message_types_by_name['DynamicPartitionMetadata'] = _DYNAMICPARTITIONMETADATA
+DESCRIPTOR.message_types_by_name['ApexInfo'] = _APEXINFO
+DESCRIPTOR.message_types_by_name['ApexMetadata'] = _APEXMETADATA
 DESCRIPTOR.message_types_by_name['DeltaArchiveManifest'] = _DELTAARCHIVEMANIFEST
 _sym_db.RegisterFileDescriptor(DESCRIPTOR)
 
@@ -939,6 +1042,20 @@
   })
 _sym_db.RegisterMessage(DynamicPartitionMetadata)
 
+ApexInfo = _reflection.GeneratedProtocolMessageType('ApexInfo', (_message.Message,), {
+  'DESCRIPTOR' : _APEXINFO,
+  '__module__' : 'update_metadata_pb2'
+  # @@protoc_insertion_point(class_scope:chromeos_update_engine.ApexInfo)
+  })
+_sym_db.RegisterMessage(ApexInfo)
+
+ApexMetadata = _reflection.GeneratedProtocolMessageType('ApexMetadata', (_message.Message,), {
+  'DESCRIPTOR' : _APEXMETADATA,
+  '__module__' : 'update_metadata_pb2'
+  # @@protoc_insertion_point(class_scope:chromeos_update_engine.ApexMetadata)
+  })
+_sym_db.RegisterMessage(ApexMetadata)
+
 DeltaArchiveManifest = _reflection.GeneratedProtocolMessageType('DeltaArchiveManifest', (_message.Message,), {
   'DESCRIPTOR' : _DELTAARCHIVEMANIFEST,
   '__module__' : 'update_metadata_pb2'
@@ -949,6 +1066,12 @@
 
 DESCRIPTOR._options = None
 _SIGNATURES_SIGNATURE.fields_by_name['version']._options = None
+_IMAGEINFO.fields_by_name['board']._options = None
+_IMAGEINFO.fields_by_name['key']._options = None
+_IMAGEINFO.fields_by_name['channel']._options = None
+_IMAGEINFO.fields_by_name['version']._options = None
+_IMAGEINFO.fields_by_name['build_channel']._options = None
+_IMAGEINFO.fields_by_name['build_version']._options = None
 _INSTALLOPERATION_TYPE.values_by_name["MOVE"]._options = None
 _INSTALLOPERATION_TYPE.values_by_name["BSDIFF"]._options = None
 _DELTAARCHIVEMANIFEST.fields_by_name['install_operations']._options = None
@@ -957,4 +1080,6 @@
 _DELTAARCHIVEMANIFEST.fields_by_name['new_kernel_info']._options = None
 _DELTAARCHIVEMANIFEST.fields_by_name['old_rootfs_info']._options = None
 _DELTAARCHIVEMANIFEST.fields_by_name['new_rootfs_info']._options = None
+_DELTAARCHIVEMANIFEST.fields_by_name['old_image_info']._options = None
+_DELTAARCHIVEMANIFEST.fields_by_name['new_image_info']._options = None
 # @@protoc_insertion_point(module_scope)
diff --git a/stable/Android.bp b/stable/Android.bp
index 2b6a318..5e54e9a 100644
--- a/stable/Android.bp
+++ b/stable/Android.bp
@@ -16,6 +16,15 @@
 
 // Stable AIDL interface between update_engine and other APEXes
 // ========================================================
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_update_engine_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_update_engine_license"],
+}
+
 aidl_interface {
     name: "libupdate_engine_stable",
 
@@ -58,7 +67,7 @@
     ],
     static_libs: [
         "libgflags",
-        "libupdate_engine_stable-ndk_platform",
+        "libupdate_engine_stable-V1-ndk_platform",
     ],
     srcs: [
         "update_engine_stable_client.cc",
diff --git a/update_metadata.proto b/update_metadata.proto
index 452b89d..a1f093c 100644
--- a/update_metadata.proto
+++ b/update_metadata.proto
@@ -348,6 +348,27 @@
   // partitions if possible. If this is unset, the update_engine daemon MUST
   // NOT create snapshots for dynamic partitions.
   optional bool snapshot_enabled = 2;
+
+  // If this is set to false, update_engine should not use VABC regardless. If
+  // this is set to true, update_engine may choose to use VABC if device
+  // supports it, but not guaranteed.
+  // VABC stands for Virtual AB Compression
+  optional bool vabc_enabled = 3;
+}
+
+// Definition has been duplicated from
+// $ANDROID_BUILD_TOP/build/tools/releasetools/ota_metadata.proto. Keep in sync.
+message ApexInfo {
+  optional string package_name = 1;
+  optional int64 version = 2;
+  optional bool is_compressed = 3;
+  optional int64 decompressed_size = 4;
+}
+
+// Definition has been duplicated from
+// $ANDROID_BUILD_TOP/build/tools/releasetools/ota_metadata.proto. Keep in sync.
+message ApexMetadata {
+  repeated ApexInfo apex_info = 1;
 }
 
 message DeltaArchiveManifest {
@@ -401,4 +422,8 @@
 
   // If the payload only updates a subset of partitions on the device.
   optional bool partial_update = 16;
+
+  // Information on compressed APEX to figure out how much space is required for
+  // their decompression
+  repeated ApexInfo apex_info = 17;
 }