Merge "Convert CheckMinSdkVersion to use providers." into main
diff --git a/Android.bp b/Android.bp
index 6c40661..523f55c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -147,6 +147,16 @@
 // Framework guests.
 cc_defaults {
     name: "cc_baremetal_defaults",
+    arch: {
+        arm64: {
+            cflags: [
+                // Prevent the compiler from optimizing code using SVE, as the
+                // baremetal environment might not have configured the hardware.
+                "-Xclang -target-feature",
+                "-Xclang -sve",
+            ],
+        },
+    },
     defaults_visibility: ["//visibility:public"],
 }
 
diff --git a/android/module_context.go b/android/module_context.go
index fe9429c..f279fd9 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -716,6 +716,17 @@
 				implicitDeps = append(implicitDeps, extraZip.zip)
 			}
 
+			var cpFlags = "-f"
+
+			// If this is a device file, copy while preserving timestamps. This is to support
+			// adb sync in soong-only builds. Because soong-only builds have 2 different staging
+			// directories, the out/target/product one and the out/soong/.intermediates one,
+			// we need to ensure the files in them have the same timestamps so that adb sync doesn't
+			// update the files on device.
+			if strings.Contains(fullInstallPath.String(), "target/product") {
+				cpFlags += " -p"
+			}
+
 			m.Build(pctx, BuildParams{
 				Rule:        rule,
 				Description: "install " + fullInstallPath.Base(),
@@ -725,7 +736,7 @@
 				OrderOnly:   orderOnlyDeps,
 				Args: map[string]string{
 					"extraCmds": extraCmds,
-					"cpFlags":   "-f",
+					"cpFlags":   cpFlags,
 				},
 			})
 		}
diff --git a/android/packaging.go b/android/packaging.go
index d216c0c..551fd4c 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -598,12 +598,12 @@
 func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, specs map[string]PackagingSpec, dir WritablePath) (entries []string) {
 	dirsToSpecs := make(map[WritablePath]map[string]PackagingSpec)
 	dirsToSpecs[dir] = specs
-	return p.CopySpecsToDirs(ctx, builder, dirsToSpecs)
+	return p.CopySpecsToDirs(ctx, builder, dirsToSpecs, false)
 }
 
 // CopySpecsToDirs is a helper that will add commands to the rule builder to copy the PackagingSpec
 // entries into corresponding directories.
