Reland: Map all partitions before calling FinishUpdate for VAB as well

In virtual AB updates, all partitions must be mapped before calling
FinishUpdate. (same requirement as VABC).

Test: recovery sideload
Bug: 393082101
Change-Id: I65f4108703cafaf9d38ca87da18e9bde5b3c0aeb
diff --git a/Android.bp b/Android.bp
index dd9b02e..628d55b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1288,6 +1288,47 @@
     ],
 }
 
+// update_engine_unittests (type: executable)
+// ========================================================
+// Main unittest file.
+cc_test {
+    name: "update_engine_recovery_unittests",
+    defaults: [
+        "ue_defaults",
+        "update_metadata-protos_exports",
+    ],
+    cflags: [
+        "-D__ANDROID_RECOVERY__",
+    ],
+
+    static_libs: [
+        "libbrillo-test-helpers",
+        "libbase",
+        "liblog",
+        "libgmock",
+        "libchrome_test_helpers",
+        "libupdate_engine_android",
+        "update_metadata-protos",
+        "libsnapshot_cow",
+        "libdm",
+    ],
+
+    header_libs: [
+        "libstorage_literals_headers",
+    ],
+    test_suites: ["device-tests"],
+    srcs: [
+        "payload_consumer/postinstall_runner_action_recovery_unittest.cc",
+        "payload_consumer/postinstall_runner_action.cc",
+        "common/subprocess.cc",
+        "common/test_utils.cc",
+        "common/utils.cc",
+        "common/dynamic_partition_control_stub.cc",
+        "common/action_processor.cc",
+        "common/error_code_utils.cc",
+    ],
+}
+
 // Brillo update payload generation script
 // ========================================================
 sh_binary {
diff --git a/aosp/update_attempter_android.cc b/aosp/update_attempter_android.cc
index 943c3e4..b46f584 100644
--- a/aosp/update_attempter_android.cc
+++ b/aosp/update_attempter_android.cc
@@ -1363,6 +1363,7 @@
   // previous ApplyPayload() call may have requested powerwash, these
   // settings would be saved in `this->install_plan_`. Inherit that setting.
   install_plan_.powerwash_required = this->install_plan_.powerwash_required;
+  install_plan_.switch_slot_on_reboot = true;
 
   CHECK_NE(install_plan_.source_slot, UINT32_MAX);
   CHECK_NE(install_plan_.target_slot, UINT32_MAX);
diff --git a/common/fake_boot_control.h b/common/fake_boot_control.h
index 8a68501..82d4827 100644
--- a/common/fake_boot_control.h
+++ b/common/fake_boot_control.h
@@ -40,6 +40,11 @@
     dynamic_partition_control_.reset(new DynamicPartitionControlStub());
   }
 
