Merge "Add a test for bug 198265278."
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 55f4ed7..a49b026 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -408,6 +408,7 @@
     FRIEND_TEST(SnapshotUpdateTest, FullUpdateFlow);
     FRIEND_TEST(SnapshotUpdateTest, MergeCannotRemoveCow);
     FRIEND_TEST(SnapshotUpdateTest, MergeInRecovery);
+    FRIEND_TEST(SnapshotUpdateTest, QueryStatusError);
     FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);
     FRIEND_TEST(SnapshotUpdateTest, SpaceSwapUpdate);
     friend class SnapshotTest;
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 1f57bbc..c3b40dc 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -100,6 +100,9 @@
         return IDeviceInfo::OpenImageManager("ota/test");
     }
     android::dm::IDeviceMapper& GetDeviceMapper() override {
+        if (dm_) {
+            return *dm_;
+        }
         return android::dm::DeviceMapper::Instance();
     }
 
@@ -111,6 +114,8 @@
     }
     void set_recovery(bool value) { recovery_ = value; }
     void set_first_stage_init(bool value) { first_stage_init_ = value; }
+    void set_dm(android::dm::IDeviceMapper* dm) { dm_ = dm; }
+
     MergeStatus merge_status() const { return merge_status_; }
 
   private:
@@ -120,6 +125,45 @@
     bool recovery_ = false;
     bool first_stage_init_ = false;
     std::unordered_set<uint32_t> unbootable_slots_;
+    android::dm::IDeviceMapper* dm_ = nullptr;
+};
+
+class DeviceMapperWrapper : public android::dm::IDeviceMapper {
+    using DmDeviceState = android::dm::DmDeviceState;
+    using DmTable = android::dm::DmTable;
+
+  public:
+    DeviceMapperWrapper() : impl_(android::dm::DeviceMapper::Instance()) {}
+    explicit DeviceMapperWrapper(android::dm::IDeviceMapper& impl) : impl_(impl) {}
+
+    virtual bool CreateDevice(const std::string& name, const DmTable& table, std::string* path,
+                              const std::chrono::milliseconds& timeout_ms) override {
+        return impl_.CreateDevice(name, table, path, timeout_ms);
+    }
+    virtual DmDeviceState GetState(const std::string& name) const override {
+        return impl_.GetState(name);
+    }
+    virtual bool LoadTableAndActivate(const std::string& name, const DmTable& table) {
+        return impl_.LoadTableAndActivate(name, table);
+    }
+    virtual bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) {
+        return impl_.GetTableInfo(name, table);
+    }
+    virtual bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) {
+        return impl_.GetTableStatus(name, table);
+    }
+    virtual bool GetDmDevicePathByName(const std::string& name, std::string* path) {
+        return impl_.GetDmDevicePathByName(name, path);
+    }
+    virtual bool GetDeviceString(const std::string& name, std::string* dev) {
+        return impl_.GetDeviceString(name, dev);
+    }
+    virtual bool DeleteDeviceIfExists(const std::string& name) {
+        return impl_.DeleteDeviceIfExists(name);
+    }
+
+  private:
+    android::dm::IDeviceMapper& impl_;
 };
 
 class SnapshotTestPropertyFetcher : public android::fs_mgr::testing::MockPropertyFetcher {
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 43c7fe2..d78ba0a 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -56,6 +56,7 @@
 using android::base::unique_fd;
 using android::dm::DeviceMapper;
 using android::dm::DmDeviceState;
+using android::dm::IDeviceMapper;
 using android::fiemap::FiemapStatus;
 using android::fiemap::IImageManager;
 using android::fs_mgr::BlockDeviceInfo;
@@ -911,6 +912,11 @@
             ASSERT_TRUE(hash.has_value());
             hashes_[name] = *hash;
         }
+
+        // OTA client blindly unmaps all partitions that are possibly mapped.
+        for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+            ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
+        }
     }
     void TearDown() override {
         RETURN_IF_NON_VIRTUAL_AB();
@@ -925,6 +931,14 @@
         MountMetadata();
         for (const auto& suffix : {"_a", "_b"}) {
             test_device->set_slot_suffix(suffix);
+
+            // Cheat our way out of merge failed states.
+            if (sm->ProcessUpdateState() == UpdateState::MergeFailed) {
+                ASSERT_TRUE(AcquireLock());
+                ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::None));
+                lock_ = {};
+            }
+
             EXPECT_TRUE(sm->CancelUpdate()) << suffix;
         }
         EXPECT_TRUE(UnmapAll());