-func (p *PackagingBase) CopySpecsToDirs(ctx ModuleContext, builder *RuleBuilder, dirsToSpecs map[WritablePath]map[string]PackagingSpec) (entries []string) {
+func (p *PackagingBase) CopySpecsToDirs(ctx ModuleContext, builder *RuleBuilder, dirsToSpecs map[WritablePath]map[string]PackagingSpec, preserveTimestamps bool) (entries []string) {
 	empty := true
 	for _, specs := range dirsToSpecs {
 		if len(specs) > 0 {
@@ -637,7 +637,11 @@
 				builder.Command().Textf("mkdir -p %s", destDir)
 			}
 			if ps.symlinkTarget == "" {
-				builder.Command().Text("cp").Input(ps.srcPath).Text(destPath)
+				cmd := builder.Command().Text("cp")
+				if preserveTimestamps {
+					cmd.Flag("-p")
+				}
+				cmd.Input(ps.srcPath).Text(destPath)
 			} else {
 				builder.Command().Textf("ln -sf %s %s", ps.symlinkTarget, destPath)
 			}
diff --git a/android/util.go b/android/util.go
index 2673a3a..8591cc6 100644
--- a/android/util.go
+++ b/android/util.go
@@ -213,10 +213,12 @@
 }
 
 // ListSetDifference checks if the two lists contain the same elements. It returns
-// a boolean which is true if there is a difference, and then returns lists of elements
+// a boolean which is true if there is a difference, and then returns lists of unique elements
 // that are in l1 but not l2, and l2 but not l1.
 func ListSetDifference[T comparable](l1, l2 []T) (bool, []T, []T) {
 	listsDiffer := false
+	l1 = firstUnique(l1)
+	l2 = firstUnique(l2)
 	diff1 := []T{}
 	diff2 := []T{}
 	m1 := setFromList(l1)
diff --git a/android/variable.go b/android/variable.go
index aace6ed..81999f3 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -666,6 +666,7 @@
 	AbOtaUpdater                      bool                                     `json:",omitempty"`
 	AbOtaPartitions                   []string                                 `json:",omitempty"`
 	AbOtaKeys                         []string                                 `json:",omitempty"`
+	AbOtaPostInstallConfig            []string                                 `json:",omitempty"`
 
 	// Avb (android verified boot) stuff
 	BoardAvbEnable          bool                                `json:",omitempty"`
@@ -712,6 +713,8 @@
 	ProductFsCasefold    string `json:",omitempty"`
 	ProductQuotaProjid   string `json:",omitempty"`
 	ProductFsCompression string `json:",omitempty"`
+
+	ReleaseToolsExtensionDir string `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/filesystem/android_device.go b/filesystem/android_device.go
index eb967ad..8d7f92f 100644
--- a/filesystem/android_device.go
+++ b/filesystem/android_device.go
@@ -70,9 +70,13 @@
 	// blueprint:"mutated" and still set it from filesystem_creator
 	Main_device *bool
 
-	Ab_ota_updater    *bool
-	Ab_ota_partitions []string
-	Ab_ota_keys       []string
+	Ab_ota_updater            *bool
+	Ab_ota_partitions         []string
+	Ab_ota_keys               []string
+	Ab_ota_postinstall_config []string
+
+	Ramdisk_node_list      *string `android:"path"`
+	Releasetools_extension *string `android:"path"`
 }
 
 type androidDevice struct {
@@ -454,11 +458,45 @@
 		abOtaKeysSorted := android.SortedUniqueStrings(a.deviceProps.Ab_ota_keys)
 		abOtaKeysSortedString := proptools.ShellEscape(strings.Join(abOtaKeysSorted, "\\n"))
 		builder.Command().Textf("echo -e").Flag(abOtaKeysSortedString).Textf(" > %s/META/otakeys.txt", targetFilesDir.String())
+		// postinstall_config.txt
+		abOtaPostInstallConfigString := proptools.ShellEscape(strings.Join(a.deviceProps.Ab_ota_postinstall_config, "\\n"))
+		builder.Command().Textf("echo -e").Flag(abOtaPostInstallConfigString).Textf(" > %s/META/postinstall_config.txt", targetFilesDir.String())
 		// selinuxfc
 		if a.getFsInfos(ctx)["system"].SelinuxFc != nil {
 			builder.Command().Textf("cp").Input(a.getFsInfos(ctx)["system"].SelinuxFc).Textf(" %s/META/file_contexts.bin", targetFilesDir.String())
 		}
 	}
+	// Copy $partition_filesystem_config.txt
+	fsInfos := a.getFsInfos(ctx)
+	for _, partition := range android.SortedKeys(fsInfos) {
+		if fsInfos[partition].FilesystemConfig == nil {
+			continue
+		}
+		if android.InList(partition, []string{"userdata"}) {
+			continue
+		}
+		builder.Command().Textf("cp").Input(fsInfos[partition].FilesystemConfig).Textf(" %s/META/%s", targetFilesDir.String(), a.filesystemConfigNameForTargetFiles(partition))
+	}
+	// Copy ramdisk_node_list
+	if ramdiskNodeList := android.PathForModuleSrc(ctx, proptools.String(a.deviceProps.Ramdisk_node_list)); ramdiskNodeList != nil {
+		builder.Command().Textf("cp").Input(ramdiskNodeList).Textf(" %s/META/", targetFilesDir.String())
+	}
+	// Copy releasetools.py
+	if releaseTools := android.PathForModuleSrc(ctx, proptools.String(a.deviceProps.Releasetools_extension)); releaseTools != nil {
+		builder.Command().Textf("cp").Input(releaseTools).Textf(" %s/META/", targetFilesDir.String())
+	}
+}
+
+// Filenames for the partition specific fs_config files.
+// Hardcode the ramdisk files to their boot image prefix
+func (a *androidDevice) filesystemConfigNameForTargetFiles(partition string) string {
+	name := partition + "_filesystem_config.txt"
+	if partition == "system" {
+		name = "filesystem_config.txt"
+	} else if partition == "ramdisk" {
+		name = "init_boot_filesystem_config.txt"
+	}
+	return name
 }
 
 func (a *androidDevice) getFilesystemInfo(ctx android.ModuleContext, depName string) FilesystemInfo {
diff --git a/filesystem/android_device_product_out.go b/filesystem/android_device_product_out.go
index 2c8cad6..7d37f1e 100644
--- a/filesystem/android_device_product_out.go
+++ b/filesystem/android_device_product_out.go
@@ -73,6 +73,12 @@
 							Rule:   android.CpWithBash,
 							Input:  fip.SourcePath,
 							Output: fip.FullInstallPath,
+							Args: map[string]string{
+								// Preserve timestamps for adb sync, so that this installed file's
+								// timestamp matches the timestamp in the filesystem's intermediate
+								// staging dir
+								"cpFlags": "-p",
+							},
 						})
 					} else {
 						ctx.Build(pctx, android.BuildParams{
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 0381951..f84993d 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -37,6 +37,7 @@
 	registerBuildComponents(android.InitRegistrationContext)
 	registerMutators(android.InitRegistrationContext)
 	pctx.HostBinToolVariable("fileslist", "fileslist")
+	pctx.HostBinToolVariable("fs_config", "fs_config")
 }
 
 func registerBuildComponents(ctx android.RegistrationContext) {
@@ -72,6 +73,10 @@
 		Command:     `build/make/tools/fileslist_util.py -c ${in} > ${out}`,
 		CommandDeps: []string{"build/make/tools/fileslist_util.py"},
 	})
+	fsConfigRule = pctx.AndroidStaticRule("fs_config_rule", blueprint.RuleParams{
+		Command:     `(cd ${rootDir}; find . -type d | sed 's,$$,/,'; find . \! -type d) | cut -c 3- | sort | sed 's,^,${prefix},' | ${fs_config} -C -D ${rootDir} -R "${prefix}" > ${out}`,
+		CommandDeps: []string{"${fs_config}"},
+	}, "rootDir", "prefix")
 )
 
 type filesystem struct {
@@ -421,6 +426,8 @@
 	ErofsCompressHints android.Path
 
 	SelinuxFc android.Path
+
+	FilesystemConfig android.Path
 }
 
 // FullInstallPathInfo contains information about the "full install" paths of all the files
@@ -665,6 +672,7 @@
 		},
 		ErofsCompressHints: erofsCompressHints,
 		SelinuxFc:          f.selinuxFc,
+		FilesystemConfig:   f.generateFilesystemConfig(ctx, rootDir, rebasedDir),
 	}
 
 	android.SetProvider(ctx, FilesystemProvider, fsInfo)
@@ -682,6 +690,30 @@
 	return android.PathForModuleOut(ctx, "staging_dir.timestamp")
 }
 
+func (f *filesystem) generateFilesystemConfig(ctx android.ModuleContext, rootDir android.Path, rebasedDir android.Path) android.Path {
+	rootDirString := rootDir.String()
+	prefix := f.partitionName() + "/"
+	if f.partitionName() == "system" {
+		rootDirString = rebasedDir.String()
+	}
+	if f.partitionName() == "ramdisk" || f.partitionName() == "recovery" {
+		// Hardcoded to match make behavior.
+		// https://cs.android.com/android/_/android/platform/build/+/2a0ef42a432d4da00201e8eb7697dcaa68fd2389:core/Makefile;l=6957-6962;drc=9ea8ad9232cef4d0a24d70133b1b9d2ce2defe5f;bpv=1;bpt=0
+		prefix = ""
+	}
+	out := android.PathForModuleOut(ctx, "filesystem_config.txt")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   fsConfigRule,
+		Input:  f.fileystemStagingDirTimestamp(ctx), // assemble the staging directory
+		Output: out,
+		Args: map[string]string{
+			"rootDir": rootDirString,
+			"prefix":  prefix,
+		},
+	})
+	return out
+}
+
 func (f *filesystem) setVbmetaPartitionProvider(ctx android.ModuleContext) {
 	var extractedPublicKey android.ModuleOutPath
 	if f.properties.Avb_private_key != nil {
@@ -868,7 +900,9 @@
 	dirsToSpecs[rootDir] = rootDirSpecs
 	dirsToSpecs[rebasedDir] = rebasedDirSpecs
 
-	return f.CopySpecsToDirs(ctx, builder, dirsToSpecs)
+	// Preserve timestamps for adb sync, so that this staging dir file matches the timestamp in the
+	// out/target/product staging directory.
+	return f.CopySpecsToDirs(ctx, builder, dirsToSpecs, true)
 }
 
 func (f *filesystem) rootDirString() string {
diff --git a/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go
index 9217e1c..c2721d2 100644
--- a/fsgen/filesystem_creator.go
+++ b/fsgen/filesystem_creator.go
@@ -320,6 +320,26 @@
 	return bootloaderFilegroupName, true
 }
 
+func (f *filesystemCreator) createReleaseToolsFilegroup(ctx android.LoadHookContext) (string, bool) {
+	releaseToolsDir := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.ReleaseToolsExtensionDir
+	if releaseToolsDir == "" {
+		return "", false
+	}
+
+	releaseToolsFilegroupName := generatedModuleName(ctx.Config(), "releasetools")
+	filegroupProps := &struct {
+		Name       *string
+		Srcs       []string
+		Visibility []string
+	}{
+		Name:       proptools.StringPtr(releaseToolsFilegroupName),
+		Srcs:       []string{"releasetools.py"},
+		Visibility: []string{"//visibility:public"},
+	}
+	ctx.CreateModuleInDirectory(android.FileGroupFactory, releaseToolsDir, filegroupProps)
+	return releaseToolsFilegroupName, true
+}
+
 func (f *filesystemCreator) createDeviceModule(
 	ctx android.LoadHookContext,
 	partitions allGeneratedPartitionData,
@@ -381,13 +401,19 @@
 	partitionProps.Vbmeta_partitions = vbmetaPartitions
 
 	deviceProps := &filesystem.DeviceProperties{
-		Main_device:       proptools.BoolPtr(true),
-		Ab_ota_updater:    proptools.BoolPtr(ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.AbOtaUpdater),
-		Ab_ota_partitions: ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.AbOtaPartitions,
+		Main_device:               proptools.BoolPtr(true),
+		Ab_ota_updater:            proptools.BoolPtr(ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.AbOtaUpdater),
+		Ab_ota_partitions:         ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.AbOtaPartitions,
+		Ab_ota_postinstall_config: ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.AbOtaPostInstallConfig,
+		Ramdisk_node_list:         proptools.StringPtr(":ramdisk_node_list"),
 	}
+
 	if bootloader, ok := f.createBootloaderFilegroup(ctx); ok {
 		deviceProps.Bootloader = proptools.StringPtr(":" + bootloader)
 	}
+	if releaseTools, ok := f.createReleaseToolsFilegroup(ctx); ok {
+		deviceProps.Releasetools_extension = proptools.StringPtr(":" + releaseTools)
+	}
 
 	ctx.CreateModule(filesystem.AndroidDeviceFactory, baseProps, partitionProps, deviceProps)
 }
diff --git a/fsgen/filesystem_creator_test.go b/fsgen/filesystem_creator_test.go
index 0b6d272..418e48b 100644
--- a/fsgen/filesystem_creator_test.go
+++ b/fsgen/filesystem_creator_test.go
@@ -28,6 +28,16 @@
 
 var prepareForTestWithFsgenBuildComponents = android.FixtureRegisterWithContext(registerBuildComponents)
 
+var prepareMockRamdiksNodeList = android.FixtureMergeMockFs(android.MockFS{
+	"ramdisk_node_list/ramdisk_node_list": nil,
+	"ramdisk_node_list/Android.bp": []byte(`
+		filegroup {
+			name: "ramdisk_node_list",
+			srcs: ["ramdisk_node_list"],
+		}
+	`),
+})
+
 func TestFileSystemCreatorSystemImageProps(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		android.PrepareForIntegrationTestWithAndroid,
@@ -47,6 +57,7 @@
 					},
 				}
 		}),
+		prepareMockRamdiksNodeList,
 		android.FixtureMergeMockFs(android.MockFS{
 			"external/avb/test/data/testkey_rsa4096.pem": nil,
 			"external/avb/test/Android.bp": []byte(`
@@ -116,6 +127,7 @@
 					},
 				}
 		}),
