Merge "Introduce module type to autogenerate RROS" into main
diff --git a/android/module.go b/android/module.go
index ce995ad..3bf4f0c 100644
--- a/android/module.go
+++ b/android/module.go
@@ -2450,6 +2450,8 @@
 					return proptools.ConfigurableValueString(v)
 				case "bool":
 					return proptools.ConfigurableValueBool(v == "true")
+				case "string_list":
+					return proptools.ConfigurableValueStringList(strings.Split(v, " "))
 				default:
 					panic("unhandled soong config variable type: " + ty)
 				}
diff --git a/android/neverallow.go b/android/neverallow.go
index 42fecaa..b55baae 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -55,7 +55,7 @@
 	AddNeverAllowRules(createJavaDeviceForHostRules()...)
 	AddNeverAllowRules(createCcSdkVariantRules()...)
 	AddNeverAllowRules(createUncompressDexRules()...)
-	AddNeverAllowRules(createInitFirstStageRules()...)
+	AddNeverAllowRules(createInstallInRootAllowingRules()...)
 	AddNeverAllowRules(createProhibitFrameworkAccessRules()...)
 	AddNeverAllowRules(createCcStubsRule())
 	AddNeverAllowRules(createProhibitHeaderOnlyRule())
@@ -236,15 +236,16 @@
 	}
 }
 
-func createInitFirstStageRules() []Rule {
+func createInstallInRootAllowingRules() []Rule {
 	return []Rule{
 		NeverAllow().
 			Without("name", "init_first_stage_defaults").
 			Without("name", "init_first_stage").
 			Without("name", "init_first_stage.microdroid").
+			Without("name", "librecovery_ui_ext").
 			With("install_in_root", "true").
 			NotModuleType("prebuilt_root").
-			Because("install_in_root is only for init_first_stage."),
+			Because("install_in_root is only for init_first_stage or librecovery_ui_ext."),
 	}
 }
 
diff --git a/android/rule_builder.go b/android/rule_builder.go
index a157386..83f8b99 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -488,21 +488,15 @@
 		Inputs(depFiles.Paths())
 }
 
-// BuildWithNinjaVars adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
-// Outputs. This function will not escape Ninja variables, so it may be used to write sandbox manifests using Ninja variables.
-func (r *RuleBuilder) BuildWithUnescapedNinjaVars(name string, desc string) {
-	r.build(name, desc, false)
-}
-
 // Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
 // Outputs.
 func (r *RuleBuilder) Build(name string, desc string) {
-	r.build(name, desc, true)
+	r.build(name, desc)
 }
 
 var sandboxEnvOnceKey = NewOnceKey("sandbox_environment_variables")
 
-func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString bool) {
+func (r *RuleBuilder) build(name string, desc string) {
 	name = ninjaNameEscape(name)
 
 	if len(r.missingDeps) > 0 {
@@ -765,30 +759,7 @@
 		if err != nil {
 			ReportPathErrorf(r.ctx, "sbox manifest failed to marshal: %q", err)
 		}
-		if ninjaEscapeCommandString {
-			WriteFileRule(r.ctx, r.sboxManifestPath, string(pbText))
-		} else {
-			// We need  to have a rule to write files that is
-			// defined on the RuleBuilder's pctx in order to
-			// write Ninja variables in the string.
-			// The WriteFileRule function above rule can only write
-			// raw strings because it is defined on the android
-			// package's pctx, and it can't access variables defined
-			// in another context.
-			r.ctx.Build(r.pctx, BuildParams{
-				Rule: r.ctx.Rule(r.pctx, "unescapedWriteFile", blueprint.RuleParams{
-					Command:        `rm -rf ${out} && cat ${out}.rsp > ${out}`,
-					Rspfile:        "${out}.rsp",
-					RspfileContent: "${content}",
-					Description:    "write file",
-				}, "content"),
-				Output:      r.sboxManifestPath,
-				Description: "write sbox manifest " + r.sboxManifestPath.Base(),
-				Args: map[string]string{
-					"content": string(pbText),
-				},
-			})
-		}
+		WriteFileRule(r.ctx, r.sboxManifestPath, string(pbText))
 
 		// Generate a new string to use as the command line of the sbox rule.  This uses
 		// a RuleBuilderCommand as a convenience method of building the command line, then
@@ -882,9 +853,7 @@
 		pool = localPool
 	}
 
-	if ninjaEscapeCommandString {
-		commandString = proptools.NinjaEscape(commandString)
-	}
+	commandString = proptools.NinjaEscape(commandString)
 
 	args_vars := make([]string, len(r.args))
 	i := 0
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index 6a8a964..e1a1e08 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -475,10 +475,9 @@
 		Srcs  []string
 		Flags []string
 
-		Restat              bool
-		Sbox                bool
-		Sbox_inputs         bool
-		Unescape_ninja_vars bool
+		Restat      bool
+		Sbox        bool
+		Sbox_inputs bool
 	}
 }
 
