Copy system_other.img to target_files.zip

Similar to https://r.android.com/3489814, this adds an additional rule
in systemOtherImage to create a hermetic version of system_other.img
with pinned timestamps. This information will be provided to
android_device via super_image.

block_list information is not implemented for system_other to match the
make behavior

Test: Built the soong built target_files.zip locally
Bug: 385383524
Change-Id: I4cbb4f8a5380203eaef846b3d8a56eb7791f8a34
diff --git a/filesystem/android_device.go b/filesystem/android_device.go
index 960c96a..c14cc4c 100644
--- a/filesystem/android_device.go
+++ b/filesystem/android_device.go
@@ -398,8 +398,12 @@
 		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].OutputHermetic).Textf(" %s/IMAGES/", targetFilesDir.String())
-				builder.Command().Textf("cp ").Input(info.SubImageInfo[partition].MapFile).Textf(" %s/IMAGES/", targetFilesDir.String())
+				if info.SubImageInfo[partition].OutputHermetic != nil {
+					builder.Command().Textf("cp ").Input(info.SubImageInfo[partition].OutputHermetic).Textf(" %s/IMAGES/", targetFilesDir.String())
+				}
+				if info.SubImageInfo[partition].MapFile != nil {
+					builder.Command().Textf("cp ").Input(info.SubImageInfo[partition].MapFile).Textf(" %s/IMAGES/", targetFilesDir.String())
+				}
 			}
 		} else {
 			ctx.ModuleErrorf("Super partition %s does set SuperImageProvider\n", superPartition.Name())
diff --git a/filesystem/super_image.go b/filesystem/super_image.go
index da007bb..58c938a 100644
--- a/filesystem/super_image.go
+++ b/filesystem/super_image.go
@@ -291,8 +291,6 @@
 		switch p {
 		case "system":
 			handleSubPartition("system", s.partitionProps.System_partition)
-			// TODO: add system_other to deps after it can be generated
-			//getFsInfo("system_other", s.partitionProps.System_other_partition, &subImageInfo.System_other)
 		case "system_dlkm":
 			handleSubPartition("system_dlkm", s.partitionProps.System_dlkm_partition)
 		case "system_ext":
@@ -321,8 +319,7 @@
 		if len(systemOtherFiles) != 1 {
 			ctx.PropertyErrorf("system_other_partition", "Expected 1 output file from module %q", *&s.properties.System_other_partition)
 		} else {
-			addStr("system_other_image", systemOtherFiles[0].String())
-			deps = append(deps, systemOtherFiles[0])
+			handleSubPartition("system_other", s.partitionProps.System_other_partition)
 		}
 	}
 
diff --git a/filesystem/system_other.go b/filesystem/system_other.go
index 28fe1ce..1c00dd3 100644
--- a/filesystem/system_other.go
+++ b/filesystem/system_other.go
@@ -85,6 +85,7 @@
 
 	output := android.PathForModuleOut(ctx, "system_other.img")
 	stagingDir := android.PathForModuleOut(ctx, "staging_dir")
+	stagingDirTimestamp := android.PathForModuleOut(ctx, "staging_dir.timestamp")
 
 	builder := android.NewRuleBuilder(pctx, ctx)
 	builder.Command().Textf("rm -rf %s && mkdir -p %s", stagingDir, stagingDir)
@@ -113,6 +114,8 @@
 	if len(m.properties.Preinstall_dexpreopt_files_from) > 0 {
 		builder.Command().Textf("touch %s", filepath.Join(stagingDir.String(), "system-other-odex-marker"))
 	}
+	builder.Command().Textf("touch").Output(stagingDirTimestamp)
+	builder.Build("assemble_filesystem_staging_dir", "Assemble filesystem staging dir")
 
 	// Most of the time, if build_image were to call a host tool, it accepts the path to the
 	// host tool in a field in the prop file. However, it doesn't have that option for fec, which
@@ -120,6 +123,7 @@
 	fec := ctx.Config().HostToolPath(ctx, "fec")
 	pathToolDirs := []string{filepath.Dir(fec.String())}
 
+	builder = android.NewRuleBuilder(pctx, ctx)
 	builder.Command().
 		Textf("PATH=%s:$PATH", strings.Join(pathToolDirs, ":")).
 		BuiltTool("build_image").
@@ -127,11 +131,44 @@
 		Input(systemInfo.BuildImagePropFile).
 		Implicits(systemInfo.BuildImagePropFileDeps).
 		Implicit(fec).
+		Implicit(stagingDirTimestamp).
 		Output(output).
 		Text(stagingDir.String())
 
 	builder.Build("build_system_other", "build system other")
 
+	// Create a hermetic system_other.img with pinned timestamps
+	builder = android.NewRuleBuilder(pctx, ctx)
+	outputHermetic := android.PathForModuleOut(ctx, "for_target_files", "system_other.img")
+	outputHermeticPropFile := m.propFileForHermeticImg(ctx, builder, systemInfo.BuildImagePropFile)
+	builder.Command().
+		Textf("PATH=%s:$PATH", strings.Join(pathToolDirs, ":")).
+		BuiltTool("build_image").
+		Text(stagingDir.String()). // input directory
+		Input(outputHermeticPropFile).
+		Implicits(systemInfo.BuildImagePropFileDeps).
+		Implicit(fec).
+		Implicit(stagingDirTimestamp).
+		Output(outputHermetic).
+		Text(stagingDir.String())
+
+	builder.Build("build_system_other_hermetic", "build system other")
+
+	fsInfo := FilesystemInfo{
+		Output:         output,
+		OutputHermetic: outputHermetic,
+		RootDir:        stagingDir,
+	}
+
+	android.SetProvider(ctx, FilesystemProvider, fsInfo)
+
 	ctx.SetOutputFiles(android.Paths{output}, "")
 	ctx.CheckbuildFile(output)
 }
+
+func (f *systemOtherImage) propFileForHermeticImg(ctx android.ModuleContext, builder *android.RuleBuilder, inputPropFile android.Path) android.Path {
+	propFilePinnedTimestamp := android.PathForModuleOut(ctx, "for_target_files", "prop")
+	builder.Command().Textf("cat").Input(inputPropFile).Flag(">").Output(propFilePinnedTimestamp).
+		Textf(" && echo use_fixed_timestamp=true >> %s", propFilePinnedTimestamp)
+	return propFilePinnedTimestamp
+}