Skip identical SOURCE_COPY operations

When Virtual A/B devices are updated, SOURCE_COPY operations that are
copying data from source to destination at the same locations:
- are useless;
- introduce an overhead for overwritingin identical data;
- increase the COW device size when using dm-snapshot.
This patch analyzes SOURCE_COPY operations and skips them if applied to
Virtual A/B devices and source and destination addresses are identical.

Bug: 141207436
Test: DynamicPartitionControlAndroidTest:ShouldSkipOperationTest
Depends-On: I146aeba1c8ede35f21cfef8e21d4af62274bda84
Change-Id: Ifec33abaf81b1d4cbd61533293735de68578c9c4
Signed-off-by: Alessio Balsini <balsini@google.com>
diff --git a/dynamic_partition_control_android_unittest.cc b/dynamic_partition_control_android_unittest.cc
index 10075ed..fc3d38c 100644
--- a/dynamic_partition_control_android_unittest.cc
+++ b/dynamic_partition_control_android_unittest.cc
@@ -124,6 +124,10 @@
   }
   void SetSlots(const TestParam& slots) { slots_ = slots; }
 
+  void SetSnapshotEnabled(bool enabled) {
+    dynamicControl().target_supports_snapshot_ = enabled;
+  }
+
   struct Listener : public ::testing::MatchResultListener {
     explicit Listener(std::ostream* os) : MatchResultListener(os) {}
   };
@@ -616,4 +620,82 @@
       << "Should not be able to apply to current slot.";
 }
 
+TEST_F(DynamicPartitionControlAndroidTest, ShouldSkipOperationTest) {
+  InstallOperation iop;
+  Extent *se, *de;
+
+  // Not a SOURCE_COPY operation, cannot skip.
+  iop.set_type(InstallOperation::REPLACE);
+  EXPECT_FALSE(dynamicControl().ShouldSkipOperation(iop));
+
+  iop.set_type(InstallOperation::SOURCE_COPY);
+
+  // By default GetVirtualAbFeatureFlag is disabled. Cannot skip operation.
+  EXPECT_FALSE(dynamicControl().ShouldSkipOperation(iop));
+
+  // Enable GetVirtualAbFeatureFlag in the mock interface.
+  ON_CALL(dynamicControl(), GetVirtualAbFeatureFlag())
+      .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+
+  // By default target_supports_snapshot_ is set to false. Cannot skip
+  // operation.
+  EXPECT_FALSE(dynamicControl().ShouldSkipOperation(iop));
+
+  SetSnapshotEnabled(true);
+
+  // Empty source and destination. Skip.
+  EXPECT_TRUE(dynamicControl().ShouldSkipOperation(iop));
+
+  se = iop.add_src_extents();
+  se->set_start_block(0);
+  se->set_num_blocks(1);
+
+  // There is something in sources, but destinations are empty. Cannot skip.
+  EXPECT_FALSE(dynamicControl().ShouldSkipOperation(iop));
+
+  InstallOperation iop2;
+
+  de = iop2.add_dst_extents();
+  de->set_start_block(0);
+  de->set_num_blocks(1);
+
+  // There is something in destinations, but sources are empty. Cannot skip.
+  EXPECT_FALSE(dynamicControl().ShouldSkipOperation(iop2));
+
+  de = iop.add_dst_extents();
+  de->set_start_block(0);
+  de->set_num_blocks(1);
+
+  // Sources and destinations are identical. Skip.
+  EXPECT_TRUE(dynamicControl().ShouldSkipOperation(iop));
+
+  se = iop.add_src_extents();
+  se->set_start_block(1);
+  se->set_num_blocks(5);
+
+  // There is something in source, but not in destination. Cannot skip.
+  EXPECT_FALSE(dynamicControl().ShouldSkipOperation(iop));
+
+  de = iop.add_dst_extents();
+  de->set_start_block(1);
+  de->set_num_blocks(5);
+
+  // There is source and destination are equal. Skip.
+  EXPECT_TRUE(dynamicControl().ShouldSkipOperation(iop));
+
+  de = iop.add_dst_extents();
+  de->set_start_block(6);
+  de->set_num_blocks(5);
+
+  // There is something extra in dest. Cannot skip.
+  EXPECT_FALSE(dynamicControl().ShouldSkipOperation(iop));
+
+  se = iop.add_src_extents();
+  se->set_start_block(6);
+  se->set_num_blocks(5);
+
+  // Source and dest are identical again. Skip.
+  EXPECT_TRUE(dynamicControl().ShouldSkipOperation(iop));
+}
+
 }  // namespace chromeos_update_engine