ResetStatus deletes snapshots for VAB.

When ResetStatus() is called, delete snapshots to free up space on
VAB devices.

On regular A/B, there is no need to cancel the update completely so that
the update may be resumed next time. However, on VAB devices, a
cancelled update should not occupy previously allocated space.

Test: manually call update_engine_client --cancel when an update is in progress, then
      call update_engine_client --reset_status, then
      call snapshotctl dump to check that snapshots are deleted.
      Then, re-apply the update.

Test: manually kill update_engine when an update is in progress, then
      call update_engine_client --reset_status, then
      call snapshotctl dump to check that snapshots are deleted.
      Then, re-apply the update.

Test: manually apply an update until UPDATED_NEED_REBOOT, then
      call update_engine_client --reset_status, then
      call snapshotctl dump to check that snapshots are deleted.
      Then, re-apply the update.

Test: manually apply an update until UPDATED_NEED_REBOOT, then
      kill update_engine, then
      call update_engine_client --reset_status, then
      call snapshotctl dump to check that snapshots are deleted.
      Then, re-apply the update.

Bug: 147696014

Change-Id: Ic7049772091a34e0e666fd7ae361ef474d5a28aa
Merged-In: Ic7049772091a34e0e666fd7ae361ef474d5a28aa
diff --git a/common/dynamic_partition_control_interface.h b/common/dynamic_partition_control_interface.h
index a878f2e..d5db9bc 100644
--- a/common/dynamic_partition_control_interface.h
+++ b/common/dynamic_partition_control_interface.h
@@ -98,10 +98,26 @@
   // - If any error, but caller should retry after reboot, action completes with
   //   kError.
   // - If any irrecoverable failures, action completes with kDeviceCorrupted.
+  //
+  // See ResetUpdate for differences between CleanuPreviousUpdateAction and
+  // ResetUpdate.
   virtual std::unique_ptr<AbstractAction> GetCleanupPreviousUpdateAction(
       BootControlInterface* boot_control,
       PrefsInterface* prefs,
       CleanupPreviousUpdateActionDelegateInterface* delegate) = 0;
+
+  // Called after an unwanted payload has been successfully applied and the
+  // device has not yet been rebooted.
+  //
+  // For snapshot updates (Virtual A/B), it calls
+  // DeltaPerformer::ResetUpdateProgress(false /* quick */) and
+  // frees previously allocated space; the next update will need to be
+  // started over.
+  //
+  // Note: CleanupPreviousUpdateAction does not do anything if an update is in
+  // progress, while ResetUpdate() forcefully free previously
+  // allocated space for snapshot updates.
+  virtual bool ResetUpdate(PrefsInterface* prefs) = 0;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/common/dynamic_partition_control_stub.cc b/common/dynamic_partition_control_stub.cc
index dee4555..1239eab 100644
--- a/common/dynamic_partition_control_stub.cc
+++ b/common/dynamic_partition_control_stub.cc
@@ -63,4 +63,8 @@
   return std::make_unique<NoOpAction>();
 }
 
+bool DynamicPartitionControlStub::ResetUpdate(PrefsInterface* prefs) {
+  return false;
+}
+
 }  // namespace chromeos_update_engine
diff --git a/common/dynamic_partition_control_stub.h b/common/dynamic_partition_control_stub.h
index ddef36d..679d028 100644
--- a/common/dynamic_partition_control_stub.h
+++ b/common/dynamic_partition_control_stub.h
@@ -45,6 +45,7 @@
       BootControlInterface* boot_control,
       PrefsInterface* prefs,
       CleanupPreviousUpdateActionDelegateInterface* delegate) override;
+  bool ResetUpdate(PrefsInterface* prefs) override;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/dynamic_partition_control_android.cc b/dynamic_partition_control_android.cc
index d75a8fc..81d0d77 100644
--- a/dynamic_partition_control_android.cc
+++ b/dynamic_partition_control_android.cc
@@ -39,6 +39,7 @@
 #include "update_engine/common/boot_control_interface.h"
 #include "update_engine/common/utils.h"
 #include "update_engine/dynamic_partition_utils.h"
+#include "update_engine/payload_consumer/delta_performer.h"
 
 using android::base::GetBoolProperty;
 using android::base::Join;
@@ -786,4 +787,24 @@
       prefs, boot_control, snapshot_.get(), delegate);
 }
 