@@ -498,7 +497,7 @@
 
 	testRuleBuilder_Build(ctx, in, implicit, orderOnly, validation, t.properties.Flags,
 		out, outDep, outDir,
-		manifestPath, t.properties.Restat, t.properties.Sbox, t.properties.Sbox_inputs, t.properties.Unescape_ninja_vars,
+		manifestPath, t.properties.Restat, t.properties.Sbox, t.properties.Sbox_inputs,
 		rspFile, rspFileContents, rspFile2, rspFileContents2)
 }
 
@@ -523,14 +522,14 @@
 	manifestPath := PathForOutput(ctx, "singleton/sbox.textproto")
 
 	testRuleBuilder_Build(ctx, in, implicit, orderOnly, validation, nil, out, outDep, outDir,
-		manifestPath, true, false, false, false,
+		manifestPath, true, false, false,
 		rspFile, rspFileContents, rspFile2, rspFileContents2)
 }
 
 func testRuleBuilder_Build(ctx BuilderContext, in Paths, implicit, orderOnly, validation Path,
 	flags []string,
 	out, outDep, outDir, manifestPath WritablePath,
-	restat, sbox, sboxInputs, unescapeNinjaVars bool,
+	restat, sbox, sboxInputs bool,
 	rspFile WritablePath, rspFileContents Paths, rspFile2 WritablePath, rspFileContents2 Paths) {
 
 	rule := NewRuleBuilder(pctx_ruleBuilderTest, ctx)
@@ -558,11 +557,7 @@
 		rule.Restat()
 	}
 
-	if unescapeNinjaVars {
-		rule.BuildWithUnescapedNinjaVars("rule", "desc")
-	} else {
-		rule.Build("rule", "desc")
-	}
+	rule.Build("rule", "desc")
 }
 
 var prepareForRuleBuilderTest = FixtureRegisterWithContext(func(ctx RegistrationContext) {
@@ -777,48 +772,3 @@
 		})
 	}
 }
-
-func TestRuleBuilderWithNinjaVarEscaping(t *testing.T) {
-	bp := `
-		rule_builder_test {
-			name: "foo_sbox_escaped",
-			flags: ["${cmdFlags}"],
-			sbox: true,
-			sbox_inputs: true,
-		}
-		rule_builder_test {
-			name: "foo_sbox_unescaped",
-			flags: ["${cmdFlags}"],
-			sbox: true,
-			sbox_inputs: true,
-			unescape_ninja_vars: true,
-		}
-	`
-	result := GroupFixturePreparers(
-		prepareForRuleBuilderTest,
-		FixtureWithRootAndroidBp(bp),
-	).RunTest(t)
-
-	escapedNinjaMod := result.ModuleForTests("foo_sbox_escaped", "").Output("sbox.textproto")
-	AssertStringEquals(t, "expected rule", "android/soong/android.rawFileCopy", escapedNinjaMod.Rule.String())
-	AssertStringDoesContain(
-		t,
-		"",
-		ContentFromFileRuleForTests(t, result.TestContext, escapedNinjaMod),
-		"${cmdFlags}",
-	)
-
-	unescapedNinjaMod := result.ModuleForTests("foo_sbox_unescaped", "").Rule("unescapedWriteFile")
-	AssertStringDoesContain(
-		t,
-		"",
-		unescapedNinjaMod.BuildParams.Args["content"],
-		"${cmdFlags}",
-	)
-	AssertStringDoesNotContain(
-		t,
-		"",
-		unescapedNinjaMod.BuildParams.Args["content"],
-		"$${cmdFlags}",
-	)
-}
diff --git a/android/selects_test.go b/android/selects_test.go
index 90d7091..1397ed8 100644
--- a/android/selects_test.go
+++ b/android/selects_test.go
@@ -1031,6 +1031,54 @@
 				my_string_list: &[]string{"d2", "e2", "f2", "a1", "b1", "c1"},
 			},
 		},
