Create build rules for hermetic .img files

Make packaging generates build rules for two $partition.img files
1. Containing inodes with build timestamps
2. Containing inodes with pinned timestamps

The former is useful for adb sync. The latter is used for
target_files.zip.

This CL creates a build rule to generate (2) with soong `filesystem` modules.
The propFile for (2) will be created by concat'ing the propFile for (1)
with `use_fixed_timestamp=true`

Test: m out/soong/.intermediates/build/soong/fsgen/aosp_cf_x86_64_phone_generated_device/android_x86_64_silvermont/target_files.zip out/target/product/vsoc_x86_64/obj/PACKAGING/target_files_intermediates/aosp_cf_x86_64_phone-target_files.zip
Test: diff -r out/target/product/vsoc_x86_64/obj/PACKAGING/target_files_intermediates/aosp_cf_x86_64_phone-target_files/ out/soong/.intermediates/build/soong/fsgen/aosp_cf_x86_64_phone_generated_device/android_x86_64_silvermont/target_files_dir/ --no-dereference
- super_empty.img and system_other.img are missing
- vbmeta*, boot* and userdata have binary diffs
- the rest are identical

Change-Id: If078220f215693660796090eb9b690b0ad41fd38
diff --git a/filesystem/android_device.go b/filesystem/android_device.go
index 8332bc5..080e6b7 100644
--- a/filesystem/android_device.go
+++ b/filesystem/android_device.go
@@ -377,7 +377,7 @@
 		superPartition := ctx.GetDirectDepProxyWithTag(*a.partitionProps.Super_partition_name, superPartitionDepTag)
 		if info, ok := android.OtherModuleProvider(ctx, superPartition, SuperImageProvider); ok {
 			for _, partition := range android.SortedKeys(info.SubImageInfo) {
-				builder.Command().Textf("cp ").Input(info.SubImageInfo[partition].Output).Textf(" %s/IMAGES/", targetFilesDir.String())
+				builder.Command().Textf("cp ").Input(info.SubImageInfo[partition].OutputHermetic).Textf(" %s/IMAGES/", targetFilesDir.String())
 				builder.Command().Textf("cp ").Input(info.SubImageInfo[partition].MapFile).Textf(" %s/IMAGES/", targetFilesDir.String())
 			}
 		} else {
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 469fd9e..37a2965 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -363,6 +363,10 @@
 type FilesystemInfo struct {
 	// The built filesystem image
 	Output android.Path
+	// An additional hermetic filesystem image.
+	// e.g. this will contain inodes with pinned timestamps.
+	// This will be copied to target_files.zip
+	OutputHermetic android.Path
 	// A text file containing the list of paths installed on the partition.
 	FileListFile android.Path
 	// The root staging directory used to build the output filesystem. If consuming this, make sure
@@ -460,9 +464,10 @@
 
 	var rootDir android.OutputPath
 	var mapFile android.Path
+	var outputHermetic android.Path
 	switch f.fsType(ctx) {
 	case ext4Type, erofsType, f2fsType:
-		f.output, rootDir = f.buildImageUsingBuildImage(ctx)
+		f.output, outputHermetic, rootDir = f.buildImageUsingBuildImage(ctx)
 		mapFile = f.getMapFile(ctx)
 	case compressedCpioType:
 		f.output, rootDir = f.buildCpioImage(ctx, true)
@@ -491,6 +496,9 @@
 	if mapFile != nil {
 		fsInfo.MapFile = mapFile
 	}
+	if outputHermetic != nil {
+		fsInfo.OutputHermetic = outputHermetic
+	}
 
 	android.SetProvider(ctx, FilesystemProvider, fsInfo)
 
@@ -649,7 +657,7 @@
 	return f.partitionName()
 }
 
-func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) (android.Path, android.OutputPath) {
+func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) (android.Path, android.Path, android.OutputPath) {
 	rootDir := android.PathForModuleOut(ctx, f.rootDirString()).OutputPath
 	rebasedDir := rootDir
 	if f.properties.Base_dir != nil {
@@ -697,6 +705,21 @@
 		Output(output).
 		Text(rootDir.String()) // directory where to find fs_config_files|dirs
 
+	// Add an additional cmd to create a hermetic img file. This will contain pinned timestamps e.g.
+	propFilePinnedTimestamp := android.PathForModuleOut(ctx, "for_target_files", "prop")
+	builder.Command().Textf("cat").Input(propFile).Flag(">").Output(propFilePinnedTimestamp).Textf(" && echo use_fixed_timestamp=true >> %s", propFilePinnedTimestamp)
+
+	outputHermetic := android.PathForModuleOut(ctx, "for_target_files", f.installFileName())
+	builder.Command().
+		Textf("PATH=%s:$PATH", strings.Join(pathToolDirs, ":")).
+		BuiltTool("build_image").
+		Text(rootDir.String()). // input directory
+		Flag(propFilePinnedTimestamp.String()).
+		Implicits(toolDeps).
+		Implicit(fec).
+		Output(outputHermetic).
+		Text(rootDir.String()) // directory where to find fs_config_files|dirs
+
 	if !ctx.Config().KatiEnabled() {
 		copyImageFileToProductOut(ctx, builder, f.partitionName(), output)
 	}
@@ -708,7 +731,7 @@
 	// rootDir is not deleted. Might be useful for quick inspection.
 	builder.Build("build_filesystem_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName()))
 
-	return output, rootDir
+	return output, outputHermetic, rootDir
 }
 
 func (f *filesystem) buildFileContexts(ctx android.ModuleContext) android.Path {
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index 33cddf8..47d06f6 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -356,7 +356,7 @@
 		inputs.Strings(),
 		"out/soong/.intermediates/libbar/android_arm64_armv8-a_shared_cov/libbar.so")
 
-	filesystemOutput := filesystem.Output("myfilesystem.img").Output
+	filesystemOutput := filesystem.OutputFiles(result.TestContext, t, "")[0]
 	prebuiltInput := result.ModuleForTests("prebuilt", "android_arm64_armv8-a").Rule("Cp").Input
 	if filesystemOutput != prebuiltInput {
 		t.Error("prebuilt should use cov variant of filesystem")