+  void SetDynamicPartitionControl(
+      std::unique_ptr<DynamicPartitionControlInterface> dynamic_control) {
+    dynamic_partition_control_ = std::move(dynamic_control);
+  }
+
   // BootControlInterface overrides.
   unsigned int GetNumSlots() const override { return num_slots_; }
   BootControlInterface::Slot GetCurrentSlot() const override {
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index da9075a..3b09916 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -35,6 +35,7 @@
 #include "update_engine/common/action_processor.h"
 #include "update_engine/common/boot_control_interface.h"
 #include "update_engine/common/error_code_utils.h"
+#include "update_engine/common/platform_constants.h"
 #include "update_engine/common/subprocess.h"
 #include "update_engine/common/utils.h"
 
@@ -108,14 +109,17 @@
   auto dynamic_control = boot_control_->GetDynamicPartitionControl();
   CHECK(dynamic_control);
 
-  // Mount snapshot partitions for Virtual AB Compression Compression.
-  if (dynamic_control->UpdateUsesSnapshotCompression()) {
-    // If we are switching slots, then we are required to MapAllPartitions,
-    // as FinishUpdate() requires all partitions to be mapped.
-    // And switching slots requires FinishUpdate() to be called first
+  // Mount snapshot partitions for Virtual AB updates.
+  // If we are switching slots, then we are required to MapAllPartitions,
+  // as FinishUpdate() requires all partitions to be mapped.
+  // And switching slots requires FinishUpdate() to be called first
+  if (dynamic_control->GetVirtualAbFeatureFlag().IsEnabled() &&
+      !constants::kIsRecovery) {
     if (!install_plan_.partitions.empty() ||
         install_plan_.switch_slot_on_reboot) {
       if (!dynamic_control->MapAllPartitions()) {
+        LOG(ERROR) << "Failed to map all partitions, this would cause "
+                      "FinishUpdate to fail. Abort early.";
         return CompletePostinstall(ErrorCode::kPostInstallMountError);
       }
     }
diff --git a/payload_consumer/postinstall_runner_action_recovery_unittest.cc b/payload_consumer/postinstall_runner_action_recovery_unittest.cc
new file mode 100644
index 0000000..3a869c9
--- /dev/null
+++ b/payload_consumer/postinstall_runner_action_recovery_unittest.cc
@@ -0,0 +1,202 @@
+//
+// Copyright (C) 2012 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "gmock/gmock.h"
+#include "update_engine/payload_consumer/postinstall_runner_action.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include "common/dynamic_partition_control_interface.h"
+
+#include <base/bind.h>
+#include <base/files/file_util.h>
+#include <base/message_loop/message_loop.h>
+#include <android-base/stringprintf.h>
+#include <brillo/message_loops/base_message_loop.h>
+#include <brillo/message_loops/message_loop_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/fake_boot_control.h"
+#include "update_engine/common/fake_hardware.h"
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/common/mock_dynamic_partition_control.h"
+
+using brillo::MessageLoop;
+using chromeos_update_engine::test_utils::ScopedLoopbackDeviceBinder;
+using std::string;
+using testing::_;
+using testing::AtLeast;
+using testing::Return;
+
+namespace chromeos_update_engine {
+
+class PostinstActionProcessorDelegate : public ActionProcessorDelegate {
+ public:
+  PostinstActionProcessorDelegate() = default;
+  void ProcessingDone(const ActionProcessor* processor,
+                      ErrorCode code) override {
+    MessageLoop::current()->BreakLoop();
+    processing_done_called_ = true;
+  }
+  void ProcessingStopped(const ActionProcessor* processor) override {
+    MessageLoop::current()->BreakLoop();
+    processing_stopped_called_ = true;
+  }
+
+  void ActionCompleted(ActionProcessor* processor,
+                       AbstractAction* action,
+                       ErrorCode code) override {
+    if (action->Type() == PostinstallRunnerAction::StaticType()) {
+      code_ = code;
+      code_set_ = true;
+    }
+  }
+
+  ErrorCode code_{ErrorCode::kError};
+  bool code_set_{false};
+  bool processing_done_called_{false};
+  bool processing_stopped_called_{false};
+};
+
+class MockPostinstallRunnerActionDelegate
+    : public PostinstallRunnerAction::DelegateInterface {
+ public:
+  MOCK_METHOD1(ProgressUpdate, void(double progress));
+};
+
+class PostinstallRunnerActionTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    loop_.SetAsCurrent();
+    {
+      auto mock_dynamic_control =
+          std::make_unique<MockDynamicPartitionControl>();
+      mock_dynamic_control_ = mock_dynamic_control.get();
+      fake_boot_control_.SetDynamicPartitionControl(
+          std::move(mock_dynamic_control));
+    }
+    ON_CALL(*mock_dynamic_control_, FinishUpdate(_))
+        .WillByDefault(Return(true));
+    ON_CALL(*mock_dynamic_control_, GetVirtualAbFeatureFlag())
+        .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+  }
+
+  // Setup an action processor and run the PostinstallRunnerAction with a single
+  // partition |device_path|, running the |postinstall_program| command from
+  // there.
+  void RunPostinstallAction(bool powerwash_required, bool save_rollback_data);
+
+  void RunPostinstallActionWithInstallPlan(const InstallPlan& install_plan);
+
+ public:
+  void ResumeRunningAction() {
+    ASSERT_NE(nullptr, postinstall_action_);
+    postinstall_action_->ResumeAction();
+  }
+
+ protected:
+  base::MessageLoopForIO base_loop_;
+  brillo::BaseMessageLoop loop_{&base_loop_};
+
+  FakeBootControl fake_boot_control_;
+  FakeHardware fake_hardware_;
+  MockDynamicPartitionControl* mock_dynamic_control_;
+  PostinstActionProcessorDelegate processor_delegate_;
+
+  // The PostinstallRunnerAction delegate receiving the progress updates.
+  PostinstallRunnerAction::DelegateInterface* setup_action_delegate_{nullptr};
+
+  // A pointer to the posinstall_runner action and the processor.
+  PostinstallRunnerAction* postinstall_action_{nullptr};
+  ActionProcessor* processor_{nullptr};
+};
+
+void PostinstallRunnerActionTest::RunPostinstallAction(
+    bool powerwash_required, bool save_rollback_data) {
+  InstallPlan::Partition part;
+  part.name = "part";
+  part.target_path = "/dev/invalid";
+  part.readonly_target_path = "/dev/invalid";
+  part.run_postinstall = false;
+  part.postinstall_path.clear();
+  InstallPlan install_plan;
+  install_plan.partitions = {part};
+  install_plan.download_url = "http://127.0.0.1:8080/update";
+  install_plan.powerwash_required = powerwash_required;
+  RunPostinstallActionWithInstallPlan(install_plan);
+}
+
+void PostinstallRunnerActionTest::RunPostinstallActionWithInstallPlan(
+    const chromeos_update_engine::InstallPlan& install_plan) {
+  ActionProcessor processor;
+  processor_ = &processor;
+  auto feeder_action = std::make_unique<ObjectFeederAction<InstallPlan>>();
+  feeder_action->set_obj(install_plan);
+  auto runner_action = std::make_unique<PostinstallRunnerAction>(
+      &fake_boot_control_, &fake_hardware_);
+  postinstall_action_ = runner_action.get();
+  base::FilePath temp_dir;
+  TEST_AND_RETURN(base::CreateNewTempDirectory("postinstall", &temp_dir));
+  postinstall_action_->SetMountDir(temp_dir.value());
+  runner_action->set_delegate(setup_action_delegate_);
+  BondActions(feeder_action.get(), runner_action.get());
+  auto collector_action =
+      std::make_unique<ObjectCollectorAction<InstallPlan>>();
+  BondActions(runner_action.get(), collector_action.get());
+  processor.EnqueueAction(std::move(feeder_action));
+  processor.EnqueueAction(std::move(runner_action));
+  processor.EnqueueAction(std::move(collector_action));
+  processor.set_delegate(&processor_delegate_);
+
+  loop_.PostTask(
+      FROM_HERE,
+      base::Bind(
+          [](ActionProcessor* processor) { processor->StartProcessing(); },
+          base::Unretained(&processor)));
+  loop_.Run();
+  ASSERT_FALSE(processor.IsRunning());
+  postinstall_action_ = nullptr;
+  processor_ = nullptr;
+  ASSERT_TRUE(processor_delegate_.processing_stopped_called_ ||
+              processor_delegate_.processing_done_called_);
+  if (processor_delegate_.processing_done_called_) {
+    // Validation check that the code was set when the processor finishes.
+    ASSERT_TRUE(processor_delegate_.code_set_);
+  }
+}
+
+// Test that postinstall succeeds in the simple case of running the default
+// /postinst command which only exits 0.
+TEST_F(PostinstallRunnerActionTest, RunAsRootSimpleTest) {
+  EXPECT_CALL(*mock_dynamic_control_, GetVirtualAbFeatureFlag())
+      .WillOnce(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+  RunPostinstallAction(false, false);
+  ASSERT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
+  ASSERT_TRUE(processor_delegate_.processing_done_called_);
+
+  // Since powerwash_required was false, this should not trigger a powerwash.
+  ASSERT_FALSE(fake_hardware_.IsPowerwashScheduled());
+  ASSERT_FALSE(fake_hardware_.GetIsRollbackPowerwashScheduled());
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/postinstall_runner_action_unittest.cc b/payload_consumer/postinstall_runner_action_unittest.cc
index 028402a..8579144 100644
--- a/payload_consumer/postinstall_runner_action_unittest.cc
+++ b/payload_consumer/postinstall_runner_action_unittest.cc
@@ -23,6 +23,7 @@
 #include <memory>
 #include <string>
 #include <utility>
+#include "common/dynamic_partition_control_interface.h"
 
 #include <base/bind.h>
 #include <base/files/file_util.h>
@@ -44,10 +45,14 @@
 #include "update_engine/common/subprocess.h"
 #include "update_engine/common/test_utils.h"
 #include "update_engine/common/utils.h"
+#include "update_engine/common/mock_dynamic_partition_control.h"
 
 using brillo::MessageLoop;
 using chromeos_update_engine::test_utils::ScopedLoopbackDeviceBinder;
 using std::string;
+using testing::_;
+using testing::AtLeast;
+using testing::Return;
 
 namespace chromeos_update_engine {
 
@@ -95,6 +100,21 @@
     // stored in the "disk_ext2_unittest.img" image.
     postinstall_image_ =
         test_utils::GetBuildArtifactsPath("gen/disk_ext2_unittest.img");
+    {
+      auto mock_dynamic_control =
+          std::make_unique<MockDynamicPartitionControl>();
+      mock_dynamic_control_ = mock_dynamic_control.get();
+      fake_boot_control_.SetDynamicPartitionControl(
+          std::move(mock_dynamic_control));
+    }
+    ON_CALL(*mock_dynamic_control_, FinishUpdate(_))
+        .WillByDefault(Return(true));
+    ON_CALL(*mock_dynamic_control_, MapAllPartitions())
+        .WillByDefault(Return(true));
+    ON_CALL(*mock_dynamic_control_, UnmapAllPartitions())
+        .WillByDefault(Return(true));
+    ON_CALL(*mock_dynamic_control_, GetVirtualAbFeatureFlag())
+        .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
   }
 
   // Setup an action processor and run the PostinstallRunnerAction with a single
@@ -173,6 +193,7 @@
 
   FakeBootControl fake_boot_control_;
   FakeHardware fake_hardware_;
+  MockDynamicPartitionControl* mock_dynamic_control_;
   PostinstActionProcessorDelegate processor_delegate_;
 
   // The PostinstallRunnerAction delegate receiving the progress updates.
@@ -398,6 +419,7 @@
 // Test that we can cancel a postinstall action while it is running.
 TEST_F(PostinstallRunnerActionTest, RunAsRootCancelPostinstallActionTest) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
+  EXPECT_CALL(*mock_dynamic_control_, MapAllPartitions()).Times(AtLeast(1));
 
   // Wait for the action to start and then cancel it.
   CancelWhenStarted();
@@ -411,6 +433,10 @@
 // Test that we parse and process the progress reports from the progress
 // file descriptor.
 TEST_F(PostinstallRunnerActionTest, RunAsRootProgressUpdatesTest) {
+  EXPECT_CALL(*mock_dynamic_control_, MapAllPartitions())
+      .Times(AtLeast(1))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(*mock_dynamic_control_, FinishUpdate(_)).Times(AtLeast(1));
   testing::StrictMock<MockPostinstallRunnerActionDelegate> mock_delegate_;
   testing::InSequence s;
   EXPECT_CALL(mock_delegate_, ProgressUpdate(0));