+		{
+			name: "string list variables",
+			bp: `
+my_module_type {
+	name: "foo",
+	my_string_list: ["a"] + select(soong_config_variable("my_namespace", "my_var"), {
+		any @ my_var: my_var,
+		default: [],
+	}),
+}
+`,
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_var": "b c",
+				},
+			},
+			vendorVarTypes: map[string]map[string]string{
+				"my_namespace": {
+					"my_var": "string_list",
+				},
+			},
+			provider: selectsTestProvider{
+				my_string_list: &[]string{"a", "b", "c"},
+			},
+		},
+		{
+			name: "string list variables don't match string matchers",
+			bp: `
+my_module_type {
+	name: "foo",
+	my_string_list: ["a"] + select(soong_config_variable("my_namespace", "my_var"), {
+		"foo": ["b"],
+		default: [],
+	}),
+}
+`,
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_var": "b c",
+				},
+			},
+			vendorVarTypes: map[string]map[string]string{
+				"my_namespace": {
+					"my_var": "string_list",
+				},
+			},
+			expectedError: `Expected all branches of a select on condition soong_config_variable\("my_namespace", "my_var"\) to have type string_list, found string`,
+		},
 	}
 
 	for _, tc := range testCases {
diff --git a/apex/apex.go b/apex/apex.go
index 0b56bf8..0e40d7c 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -434,6 +434,7 @@
 	archProperties        apexArchBundleProperties
 	overridableProperties overridableProperties
 	vndkProperties        apexVndkProperties // only for apex_vndk modules
+	testProperties        apexTestProperties // only for apex_test modules
 
 	///////////////////////////////////////////////////////////////////////////////////////////
 	// Inputs
@@ -1296,6 +1297,23 @@
 	return proptools.BoolDefault(a.properties.Platform_apis, false)
 }
 
+type apexValidationType int
+
+const (
+	hostApexVerifier apexValidationType = iota
+	apexSepolicyTests
+)
+
+func (a *apexBundle) skipValidation(validationType apexValidationType) bool {
+	switch validationType {
+	case hostApexVerifier:
+		return proptools.Bool(a.testProperties.Skip_validations.Host_apex_verifier)
+	case apexSepolicyTests:
+		return proptools.Bool(a.testProperties.Skip_validations.Apex_sepolicy_tests)
+	}
+	panic("Unknown validation type")
+}
+
 // getCertString returns the name of the cert that should be used to sign this APEX. This is
 // basically from the "certificate" property, but could be overridden by the device config.
 func (a *apexBundle) getCertString(ctx android.BaseModuleContext) string {
@@ -2427,10 +2445,14 @@
 	return module
 }
 