+		prepareMockRamdiksNodeList,
 		android.FixtureMergeMockFs(android.MockFS{
 			"external/avb/test/data/testkey_rsa4096.pem": nil,
 			"build/soong/fsgen/Android.bp": []byte(`
@@ -172,6 +184,7 @@
 				}
 		}),
 		android.PrepareForNativeBridgeEnabled,
+		prepareMockRamdiksNodeList,
 		android.FixtureMergeMockFs(android.MockFS{
 			"external/avb/test/data/testkey_rsa4096.pem": nil,
 			"build/soong/fsgen/Android.bp": []byte(`
@@ -229,6 +242,7 @@
 		android.PrepareForTestWithAllowMissingDependencies,
 		prepareForTestWithFsgenBuildComponents,
 		java.PrepareForTestWithJavaBuildComponents,
+		prepareMockRamdiksNodeList,
 		android.FixtureMergeMockFs(android.MockFS{
 			"external/avb/test/data/testkey_rsa4096.pem": nil,
 			"build/soong/fsgen/Android.bp": []byte(`
@@ -281,6 +295,7 @@
 					},
 				}
 		}),
+		prepareMockRamdiksNodeList,
 		android.FixtureMergeMockFs(android.MockFS{
 			"external/avb/test/data/testkey_rsa4096.pem": nil,
 			"build/soong/fsgen/Android.bp": []byte(`
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 3b6f7bb..6bd1fcc 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -281,6 +281,7 @@
 			"hardware/google/camera/common/hal/aidl_service:aidl_camera_build_version",
 			"tools/tradefederation/core:tradefed_zip",
 			"vendor/google/services/LyricCameraHAL/src/apex:com.google.pixel.camera.hal.manifest",
+			"vendor/google_tradefederation/core:gen_google_tradefed_zip",
 			// go/keep-sorted end
 		}
 		allowlistMap := make(map[string]bool, len(allowlist))