+bool DynamicPartitionControlAndroid::ResetUpdate(PrefsInterface* prefs) {
+  if (!GetVirtualAbFeatureFlag().IsEnabled()) {
+    return true;
+  }
+
+  LOG(INFO) << __func__ << " resetting update state and deleting snapshots.";
+  TEST_AND_RETURN_FALSE(prefs != nullptr);
+
+  // If the device has already booted into the target slot,
+  // ResetUpdateProgress may pass but CancelUpdate fails.
+  // This is expected. A scheduled CleanupPreviousUpdateAction should free
+  // space when it is done.
+  TEST_AND_RETURN_FALSE(DeltaPerformer::ResetUpdateProgress(
+      prefs, false /* quick */, false /* skip dynamic partitions metadata */));
+
+  TEST_AND_RETURN_FALSE(snapshot_->CancelUpdate());
+
+  return true;
+}
+
 }  // namespace chromeos_update_engine
diff --git a/dynamic_partition_control_android.h b/dynamic_partition_control_android.h
index 1ff59a9..a11889a 100644
--- a/dynamic_partition_control_android.h
+++ b/dynamic_partition_control_android.h
@@ -51,6 +51,8 @@
       PrefsInterface* prefs,
       CleanupPreviousUpdateActionDelegateInterface* delegate) override;
 
+  bool ResetUpdate(PrefsInterface* prefs) override;
+
   // Return the device for partition |partition_name| at slot |slot|.
   // |current_slot| should be set to the current active slot.
   // Note: this function is only used by BootControl*::GetPartitionDevice.
diff --git a/dynamic_partition_control_android_unittest.cc b/dynamic_partition_control_android_unittest.cc
index 4b1870d..457ea10 100644
--- a/dynamic_partition_control_android_unittest.cc
+++ b/dynamic_partition_control_android_unittest.cc
@@ -24,6 +24,7 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include "update_engine/common/mock_prefs.h"
 #include "update_engine/dynamic_partition_test_utils.h"
 #include "update_engine/mock_dynamic_partition_control.h"
 
@@ -751,4 +752,9 @@
   EXPECT_FALSE(dynamicControl().OptimizeOperation("bar", iop, &optimized));
 }
 
+TEST_F(DynamicPartitionControlAndroidTest, ResetUpdate) {
+  MockPrefs prefs;
+  ASSERT_TRUE(dynamicControl().ResetUpdate(&prefs));
+}
+
 }  // namespace chromeos_update_engine
diff --git a/update_attempter_android.cc b/update_attempter_android.cc
index 4b198e2..b7d119f 100644
--- a/update_attempter_android.cc
+++ b/update_attempter_android.cc
@@ -350,15 +350,16 @@
             << UpdateStatusToString(status_) << " to UpdateStatus::IDLE";
 
   switch (status_) {
-    case UpdateStatus::IDLE:
+    case UpdateStatus::IDLE: {
+      if (!boot_control_->GetDynamicPartitionControl()->ResetUpdate(prefs_)) {
+        LOG(WARNING) << "Failed to reset snapshots. UpdateStatus is IDLE but"
+                     << "space might not be freed.";
+      }
       return true;
+    }
 
     case UpdateStatus::UPDATED_NEED_REBOOT: {
-      // Remove the reboot marker so that if the machine is rebooted
-      // after resetting to idle state, it doesn't go back to
-      // UpdateStatus::UPDATED_NEED_REBOOT state.
-      bool ret_value = prefs_->Delete(kPrefsUpdateCompletedOnBootId);
-      ClearMetricsPrefs();
+      bool ret_value = true;
 
       // Update the boot flags so the current slot has higher priority.
       if (!boot_control_->SetActiveBootSlot(GetCurrentSlot()))
@@ -373,6 +374,17 @@
       // Resets the warm reset property since we won't switch the slot.
       hardware_->SetWarmReset(false);
 
+      // Remove update progress for DeltaPerformer and remove snapshots.
+      if (!boot_control_->GetDynamicPartitionControl()->ResetUpdate(prefs_))
+        ret_value = false;
+
+      // Remove the reboot marker so that if the machine is rebooted
+      // after resetting to idle state, it doesn't go back to
+      // UpdateStatus::UPDATED_NEED_REBOOT state.
+      if (!prefs_->Delete(kPrefsUpdateCompletedOnBootId))
+        ret_value = false;
+      ClearMetricsPrefs();
+
       if (!ret_value) {
         return LogAndSetError(
             error, FROM_HERE, "Failed to reset the status to ");