-func ApexBundleFactory(testApex bool) android.Module {
-	bundle := newApexBundle()
-	bundle.testApex = testApex
-	return bundle
+type apexTestProperties struct {
+	// Boolean flags for validation checks. Test APEXes can turn on/off individual checks.
+	Skip_validations struct {
+		// Skips `Apex_sepolicy_tests` check if true
+		Apex_sepolicy_tests *bool
+		// Skips `Host_apex_verifier` check if true
+		Host_apex_verifier *bool
+	}
 }
 
 // apex_test is an APEX for testing. The difference from the ordinary apex module type is that
@@ -2438,6 +2460,7 @@
 func TestApexBundleFactory() android.Module {
 	bundle := newApexBundle()
 	bundle.testApex = true
+	bundle.AddProperties(&bundle.testProperties)
 	return bundle
 }
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 5b5fe5f..6e92959 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -7101,6 +7101,51 @@
 	`)
 }
 
+func TestApexValidation_TestApexCanSkipInitRcCheck(t *testing.T) {
+	t.Parallel()
+	ctx := testApex(t, `
+		apex_test {
+			name: "myapex",
+			key: "myapex.key",
+			skip_validations: {
+				host_apex_verifier: true,
+			},
+			updatable: false,
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+
+	validations := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("signapk").Validations.Strings()
+	if android.SuffixInList(validations, "host_apex_verifier.timestamp") {
+		t.Error("should not run host_apex_verifier")
+	}
+}
+
+func TestApexValidation_TestApexCheckInitRc(t *testing.T) {
+	t.Parallel()
+	ctx := testApex(t, `
+		apex_test {
+			name: "myapex",
+			key: "myapex.key",
+			updatable: false,
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+
+	validations := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("signapk").Validations.Strings()
+	if !android.SuffixInList(validations, "host_apex_verifier.timestamp") {
+		t.Error("should run host_apex_verifier")
+	}
+}
+
 func TestOverrideApex(t *testing.T) {
 	t.Parallel()
 	ctx := testApex(t, `
diff --git a/apex/builder.go b/apex/builder.go
index d0acc8d..e5ae106 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -924,14 +924,14 @@
 	var validations android.Paths
 	validations = append(validations, runApexLinkerconfigValidation(ctx, unsignedOutputFile, imageDir))
 	// TODO(b/279688635) deapexer supports [ext4]
-	if !a.testApex && suffix == imageApexSuffix && ext4 == a.payloadFsType {
+	if !a.skipValidation(apexSepolicyTests) && suffix == imageApexSuffix && ext4 == a.payloadFsType {
 		validations = append(validations, runApexSepolicyTests(ctx, unsignedOutputFile))
 	}
 	if !a.testApex && len(a.properties.Unwanted_transitive_deps) > 0 {
 		validations = append(validations,
 			runApexElfCheckerUnwanted(ctx, unsignedOutputFile, a.properties.Unwanted_transitive_deps))
 	}
-	if !a.testApex && android.InList(a.payloadFsType, []fsType{ext4, erofs}) {
+	if !a.skipValidation(hostApexVerifier) && android.InList(a.payloadFsType, []fsType{ext4, erofs}) {
 		validations = append(validations, runApexHostVerifier(ctx, a, unsignedOutputFile))
 	}
 	ctx.Build(pctx, android.BuildParams{
@@ -1220,7 +1220,7 @@
 // $ deapexer list -Z {apex_file} > {file_contexts}
 // $ apex_sepolicy_tests -f {file_contexts}
 func runApexSepolicyTests(ctx android.ModuleContext, apexFile android.Path) android.Path {
-	timestamp := android.PathForModuleOut(ctx, "sepolicy_tests.timestamp")
+	timestamp := android.PathForModuleOut(ctx, "apex_sepolicy_tests.timestamp")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   apexSepolicyTestsRule,
 		Input:  apexFile,
diff --git a/cc/config/global.go b/cc/config/global.go
index 6984ea4..27aac95 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -296,6 +296,7 @@
 
 		// Allow using VLA CXX extension.
 		"-Wno-vla-cxx-extension",
+		"-Wno-cast-function-type-mismatch",
 	}
 
 	noOverride64GlobalCflags = []string{}
@@ -384,7 +385,7 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r530567"
+	ClangDefaultVersion      = "clang-r536225"
 	ClangDefaultShortVersion = "19"
 
 	// Directories with warnings from Android.bp files.
diff --git a/cc/linker.go b/cc/linker.go
index f9d58ea..b96d139 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -235,13 +235,16 @@
 	// Generate compact dynamic relocation table, default true.
 	Pack_relocations *bool `android:"arch_variant"`
 
-	// local file name to pass to the linker as --version-script
+	// local file name to pass to the linker as --version-script.  Not supported on darwin, and will fail to build
+	// if provided to the darwin variant of a module.
 	Version_script *string `android:"path,arch_variant"`
 
-	// local file name to pass to the linker as --dynamic-list
+	// local file name to pass to the linker as --dynamic-list.  Not supported on darwin, and will fail to build
+	// if provided to the darwin variant of a module.
 	Dynamic_list *string `android:"path,arch_variant"`
 
-	// local files to pass to the linker as --script
+	// local files to pass to the linker as --script.  Not supported on darwin or windows, and will fail to build
+	// if provided to the darwin or windows variant of a module.
 	Linker_scripts []string `android:"path,arch_variant"`
 
 	// list of static libs that should not be used to build this module
@@ -560,7 +563,7 @@
 
 		if versionScript.Valid() {
 			if ctx.Darwin() {
-				ctx.PropertyErrorf("version_script", "Not supported on Darwin")
+				ctx.AddMissingDependencies([]string{"version_script_not_supported_on_darwin"})
 			} else {
 				flags.Local.LdFlags = append(flags.Local.LdFlags,
 					config.VersionScriptFlagPrefix+versionScript.String())
@@ -578,7 +581,7 @@
 		dynamicList := android.OptionalPathForModuleSrc(ctx, linker.Properties.Dynamic_list)
 		if dynamicList.Valid() {
 			if ctx.Darwin() {
-				ctx.PropertyErrorf("dynamic_list", "Not supported on Darwin")
+				ctx.AddMissingDependencies([]string{"dynamic_list_not_supported_on_darwin"})
 			} else {
 				flags.Local.LdFlags = append(flags.Local.LdFlags,
 					"-Wl,--dynamic-list,"+dynamicList.String())
@@ -587,13 +590,17 @@
 		}
 
 		linkerScriptPaths := android.PathsForModuleSrc(ctx, linker.Properties.Linker_scripts)
-		if len(linkerScriptPaths) > 0 && (ctx.Darwin() || ctx.Windows()) {
-			ctx.PropertyErrorf("linker_scripts", "Only supported for ELF files")
-		} else {
-			for _, linkerScriptPath := range linkerScriptPaths {
-				flags.Local.LdFlags = append(flags.Local.LdFlags,
-					"-Wl,--script,"+linkerScriptPath.String())
-				flags.LdFlagsDeps = append(flags.LdFlagsDeps, linkerScriptPath)
+		if len(linkerScriptPaths) > 0 {
+			if ctx.Darwin() {
+				ctx.AddMissingDependencies([]string{"linker_scripts_not_supported_on_darwin"})
+			} else if ctx.Windows() {
+				ctx.PropertyErrorf("linker_scripts", "Only supported for ELF files")
+			} else {
+				for _, linkerScriptPath := range linkerScriptPaths {
+					flags.Local.LdFlags = append(flags.Local.LdFlags,
+						"-Wl,--script,"+linkerScriptPath.String())
+					flags.LdFlagsDeps = append(flags.LdFlagsDeps, linkerScriptPath)
+				}
 			}
 		}
 	}
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
index c8e27e5..0ffec26 100644
--- a/filesystem/bootimg.go
+++ b/filesystem/bootimg.go
@@ -330,9 +330,17 @@
 		cmd.Flag("--dynamic_partition_size")
 	}
 
+	// If you don't provide a salt, avbtool will use random bytes for the salt.
+	// This is bad for determinism (cached builds and diff tests are affected), so instead,
+	// we try to provide a salt. The requirements for a salt are not very clear, one aspect of it
+	// is that if it's unpredictable, attackers trying to change the contents of a partition need
+	// to find a new hash collision every release, because the salt changed.
 	if kernel != nil {
 		cmd.Textf(`--salt $(sha256sum "%s" | cut -d " " -f 1)`, kernel.String())
 		cmd.Implicit(kernel)
+	} else {
+		cmd.Textf(`--salt $(sha256sum "%s" "%s" | cut -d " " -f 1 | tr -d '\n')`, ctx.Config().BuildNumberFile(ctx), ctx.Config().Getenv("BUILD_DATETIME_FILE"))
+		cmd.OrderOnly(ctx.Config().BuildNumberFile(ctx))
 	}
 
 	cmd.FlagWithArg("--partition_name ", b.bootImageType.String())