@@ -1097,11 +1111,6 @@
 // Also test UnmapUpdateSnapshot unmaps everything.
 // Also test first stage mount and merge after this.
 TEST_F(SnapshotUpdateTest, FullUpdateFlow) {
-    // OTA client blindly unmaps all partitions that are possibly mapped.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
-    }
-
     // Grow all partitions. Set |prd| large enough that |sys| and |vnd|'s COWs
     // fit in super, but not |prd|.
     constexpr uint64_t partition_size = 3788_KiB;
@@ -1189,11 +1198,6 @@
         GTEST_SKIP() << "Compression-only test";
     }
 
-    // OTA client blindly unmaps all partitions that are possibly mapped.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
-    }
-
     // Execute the update.
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
@@ -1239,11 +1243,6 @@
         GTEST_SKIP() << "Skipping Virtual A/B Compression test";
     }
 
-    // OTA client blindly unmaps all partitions that are possibly mapped.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
-    }
-
     auto old_sys_size = GetSize(sys_);
     auto old_prd_size = GetSize(prd_);
 
@@ -1630,11 +1629,6 @@
     ASSERT_NE(nullptr, metadata);
     ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *metadata.get(), 0));
 
-    // OTA client blindly unmaps all partitions that are possibly mapped.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
-    }
-
     // Add operations for sys. The whole device is written.
     AddOperation(sys_);
 
@@ -2074,11 +2068,6 @@
 }
 
 TEST_F(SnapshotUpdateTest, AddPartition) {
-    // OTA client blindly unmaps all partitions that are possibly mapped.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
-    }
-
     group_->add_partition_names("dlkm");
 
     auto dlkm = manifest_.add_partitions();
@@ -2249,6 +2238,60 @@
     ASSERT_TRUE(sm->BeginUpdate());
 }
 
+TEST_F(SnapshotUpdateTest, QueryStatusError) {
+    // Grow all partitions. Set |prd| large enough that |sys| and |vnd|'s COWs
+    // fit in super, but not |prd|.
+    constexpr uint64_t partition_size = 3788_KiB;
+    SetSize(sys_, partition_size);
+
+    AddOperationForPartitions({sys_});
+
+    // Execute the update.
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+    ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+    ASSERT_TRUE(UnmapAll());
+
+    class DmStatusFailure final : public DeviceMapperWrapper {
+      public:
+        bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) override {
+            if (!DeviceMapperWrapper::GetTableStatus(name, table)) {
+                return false;
+            }
+            if (name == "sys_b" && !table->empty()) {
+                auto& info = table->at(0);
+                if (DeviceMapper::GetTargetType(info.spec) == "snapshot-merge") {
+                    info.data = "Merge failed";
+                }
+            }
+            return true;
+        }
+    };
+    DmStatusFailure wrapper;
+
+    // After reboot, init does first stage mount.
+    auto info = new TestDeviceInfo(fake_super, "_b");
+    info->set_dm(&wrapper);
+
+    auto init = NewManagerForFirstStageMount(info);
+    ASSERT_NE(init, nullptr);
+
+    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+
+    // Initiate the merge and wait for it to be completed.
+    ASSERT_TRUE(init->InitiateMerge());
+    ASSERT_EQ(UpdateState::MergeFailed, init->ProcessUpdateState());
+
+    // Simulate a reboot that tries the merge again, with the non-failing dm.
+    ASSERT_TRUE(UnmapAll());
+    init = NewManagerForFirstStageMount("_b");
+    ASSERT_NE(init, nullptr);
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
+}
+
 class FlashAfterUpdateTest : public SnapshotUpdateTest,
                              public WithParamInterface<std::tuple<uint32_t, bool>> {
   public:
@@ -2265,11 +2308,6 @@
 };
 
 TEST_P(FlashAfterUpdateTest, FlashSlotAfterUpdate) {
-    // OTA client blindly unmaps all partitions that are possibly mapped.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
-    }
-
     // Execute the update.
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));