Merge "Upload build.trace.gz for user builds." into main
diff --git a/aconfig/build_flags/Android.bp b/aconfig/build_flags/Android.bp
new file mode 100644
index 0000000..b3c7339
--- /dev/null
+++ b/aconfig/build_flags/Android.bp
@@ -0,0 +1,24 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-aconfig-build_flags",
+    pkgPath: "android/soong/aconfig/build_flags",
+    deps: [
+        "blueprint",
+        "blueprint-pathtools",
+        "sbox_proto",
+        "soong",
+        "soong-android",
+    ],
+    srcs: [
+        "all_build_flag_declarations.go",
+        "build_flags.go",
+        "declarations.go",
+        "init.go",
+    ],
+    testSrcs: [
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/aconfig/build_flags/all_build_flag_declarations.go b/aconfig/build_flags/all_build_flag_declarations.go
new file mode 100644
index 0000000..282c9dc
--- /dev/null
+++ b/aconfig/build_flags/all_build_flag_declarations.go
@@ -0,0 +1,78 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build_flags
+
+import (
+	"android/soong/android"
+)
+
+// A singleton module that collects all of the build flags declared in the
+// tree into a single combined file for export to the external flag setting
+// server (inside Google it's Gantry).
+//
+// Note that this is ALL build_declarations modules present in the tree, not just
+// ones that are relevant to the product currently being built, so that that infra
+// doesn't need to pull from multiple builds and merge them.
+func AllBuildFlagDeclarationsFactory() android.Singleton {
+	return &allBuildFlagDeclarationsSingleton{}
+}
+
+type allBuildFlagDeclarationsSingleton struct {
+	intermediateBinaryProtoPath android.OutputPath
+	intermediateTextProtoPath   android.OutputPath
+}
+
+func (this *allBuildFlagDeclarationsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	// Find all of the build_flag_declarations modules
+	var intermediateFiles android.Paths
+	ctx.VisitAllModules(func(module android.Module) {
+		decl, ok := android.SingletonModuleProvider(ctx, module, BuildFlagDeclarationsProviderKey)
+		if !ok {
+			return
+		}
+		intermediateFiles = append(intermediateFiles, decl.IntermediateCacheOutputPath)
+	})
+
+	// Generate build action for build_flag (binary proto output)
+	this.intermediateBinaryProtoPath = android.PathForIntermediates(ctx, "all_build_flag_declarations.pb")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        allDeclarationsRule,
+		Inputs:      intermediateFiles,
+		Output:      this.intermediateBinaryProtoPath,
+		Description: "all_build_flag_declarations",
+		Args: map[string]string{
+			"intermediates": android.JoinPathsWithPrefix(intermediateFiles, "--intermediate "),
+		},
+	})
+	ctx.Phony("all_build_flag_declarations", this.intermediateBinaryProtoPath)
+
+	// Generate build action for build_flag (text proto output)
+	this.intermediateTextProtoPath = android.PathForIntermediates(ctx, "all_build_flag_declarations.textproto")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        allDeclarationsRuleTextProto,
+		Input:       this.intermediateBinaryProtoPath,
+		Output:      this.intermediateTextProtoPath,
+		Description: "all_build_flag_declarations_textproto",
+	})
+	ctx.Phony("all_build_flag_declarations_textproto", this.intermediateTextProtoPath)
+}
+
+func (this *allBuildFlagDeclarationsSingleton) MakeVars(ctx android.MakeVarsContext) {
+	ctx.DistForGoal("droid", this.intermediateBinaryProtoPath)
+	for _, goal := range []string{"docs", "droid", "sdk"} {
+		ctx.DistForGoalWithFilename(goal, this.intermediateBinaryProtoPath, "build_flags/all_flags.pb")
+		ctx.DistForGoalWithFilename(goal, this.intermediateTextProtoPath, "build_flags/all_flags.textproto")
+	}
+}
diff --git a/aconfig/build_flags/build_flags.go b/aconfig/build_flags/build_flags.go
new file mode 100644
index 0000000..e878b5a
--- /dev/null
+++ b/aconfig/build_flags/build_flags.go
@@ -0,0 +1,71 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build_flags
+
+import (
+	"fmt"
+
+	"android/soong/android"
+)
+
+const (
+	outJsonFileName = "build_flags.json"
+)
+
+func init() {
+	registerBuildFlagsModuleType(android.InitRegistrationContext)
+}
+
+func registerBuildFlagsModuleType(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("build_flags_json", buildFlagsFactory)
+}
+
+type buildFlags struct {
+	android.ModuleBase
+
+	outputPath android.OutputPath
+}
+
+func buildFlagsFactory() android.Module {
+	module := &buildFlags{}
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	return module
+}
+
+func (m *buildFlags) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Read the build_flags_<partition>.json file generated by soong
+	// 'release-config' command.
+	srcPath := android.PathForOutput(ctx, "release-config", fmt.Sprintf("build_flags_%s.json", m.PartitionTag(ctx.DeviceConfig())))
+	m.outputPath = android.PathForModuleOut(ctx, outJsonFileName).OutputPath
+
+	// The 'release-config' command is called for every build, and generates the
+	// build_flags_<partition>.json file.
+	// Update the output file only if the source file is changed.
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.CpIfChanged,
+		Input:  srcPath,
+		Output: m.outputPath,
+	})
+
+	installPath := android.PathForModuleInstall(ctx, "etc")
+	ctx.InstallFile(installPath, outJsonFileName, m.outputPath)
+}
+
+func (m *buildFlags) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(m.outputPath),
+	}}
+}
diff --git a/aconfig/build_flags/declarations.go b/aconfig/build_flags/declarations.go
new file mode 100644
index 0000000..f6a6ee1
--- /dev/null
+++ b/aconfig/build_flags/declarations.go
@@ -0,0 +1,118 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build_flags
+
+import (
+	"fmt"
+	"strings"
+
+	"android/soong/android"
+
+	"github.com/google/blueprint"
+)
+
+type BuildFlagDeclarationsProviderData struct {
+	IntermediateCacheOutputPath android.WritablePath
+	IntermediateDumpOutputPath  android.WritablePath
+}
+
+var BuildFlagDeclarationsProviderKey = blueprint.NewProvider[BuildFlagDeclarationsProviderData]()
+
+type DeclarationsModule struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+
+	// Properties for "aconfig_declarations"
+	properties struct {
+		// aconfig files, relative to this Android.bp file
+		Srcs []string `android:"path"`
+	}
+
+	intermediatePath android.WritablePath
+}
+
+func DeclarationsFactory() android.Module {
+	module := &DeclarationsModule{}
+
+	android.InitAndroidModule(module)
+	android.InitDefaultableModule(module)
+	module.AddProperties(&module.properties)
+
+	return module
+}
+
+func (module *DeclarationsModule) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		// The default output of this module is the intermediates format, which is
+		// not installable and in a private format that no other rules can handle
+		// correctly.
+		return []android.Path{module.intermediatePath}, nil
+	default:
+		return nil, fmt.Errorf("unsupported build_flags_declarations module reference tag %q", tag)
+	}
+}
+
+func joinAndPrefix(prefix string, values []string) string {
+	var sb strings.Builder
+	for _, v := range values {
+		sb.WriteString(prefix)
+		sb.WriteString(v)
+	}
+	return sb.String()
+}
+
+func optionalVariable(prefix string, value string) string {
+	var sb strings.Builder
+	if value != "" {
+		sb.WriteString(prefix)
+		sb.WriteString(value)
+	}
+	return sb.String()
+}
+
+func (module *DeclarationsModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Intermediate format
+	declarationFiles := android.PathsForModuleSrc(ctx, module.properties.Srcs)
+	intermediateCacheFilePath := android.PathForModuleOut(ctx, "build_flag_intermediate.pb")
+	inputFiles := make([]android.Path, len(declarationFiles))
+	copy(inputFiles, declarationFiles)
+
+	// TODO(lamont): generate the rc_proto.FlagArtifacts message for the sources.
+	args := map[string]string{
+		"release_version": ctx.Config().ReleaseVersion(),
+		"declarations":    android.JoinPathsWithPrefix(declarationFiles, "--decl "),
+	}
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        buildFlagRule,
+		Output:      intermediateCacheFilePath,
+		Inputs:      inputFiles,
+		Description: "build_flag_declarations",
+		Args:        args,
+	})
+
+	intermediateDumpFilePath := android.PathForModuleOut(ctx, "build_flag_intermediate.textproto")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        buildFlagTextRule,
+		Output:      intermediateDumpFilePath,
+		Input:       intermediateCacheFilePath,
+		Description: "build_flag_declarations_text",
+	})
+
+	android.SetProvider(ctx, BuildFlagDeclarationsProviderKey, BuildFlagDeclarationsProviderData{
+		IntermediateCacheOutputPath: intermediateCacheFilePath,
+		IntermediateDumpOutputPath:  intermediateDumpFilePath,
+	})
+}
diff --git a/aconfig/build_flags/init.go b/aconfig/build_flags/init.go
new file mode 100644
index 0000000..dc1369c
--- /dev/null
+++ b/aconfig/build_flags/init.go
@@ -0,0 +1,79 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build_flags
+
+import (
+	"android/soong/android"
+
+	"github.com/google/blueprint"
+)
+
+var (
+	pctx = android.NewPackageContext("android/soong/aconfig/build_flags")
+
+	// For build_flag_declarations: Generate cache file
+	buildFlagRule = pctx.AndroidStaticRule("build-flag-declarations",
+		blueprint.RuleParams{
+			Command: `${buildFlagDeclarations} ` +
+				` ${declarations}` +
+				` --format pb` +
+				` --output ${out}.tmp` +
+				` && ( if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`,
+			CommandDeps: []string{
+				"${buildFlagDeclarations}",
+			},
+			Restat: true,
+		}, "release_version", "declarations")
+
+	buildFlagTextRule = pctx.AndroidStaticRule("build-flag-declarations-text",
+		blueprint.RuleParams{
+			Command: `${buildFlagDeclarations} --format=textproto` +
+				` --intermediate ${in}` +
+				` --format textproto` +
+				` --output ${out}.tmp` +
+				` && ( if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`,
+			CommandDeps: []string{
+				"${buildFlagDeclarations}",
+			},
+			Restat: true,
+		})
+
+	allDeclarationsRule = pctx.AndroidStaticRule("all-build-flag-declarations-dump",
+		blueprint.RuleParams{
+			Command: `${buildFlagDeclarations} ${intermediates} --format pb --output ${out}`,
+			CommandDeps: []string{
+				"${buildFlagDeclarations}",
+			},
+		}, "intermediates")
+
+	allDeclarationsRuleTextProto = pctx.AndroidStaticRule("All_build_flag_declarations_dump_textproto",
+		blueprint.RuleParams{
+			Command: `${buildFlagDeclarations} --intermediate ${in} --format textproto --output ${out}`,
+			CommandDeps: []string{
+				"${buildFlagDeclarations}",
+			},
+		})
+)
+
+func init() {
+	RegisterBuildComponents(android.InitRegistrationContext)
+	pctx.Import("android/soong/android")
+	pctx.HostBinToolVariable("buildFlagDeclarations", "build-flag-declarations")
+}
+
+func RegisterBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("build_flag_declarations", DeclarationsFactory)
+	ctx.RegisterParallelSingletonType("all_build_flag_declarations", AllBuildFlagDeclarationsFactory)
+}
diff --git a/aconfig/codegen/cc_aconfig_library_test.go b/aconfig/codegen/cc_aconfig_library_test.go
index d01d13b..2f6c1a6 100644
--- a/aconfig/codegen/cc_aconfig_library_test.go
+++ b/aconfig/codegen/cc_aconfig_library_test.go
@@ -74,11 +74,6 @@
     		srcs: ["libaconfig_storage_read_api_cc.cc"],
 			}
 
-			cc_library {
-    		name: "libaconfig_storage_protos_cc",
-    		srcs: ["libaconfig_storage_protos_cc.cc"],
-			}
-
 			cc_aconfig_library {
 				name: "my_cc_aconfig_library",
 				aconfig_declarations: "my_aconfig_declarations",
@@ -137,12 +132,6 @@
     		srcs: ["libaconfig_storage_read_api_cc.cc"],
 			}
 
-			cc_library {
-    		name: "libaconfig_storage_protos_cc",
-    		srcs: ["libaconfig_storage_protos_cc.cc"],
-			}
-
-
 			cc_aconfig_library {
 				name: "my_cc_aconfig_library",
 				aconfig_declarations: "my_aconfig_declarations",
@@ -214,12 +203,6 @@
 			srcs: ["libaconfig_storage_read_api_cc.cc"],
 			vendor_available: true,
 		}
-
-		cc_library {
-			name: "libaconfig_storage_protos_cc",
-			srcs: ["libaconfig_storage_protos_cc.cc"],
-			vendor_available: true,
-		}
 	`
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithAconfigBuildComponents,
diff --git a/android/apex.go b/android/apex.go
index dc0aeed..2ac6ed0 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -37,11 +37,7 @@
 // Accessible via `ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)`
 type ApexInfo struct {
 	// Name of the apex variation that this module (i.e. the apex variant of the module) is
-	// mutated into, or "" for a platform (i.e. non-APEX) variant. Note that this name and the
-	// Soong module name of the APEX can be different. That happens when there is
-	// `override_apex` that overrides `apex`. In that case, both Soong modules have the same
-	// apex variation name which usually is `com.android.foo`. This name is also the `name`
-	// in the path `/apex/<name>` where this apex is activated on at runtime.
+	// mutated into, or "" for a platform (i.e. non-APEX) variant.
 	//
 	// Also note that a module can be included in multiple APEXes, in which case, the module is
 	// mutated into one or more variants, each of which is for an APEX. The variants then can
@@ -354,7 +350,8 @@
 // ApexModuleBase provides the default implementation for the ApexModule interface. APEX-aware
 // modules are expected to include this struct and call InitApexModule().
 type ApexModuleBase struct {
-	ApexProperties ApexProperties
+	ApexProperties     ApexProperties
+	apexPropertiesLock sync.Mutex // protects ApexProperties during parallel apexDirectlyInAnyMutator
 
 	canHaveApexVariants bool
 
@@ -765,18 +762,22 @@
 
 // UpdateDirectlyInAnyApex uses the final module to store if any variant of this module is directly
 // in any APEX, and then copies the final value to all the modules. It also copies the
-// DirectlyInAnyApex value to any direct dependencies with a CopyDirectlyInAnyApexTag dependency
-// tag.
+// DirectlyInAnyApex value to any transitive dependencies with a CopyDirectlyInAnyApexTag
+// dependency tag.
 func UpdateDirectlyInAnyApex(mctx BottomUpMutatorContext, am ApexModule) {
 	base := am.apexModuleBase()
-	// Copy DirectlyInAnyApex and InAnyApex from any direct dependencies with a
+	// Copy DirectlyInAnyApex and InAnyApex from any transitive dependencies with a
 	// CopyDirectlyInAnyApexTag dependency tag.
-	mctx.VisitDirectDeps(func(dep Module) {
-		if _, ok := mctx.OtherModuleDependencyTag(dep).(CopyDirectlyInAnyApexTag); ok {
-			depBase := dep.(ApexModule).apexModuleBase()
+	mctx.WalkDeps(func(child, parent Module) bool {
+		if _, ok := mctx.OtherModuleDependencyTag(child).(CopyDirectlyInAnyApexTag); ok {
+			depBase := child.(ApexModule).apexModuleBase()
+			depBase.apexPropertiesLock.Lock()
+			defer depBase.apexPropertiesLock.Unlock()
 			depBase.ApexProperties.DirectlyInAnyApex = base.ApexProperties.DirectlyInAnyApex
 			depBase.ApexProperties.InAnyApex = base.ApexProperties.InAnyApex
+			return true
 		}
+		return false
 	})
 
 	if base.ApexProperties.DirectlyInAnyApex {
diff --git a/android/apex_contributions.go b/android/apex_contributions.go
index 91549e5..8b72f8e 100644
--- a/android/apex_contributions.go
+++ b/android/apex_contributions.go
@@ -106,7 +106,13 @@
 
 // Creates a dep to each selected apex_contributions
 func (a *allApexContributions) DepsMutator(ctx BottomUpMutatorContext) {
-	ctx.AddDependency(ctx.Module(), AcDepTag, ctx.Config().AllApexContributions()...)
+	// Skip apex_contributions if BuildApexContributionContents is true
+	// This product config var allows some products in the same family to use mainline modules from source
+	// (e.g. shiba and shiba_fullmte)
+	// Eventually these product variants will have their own release config maps.
+	if !proptools.Bool(ctx.Config().BuildIgnoreApexContributionContents()) {
+		ctx.AddDependency(ctx.Module(), AcDepTag, ctx.Config().AllApexContributions()...)
+	}
 }
 
 // Set PrebuiltSelectionInfoProvider in post deps phase
@@ -126,19 +132,13 @@
 	}
 
 	p := PrebuiltSelectionInfoMap{}
-	// Skip apex_contributions if BuildApexContributionContents is true
-	// This product config var allows some products in the same family to use mainline modules from source
-	// (e.g. shiba and shiba_fullmte)
-	// Eventually these product variants will have their own release config maps.
-	if !proptools.Bool(ctx.Config().BuildIgnoreApexContributionContents()) {
-		ctx.VisitDirectDepsWithTag(AcDepTag, func(child Module) {
-			if m, ok := child.(*apexContributions); ok {
-				addContentsToProvider(&p, m)
-			} else {
-				ctx.ModuleErrorf("%s is not an apex_contributions module\n", child.Name())
-			}
-		})
-	}
+	ctx.VisitDirectDepsWithTag(AcDepTag, func(child Module) {
+		if m, ok := child.(*apexContributions); ok {
+			addContentsToProvider(&p, m)
+		} else {
+			ctx.ModuleErrorf("%s is not an apex_contributions module\n", child.Name())
+		}
+	})
 	SetProvider(ctx, PrebuiltSelectionInfoProvider, p)
 }
 
diff --git a/android/config.go b/android/config.go
index 76c590a..5a6d40f 100644
--- a/android/config.go
+++ b/android/config.go
@@ -827,6 +827,10 @@
 	return value == "0" || value == "n" || value == "no" || value == "off" || value == "false"
 }
 
+func (c *config) TargetsJava21() bool {
+	return c.IsEnvTrue("EXPERIMENTAL_TARGET_JAVA_VERSION_21")
+}
+
 // EnvDeps returns the environment variables this build depends on. The first
 // call to this function blocks future reads from the environment.
 func (c *config) EnvDeps() map[string]string {
@@ -1462,10 +1466,6 @@
 	return c.config.productVariables.ExtraVndkVersions
 }
 
-func (c *deviceConfig) VndkUseCoreVariant() bool {
-	return Bool(c.config.productVariables.VndkUseCoreVariant) && Bool(c.config.productVariables.KeepVndk)
-}
-
 func (c *deviceConfig) SystemSdkVersions() []string {
 	return c.config.productVariables.DeviceSystemSdkVersions
 }
@@ -2116,6 +2116,7 @@
 		"RELEASE_APEX_CONTRIBUTIONS_SDKEXTENSIONS",
 		"RELEASE_APEX_CONTRIBUTIONS_SWCODEC",
 		"RELEASE_APEX_CONTRIBUTIONS_STATSD",
+		"RELEASE_APEX_CONTRIBUTIONS_TELEMETRY_TVP",
 		"RELEASE_APEX_CONTRIBUTIONS_TZDATA",
 		"RELEASE_APEX_CONTRIBUTIONS_UWB",
 		"RELEASE_APEX_CONTRIBUTIONS_WIFI",
diff --git a/android/filegroup.go b/android/filegroup.go
index a8326d4..ff0f74e 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -19,6 +19,7 @@
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 func init() {
@@ -36,9 +37,9 @@
 
 type fileGroupProperties struct {
 	// srcs lists files that will be included in this filegroup
-	Srcs []string `android:"path"`
+	Srcs proptools.Configurable[[]string] `android:"path"`
 
-	Exclude_srcs []string `android:"path"`
+	Exclude_srcs proptools.Configurable[[]string] `android:"path"`
 
 	// The base path to the files.  May be used by other modules to determine which portion
 	// of the path to use.  For example, when a filegroup is used as data in a cc_test rule,
@@ -89,7 +90,7 @@
 }
 
 func (fg *fileGroup) GenerateAndroidBuildActions(ctx ModuleContext) {
-	fg.srcs = PathsForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs)
+	fg.srcs = PathsForModuleSrcExcludes(ctx, fg.properties.Srcs.GetOrDefault(ctx, nil), fg.properties.Exclude_srcs.GetOrDefault(ctx, nil))
 	if fg.properties.Path != nil {
 		fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path))
 	}
diff --git a/android/module.go b/android/module.go
index d4e4c2d..fed1d0e 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1076,6 +1076,12 @@
 			return
 		}
 
+		// ... also don't make a dependency between native bridge arch and non-native bridge
+		// arches. b/342945184
+		if ctx.Target().NativeBridge != target.NativeBridge {
+			return
+		}
+
 		variation := target.Variations()
 		if ctx.OtherModuleFarDependencyVariantExists(variation, depName) {
 			ctx.AddFarVariationDependencies(variation, RequiredDepTag, depName)
@@ -1982,6 +1988,7 @@
 			TargetDependencies: targetRequired,
 			HostDependencies:   hostRequired,
 			Data:               data,
+			Required:           m.RequiredModuleNames(),
 		}
 		SetProvider(ctx, ModuleInfoJSONProvider, m.moduleInfoJSON)
 	}
@@ -2156,14 +2163,34 @@
 			ctx.OtherModulePropertyErrorf(m, property, "release_flag requires 1 argument, found %d", condition.NumArgs())
 			return proptools.ConfigurableValueUndefined()
 		}
-		if v, ok := ctx.Config().productVariables.BuildFlags[condition.Arg(0)]; ok {
-			return proptools.ConfigurableValueString(v)
+		if ty, ok := ctx.Config().productVariables.BuildFlagTypes[condition.Arg(0)]; ok {
+			v := ctx.Config().productVariables.BuildFlags[condition.Arg(0)]
+			switch ty {
+			case "unspecified", "obsolete":
+				return proptools.ConfigurableValueUndefined()
+			case "string":
+				return proptools.ConfigurableValueString(v)
+			case "bool":
+				return proptools.ConfigurableValueBool(v == "true")
+			default:
+				panic("unhandled release flag type: " + ty)
+			}
 		}
 		return proptools.ConfigurableValueUndefined()
 	case "product_variable":
-		// TODO(b/323382414): Might add these on a case-by-case basis
-		ctx.OtherModulePropertyErrorf(m, property, "TODO(b/323382414): Product variables are not yet supported in selects")
-		return proptools.ConfigurableValueUndefined()
+		if condition.NumArgs() != 1 {
+			ctx.OtherModulePropertyErrorf(m, property, "product_variable requires 1 argument, found %d", condition.NumArgs())
+			return proptools.ConfigurableValueUndefined()
+		}
+		variable := condition.Arg(0)
+		switch variable {
+		case "debuggable":
+			return proptools.ConfigurableValueBool(ctx.Config().Debuggable())
+		default:
+			// TODO(b/323382414): Might add these on a case-by-case basis
+			ctx.OtherModulePropertyErrorf(m, property, fmt.Sprintf("TODO(b/323382414): Product variable %q is not yet supported in selects", variable))
+			return proptools.ConfigurableValueUndefined()
+		}
 	case "soong_config_variable":
 		if condition.NumArgs() != 2 {
 			ctx.OtherModulePropertyErrorf(m, property, "soong_config_variable requires 2 arguments, found %d", condition.NumArgs())
diff --git a/android/module_info_json.go b/android/module_info_json.go
index 1c0a38e..ee552dc 100644
--- a/android/module_info_json.go
+++ b/android/module_info_json.go
@@ -17,6 +17,7 @@
 	HostDependencies   []string `json:"host_dependencies,omitempty"`   // $(sort $(ALL_MODULES.$(m).HOST_REQUIRED_FROM_TARGET))
 	TargetDependencies []string `json:"target_dependencies,omitempty"` // $(sort $(ALL_MODULES.$(m).TARGET_REQUIRED_FROM_HOST))
 	Data               []string `json:"data,omitempty"`                // $(sort $(ALL_MODULES.$(m).TEST_DATA))
+	Required           []string `json:"required,omitempty"`            // $(sort $(ALL_MODULES.$(m).REQUIRED_FROM_TARGET))
 }
 
 type ModuleInfoJSON struct {
@@ -77,6 +78,7 @@
 	sortAndUnique(&moduleInfoJSONCopy.core.HostDependencies)
 	sortAndUnique(&moduleInfoJSONCopy.core.TargetDependencies)
 	sortAndUnique(&moduleInfoJSONCopy.core.Data)
+	sortAndUnique(&moduleInfoJSONCopy.core.Required)
 
 	sortAndUnique(&moduleInfoJSONCopy.Class)
 	sortAndUnique(&moduleInfoJSONCopy.Tags)
diff --git a/android/override_module.go b/android/override_module.go
index 21cf381..f69f963 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -28,6 +28,7 @@
 // module based on it.
 
 import (
+	"fmt"
 	"sort"
 	"sync"
 
@@ -120,7 +121,7 @@
 	addOverride(o OverrideModule)
 	getOverrides() []OverrideModule
 
-	override(ctx BaseModuleContext, m Module, o OverrideModule)
+	override(ctx BaseModuleContext, bm OverridableModule, o OverrideModule)
 	GetOverriddenBy() string
 	GetOverriddenByModuleDir() string
 
@@ -191,15 +192,14 @@
 }
 
 // Overrides a base module with the given OverrideModule.
-func (b *OverridableModuleBase) override(ctx BaseModuleContext, m Module, o OverrideModule) {
-
+func (b *OverridableModuleBase) override(ctx BaseModuleContext, bm OverridableModule, o OverrideModule) {
 	for _, p := range b.overridableProperties {
 		for _, op := range o.getOverridingProperties() {
 			if proptools.TypeEqual(p, op) {
 				err := proptools.ExtendProperties(p, op, nil, proptools.OrderReplace)
 				if err != nil {
 					if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
-						ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+						ctx.OtherModulePropertyErrorf(bm, propertyErr.Property, "%s", propertyErr.Err.Error())
 					} else {
 						panic(err)
 					}
@@ -210,7 +210,7 @@
 	// Adds the base module to the overrides property, if exists, of the overriding module. See the
 	// comment on OverridableModuleBase.overridesProperty for details.
 	if b.overridesProperty != nil {
-		*b.overridesProperty = append(*b.overridesProperty, ctx.ModuleName())
+		*b.overridesProperty = append(*b.overridesProperty, ctx.OtherModuleName(bm))
 	}
 	b.overridableModuleProperties.OverriddenBy = o.Name()
 	b.overridableModuleProperties.OverriddenByModuleDir = o.ModuleDir()
@@ -235,7 +235,7 @@
 // to keep them in this order and not put any order mutators between them.
 func RegisterOverridePostDepsMutators(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("override_deps", overrideModuleDepsMutator).Parallel()
-	ctx.BottomUp("perform_override", performOverrideMutator).Parallel()
+	ctx.Transition("override", &overrideTransitionMutator{})
 	// overridableModuleDepsMutator calls OverridablePropertiesDepsMutator so that overridable modules can
 	// add deps from overridable properties.
 	ctx.BottomUp("overridable_deps", overridableModuleDepsMutator).Parallel()
@@ -253,6 +253,15 @@
 
 var overrideBaseDepTag overrideBaseDependencyTag
 
+// Override module should always override the source module.
+// Overrides are implemented as a variant of the overridden module, and the build actions are created in the
+// module context of the overridden module.
+// If we replace override module with the prebuilt of the overridden module, `GenerateAndroidBuildActions` for
+// the override module will have a very different meaning.
+func (tag overrideBaseDependencyTag) ReplaceSourceWithPrebuilt() bool {
+	return false
+}
+
 // Adds dependency on the base module to the overriding module so that they can be visited in the
 // next phase.
 func overrideModuleDepsMutator(ctx BottomUpMutatorContext) {
@@ -262,18 +271,6 @@
 			ctx.PropertyErrorf("base", "%q is not a valid module name", base)
 			return
 		}
-		// See if there's a prebuilt module that overrides this override module with prefer flag,
-		// in which case we call HideFromMake on the corresponding variant later.
-		ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(dep Module) {
-			prebuilt := GetEmbeddedPrebuilt(dep)
-			if prebuilt == nil {
-				panic("PrebuiltDepTag leads to a non-prebuilt module " + dep.Name())
-			}
-			if prebuilt.UsePrebuilt() {
-				module.setOverriddenByPrebuilt(dep)
-				return
-			}
-		})
 		baseModule := ctx.AddDependency(ctx.Module(), overrideBaseDepTag, *module.getOverrideModuleProperties().Base)[0]
 		if o, ok := baseModule.(OverridableModule); ok {
 			overrideModule := ctx.Module().(OverrideModule)
@@ -285,11 +282,13 @@
 
 // Now, goes through all overridable modules, finds all modules overriding them, creates a local
 // variant for each of them, and performs the actual overriding operation by calling override().
-func performOverrideMutator(ctx BottomUpMutatorContext) {
+type overrideTransitionMutator struct{}
+
+func (overrideTransitionMutator) Split(ctx BaseModuleContext) []string {
 	if b, ok := ctx.Module().(OverridableModule); ok {
 		overrides := b.getOverrides()
 		if len(overrides) == 0 {
-			return
+			return []string{""}
 		}
 		variants := make([]string, len(overrides)+1)
 		// The first variant is for the original, non-overridden, base module.
@@ -297,27 +296,69 @@
 		for i, o := range overrides {
 			variants[i+1] = o.(Module).Name()
 		}
-		mods := ctx.CreateLocalVariations(variants...)
-		// Make the original variation the default one to depend on if no other override module variant
-		// is specified.
-		ctx.AliasVariation(variants[0])
-		for i, o := range overrides {
-			mods[i+1].(OverridableModule).override(ctx, mods[i+1], o)
-			if prebuilt := o.getOverriddenByPrebuilt(); prebuilt != nil {
-				// The overriding module itself, too, is overridden by a prebuilt.
-				// Perform the same check for replacement
-				checkInvariantsForSourceAndPrebuilt(ctx, mods[i+1], prebuilt)
-				// Copy the flag and hide it in make
-				mods[i+1].ReplacedByPrebuilt()
-			}
-		}
+		return variants
 	} else if o, ok := ctx.Module().(OverrideModule); ok {
 		// Create a variant of the overriding module with its own name. This matches the above local
 		// variant name rule for overridden modules, and thus allows ReplaceDependencies to match the
 		// two.
-		ctx.CreateLocalVariations(o.Name())
-		// To allow dependencies to be added without having to know the above variation.
-		ctx.AliasVariation(o.Name())
+		return []string{o.Name()}
+	}
+
+	return []string{""}
+}
+
+func (overrideTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string {
+	if o, ok := ctx.Module().(OverrideModule); ok {
+		if ctx.DepTag() == overrideBaseDepTag {
+			return o.Name()
+		}
+	}
+
+	// Variations are always local and shouldn't affect the variant used for dependencies
+	return ""
+}
+
+func (overrideTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string {
+	if _, ok := ctx.Module().(OverridableModule); ok {
+		return incomingVariation
+	} else if o, ok := ctx.Module().(OverrideModule); ok {
+		// To allow dependencies to be added without having to know the variation.
+		return o.Name()
+	}
+
+	return ""
+}
+
+func (overrideTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) {
+	if o, ok := ctx.Module().(OverrideModule); ok {
+		overridableDeps := ctx.GetDirectDepsWithTag(overrideBaseDepTag)
+		if len(overridableDeps) > 1 {
+			panic(fmt.Errorf("expected a single dependency with overrideBaseDepTag, found %q", overridableDeps))
+		} else if len(overridableDeps) == 1 {
+			b := overridableDeps[0].(OverridableModule)
+			b.override(ctx, b, o)
+
+			checkPrebuiltReplacesOverride(ctx, b)
+		}
+	}
+}
+
+func checkPrebuiltReplacesOverride(ctx BottomUpMutatorContext, b OverridableModule) {
+	// See if there's a prebuilt module that overrides this override module with prefer flag,
+	// in which case we call HideFromMake on the corresponding variant later.
+	prebuiltDeps := ctx.GetDirectDepsWithTag(PrebuiltDepTag)
+	for _, prebuiltDep := range prebuiltDeps {
+		prebuilt := GetEmbeddedPrebuilt(prebuiltDep)
+		if prebuilt == nil {
+			panic("PrebuiltDepTag leads to a non-prebuilt module " + prebuiltDep.Name())
+		}
+		if prebuilt.UsePrebuilt() {
+			// The overriding module itself, too, is overridden by a prebuilt.
+			// Perform the same check for replacement
+			checkInvariantsForSourceAndPrebuilt(ctx, b, prebuiltDep)
+			// Copy the flag and hide it in make
+			b.ReplacedByPrebuilt()
+		}
 	}
 }
 
diff --git a/android/packaging.go b/android/packaging.go
index 383f828..ae412e1 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -20,6 +20,7 @@
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 // PackagingSpec abstracts a request to place a built artifact at a certain path in a package. A
@@ -142,18 +143,24 @@
 	// for rare cases like when there's a dependency to a module which exists in certain repo
 	// checkouts, this is needed.
 	IgnoreMissingDependencies bool
+
+	// If this is set to true by a module type inheriting PackagingBase, the deps property
+	// collects the first target only even with compile_multilib: true.
+	DepsCollectFirstTargetOnly bool
 }
 
 type depsProperty struct {
 	// Modules to include in this package
-	Deps []string `android:"arch_variant"`
+	Deps proptools.Configurable[[]string] `android:"arch_variant"`
 }
 
 type packagingMultilibProperties struct {
-	First  depsProperty `android:"arch_variant"`
-	Common depsProperty `android:"arch_variant"`
-	Lib32  depsProperty `android:"arch_variant"`
-	Lib64  depsProperty `android:"arch_variant"`
+	First    depsProperty `android:"arch_variant"`
+	Common   depsProperty `android:"arch_variant"`
+	Lib32    depsProperty `android:"arch_variant"`
+	Lib64    depsProperty `android:"arch_variant"`
+	Both     depsProperty `android:"arch_variant"`
+	Prefer32 depsProperty `android:"arch_variant"`
 }
 
 type packagingArchProperties struct {
@@ -164,8 +171,8 @@
 }
 
 type PackagingProperties struct {
-	Deps     []string                    `android:"arch_variant"`
-	Multilib packagingMultilibProperties `android:"arch_variant"`
+	Deps     proptools.Configurable[[]string] `android:"arch_variant"`
+	Multilib packagingMultilibProperties      `android:"arch_variant"`
 	Arch     packagingArchProperties
 }
 
@@ -183,22 +190,55 @@
 // multi target, deps is selected for each of the targets and is NOT selected for the current
 // architecture which would be Common.
 func (p *PackagingBase) getDepsForArch(ctx BaseModuleContext, arch ArchType) []string {
-	var ret []string
-	if arch == ctx.Target().Arch.ArchType && len(ctx.MultiTargets()) == 0 {
-		ret = append(ret, p.properties.Deps...)
-	} else if arch.Multilib == "lib32" {
-		ret = append(ret, p.properties.Multilib.Lib32.Deps...)
-	} else if arch.Multilib == "lib64" {
-		ret = append(ret, p.properties.Multilib.Lib64.Deps...)
-	} else if arch == Common {
-		ret = append(ret, p.properties.Multilib.Common.Deps...)
+	get := func(prop proptools.Configurable[[]string]) []string {
+		return prop.GetOrDefault(ctx, nil)
 	}
 
-	for i, t := range ctx.MultiTargets() {
-		if t.Arch.ArchType == arch {
-			ret = append(ret, p.properties.Deps...)
-			if i == 0 {
-				ret = append(ret, p.properties.Multilib.First.Deps...)
+	var ret []string
+	if arch == ctx.Target().Arch.ArchType && len(ctx.MultiTargets()) == 0 {
+		ret = append(ret, get(p.properties.Deps)...)
+	} else if arch.Multilib == "lib32" {
+		ret = append(ret, get(p.properties.Multilib.Lib32.Deps)...)
+		// multilib.prefer32.deps are added for lib32 only when they support 32-bit arch
+		for _, dep := range get(p.properties.Multilib.Prefer32.Deps) {
+			if checkIfOtherModuleSupportsLib32(ctx, dep) {
+				ret = append(ret, dep)
+			}
+		}
+	} else if arch.Multilib == "lib64" {
+		ret = append(ret, get(p.properties.Multilib.Lib64.Deps)...)
+		// multilib.prefer32.deps are added for lib64 only when they don't support 32-bit arch
+		for _, dep := range get(p.properties.Multilib.Prefer32.Deps) {
+			if !checkIfOtherModuleSupportsLib32(ctx, dep) {
+				ret = append(ret, dep)
+			}
+		}
+	} else if arch == Common {
+		ret = append(ret, get(p.properties.Multilib.Common.Deps)...)
+	}
+
+	if p.DepsCollectFirstTargetOnly {
+		if len(get(p.properties.Multilib.First.Deps)) > 0 {
+			ctx.PropertyErrorf("multilib.first.deps", "not supported. use \"deps\" instead")
+		}
+		for i, t := range ctx.MultiTargets() {
+			if t.Arch.ArchType == arch {
+				ret = append(ret, get(p.properties.Multilib.Both.Deps)...)
+				if i == 0 {
+					ret = append(ret, get(p.properties.Deps)...)
+				}
+			}
+		}
+	} else {
+		if len(get(p.properties.Multilib.Both.Deps)) > 0 {
+			ctx.PropertyErrorf("multilib.both.deps", "not supported. use \"deps\" instead")
+		}
+		for i, t := range ctx.MultiTargets() {
+			if t.Arch.ArchType == arch {
+				ret = append(ret, get(p.properties.Deps)...)
+				if i == 0 {
+					ret = append(ret, get(p.properties.Multilib.First.Deps)...)
+				}
 			}
 		}
 	}
@@ -206,20 +246,20 @@
 	if ctx.Arch().ArchType == Common {
 		switch arch {
 		case Arm64:
-			ret = append(ret, p.properties.Arch.Arm64.Deps...)
+			ret = append(ret, get(p.properties.Arch.Arm64.Deps)...)
 		case Arm:
-			ret = append(ret, p.properties.Arch.Arm.Deps...)
+			ret = append(ret, get(p.properties.Arch.Arm.Deps)...)
 		case X86_64:
-			ret = append(ret, p.properties.Arch.X86_64.Deps...)
+			ret = append(ret, get(p.properties.Arch.X86_64.Deps)...)
 		case X86:
-			ret = append(ret, p.properties.Arch.X86.Deps...)
+			ret = append(ret, get(p.properties.Arch.X86.Deps)...)
 		}
 	}
 
 	return FirstUniqueStrings(ret)
 }
 
-func (p *PackagingBase) getSupportedTargets(ctx BaseModuleContext) []Target {
+func getSupportedTargets(ctx BaseModuleContext) []Target {
 	var ret []Target
 	// The current and the common OS targets are always supported
 	ret = append(ret, ctx.Target())
@@ -231,6 +271,28 @@
 	return ret
 }
 
+// getLib32Target returns the 32-bit target from the list of targets this module supports. If this
+// module doesn't support 32-bit target, nil is returned.
+func getLib32Target(ctx BaseModuleContext) *Target {
+	for _, t := range getSupportedTargets(ctx) {
+		if t.Arch.ArchType.Multilib == "lib32" {
+			return &t
+		}
+	}
+	return nil
+}
+
+// checkIfOtherModuleSUpportsLib32 returns true if 32-bit variant of dep exists.
+func checkIfOtherModuleSupportsLib32(ctx BaseModuleContext, dep string) bool {
+	t := getLib32Target(ctx)
+	if t == nil {
+		// This packaging module doesn't support 32bit. No point of checking if dep supports 32-bit
+		// or not.
+		return false
+	}
+	return ctx.OtherModuleFarDependencyVariantExists(t.Variations(), dep)
+}
+
 // PackagingItem is a marker interface for dependency tags.
 // Direct dependencies with a tag implementing PackagingItem are packaged in CopyDepsToZip().
 type PackagingItem interface {
@@ -251,7 +313,7 @@
 
 // See PackageModule.AddDeps
 func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag) {
-	for _, t := range p.getSupportedTargets(ctx) {
+	for _, t := range getSupportedTargets(ctx) {
 		for _, dep := range p.getDepsForArch(ctx, t.Arch.ArchType) {
 			if p.IgnoreMissingDependencies && !ctx.OtherModuleExists(dep) {
 				continue
@@ -265,7 +327,7 @@
 	m := make(map[string]PackagingSpec)
 
 	var arches []ArchType
-	for _, target := range p.getSupportedTargets(ctx) {
+	for _, target := range getSupportedTargets(ctx) {
 		arches = append(arches, target.Arch.ArchType)
 	}
 
diff --git a/android/packaging_test.go b/android/packaging_test.go
index 3833437..19b46fe 100644
--- a/android/packaging_test.go
+++ b/android/packaging_test.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"strings"
 	"testing"
 
 	"github.com/google/blueprint"
@@ -67,18 +68,15 @@
 	entries []string
 }
 
-func packageMultiTargetTestModuleFactory() Module {
+func packageTestModuleFactory(multiTarget bool, depsCollectFirstTargetOnly bool) Module {
 	module := &packageTestModule{}
 	InitPackageModule(module)
-	InitAndroidMultiTargetsArchModule(module, DeviceSupported, MultilibCommon)
-	module.AddProperties(&module.properties)
-	return module
-}
-
-func packageTestModuleFactory() Module {
-	module := &packageTestModule{}
-	InitPackageModule(module)
-	InitAndroidArchModule(module, DeviceSupported, MultilibBoth)
+	module.DepsCollectFirstTargetOnly = depsCollectFirstTargetOnly
+	if multiTarget {
+		InitAndroidMultiTargetsArchModule(module, DeviceSupported, MultilibCommon)
+	} else {
+		InitAndroidArchModule(module, DeviceSupported, MultilibBoth)
+	}
 	module.AddProperties(&module.properties)
 	return module
 }
@@ -98,17 +96,24 @@
 	m.entries = m.CopyDepsToZip(ctx, m.GatherPackagingSpecs(ctx), zipFile)
 }
 
-func runPackagingTest(t *testing.T, multitarget bool, bp string, expected []string) {
+type testConfig struct {
+	multiTarget                bool
+	depsCollectFirstTargetOnly bool
+	debuggable                 bool
+}
+
+func runPackagingTest(t *testing.T, config testConfig, bp string, expected []string) {
 	t.Helper()
 
 	var archVariant string
-	var moduleFactory ModuleFactory
-	if multitarget {
+	if config.multiTarget {
 		archVariant = "android_common"
-		moduleFactory = packageMultiTargetTestModuleFactory
 	} else {
 		archVariant = "android_arm64_armv8-a"
-		moduleFactory = packageTestModuleFactory
+	}
+
+	moduleFactory := func() Module {
+		return packageTestModuleFactory(config.multiTarget, config.depsCollectFirstTargetOnly)
 	}
 
 	result := GroupFixturePreparers(
@@ -117,6 +122,9 @@
 			ctx.RegisterModuleType("component", componentTestModuleFactory)
 			ctx.RegisterModuleType("package_module", moduleFactory)
 		}),
+		FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+			variables.Debuggable = proptools.BoolPtr(config.debuggable)
+		}),
 		FixtureWithRootAndroidBp(bp),
 	).RunTest(t)
 
@@ -128,8 +136,11 @@
 }
 
 func TestPackagingBaseMultiTarget(t *testing.T) {
-	multiTarget := true
-	runPackagingTest(t, multiTarget,
+	config := testConfig{
+		multiTarget:                true,
+		depsCollectFirstTargetOnly: false,
+	}
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -141,7 +152,7 @@
 		}
 		`, []string{"lib64/foo"})
 
-	runPackagingTest(t, multiTarget,
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -158,7 +169,7 @@
 		}
 		`, []string{"lib64/foo", "lib64/bar"})
 
-	runPackagingTest(t, multiTarget,
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -176,7 +187,7 @@
 		}
 		`, []string{"lib32/foo", "lib32/bar", "lib64/foo", "lib64/bar"})
 
-	runPackagingTest(t, multiTarget,
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -199,7 +210,7 @@
 		}
 		`, []string{"lib32/foo", "lib32/bar", "lib64/foo"})
 
-	runPackagingTest(t, multiTarget,
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -221,7 +232,7 @@
 		}
 		`, []string{"lib32/foo", "lib64/foo", "lib64/bar"})
 
-	runPackagingTest(t, multiTarget,
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -252,8 +263,11 @@
 }
 
 func TestPackagingBaseSingleTarget(t *testing.T) {
-	multiTarget := false
-	runPackagingTest(t, multiTarget,
+	config := testConfig{
+		multiTarget:                false,
+		depsCollectFirstTargetOnly: false,
+	}
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -265,7 +279,7 @@
 		}
 		`, []string{"lib64/foo"})
 
-	runPackagingTest(t, multiTarget,
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -282,7 +296,7 @@
 		}
 		`, []string{"lib64/foo", "lib64/bar"})
 
-	runPackagingTest(t, multiTarget,
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -304,7 +318,7 @@
 		}
 		`, []string{"lib64/foo"})
 
-	runPackagingTest(t, multiTarget,
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -325,7 +339,7 @@
 		}
 		`, []string{"lib64/foo", "lib64/bar"})
 
-	runPackagingTest(t, multiTarget,
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -353,7 +367,7 @@
 		}
 		`, []string{"lib64/foo", "lib64/bar"})
 
-	runPackagingTest(t, multiTarget,
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -374,8 +388,11 @@
 func TestPackagingWithSkipInstallDeps(t *testing.T) {
 	// package -[dep]-> foo -[dep]-> bar      -[dep]-> baz
 	// Packaging should continue transitively through modules that are not installed.
-	multiTarget := false
-	runPackagingTest(t, multiTarget,
+	config := testConfig{
+		multiTarget:                false,
+		depsCollectFirstTargetOnly: false,
+	}
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -398,3 +415,238 @@
 		}
 		`, []string{"lib64/foo", "lib64/bar", "lib64/baz"})
 }
+
+func TestPackagingWithDepsCollectFirstTargetOnly(t *testing.T) {
+	config := testConfig{
+		multiTarget:                true,
+		depsCollectFirstTargetOnly: true,
+	}
+	runPackagingTest(t, config,
+		`
+		component {
+			name: "foo",
+		}
+
+		package_module {
+			name: "package",
+			deps: ["foo"],
+		}
+		`, []string{"lib64/foo"})
+
+	runPackagingTest(t, config,
+		`
+		component {
+			name: "foo",
+			deps: ["bar"],
+		}
+
+		component {
+			name: "bar",
+		}
+
+		package_module {
+			name: "package",
+			deps: ["foo"],
+		}
+		`, []string{"lib64/foo", "lib64/bar"})
+
+	runPackagingTest(t, config,
+		`
+		component {
+			name: "foo",
+			deps: ["bar"],
+		}
+
+		component {
+			name: "bar",
+		}
+
+		package_module {
+			name: "package",
+			deps: ["foo"],
+			compile_multilib: "both",
+		}
+		`, []string{"lib64/foo", "lib64/bar"})
+
+	runPackagingTest(t, config,
+		`
+		component {
+			name: "foo",
+		}
+
+		component {
+			name: "bar",
+			compile_multilib: "32",
+		}
+
+		package_module {
+			name: "package",
+			deps: ["foo"],
+			multilib: {
+				lib32: {
+					deps: ["bar"],
+				},
+			},
+			compile_multilib: "both",
+		}
+		`, []string{"lib32/bar", "lib64/foo"})
+
+	runPackagingTest(t, config,
+		`
+		component {
+			name: "foo",
+		}
+
+		component {
+			name: "bar",
+		}
+
+		package_module {
+			name: "package",
+			deps: ["foo"],
+			multilib: {
+				both: {
+					deps: ["bar"],
+				},
+			},
+			compile_multilib: "both",
+		}
+		`, []string{"lib64/foo", "lib32/bar", "lib64/bar"})
+
+	runPackagingTest(t, config,
+		`
+		component {
+			name: "foo",
+		}
+
+		component {
+			name: "bar",
+		}
+
+		component {
+			name: "baz",
+		}
+
+		package_module {
+			name: "package",
+			deps: ["foo"],
+			arch: {
+				arm64: {
+					deps: ["bar"],
+				},
+				x86_64: {
+					deps: ["baz"],
+				},
+			},
+			compile_multilib: "both",
+		}
+		`, []string{"lib64/foo", "lib64/bar"})
+}
+
+func TestDebuggableDeps(t *testing.T) {
+	bp := `
+		component {
+			name: "foo",
+		}
+
+		component {
+			name: "bar",
+			deps: ["baz"],
+		}
+
+		component {
+			name: "baz",
+		}
+
+		package_module {
+			name: "package",
+			deps: ["foo"] + select(product_variable("debuggable"), {
+				true: ["bar"],
+				default: [],
+			}),
+		}`
+	testcases := []struct {
+		debuggable bool
+		expected   []string
+	}{
+		{
+			debuggable: true,
+			expected:   []string{"lib64/foo", "lib64/bar", "lib64/baz"},
+		},
+		{
+			debuggable: false,
+			expected:   []string{"lib64/foo"},
+		},
+	}
+	for _, tc := range testcases {
+		config := testConfig{
+			debuggable: tc.debuggable,
+		}
+		runPackagingTest(t, config, bp, tc.expected)
+	}
+}
+
+func TestPrefer32Deps(t *testing.T) {
+	bpTemplate := `
+		component {
+			name: "foo",
+			compile_multilib: "both", // not needed but for clarity
+		}
+
+		component {
+			name: "foo_32only",
+			compile_multilib: "prefer32",
+		}
+
+		component {
+			name: "foo_64only",
+			compile_multilib: "64",
+		}
+
+		package_module {
+			name: "package",
+			compile_multilib: "%COMPILE_MULTILIB%",
+			multilib: {
+				prefer32: {
+					deps: %DEPS%,
+				},
+			},
+		}
+	`
+
+	testcases := []struct {
+		compileMultilib string
+		deps            []string
+		expected        []string
+	}{
+		{
+			compileMultilib: "first",
+			deps:            []string{"foo", "foo_64only"},
+			expected:        []string{"lib64/foo", "lib64/foo_64only"},
+		},
+		{
+			compileMultilib: "64",
+			deps:            []string{"foo", "foo_64only"},
+			expected:        []string{"lib64/foo", "lib64/foo_64only"},
+		},
+		{
+			compileMultilib: "32",
+			deps:            []string{"foo", "foo_32only"},
+			expected:        []string{"lib32/foo", "lib32/foo_32only"},
+		},
+		{
+			compileMultilib: "both",
+			deps:            []string{"foo", "foo_32only", "foo_64only"},
+			expected:        []string{"lib32/foo", "lib32/foo_32only", "lib64/foo_64only"},
+		},
+	}
+	for _, tc := range testcases {
+		config := testConfig{
+			multiTarget:                true,
+			depsCollectFirstTargetOnly: true,
+		}
+		bp := strings.Replace(bpTemplate, "%COMPILE_MULTILIB%", tc.compileMultilib, -1)
+		bp = strings.Replace(bp, "%DEPS%", `["`+strings.Join(tc.deps, `", "`)+`"]`, -1)
+		runPackagingTest(t, config, bp, tc.expected)
+	}
+}
diff --git a/android/selects_test.go b/android/selects_test.go
index d9499a5..6f980ce 100644
--- a/android/selects_test.go
+++ b/android/selects_test.go
@@ -546,6 +546,24 @@
 			},
 		},
 		{
+			name: "Unhandled string value",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select(soong_config_variable("my_namespace", "my_variable"), {
+					"foo": "a",
+					"bar": "b",
+				}),
+			}
+			`,
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable": "baz",
+				},
+			},
+			expectedError: `my_string: soong_config_variable\("my_namespace", "my_variable"\) had value "baz", which was not handled by the select statement`,
+		},
+		{
 			name: "Select on boolean",
 			bp: `
 			my_module_type {
@@ -596,7 +614,7 @@
 				}),
 			}
 			`,
-			expectedError: "foo",
+			expectedError: `my_string: boolean_var_for_testing\(\) had value undefined, which was not handled by the select statement`,
 		},
 		{
 			name: "Select on boolean undefined with default",
@@ -688,6 +706,78 @@
 			`,
 			expectedError: `can't assign select statement to non-configurable property "my_nonconfigurable_string_list"`,
 		},
+		{
+			name: "Select in variable",
+			bp: `
+			my_second_variable = ["after.cpp"]
+			my_variable = select(soong_config_variable("my_namespace", "my_variable"), {
+				"a": ["a.cpp"],
+				"b": ["b.cpp"],
+				default: ["c.cpp"],
+			}) + my_second_variable
+			my_module_type {
+				name: "foo",
+				my_string_list: ["before.cpp"] + my_variable,
+			}
+			`,
+			provider: selectsTestProvider{
+				my_string_list: &[]string{"before.cpp", "a.cpp", "after.cpp"},
+			},
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable": "a",
+				},
+			},
+		},
+		{
+			name: "Soong config value variable on configurable property",
+			bp: `
+			soong_config_module_type {
+				name: "soong_config_my_module_type",
+				module_type: "my_module_type",
+				config_namespace: "my_namespace",
+				value_variables: ["my_variable"],
+				properties: ["my_string", "my_string_list"],
+			}
+
+			soong_config_my_module_type {
+				name: "foo",
+				my_string_list: ["before.cpp"],
+				soong_config_variables: {
+					my_variable: {
+						my_string_list: ["after_%s.cpp"],
+						my_string: "%s.cpp",
+					},
+				},
+			}
+			`,
+			provider: selectsTestProvider{
+				my_string:      proptools.StringPtr("foo.cpp"),
+				my_string_list: &[]string{"before.cpp", "after_foo.cpp"},
+			},
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable": "foo",
+				},
+			},
+		},
+		{
+			name: "Property appending with variable",
+			bp: `
+			my_variable = ["b.cpp"]
+			my_module_type {
+				name: "foo",
+				my_string_list: ["a.cpp"] + my_variable + select(soong_config_variable("my_namespace", "my_variable"), {
+					"a": ["a.cpp"],
+					"b": ["b.cpp"],
+					default: ["c.cpp"],
+				}),
+			}
+			`,
+			provider: selectsTestProvider{
+				my_string_list: &[]string{"a.cpp", "b.cpp", "c.cpp"},
+			},
+		},
 	}
 
 	for _, tc := range testCases {
@@ -695,6 +785,7 @@
 			fixtures := GroupFixturePreparers(
 				PrepareForTestWithDefaults,
 				PrepareForTestWithArchMutator,
+				PrepareForTestWithSoongConfigModuleBuildComponents,
 				FixtureRegisterWithContext(func(ctx RegistrationContext) {
 					ctx.RegisterModuleType("my_module_type", newSelectsMockModule)
 					ctx.RegisterModuleType("my_defaults", newSelectsMockModuleDefaults)
@@ -749,7 +840,7 @@
 		myStringStr = *p.my_string
 	}
 	myNonconfigurableStringStr := "nil"
-	if p.my_string != nil {
+	if p.my_nonconfigurable_string != nil {
 		myNonconfigurableStringStr = *p.my_nonconfigurable_string
 	}
 	return fmt.Sprintf(`selectsTestProvider {
@@ -792,13 +883,21 @@
 	properties selectsMockModuleProperties
 }
 
+func optionalToPtr[T any](o proptools.ConfigurableOptional[T]) *T {
+	if o.IsEmpty() {
+		return nil
+	}
+	x := o.Get()
+	return &x
+}
+
 func (p *selectsMockModule) GenerateAndroidBuildActions(ctx ModuleContext) {
 	SetProvider(ctx, selectsTestProviderKey, selectsTestProvider{
-		my_bool:                        p.properties.My_bool.Get(ctx),
-		my_string:                      p.properties.My_string.Get(ctx),
-		my_string_list:                 p.properties.My_string_list.Get(ctx),
-		my_paths:                       p.properties.My_paths.Get(ctx),
-		replacing_string_list:          p.properties.Replacing_string_list.Get(ctx),
+		my_bool:                        optionalToPtr(p.properties.My_bool.Get(ctx)),
+		my_string:                      optionalToPtr(p.properties.My_string.Get(ctx)),
+		my_string_list:                 optionalToPtr(p.properties.My_string_list.Get(ctx)),
+		my_paths:                       optionalToPtr(p.properties.My_paths.Get(ctx)),
+		replacing_string_list:          optionalToPtr(p.properties.Replacing_string_list.Get(ctx)),
 		my_nonconfigurable_bool:        p.properties.My_nonconfigurable_bool,
 		my_nonconfigurable_string:      p.properties.My_nonconfigurable_string,
 		my_nonconfigurable_string_list: p.properties.My_nonconfigurable_string_list,
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index c910974..87af774 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -733,11 +733,18 @@
 		case reflect.Bool:
 			// Nothing to do
 		case reflect.Struct:
-			fieldName = append(fieldName, propStruct.Type().Field(i).Name)
-			if err := s.printfIntoPropertyRecursive(fieldName, field, configValue); err != nil {
-				return err
+			if proptools.IsConfigurable(field.Type()) {
+				if err := proptools.PrintfIntoConfigurable(field.Interface(), configValue); err != nil {
+					fieldName = append(fieldName, propStruct.Type().Field(i).Name)
+					return fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, strings.Join(fieldName, "."), err)
+				}
+			} else {
+				fieldName = append(fieldName, propStruct.Type().Field(i).Name)
+				if err := s.printfIntoPropertyRecursive(fieldName, field, configValue); err != nil {
+					return err
+				}
+				fieldName = fieldName[:len(fieldName)-1]
 			}
-			fieldName = fieldName[:len(fieldName)-1]
 		default:
 			fieldName = append(fieldName, propStruct.Type().Field(i).Name)
 			return fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, strings.Join(fieldName, "."), kind)
diff --git a/android/util.go b/android/util.go
index 698a856..e21e66b 100644
--- a/android/util.go
+++ b/android/util.go
@@ -24,6 +24,8 @@
 	"sort"
 	"strings"
 	"sync"
+
+	"github.com/google/blueprint/proptools"
 )
 
 // CopyOf returns a new slice that has the same contents as s.
@@ -302,6 +304,24 @@
 	return removed, result
 }
 
+// FirstUniqueFunc returns all unique elements of a slice, keeping the first copy of
+// each.  It does not modify the input slice. The eq function should return true
+// if two elements can be considered equal.
+func FirstUniqueFunc[SortableList ~[]Sortable, Sortable any](list SortableList, eq func(a, b Sortable) bool) SortableList {
+	k := 0
+outer:
+	for i := 0; i < len(list); i++ {
+		for j := 0; j < k; j++ {
+			if eq(list[i], list[j]) {
+				continue outer
+			}
+		}
+		list[k] = list[i]
+		k++
+	}
+	return list[:k]
+}
+
 // FirstUniqueStrings returns all unique elements of a slice of strings, keeping the first copy of
 // each.  It does not modify the input slice.
 func FirstUniqueStrings(list []string) []string {
@@ -524,25 +544,9 @@
 	return root, suffix, ext
 }
 
-func shard[T ~[]E, E any](toShard T, shardSize int) []T {
-	if len(toShard) == 0 {
-		return nil
-	}
-
-	ret := make([]T, 0, (len(toShard)+shardSize-1)/shardSize)
-	for len(toShard) > shardSize {
-		ret = append(ret, toShard[0:shardSize])
-		toShard = toShard[shardSize:]
-	}
-	if len(toShard) > 0 {
-		ret = append(ret, toShard)
-	}
-	return ret
-}
-
 // ShardPaths takes a Paths, and returns a slice of Paths where each one has at most shardSize paths.
 func ShardPaths(paths Paths, shardSize int) []Paths {
-	return shard(paths, shardSize)
+	return proptools.ShardBySize(paths, shardSize)
 }
 
 // ShardString takes a string and returns a slice of strings where the length of each one is
@@ -565,7 +569,7 @@
 // ShardStrings takes a slice of strings, and returns a slice of slices of strings where each one has at most shardSize
 // elements.
 func ShardStrings(s []string, shardSize int) [][]string {
-	return shard(s, shardSize)
+	return proptools.ShardBySize(s, shardSize)
 }
 
 // CheckDuplicate checks if there are duplicates in given string list.
diff --git a/android/variable.go b/android/variable.go
index 419bd61..9a95563 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -181,8 +181,10 @@
 		// release_aidl_use_unfrozen is "true" when a device can
 		// use the unfrozen versions of AIDL interfaces.
 		Release_aidl_use_unfrozen struct {
-			Cflags []string
-			Cmd    *string
+			Cflags          []string
+			Cmd             *string
+			Required        []string
+			Vintf_fragments []string
 		}
 	} `android:"arch_variant"`
 }
@@ -362,7 +364,6 @@
 
 	PgoAdditionalProfileDirs []string `json:",omitempty"`
 
-	VndkUseCoreVariant         *bool `json:",omitempty"`
 	VndkSnapshotBuildArtifacts *bool `json:",omitempty"`
 
 	DirectedVendorSnapshot bool            `json:",omitempty"`
@@ -492,6 +493,8 @@
 
 	BuildFlags map[string]string `json:",omitempty"`
 
+	BuildFlagTypes map[string]string `json:",omitempty"`
+
 	BuildFromSourceStub *bool `json:",omitempty"`
 
 	BuildIgnoreApexContributionContents *bool `json:",omitempty"`
diff --git a/apex/aconfig_test.go b/apex/aconfig_test.go
index 726041c..14c0b63 100644
--- a/apex/aconfig_test.go
+++ b/apex/aconfig_test.go
@@ -23,6 +23,7 @@
 	"android/soong/genrule"
 	"android/soong/java"
 	"android/soong/rust"
+
 	"github.com/google/blueprint/proptools"
 )
 
@@ -173,10 +174,6 @@
 					name: "libaconfig_storage_read_api_cc",
 					srcs: ["libaconfig_storage_read_api_cc.cc"],
 				}
-				cc_library {
-					name: "libaconfig_storage_protos_cc",
-					srcs: ["libaconfig_storage_protos_cc.cc"],
-				}
 				aconfig_declarations {
 					name: "my_aconfig_declarations_bar",
 					package: "com.example.package",
@@ -436,10 +433,6 @@
 					name: "libaconfig_storage_read_api_cc",
 					srcs: ["libaconfig_storage_read_api_cc.cc"],
 				}
-				cc_library {
-					name: "libaconfig_storage_protos_cc",
-					srcs: ["libaconfig_storage_protos_cc.cc"],
-				}
 				aconfig_declarations {
 					name: "my_aconfig_declarations_foo",
 					package: "com.example.package",
@@ -501,10 +494,6 @@
 					name: "libaconfig_storage_read_api_cc",
 					srcs: ["libaconfig_storage_read_api_cc.cc"],
 				}
-				cc_library {
-					name: "libaconfig_storage_protos_cc",
-					srcs: ["libaconfig_storage_protos_cc.cc"],
-				}
 				aconfig_declarations {
 					name: "my_aconfig_declarations_foo",
 					package: "com.example.package",
diff --git a/apex/apex.go b/apex/apex.go
index 8029910..1dec61b 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -137,10 +137,6 @@
 	// Rust binaries with prefer_rlib:true add unnecessary dependencies.
 	Unwanted_transitive_deps []string
 
-	// The minimum SDK version that this APEX must support at minimum. This is usually set to
-	// the SDK version that the APEX was first introduced.
-	Min_sdk_version *string
-
 	// Whether this APEX is considered updatable or not. When set to true, this will enforce
 	// additional rules for making sure that the APEX is truly updatable. To be updatable,
 	// min_sdk_version should be set as well. This will also disable the size optimizations like
@@ -388,6 +384,10 @@
 
 	// Trim against a specific Dynamic Common Lib APEX
 	Trim_against *string
+
+	// The minimum SDK version that this APEX must support at minimum. This is usually set to
+	// the SDK version that the APEX was first introduced.
+	Min_sdk_version *string
 }
 
 type apexBundle struct {
@@ -699,7 +699,12 @@
 func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeModules ApexNativeDependencies, target android.Target, imageVariation string) {
 	binVariations := target.Variations()
 	libVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"})
-	rustLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "rust_libraries", Variation: "dylib"})
+	rustLibVariations := append(
+		target.Variations(), []blueprint.Variation{
+			{Mutator: "rust_libraries", Variation: "dylib"},
+			{Mutator: "link", Variation: ""},
+		}...,
+	)
 
 	// Append "image" variation
 	binVariations = append(binVariations, blueprint.Variation{Mutator: "image", Variation: imageVariation})
@@ -1030,6 +1035,11 @@
 	// be built for this apexBundle.
 
 	apexVariationName := mctx.ModuleName() // could be com.android.foo
+	if overridable, ok := mctx.Module().(android.OverridableModule); ok && overridable.GetOverriddenBy() != "" {
+		// use the overridden name com.mycompany.android.foo
+		apexVariationName = overridable.GetOverriddenBy()
+	}
+
 	a.properties.ApexVariationName = apexVariationName
 	testApexes := []string{}
 	if a.testApex {
@@ -1094,7 +1104,7 @@
 	if !mctx.Module().Enabled(mctx) {
 		return
 	}
-	if apex, ok := mctx.Module().(*apexBundle); ok && apex.checkStrictUpdatabilityLinting() {
+	if apex, ok := mctx.Module().(*apexBundle); ok && apex.checkStrictUpdatabilityLinting(mctx) {
 		mctx.WalkDeps(func(child, parent android.Module) bool {
 			// b/208656169 Do not propagate strict updatability linting to libcore/
 			// These libs are available on the classpath during compilation
@@ -1188,8 +1198,9 @@
 	}
 )
 
-func (a *apexBundle) checkStrictUpdatabilityLinting() bool {
-	return a.Updatable() && !android.InList(a.ApexVariationName(), skipStrictUpdatabilityLintAllowlist)
+func (a *apexBundle) checkStrictUpdatabilityLinting(mctx android.TopDownMutatorContext) bool {
+	// The allowlist contains the base apex name, so use that instead of the ApexVariationName
+	return a.Updatable() && !android.InList(mctx.ModuleName(), skipStrictUpdatabilityLintAllowlist)
 }
 
 // apexUniqueVariationsMutator checks if any dependencies use unique apex variations. If so, use
@@ -1290,13 +1301,12 @@
 func (a *apexTransitionMutator) Split(ctx android.BaseModuleContext) []string {
 	// apexBundle itself is mutated so that it and its dependencies have the same apex variant.
 	if ai, ok := ctx.Module().(ApexInfoMutator); ok && apexModuleTypeRequiresVariant(ai) {
-		return []string{ai.ApexVariationName()}
-	} else if o, ok := ctx.Module().(*OverrideApex); ok {
-		apexBundleName := o.GetOverriddenModuleName()
-		if apexBundleName == "" {
-			ctx.ModuleErrorf("base property is not set")
+		if overridable, ok := ctx.Module().(android.OverridableModule); ok && overridable.GetOverriddenBy() != "" {
+			return []string{overridable.GetOverriddenBy()}
 		}
-		return []string{apexBundleName}
+		return []string{ai.ApexVariationName()}
+	} else if _, ok := ctx.Module().(*OverrideApex); ok {
+		return []string{ctx.ModuleName()}
 	}
 	return []string{""}
 }
@@ -1309,9 +1319,12 @@
 	if am, ok := ctx.Module().(android.ApexModule); ok && am.CanHaveApexVariants() {
 		return android.IncomingApexTransition(ctx, incomingVariation)
 	} else if ai, ok := ctx.Module().(ApexInfoMutator); ok {
+		if overridable, ok := ctx.Module().(android.OverridableModule); ok && overridable.GetOverriddenBy() != "" {
+			return overridable.GetOverriddenBy()
+		}
 		return ai.ApexVariationName()
-	} else if o, ok := ctx.Module().(*OverrideApex); ok {
-		return o.GetOverriddenModuleName()
+	} else if _, ok := ctx.Module().(*OverrideApex); ok {
+		return ctx.Module().Name()
 	}
 
 	return ""
@@ -2647,7 +2660,7 @@
 	// Only override the minSdkVersion value on Apexes which already specify
 	// a min_sdk_version (it's optional for non-updatable apexes), and that its
 	// min_sdk_version value is lower than the one to override with.
-	minApiLevel := android.MinSdkVersionFromValue(ctx, proptools.String(a.properties.Min_sdk_version))
+	minApiLevel := android.MinSdkVersionFromValue(ctx, proptools.String(a.overridableProperties.Min_sdk_version))
 	if minApiLevel.IsNone() {
 		return ""
 	}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 74b2eec..c60ee73 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -5049,6 +5049,20 @@
 		// Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding
 		// is disabled.
 		android.FixtureAddTextFile("frameworks/base/Android.bp", ""),
+
+		// Make sure that we have atleast one platform library so that we can check the monolithic hiddenapi
+		// file creation.
+		java.FixtureConfigureBootJars("platform:foo"),
+		android.FixtureModifyMockFS(func(fs android.MockFS) {
+			fs["platform/Android.bp"] = []byte(`
+		java_library {
+			name: "foo",
+			srcs: ["Test.java"],
+			compile_dex: true,
+		}
+		`)
+			fs["platform/Test.java"] = nil
+		}),
 	)
 
 	checkBootDexJarPath := func(t *testing.T, ctx *android.TestContext, stem string, bootDexJarPath string) {
@@ -5143,7 +5157,7 @@
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
-		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
+		checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`)
 		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
 			my-bootclasspath-fragment/index.csv
 			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
@@ -5221,7 +5235,7 @@
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
-		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
+		checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`)
 		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
 			my-bootclasspath-fragment/index.csv
 			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
@@ -5410,7 +5424,7 @@
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
-		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
+		checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`)
 		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
 			my-bootclasspath-fragment/index.csv
 			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
@@ -5507,7 +5521,7 @@
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/my-bootclasspath-fragment/android_common_myapex/hiddenapi-modular/encoded/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
-		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
+		checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`)
 		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
 			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
 			out/soong/.intermediates/my-bootclasspath-fragment/android_common_myapex/modular-hiddenapi/index.csv
@@ -5620,7 +5634,7 @@
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
-		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
+		checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`)
 		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
 			my-bootclasspath-fragment/index.csv
 			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
@@ -5908,6 +5922,7 @@
 			srcs: ["foo/bar/MyClass.java"],
 			sdk_version: "current",
 			system_modules: "none",
+			use_embedded_native_libs: true,
 			jni_libs: ["libjni"],
 			stl: "none",
 			apex_available: [ "myapex" ],
@@ -6462,7 +6477,7 @@
 		t.Errorf("expected to find defaultVersion %q; got %q", barExpectedDefaultVersion, barActualDefaultVersion)
 	}
 
-	overrideBarManifestRule := result.ModuleForTests("bar", "android_common_myoverrideapex_bar").Rule("apexManifestRule")
+	overrideBarManifestRule := result.ModuleForTests("bar", "android_common_myoverrideapex_myoverrideapex").Rule("apexManifestRule")
 	overrideBarActualDefaultVersion := overrideBarManifestRule.Args["default_version"]
 	if overrideBarActualDefaultVersion != barExpectedDefaultVersion {
 		t.Errorf("expected to find defaultVersion %q; got %q", barExpectedDefaultVersion, barActualDefaultVersion)
@@ -6842,7 +6857,7 @@
 	`, withManifestPackageNameOverrides([]string{"myapex:com.android.myapex"}))
 
 	originalVariant := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(android.OverridableModule)
-	overriddenVariant := ctx.ModuleForTests("myapex", "android_common_override_myapex_myapex").Module().(android.OverridableModule)
+	overriddenVariant := ctx.ModuleForTests("myapex", "android_common_override_myapex_override_myapex").Module().(android.OverridableModule)
 	if originalVariant.GetOverriddenBy() != "" {
 		t.Errorf("GetOverriddenBy should be empty, but was %q", originalVariant.GetOverriddenBy())
 	}
@@ -6850,7 +6865,7 @@
 		t.Errorf("GetOverriddenBy should be \"override_myapex\", but was %q", overriddenVariant.GetOverriddenBy())
 	}
 
-	module := ctx.ModuleForTests("myapex", "android_common_override_myapex_myapex")
+	module := ctx.ModuleForTests("myapex", "android_common_override_myapex_override_myapex")
 	apexRule := module.Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
@@ -8942,7 +8957,7 @@
 		t.Errorf("allowed_files_file: expected %q but got %q", expected, actual)
 	}
 
-	rule2 := ctx.ModuleForTests("myapex", "android_common_override_myapex_myapex").Rule("diffApexContentRule")
+	rule2 := ctx.ModuleForTests("myapex", "android_common_override_myapex_override_myapex").Rule("diffApexContentRule")
 	if expected, actual := "sub/allowed.txt", rule2.Args["allowed_files_file"]; expected != actual {
 		t.Errorf("allowed_files_file: expected %q but got %q", expected, actual)
 	}
@@ -10694,10 +10709,6 @@
 			name: "libaconfig_storage_read_api_cc",
 			srcs: ["libaconfig_storage_read_api_cc.cc"],
 		}
-		cc_library {
-			name: "libaconfig_storage_protos_cc",
-			srcs: ["libaconfig_storage_protos_cc.cc"],
-		}
 	`)
 
 	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
@@ -11238,6 +11249,20 @@
 			android.FixtureMergeMockFs(map[string][]byte{
 				"system/sepolicy/apex/com.android.foo-file_contexts": nil,
 			}),
+			// Make sure that we have atleast one platform library so that we can check the monolithic hiddenapi
+			// file creation.
+			java.FixtureConfigureBootJars("platform:foo"),
+			android.FixtureModifyMockFS(func(fs android.MockFS) {
+				fs["platform/Android.bp"] = []byte(`
+		java_library {
+			name: "foo",
+			srcs: ["Test.java"],
+			compile_dex: true,
+		}
+		`)
+				fs["platform/Test.java"] = nil
+			}),
+
 			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 				variables.BuildFlags = map[string]string{
 					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": tc.selectedApexContributions,
@@ -11266,7 +11291,7 @@
 		variation := func(moduleName string) string {
 			ret := "android_common_com.android.foo"
 			if moduleName == "com.google.android.foo" {
-				ret = "android_common_com.google.android.foo_com.android.foo"
+				ret = "android_common_com.google.android.foo_com.google.android.foo"
 			}
 			return ret
 		}
@@ -11570,3 +11595,114 @@
 	android.AssertStringDoesContain(t, "not found", androidMk, "LOCAL_MODULE := etc_myfilename.myapex")
 	android.AssertStringDoesContain(t, "not found", androidMk, "LOCAL_MODULE := etc_mysubdir_myfilename.myapex")
 }
+
+func TestApexMinSdkVersionOverride(t *testing.T) {
+	checkMinSdkVersion := func(t *testing.T, module android.TestingModule, expectedMinSdkVersion string) {
+		args := module.Rule("apexRule").Args
+		optFlags := args["opt_flags"]
+		if !strings.Contains(optFlags, "--min_sdk_version "+expectedMinSdkVersion) {
+			t.Errorf("%s: Expected min_sdk_version=%s, got: %s", module.Module(), expectedMinSdkVersion, optFlags)
+		}
+	}
+
+	checkHasDep := func(t *testing.T, ctx *android.TestContext, m android.Module, wantDep android.Module) {
+		t.Helper()
+		found := false
+		ctx.VisitDirectDeps(m, func(dep blueprint.Module) {
+			if dep == wantDep {
+				found = true
+			}
+		})
+		if !found {
+			t.Errorf("Could not find a dependency from %v to %v\n", m, wantDep)
+		}
+	}
+
+	ctx := testApex(t, `
+		apex {
+			name: "com.android.apex30",
+			min_sdk_version: "30",
+			key: "apex30.key",
+			java_libs: ["javalib"],
+		}
+
+		java_library {
+			name: "javalib",
+			srcs: ["A.java"],
+			apex_available: ["com.android.apex30"],
+			min_sdk_version: "30",
+			sdk_version: "current",
+		}
+
+		override_apex {
+			name: "com.mycompany.android.apex30",
+			base: "com.android.apex30",
+		}
+
+		override_apex {
+			name: "com.mycompany.android.apex31",
+			base: "com.android.apex30",
+			min_sdk_version: "31",
+		}
+
+		apex_key {
+			name: "apex30.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+	`, android.FixtureMergeMockFs(android.MockFS{
+		"system/sepolicy/apex/com.android.apex30-file_contexts": nil,
+	}),
+	)
+
+	baseModule := ctx.ModuleForTests("com.android.apex30", "android_common_com.android.apex30")
+	checkMinSdkVersion(t, baseModule, "30")
+
+	// Override module, but uses same min_sdk_version
+	overridingModuleSameMinSdkVersion := ctx.ModuleForTests("com.android.apex30", "android_common_com.mycompany.android.apex30_com.mycompany.android.apex30")
+	javalibApex30Variant := ctx.ModuleForTests("javalib", "android_common_apex30")
+	checkMinSdkVersion(t, overridingModuleSameMinSdkVersion, "30")
+	checkHasDep(t, ctx, overridingModuleSameMinSdkVersion.Module(), javalibApex30Variant.Module())
+
+	// Override module, uses different min_sdk_version
+	overridingModuleDifferentMinSdkVersion := ctx.ModuleForTests("com.android.apex30", "android_common_com.mycompany.android.apex31_com.mycompany.android.apex31")
+	javalibApex31Variant := ctx.ModuleForTests("javalib", "android_common_apex31")
+	checkMinSdkVersion(t, overridingModuleDifferentMinSdkVersion, "31")
+	checkHasDep(t, ctx, overridingModuleDifferentMinSdkVersion.Module(), javalibApex31Variant.Module())
+}
+
+func TestOverrideApexWithPrebuiltApexPreferred(t *testing.T) {
+	context := android.GroupFixturePreparers(
+		android.PrepareForIntegrationTestWithAndroid,
+		PrepareForTestWithApexBuildComponents,
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/foo-file_contexts": nil,
+		}),
+	)
+	res := context.RunTestWithBp(t, `
+		apex {
+			name: "foo",
+			key: "myapex.key",
+			apex_available_name: "com.android.foo",
+			variant_version: "0",
+			updatable: false,
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+		prebuilt_apex {
+			name: "foo",
+			src: "foo.apex",
+			prefer: true,
+		}
+		override_apex {
+			name: "myoverrideapex",
+			base: "foo",
+		}
+	`)
+
+	java.CheckModuleHasDependency(t, res.TestContext, "myoverrideapex", "android_common_myoverrideapex_myoverrideapex", "foo")
+}
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 2be9c10..4a20cf0 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -795,3 +795,157 @@
 			}
 		`)
 }
+
+// Source and prebuilt apex provide different set of boot jars
+func TestNonBootJarMissingInPrebuiltFragment(t *testing.T) {
+	bp := `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			bootclasspath_fragments: ["apex-fragment"],
+			updatable: false,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_library {
+			name: "foo",
+			srcs: ["b.java"],
+			installable: true,
+			apex_available: ["myapex"],
+			permitted_packages: ["foo"],
+		}
+
+		java_library {
+			name: "bar",
+			srcs: ["b.java"],
+			installable: true,
+			apex_available: ["myapex"],
+			permitted_packages: ["bar"],
+		}
+
+		bootclasspath_fragment {
+			name: "apex-fragment",
+			contents: ["foo", "bar"],
+			apex_available:[ "myapex" ],
+			hidden_api: {
+				split_packages: ["*"],
+			},
+		}
+
+		prebuilt_apex {
+			name: "com.google.android.myapex", // mainline prebuilt selection logic in soong relies on the naming convention com.google.android
+			apex_name: "myapex",
+			source_apex_name: "myapex",
+			src: "myapex.apex",
+			exported_bootclasspath_fragments: ["apex-fragment"],
+		}
+
+		java_import {
+			name: "foo",
+			jars: ["foo.jar"],
+			apex_available: ["myapex"],
+			permitted_packages: ["foo"],
+		}
+
+		prebuilt_bootclasspath_fragment {
+			name: "apex-fragment",
+			contents: ["foo"], // Unlike the source fragment, this is missing bar
+			apex_available:[ "myapex" ],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
+		}
+
+		// Another prebuilt apex, but this is not selected during the build.
+		prebuilt_apex {
+			name: "com.google.android.myapex.v2", // mainline prebuilt selection logic in soong relies on the naming convention com.google.android
+			apex_name: "myapex",
+			source_apex_name: "myapex",
+			src: "myapex.apex",
+			exported_bootclasspath_fragments: ["apex-fragment.v2"],
+		}
+
+		java_import {
+			name: "bar",
+			jars: ["bar.jar"],
+			apex_available: ["myapex"],
+			permitted_packages: ["bar"],
+		}
+
+		prebuilt_bootclasspath_fragment {
+			name: "apex-fragment.v2",
+			contents: ["bar"], // Unlike the source fragment, this is missing foo
+			apex_available:[ "myapex" ],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
+		}
+
+
+		apex_contributions {
+			name: "my_apex_contributions",
+			api_domain: "myapex",
+			contents: [%v],
+		}
+	`
+	testCases := []struct {
+		desc                     string
+		configuredBootJars       []string
+		apexContributionContents string
+		errorExpected            bool
+	}{
+		{
+			desc:               "Source apex is selected, and APEX_BOOT_JARS is correctly configured for source apex builds",
+			configuredBootJars: []string{"myapex:foo", "myapex:bar"},
+		},
+		{
+			desc:               "Source apex is selected, and APEX_BOOT_JARS is missing bar",
+			configuredBootJars: []string{"myapex:foo"},
+			errorExpected:      true,
+		},
+		{
+			desc:                     "Prebuilt apex is selected, and APEX_BOOT_JARS is correctly configured for prebuilt apex build",
+			configuredBootJars:       []string{"myapex:foo"},
+			apexContributionContents: `"prebuilt_com.google.android.myapex"`,
+		},
+		{
+			desc:                     "Prebuilt apex is selected, and APEX_BOOT_JARS is missing foo",
+			configuredBootJars:       []string{"myapex:bar"},
+			apexContributionContents: `"prebuilt_com.google.android.myapex"`,
+			errorExpected:            true,
+		},
+	}
+
+	for _, tc := range testCases {
+		fixture := android.GroupFixturePreparers(
+			prepareForTestWithPlatformBootclasspath,
+			PrepareForTestWithApexBuildComponents,
+			prepareForTestWithMyapex,
+			java.FixtureConfigureApexBootJars(tc.configuredBootJars...),
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.BuildFlags = map[string]string{
+					"RELEASE_APEX_CONTRIBUTIONS_ART": "my_apex_contributions",
+				}
+			}),
+		)
+		if tc.errorExpected {
+			fixture = fixture.ExtendWithErrorHandler(
+				android.FixtureExpectsAtLeastOneErrorMatchingPattern(`in contents.*must also be declared in PRODUCT_APEX_BOOT_JARS`),
+			)
+		}
+		fixture.RunTestWithBp(t, fmt.Sprintf(bp, tc.apexContributionContents))
+	}
+}
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 72a9e52..9ad5159 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -835,7 +835,23 @@
 	android.SetProvider(ctx, android.PrebuiltInfoProvider, info)
 }
 
+// Uses an object provided by its deps to validate that the contents of bcpf have been added to the global
+// PRODUCT_APEX_BOOT_JARS
+// This validation will only run on the apex which is active for this product/release_config
+func validateApexClasspathFragments(ctx android.ModuleContext) {
+	ctx.VisitDirectDeps(func(m android.Module) {
+		if info, exists := android.OtherModuleProvider(ctx, m, java.ClasspathFragmentValidationInfoProvider); exists {
+			ctx.ModuleErrorf("%s in contents of %s must also be declared in PRODUCT_APEX_BOOT_JARS", info.UnknownJars, info.ClasspathFragmentModuleName)
+		}
+	})
+}
+
 func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Validate contents of classpath fragments
+	if !p.IsHideFromMake() {
+		validateApexClasspathFragments(ctx)
+	}
+
 	p.apexKeysPath = writeApexKeys(ctx, p)
 	// TODO(jungjw): Check the key validity.
 	p.inputApex = android.OptionalPathForModuleSrc(ctx, p.prebuiltCommonProperties.Selected_apex).Path()
@@ -1059,6 +1075,11 @@
 }
 
 func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Validate contents of classpath fragments
+	if !a.IsHideFromMake() {
+		validateApexClasspathFragments(ctx)
+	}
+
 	a.apexKeysPath = writeApexKeys(ctx, a)
 	a.installFilename = a.InstallFilename()
 	if !strings.HasSuffix(a.installFilename, imageApexSuffix) && !strings.HasSuffix(a.installFilename, imageCapexSuffix) {
diff --git a/bin/allmod b/bin/allmod
new file mode 100755
index 0000000..f7d25e5
--- /dev/null
+++ b/bin/allmod
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# List all modules for the current device, as cached in all_modules.txt. If any build change is
+# made and it should be reflected in the output, you should run 'refreshmod' first.
+
+cat $ANDROID_PRODUCT_OUT/all_modules.txt 2>/dev/null
+
diff --git a/bin/build-flag-declarations b/bin/build-flag-declarations
new file mode 100755
index 0000000..222f083
--- /dev/null
+++ b/bin/build-flag-declarations
@@ -0,0 +1,28 @@
+#!/bin/bash -eu
+#
+# Copyright 2017 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+require_top
+
+# Save the current PWD for use in soong_ui
+export ORIGINAL_PWD=${PWD}
+export TOP=$(gettop)
+source ${TOP}/build/soong/scripts/microfactory.bash
+
+soong_build_go build-flag-declarations android/soong/cmd/release_config/build_flag_declarations
+
+cd ${TOP}
+exec "$(getoutdir)/build-flag-declarations" "$@"
diff --git a/bin/cgrep b/bin/cgrep
new file mode 100755
index 0000000..6c9130c
--- /dev/null
+++ b/bin/cgrep
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.c' \
+        -o -name '*.cc' \
+        -o -name '*.cpp' \
+        -o -name '*.h' \
+        -o -name '*.hpp' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/core b/bin/core
new file mode 100755
index 0000000..01dbd13
--- /dev/null
+++ b/bin/core
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# core - send SIGV and pull the core for process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must be called once per boot for core dumps to be
+#       enabled globally.
+
+set -e
+
+PID=$1;
+
+if [ -z "$PID" ]; then
+    printf "Expecting a PID!\n";
+    exit 1
+fi;
+
+CORENAME=core.$PID;
+COREPATH=/cores/$CORENAME;
+SIG=SEGV;
+
+coredump_enable $1;
+
+done=0;
+while [ $(adb shell "[ -d /proc/$PID ] && echo -n yes") ]; do
+    printf "\tSending SIG%s to %d...\n" $SIG $PID;
+    adb shell kill -$SIG $PID;
+    sleep 1;
+done;
+
+adb shell "while [ ! -f $COREPATH ] ; do echo waiting for $COREPATH to be generated; sleep 1; done"
+echo "Done: core is under $COREPATH on device.";
+
+
diff --git a/bin/coredump_enable b/bin/coredump_enable
new file mode 100755
index 0000000..da63a0c
--- /dev/null
+++ b/bin/coredump_enable
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+PID=$1;
+if [ -z "$PID" ]; then
+    printf "Expecting a PID!\n";
+    exit 1
+fi;
+echo "Setting core limit for $PID to infinite...";
+adb shell /system/bin/ulimit -P $PID -c unlimited
+
diff --git a/bin/coredump_setup b/bin/coredump_setup
new file mode 100755
index 0000000..7647659
--- /dev/null
+++ b/bin/coredump_setup
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_setup - enable core dumps globally for any process
+#                  that has the core-file-size limit set correctly
+#
+# NOTE: You must call also coredump_enable for a specific process
+#       if its core-file-size limit is not set already.
+# NOTE: Core dumps are written to ramdisk; they will not survive a reboot!
+
+set -e
+
+echo "Getting root...";
+adb root;
+adb wait-for-device;
+
+echo "Remounting root partition read-write...";
+adb shell mount -w -o remount -t rootfs rootfs;
+sleep 1;
+adb wait-for-device;
+adb shell mkdir -p /cores;
+adb shell mount -t tmpfs tmpfs /cores;
+adb shell chmod 0777 /cores;
+
+echo "Granting SELinux permission to dump in /cores...";
+adb shell restorecon -R /cores;
+
+echo "Set core pattern.";
+adb shell 'echo /cores/core.%p > /proc/sys/kernel/core_pattern';
+
+echo "Done."
+
diff --git a/bin/dirmods b/bin/dirmods
new file mode 100755
index 0000000..d43506a
--- /dev/null
+++ b/bin/dirmods
@@ -0,0 +1,52 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sys
+sys.dont_write_bytecode = True
+
+import modinfo
+
+import os
+
+# Get the path of a specific module in the android tree, as cached in module-info.json.
+# If any build change is made, and it should be reflected in the output, you should run
+# 'refreshmod' first.  Note: This is the inverse of pathmod.
+
+def main(argv):
+    if len(argv) != 2:
+        sys.stderr.write("usage: usage: dirmods <path>\n")
+        sys.exit(1)
+
+    d = argv[1]
+    while d.endswith('/'):
+        d = d[:-1]
+    prefix = d + '/'
+
+    module_info = modinfo.ReadModuleInfo()
+
+    results = set()
+    for m in module_info.values():
+        for path in m.get(u'path', []):
+            if path == d or path.startswith(prefix):
+                name = m.get(u'module_name')
+                if name:
+                    results.add(name)
+
+    for name in sorted(results):
+        print(name)
+
+if __name__ == "__main__":
+    main(sys.argv)
diff --git a/bin/get_abs_build_var b/bin/get_abs_build_var
new file mode 100755
index 0000000..8072cf1
--- /dev/null
+++ b/bin/get_abs_build_var
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Get the value of a build variable as an absolute path.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+$TOP/build/soong/soong_ui.bash --dumpvar-mode --abs $1
+
+exit $?
diff --git a/bin/get_build_var b/bin/get_build_var
new file mode 100755
index 0000000..9fdf55f
--- /dev/null
+++ b/bin/get_build_var
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Get the exact value of a build variable.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+$TOP/build/soong/soong_ui.bash --dumpvar-mode $1
+
+exit $?
diff --git a/bin/getlastscreenshot b/bin/getlastscreenshot
new file mode 100755
index 0000000..dfe9a6b
--- /dev/null
+++ b/bin/getlastscreenshot
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+screenshot_path=$(getscreenshotpath)
+screenshot=`adb ${adbOptions} ls ${screenshot_path} | grep Screenshot_[0-9-]*.*\.png | sort -rk 3 | cut -d " " -f 4 | head -n 1`
+if [ "$screenshot" = "" ]; then
+    echo "No screenshots found."
+    exit 1
+fi
+echo "${screenshot}"
+adb ${adbOptions} pull ${screenshot_path}/${screenshot}
+
diff --git a/bin/getprebuilt b/bin/getprebuilt
new file mode 100755
index 0000000..68e65b4
--- /dev/null
+++ b/bin/getprebuilt
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+get_abs_build_var ANDROID_PREBUILTS
+
diff --git a/bin/getscreenshotpath b/bin/getscreenshotpath
new file mode 100755
index 0000000..ff8e327
--- /dev/null
+++ b/bin/getscreenshotpath
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+echo "$(getsdcardpath)/Pictures/Screenshots"
+
diff --git a/bin/getsdcardpath b/bin/getsdcardpath
new file mode 100755
index 0000000..655659a
--- /dev/null
+++ b/bin/getsdcardpath
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+adb ${adbOptions} shell echo -n \$\{EXTERNAL_STORAGE\}
+
diff --git a/bin/gettargetarch b/bin/gettargetarch
new file mode 100755
index 0000000..e53ce3f
--- /dev/null
+++ b/bin/gettargetarch
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+get_build_var TARGET_ARCH
+
diff --git a/bin/ggrep b/bin/ggrep
new file mode 100755
index 0000000..fce8c84
--- /dev/null
+++ b/bin/ggrep
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.gradle' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/gogrep b/bin/gogrep
new file mode 100755
index 0000000..0265ccf
--- /dev/null
+++ b/bin/gogrep
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.go' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/hmm b/bin/hmm
new file mode 100755
index 0000000..161bad6
--- /dev/null
+++ b/bin/hmm
@@ -0,0 +1,81 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+cat <<EOF
+
+Run "m help" for help with the build system itself.
+
+Invoke ". build/envsetup.sh" from your shell to add the following functions to your environment:
+- lunch:      lunch <product_name>-<release_type>-<build_variant>
+              Selects <product_name> as the product to build, and <build_variant> as the variant to
+              build, and stores those selections in the environment to be read by subsequent
+              invocations of 'm' etc.
+- tapas:      tapas [<App1> <App2> ...] [arm|x86|arm64|x86_64] [eng|userdebug|user]
+              Sets up the build environment for building unbundled apps (APKs).
+- banchan:    banchan <module1> [<module2> ...] \\
+                      [arm|x86|arm64|riscv64|x86_64|arm64_only|x86_64only] [eng|userdebug|user]
+              Sets up the build environment for building unbundled modules (APEXes).
+- croot:      Changes directory to the top of the tree, or a subdirectory thereof.
+- m:          Makes from the top of the tree.
+- mm:         Builds and installs all of the modules in the current directory, and their
+              dependencies.
+- mmm:        Builds and installs all of the modules in the supplied directories, and their
+              dependencies.
+              To limit the modules being built use the syntax: mmm dir/:target1,target2.
+- mma:        Same as 'mm'
+- mmma:       Same as 'mmm'
+- provision:  Flash device with all required partitions. Options will be passed on to fastboot.
+- cgrep:      Greps on all local C/C++ files.
+- ggrep:      Greps on all local Gradle files.
+- gogrep:     Greps on all local Go files.
+- jgrep:      Greps on all local Java files.
+- jsongrep:   Greps on all local Json files.
+- ktgrep:     Greps on all local Kotlin files.
+- resgrep:    Greps on all local res/*.xml files.
+- mangrep:    Greps on all local AndroidManifest.xml files.
+- mgrep:      Greps on all local Makefiles and *.bp files.
+- owngrep:    Greps on all local OWNERS files.
+- rsgrep:     Greps on all local Rust files.
+- sepgrep:    Greps on all local sepolicy files.
+- sgrep:      Greps on all local source files.
+- tomlgrep:   Greps on all local Toml files.
+- pygrep:     Greps on all local Python files.
+- godir:      Go to the directory containing a file.
+- allmod:     List all modules.
+- gomod:      Go to the directory containing a module.
+- pathmod:    Get the directory containing a module.
+- outmod:     Gets the location of a module's installed outputs with a certain extension.
+- dirmods:    Gets the modules defined in a given directory.
+- installmod: Adb installs a module's built APK.
+- refreshmod: Refresh list of modules for allmod/gomod/pathmod/outmod/installmod.
+- syswrite:   Remount partitions (e.g. system.img) as writable, rebooting if necessary.
+
+Environment options:
+- SANITIZE_HOST: Set to 'address' to use ASAN for all host modules.
+- ANDROID_QUIET_BUILD: set to 'true' to display only the essential messages.
+
+Look at the source to view more functions. The complete list is:
+EOF
+    T=$(gettop)
+    A=""
+    for i in `cat $T/build/envsetup.sh | sed -n "/^[[:blank:]]*function /s/function \([a-z_]*\).*/\1/p" | sort | uniq`; do
+      A="$A $i"
+    done
+    echo $A
+
diff --git a/bin/installmod b/bin/installmod
new file mode 100755
index 0000000..1d0d836
--- /dev/null
+++ b/bin/installmod
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# adb install a module's apk, as cached in module-info.json. If any build change
+# is made, and it should be reflected in the output, you should run 'refreshmod' first.
+# Usage: installmod [adb install arguments] <module>
+# For example: installmod -r Dialer -> adb install -r /path/to/Dialer.apk
+
+if [[ $# -eq 0 ]]; then
+    echo "usage: installmod [adb install arguments] <module>" >&2
+    echo "" >&2
+    echo "Only flags to be passed after the \"install\" in adb install are supported," >&2
+    echo "with the exception of -s. If -s is passed it will be placed before the \"install\"." >&2
+    echo "-s must be the first flag passed if it exists." >&2
+    return 1
+fi
+
+local _path
+_path=$(outmod ${@:$#:1})
+if [ $? -ne 0 ]; then
+    return 1
+fi
+
+_path=$(echo "$_path" | grep -E \\.apk$ | head -n 1)
+if [ -z "$_path" ]; then
+    echo "Module '$1' does not produce a file ending with .apk (try 'refreshmod' if there have been build changes?)" >&2
+    return 1
+fi
+local serial_device=""
+if [[ "$1" == "-s" ]]; then
+    if [[ $# -le 2 ]]; then
+        echo "-s requires an argument" >&2
+        return 1
+    fi
+    serial_device="-s $2"
+    shift 2
+fi
+local length=$(( $# - 1 ))
+echo adb $serial_device install ${@:1:$length} $_path
+adb $serial_device install ${@:1:$length} $_path
+
diff --git a/bin/is64bit b/bin/is64bit
new file mode 100755
index 0000000..35bbcc3
--- /dev/null
+++ b/bin/is64bit
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Read the ELF header from /proc/$PID/exe to determine if the process is
+# 64-bit.
+
+set -e
+
+local PID="$1"
+if [ "$PID" ] ; then
+    if [[ "$(adb shell cat /proc/$PID/exe | xxd -l 1 -s 4 -p)" -eq "02" ]] ; then
+        echo "64"
+    else
+        echo ""
+    fi
+else
+    echo ""
+fi
+
diff --git a/bin/isviewserverstarted b/bin/isviewserverstarted
new file mode 100755
index 0000000..c7c82af
--- /dev/null
+++ b/bin/isviewserverstarted
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+adb shell service call window 3
+
diff --git a/bin/jgrep b/bin/jgrep
new file mode 100755
index 0000000..afe70db
--- /dev/null
+++ b/bin/jgrep
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.java' \
+        -o -name '*.kt' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/jsongrep b/bin/jsongrep
new file mode 100755
index 0000000..6e14d0c
--- /dev/null
+++ b/bin/jsongrep
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.json' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/key_back b/bin/key_back
new file mode 100755
index 0000000..2de8d07
--- /dev/null
+++ b/bin/key_back
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+adb shell input keyevent 4
+
diff --git a/bin/key_home b/bin/key_home
new file mode 100755
index 0000000..653a5f9
--- /dev/null
+++ b/bin/key_home
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+adb shell input keyevent 3
+
diff --git a/bin/key_menu b/bin/key_menu
new file mode 100755
index 0000000..29b2bc6
--- /dev/null
+++ b/bin/key_menu
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+adb shell input keyevent 82
+
diff --git a/bin/ktgrep b/bin/ktgrep
new file mode 120000
index 0000000..9b51491
--- /dev/null
+++ b/bin/ktgrep
@@ -0,0 +1 @@
+jgrep
\ No newline at end of file
diff --git a/bin/list_products b/bin/list_products
new file mode 100755
index 0000000..cd8dd5c
--- /dev/null
+++ b/bin/list_products
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+# In almost call cases including get_build_var, TARGET_RELEASE is required,
+# but the list of available products is not dependent on the release config
+# (but note that the list of available release configs is dependent on the
+# product). So for list_products, we'll just set it to trunk_staging, which
+# exists everwhere, so we don't trigger the unspecified TARGET_RELEASE error.
+
+# We also unset TARGET_BUILD_APPS, so it doesn't interfere.
+
+TARGET_RELEASE=trunk_staging TARGET_BUILD_APPS= $TOP/build/soong/soong_ui.bash --dumpvar-mode all_named_products | sed 's/ /\n/g'
+
+exit $?
diff --git a/bin/list_releases b/bin/list_releases
new file mode 100755
index 0000000..ca18110
--- /dev/null
+++ b/bin/list_releases
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+if [[ $# -eq 1 ]]; then
+    # Override anything that's already set
+    export TARGET_PRODUCT=$1
+elif [[ -z $TARGET_PRODUCT ]]; then
+    echo "Usage: list_releases [PRODUCT]" 1>&2
+    echo "" 1>&2
+    echo "If the optional PRODUCT parameter is bit provided, then TARGET_PRODUCT" 1>&2
+    echo "must have been set, for example by lunch or banchan." 1>&2
+    exit 1
+fi
+
+
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+# In almost call cases including get_build_var, TARGET_RELEASE is required,
+# but the list of available products is not dependent on the release config
+# (but note that the list of available release configs is dependent on the
+# product). So for list_products, we'll just set it to trunk_staging, which
+# exists everwhere, so we don't trigger the unspecified TARGET_RELEASE error.
+
+# We also unset TARGET_BUILD_APPS, so it doesn't interfere.
+
+TARGET_RELEASE=trunk_staging TARGET_BUILD_APPS= $TOP/build/soong/soong_ui.bash --dumpvar-mode ALL_RELEASE_CONFIGS_FOR_PRODUCT | sed 's/ /\n/g'
+
+exit $?
diff --git a/bin/list_variants b/bin/list_variants
new file mode 100755
index 0000000..ac89e6a
--- /dev/null
+++ b/bin/list_variants
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+echo user
+echo userdebug
+echo eng
+
diff --git a/bin/m b/bin/m
new file mode 100755
index 0000000..edcfce5
--- /dev/null
+++ b/bin/m
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --all-modules --dir="$(pwd)" "$@"
+
+exit $?
diff --git a/bin/mangrep b/bin/mangrep
new file mode 100755
index 0000000..a343000
--- /dev/null
+++ b/bin/mangrep
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name 'AndroidManifest.xml' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/mgrep b/bin/mgrep
new file mode 100755
index 0000000..793730d
--- /dev/null
+++ b/bin/mgrep
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name 'Makefile' \
+        -o -name 'Makefile.*' \
+        -o -name '*.make' \
+        -o -name '*.mak' \
+        -o -name '*.mk' \
+        -o -name '*.bp' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/mm b/bin/mm
new file mode 100755
index 0000000..6461b1e
--- /dev/null
+++ b/bin/mm
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --modules-in-a-dir-no-deps --dir="$(pwd)" "$@"
+
+exit $?
diff --git a/bin/mma b/bin/mma
new file mode 100755
index 0000000..6f1c934
--- /dev/null
+++ b/bin/mma
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --modules-in-a-dir --dir="$(pwd)" "$@"
+
+exit $?
diff --git a/bin/mmm b/bin/mmm
new file mode 100755
index 0000000..ab3a632
--- /dev/null
+++ b/bin/mmm
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --modules-in-dirs-no-deps --dir="$(pwd)" "$@"
+
+exit $?
diff --git a/bin/mmma b/bin/mmma
new file mode 100755
index 0000000..d9190e5
--- /dev/null
+++ b/bin/mmma
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --modules-in-dirs --dir="$(pwd)" "$@"
+
+exit $?
diff --git a/bin/modinfo.py b/bin/modinfo.py
new file mode 100644
index 0000000..015129f
--- /dev/null
+++ b/bin/modinfo.py
@@ -0,0 +1,44 @@
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import os
+import pathlib
+import sys
+
+
+def OpenModuleInfoFile():
+    product_out = os.getenv("ANDROID_PRODUCT_OUT")
+    if not product_out:
+        if os.getenv("QUIET_VERIFYMODINFO") != "true":
+            sys.stderr.write("No ANDROID_PRODUCT_OUT. Try running 'lunch' first.\n")
+        sys.exit(1)
+    try:
+        return open(pathlib.Path(product_out) / "module-info.json")
+    except (FileNotFoundError, PermissionError):
+        if os.getenv("QUIET_VERIFYMODINFO") != "true":
+            sys.stderr.write("Could not find module-info.json. Please run 'refreshmod' first.\n")
+        sys.exit(1)
+
+
+def ReadModuleInfo():
+    with OpenModuleInfoFile() as f:
+        return json.load(f)
+
+def GetModule(modules, module_name):
+    if module_name not in modules:
+        sys.stderr.write(f"Could not find module '{module_name}' (try 'refreshmod' if there have been build changes?)\n")
+        sys.exit(1)
+    return modules[module_name]
+
diff --git a/bin/outmod b/bin/outmod
new file mode 100755
index 0000000..681b405
--- /dev/null
+++ b/bin/outmod
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Get the path of a specific module in the android tree, as cached in module-info.json.
+# If any build change is made, and it should be reflected in the output, you should run
+# 'refreshmod' first.  Note: This is the inverse of dirmods.
+
+import sys
+sys.dont_write_bytecode = True
+
+import modinfo
+
+import os
+
+
+def main(argv):
+    if len(argv) != 2:
+        sys.stderr.write("usage: outmod <module>\n")
+        sys.exit(1)
+
+    for output in modinfo.GetModule(modinfo.ReadModuleInfo(), argv[1])['installed']:
+        print(os.path.join(os.getenv("ANDROID_BUILD_TOP"), output))
+
+if __name__ == "__main__":
+    main(sys.argv)
+
diff --git a/bin/owngrep b/bin/owngrep
new file mode 100755
index 0000000..26ce6e8
--- /dev/null
+++ b/bin/owngrep
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name 'OWNERS' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/pathmod b/bin/pathmod
new file mode 100755
index 0000000..90d84b5
--- /dev/null
+++ b/bin/pathmod
@@ -0,0 +1,37 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Get the path of a specific module in the android tree, as cached in module-info.json.
+# If any build change is made, and it should be reflected in the output, you should run
+# 'refreshmod' first.  Note: This is the inverse of dirmods.
+
+import sys
+sys.dont_write_bytecode = True
+
+import modinfo
+
+import os
+
+
+def main(argv):
+    if len(argv) != 2:
+        sys.stderr.write("usage: pathmod <module>\n")
+        sys.exit(1)
+
+    print(modinfo.GetModule(modinfo.ReadModuleInfo(), argv[1])['path'][0])
+
+if __name__ == "__main__":
+    main(sys.argv)
diff --git a/bin/pygrep b/bin/pygrep
new file mode 100755
index 0000000..e072289
--- /dev/null
+++ b/bin/pygrep
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.py' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/qpid b/bin/qpid
new file mode 100755
index 0000000..b47cb6b
--- /dev/null
+++ b/bin/qpid
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# adb install a module's apk, as cached in module-info.json. If any build change
+# is made, and it should be reflected in the output, you should run 'refreshmod' first.
+# Usage: installmod [adb install arguments] <module>
+# For example: installmod -r Dialer -> adb install -r /path/to/Dialer.apk
+
+function _impl() {
+    local prepend=''
+    local append=''
+    if [ "$1" = "--exact" ]; then
+        prepend=' '
+        append='$'
+        shift
+    elif [ "$1" = "--help" -o "$1" = "-h" ]; then
+        echo "usage: qpid [[--exact] <process name|pid>"
+        return 255
+    fi
+
+    local EXE="$1"
+    if [ "$EXE" ] ; then
+        _impl | \grep "$prepend$EXE$append"
+    else
+        adb shell ps \
+            | tr -d '\r' \
+            | sed -e 1d -e 's/^[^ ]* *\([0-9]*\).* \([^ ]*\)$/\1 \2/'
+    fi
+}
+
+_impl "$@"
diff --git a/bin/rcgrep b/bin/rcgrep
new file mode 100755
index 0000000..ff93e51
--- /dev/null
+++ b/bin/rcgrep
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.rc' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/refreshmod b/bin/refreshmod
new file mode 100755
index 0000000..f511846
--- /dev/null
+++ b/bin/refreshmod
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Update module-info.json in out.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+if [ ! "$ANDROID_PRODUCT_OUT" ]; then
+    echo "No ANDROID_PRODUCT_OUT. Try running 'lunch' first." >&2
+    return 1
+fi
+
+echo "Refreshing modules (building module-info.json)" >&2
+
+_wrap_build $TOP/build/soong/soong_ui.bash --build-mode --all-modules --dir="$(pwd)" module-info
diff --git a/bin/resgrep b/bin/resgrep
new file mode 100755
index 0000000..600091f
--- /dev/null
+++ b/bin/resgrep
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+for dir in `find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -name res -type d`; do
+    find $dir -type f -name '*\.xml' -exec grep --color -n "$@" {} +
+done
diff --git a/bin/rsgrep b/bin/rsgrep
new file mode 100755
index 0000000..8c24151
--- /dev/null
+++ b/bin/rsgrep
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.rs' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/sepgrep b/bin/sepgrep
new file mode 100755
index 0000000..0e0d1ba
--- /dev/null
+++ b/bin/sepgrep
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -name sepolicy -type d \
+    -exec grep --color -n -r --exclude-dir=\.git "$@" {} +
+exit $?
diff --git a/bin/sgrep b/bin/sgrep
new file mode 100755
index 0000000..f186553
--- /dev/null
+++ b/bin/sgrep
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.c' \
+        -o -name '*.cc' \
+        -o -name '*.cpp' \
+        -o -name '*.h' \
+        -o -name '*.hpp' \
+        -o -name '*.S' \
+        -o -name '*.java' \
+        -o -name '*.kt' \
+        -o -name '*.xml' \
+        -o -name '*.sh' \
+        -o -name '*.mk' \
+        -o -name '*.bp' \
+        -o -name '*.aidl' \
+        -o -name '*.vts' \
+        -o -name '*.proto' \
+        -o -name '*.rs' \
+        -o -name '*.go' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/startviewserver b/bin/startviewserver
new file mode 100755
index 0000000..4d612b8
--- /dev/null
+++ b/bin/startviewserver
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+port=4939
+if [ $# -gt 0 ]; then
+    port=$1
+fi
+adb shell service call window 1 i32 $port
+
diff --git a/bin/stopviewserver b/bin/stopviewserver
new file mode 100755
index 0000000..a734e4b
--- /dev/null
+++ b/bin/stopviewserver
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+adb shell service call window 2
+
diff --git a/bin/systemstack b/bin/systemstack
new file mode 100755
index 0000000..b259133
--- /dev/null
+++ b/bin/systemstack
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# systemstack - dump the current stack trace of all threads in the system process
+# to the usual ANR traces file
+
+stacks system_server
+
diff --git a/bin/syswrite b/bin/syswrite
new file mode 100755
index 0000000..46201e3
--- /dev/null
+++ b/bin/syswrite
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# syswrite - disable verity, reboot if needed, and remount image
+# Easy way to make system.img/etc writable
+
+adb wait-for-device && adb root && adb wait-for-device || exit 1
+if [[ $(adb disable-verity | grep -i "reboot") ]]; then
+  echo "rebooting"
+  adb reboot && adb wait-for-device && adb root && adb wait-for-device || exit 1
+fi
+adb remount || exit 1
diff --git a/bin/tomlgrep b/bin/tomlgrep
new file mode 100755
index 0000000..636ef22
--- /dev/null
+++ b/bin/tomlgrep
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.toml' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/treegrep b/bin/treegrep
new file mode 100755
index 0000000..b83d419
--- /dev/null
+++ b/bin/treegrep
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.c' \
+        -o -name '*.cc' \
+        -o -name '*.cpp' \
+        -o -name '*.h' \
+        -o -name '*.hpp' \
+        -o -name '*.S' \
+        -o -name '*.java' \
+        -o -name '*.kt' \
+        -o -name '*.xml' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 0c6f97c..e1bc336 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -261,15 +261,6 @@
 		if library.coverageOutputFile.Valid() {
 			entries.SetString("LOCAL_PREBUILT_COVERAGE_ARCHIVE", library.coverageOutputFile.String())
 		}
-
-		if library.useCoreVariant {
-			entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
-			entries.SetBool("LOCAL_NO_NOTICE_FILE", true)
-			entries.SetBool("LOCAL_VNDK_DEPEND_ON_CORE_VARIANT", true)
-		}
-		if library.checkSameCoreVariant {
-			entries.SetBool("LOCAL_CHECK_SAME_VNDK_VARIANTS", true)
-		}
 	})
 
 	if library.shared() && !library.buildStubs() {
diff --git a/cc/binary.go b/cc/binary.go
index 7aa8e20..3ff35de 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -18,6 +18,7 @@
 	"path/filepath"
 
 	"android/soong/android"
+
 	"github.com/google/blueprint"
 )
 
@@ -425,6 +426,10 @@
 	validations = append(validations, objs.tidyDepFiles...)
 	linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
 
+	if generatedLib := generateRustStaticlib(ctx, deps.RustRlibDeps); generatedLib != nil {
+		deps.StaticLibs = append(deps.StaticLibs, generatedLib)
+	}
+
 	// Register link action.
 	transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs, deps.StaticLibs,
 		deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, deps.CrtEnd, true,
diff --git a/cc/builder.go b/cc/builder.go
index e255cbe..7a3394a 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -19,6 +19,7 @@
 // functions.
 
 import (
+	"fmt"
 	"path/filepath"
 	"runtime"
 	"strconv"
@@ -45,18 +46,18 @@
 		blueprint.RuleParams{
 			Depfile:     "${out}.d",
 			Deps:        blueprint.DepsGCC,
-			Command:     "$relPwd ${config.CcWrapper}$ccCmd -c $cFlags -MD -MF ${out}.d -o $out $in",
+			Command:     "$relPwd ${config.CcWrapper}$ccCmd -c $cFlags -MD -MF ${out}.d -o $out $in$postCmd",
 			CommandDeps: []string{"$ccCmd"},
 		},
-		"ccCmd", "cFlags")
+		"ccCmd", "cFlags", "postCmd")
 
 	// Rule to invoke gcc with given command and flags, but no dependencies.
 	ccNoDeps = pctx.AndroidStaticRule("ccNoDeps",
 		blueprint.RuleParams{
-			Command:     "$relPwd $ccCmd -c $cFlags -o $out $in",
+			Command:     "$relPwd $ccCmd -c $cFlags -o $out $in$postCmd",
 			CommandDeps: []string{"$ccCmd"},
 		},
-		"ccCmd", "cFlags")
+		"ccCmd", "cFlags", "postCmd")
 
 	// Rules to invoke ld to link binaries. Uses a .rsp file to list dependencies, as there may
 	// be many.
@@ -330,6 +331,15 @@
 			CommandDeps: []string{"$cxxExtractor", "$kytheVnames"},
 		},
 		"cFlags")
+
+	// Function pointer for producting staticlibs from rlibs. Corresponds to
+	// rust.TransformRlibstoStaticlib(), initialized in soong-rust (rust/builder.go init())
+	//
+	// This is required since soong-rust depends on soong-cc, so soong-cc cannot depend on soong-rust
+	// without resulting in a circular dependency. Setting this function pointer in soong-rust allows
+	// soong-cc to call into this particular function.
+	TransformRlibstoStaticlib (func(ctx android.ModuleContext, mainSrc android.Path, deps []RustRlibDep,
+		outputFile android.WritablePath) android.Path) = nil
 )
 
 func PwdPrefix() string {
@@ -390,6 +400,7 @@
 	gcovCoverage  bool
 	sAbiDump      bool
 	emitXrefs     bool
+	clangVerify   bool
 
 	assemblerWithCpp bool // True if .s files should be processed with the c preprocessor.
 
@@ -581,6 +592,7 @@
 		var moduleToolingFlags string
 
 		var ccCmd string
+		var postCmd string
 		tidy := flags.tidy
 		coverage := flags.gcovCoverage
 		dump := flags.sAbiDump
@@ -625,6 +637,10 @@
 
 		ccCmd = "${config.ClangBin}/" + ccCmd
 
+		if flags.clangVerify {
+			postCmd = " && touch " + objFile.String()
+		}
+
 		var implicitOutputs android.WritablePaths
 		if coverage {
 			gcnoFile := android.ObjPathWithExt(ctx, subdir, srcFile, "gcno")
@@ -641,8 +657,9 @@
 			Implicits:       cFlagsDeps,
 			OrderOnly:       pathDeps,
 			Args: map[string]string{
-				"cFlags": shareFlags("cFlags", moduleFlags),
-				"ccCmd":  ccCmd, // short and not shared
+				"cFlags":  shareFlags("cFlags", moduleFlags),
+				"ccCmd":   ccCmd, // short and not shared
+				"postCmd": postCmd,
 			},
 		})
 
@@ -778,6 +795,47 @@
 	}
 }
 
+// Generate a Rust staticlib from a list of rlibDeps. Returns nil if TransformRlibstoStaticlib is nil or rlibDeps is empty.
+func generateRustStaticlib(ctx android.ModuleContext, rlibDeps []RustRlibDep) android.Path {
+	if TransformRlibstoStaticlib == nil && len(rlibDeps) > 0 {
+		// This should only be reachable if a module defines static_rlibs and
+		// soong-rust hasn't been loaded alongside soong-cc (e.g. in soong-cc tests).
+		panic(fmt.Errorf("TransformRlibstoStaticlib is not set and static_rlibs is defined in %s", ctx.ModuleName()))
+	} else if len(rlibDeps) == 0 {
+		return nil
+	}
+
+	output := android.PathForModuleOut(ctx, "generated_rust_staticlib", "lib"+ctx.ModuleName()+"_rust_staticlib.a")
+	stemFile := output.ReplaceExtension(ctx, "rs")
+	crateNames := []string{}
+
+	// Collect crate names
+	for _, lib := range rlibDeps {
+		// Exclude libstd so this can support no_std builds.
+		if lib.CrateName != "libstd" {
+			crateNames = append(crateNames, lib.CrateName)
+		}
+	}
+
+	// Deduplicate any crateNames just to be safe
+	crateNames = android.FirstUniqueStrings(crateNames)
+
+	// Write the source file
+	android.WriteFileRule(ctx, stemFile, genRustStaticlibSrcFile(crateNames))
+
+	return TransformRlibstoStaticlib(ctx, stemFile, rlibDeps, output)
+}
+
+func genRustStaticlibSrcFile(crateNames []string) string {
+	lines := []string{
+		"// @Soong generated Source",
+	}
+	for _, crate := range crateNames {
+		lines = append(lines, fmt.Sprintf("extern crate %s;", crate))
+	}
+	return strings.Join(lines, "\n")
+}
+
 // Generate a rule for compiling multiple .o files, plus static libraries, whole static libraries,
 // and shared libraries, to a shared library (.so) or dynamic executable
 func transformObjToDynamicBinary(ctx android.ModuleContext,
diff --git a/cc/cc.go b/cc/cc.go
index eb6e974..cb82f86 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -100,6 +100,7 @@
 	StaticLibs, LateStaticLibs, WholeStaticLibs []string
 	HeaderLibs                                  []string
 	RuntimeLibs                                 []string
+	Rlibs                                       []string
 
 	// UnexportedStaticLibs are static libraries that are also passed to -Wl,--exclude-libs= to
 	// prevent automatically exporting symbols.
@@ -145,6 +146,17 @@
 	LlndkHeaderLibs []string
 }
 
+// A struct which to collect flags for rlib dependencies
+type RustRlibDep struct {
+	LibPath   android.Path // path to the rlib
+	LinkDirs  []string     // flags required for dependency (e.g. -L flags)
+	CrateName string       // crateNames associated with rlibDeps
+}
+
+func EqRustRlibDeps(a RustRlibDep, b RustRlibDep) bool {
+	return a.LibPath == b.LibPath
+}
+
 // PathDeps is a struct containing file paths to dependencies of a module.
 // It's constructed in depsToPath() by traversing the direct dependencies of the current module.
 // It's used to construct flags for various build statements (such as for compiling and linking).
@@ -157,6 +169,8 @@
 	SharedLibsDeps, EarlySharedLibsDeps, LateSharedLibsDeps android.Paths
 	// Paths to .a files
 	StaticLibs, LateStaticLibs, WholeStaticLibs android.Paths
+	// Paths and crateNames for RustStaticLib dependencies
+	RustRlibDeps []RustRlibDep
 
 	// Transitive static library dependencies of static libraries for use in ordering.
 	TranstiveStaticLibrariesForOrdering *android.DepSet[android.Path]
@@ -185,6 +199,7 @@
 	ReexportedFlags            []string
 	ReexportedGeneratedHeaders android.Paths
 	ReexportedDeps             android.Paths
+	ReexportedRustRlibDeps     []RustRlibDep
 
 	// Paths to crt*.o files
 	CrtBegin, CrtEnd android.Paths
@@ -245,6 +260,7 @@
 	GcovCoverage  bool // True if coverage files should be generated.
 	SAbiDump      bool // True if header abi dumps should be generated.
 	EmitXrefs     bool // If true, generate Ninja rules to generate emitXrefs input files for Kythe
+	ClangVerify   bool // If true, append cflags "-Xclang -verify" and append "&& touch $out" to the clang command line.
 
 	// The instruction set required for clang ("arm" or "thumb").
 	RequiredInstructionSet string
@@ -298,6 +314,7 @@
 
 	AndroidMkSharedLibs       []string `blueprint:"mutated"`
 	AndroidMkStaticLibs       []string `blueprint:"mutated"`
+	AndroidMkRlibs            []string `blueprint:"mutated"`
 	AndroidMkRuntimeLibs      []string `blueprint:"mutated"`
 	AndroidMkWholeStaticLibs  []string `blueprint:"mutated"`
 	AndroidMkHeaderLibs       []string `blueprint:"mutated"`
@@ -357,10 +374,6 @@
 	// build system and source tree.
 	Cmake_snapshot_supported *bool
 
-	// Even if DeviceConfig().VndkUseCoreVariant() is set, this module must use vendor variant.
-	// see soong/cc/config/vndk.go
-	MustUseVendorVariant bool `blueprint:"mutated"`
-
 	Installable *bool `android:"arch_variant"`
 
 	// Set by factories of module types that can only be referenced from variants compiled against
@@ -462,10 +475,6 @@
 	// IsLLNDK is set to true for the vendor variant of a cc_library module that has LLNDK stubs.
 	IsLLNDK bool `blueprint:"mutated"`
 
-	// IsVNDKUsingCoreVariant is true for VNDK modules if the global VndkUseCoreVariant option is
-	// set and the module is not listed in VndkMustUseVendorVariantList.
-	IsVNDKUsingCoreVariant bool `blueprint:"mutated"`
-
 	// IsVNDKCore is set if a VNDK module does not set the vndk.support_system_process property.
 	IsVNDKCore bool `blueprint:"mutated"`
 
@@ -531,7 +540,6 @@
 	apexVariationName() string
 	apexSdkVersion() android.ApiLevel
 	bootstrap() bool
-	mustUseVendorVariant() bool
 	nativeCoverage() bool
 	directlyInAnyApex() bool
 	isPreventInstall() bool
@@ -660,6 +668,7 @@
 	headerLibraryDependency = iota
 	sharedLibraryDependency
 	staticLibraryDependency
+	rlibLibraryDependency
 )
 
 func (k libraryDependencyKind) String() string {
@@ -670,6 +679,8 @@
 		return "sharedLibraryDependency"
 	case staticLibraryDependency:
 		return "staticLibraryDependency"
+	case rlibLibraryDependency:
+		return "rlibLibraryDependency"
 	default:
 		panic(fmt.Errorf("unknown libraryDependencyKind %d", k))
 	}
@@ -747,6 +758,11 @@
 	return d.Kind == staticLibraryDependency
 }
 
+// rlib returns true if the libraryDependencyTag is tagging an rlib dependency.
+func (d libraryDependencyTag) rlib() bool {
+	return d.Kind == rlibLibraryDependency
+}
+
 func (d libraryDependencyTag) LicenseAnnotations() []android.LicenseAnnotation {
 	if d.shared() {
 		return []android.LicenseAnnotation{android.LicenseAnnotationSharedDependency}
@@ -1114,6 +1130,14 @@
 	return false
 }
 
+func (c *Module) CrateName() string {
+	panic(fmt.Errorf("CrateName called on non-Rust module: %q", c.BaseModuleName()))
+}
+
+func (c *Module) ExportedCrateLinkDirs() []string {
+	panic(fmt.Errorf("ExportedCrateLinkDirs called on non-Rust module: %q", c.BaseModuleName()))
+}
+
 func (c *Module) IsFuzzModule() bool {
 	if _, ok := c.compiler.(*fuzzBinary); ok {
 		return true
@@ -1439,10 +1463,6 @@
 	return c.Properties.SubName
 }
 
-func (c *Module) MustUseVendorVariant() bool {
-	return c.IsVndkSp() || c.Properties.MustUseVendorVariant
-}
-
 func (c *Module) getVndkExtendsModuleName() string {
 	if vndkdep := c.vndkdep; vndkdep != nil {
 		return vndkdep.getVndkExtendsModuleName()
@@ -1735,10 +1755,6 @@
 	return ctx.mod.IsVendorPublicLibrary()
 }
 
-func (ctx *moduleContextImpl) mustUseVendorVariant() bool {
-	return ctx.mod.MustUseVendorVariant()
-}
-
 func (ctx *moduleContextImpl) selectedStl() string {
 	if stl := ctx.mod.stl; stl != nil {
 		return stl.Properties.SelectedStl
@@ -2309,6 +2325,7 @@
 
 	deps.WholeStaticLibs = android.LastUniqueStrings(deps.WholeStaticLibs)
 	deps.StaticLibs = android.LastUniqueStrings(deps.StaticLibs)
+	deps.Rlibs = android.LastUniqueStrings(deps.Rlibs)
 	deps.LateStaticLibs = android.LastUniqueStrings(deps.LateStaticLibs)
 	deps.SharedLibs = android.LastUniqueStrings(deps.SharedLibs)
 	deps.LateSharedLibs = android.LastUniqueStrings(deps.LateSharedLibs)
@@ -2616,6 +2633,15 @@
 		}, depTag, lib)
 	}
 
+	for _, lib := range deps.Rlibs {
+		depTag := libraryDependencyTag{Kind: rlibLibraryDependency}
+		actx.AddVariationDependencies([]blueprint.Variation{
+			{Mutator: "link", Variation: ""},
+			{Mutator: "rust_libraries", Variation: "rlib"},
+			{Mutator: "rust_stdlinkage", Variation: "rlib-std"},
+		}, depTag, lib)
+	}
+
 	// staticUnwinderDep is treated as staticDep for Q apexes
 	// so that native libraries/binaries are linked with static unwinder
 	// because Q libc doesn't have unwinder APIs
@@ -3225,6 +3251,14 @@
 				default:
 					panic(fmt.Errorf("unexpected library dependency order %d", libDepTag.Order))
 				}
+
+			case libDepTag.rlib():
+				rlibDep := RustRlibDep{LibPath: linkFile.Path(), CrateName: ccDep.CrateName(), LinkDirs: ccDep.ExportedCrateLinkDirs()}
+				depPaths.ReexportedRustRlibDeps = append(depPaths.ReexportedRustRlibDeps, rlibDep)
+				depPaths.RustRlibDeps = append(depPaths.RustRlibDeps, rlibDep)
+				depPaths.IncludeDirs = append(depPaths.IncludeDirs, depExporterInfo.IncludeDirs...)
+				depPaths.ReexportedDirs = append(depPaths.ReexportedDirs, depExporterInfo.IncludeDirs...)
+
 			case libDepTag.static():
 				staticLibraryInfo, isStaticLib := android.OtherModuleProvider(ctx, dep, StaticLibraryInfoProvider)
 				if !isStaticLib {
@@ -3277,6 +3311,12 @@
 						panic(fmt.Errorf("unexpected library dependency order %d", libDepTag.Order))
 					}
 				}
+
+				// We re-export the Rust static_rlibs so rlib dependencies don't need to be redeclared by cc_library_static dependents.
+				// E.g. libfoo (cc_library_static) depends on libfoo.ffi (a rust_ffi rlib), libbar depending on libfoo shouldn't have to also add libfoo.ffi to static_rlibs.
+				depPaths.ReexportedRustRlibDeps = append(depPaths.ReexportedRustRlibDeps, depExporterInfo.RustRlibDeps...)
+				depPaths.RustRlibDeps = append(depPaths.RustRlibDeps, depExporterInfo.RustRlibDeps...)
+
 				if libDepTag.unexportedSymbols {
 					depPaths.LdFlags = append(depPaths.LdFlags,
 						"-Wl,--exclude-libs="+staticLibraryInfo.StaticLibrary.Base())
@@ -3329,6 +3369,12 @@
 			depPaths.SystemIncludeDirs = append(depPaths.SystemIncludeDirs, depExporterInfo.SystemIncludeDirs...)
 			depPaths.GeneratedDeps = append(depPaths.GeneratedDeps, depExporterInfo.Deps...)
 			depPaths.Flags = append(depPaths.Flags, depExporterInfo.Flags...)
+			depPaths.RustRlibDeps = append(depPaths.RustRlibDeps, depExporterInfo.RustRlibDeps...)
+
+			// Only re-export RustRlibDeps for cc static libs
+			if c.static() {
+				depPaths.ReexportedRustRlibDeps = append(depPaths.ReexportedRustRlibDeps, depExporterInfo.RustRlibDeps...)
+			}
 
 			if libDepTag.reexportFlags {
 				reexportExporter(depExporterInfo)
@@ -3401,11 +3447,14 @@
 	depPaths.IncludeDirs = android.FirstUniquePaths(depPaths.IncludeDirs)
 	depPaths.SystemIncludeDirs = android.FirstUniquePaths(depPaths.SystemIncludeDirs)
 	depPaths.GeneratedDeps = android.FirstUniquePaths(depPaths.GeneratedDeps)
+	depPaths.RustRlibDeps = android.FirstUniqueFunc(depPaths.RustRlibDeps, EqRustRlibDeps)
+
 	depPaths.ReexportedDirs = android.FirstUniquePaths(depPaths.ReexportedDirs)
 	depPaths.ReexportedSystemDirs = android.FirstUniquePaths(depPaths.ReexportedSystemDirs)
 	depPaths.ReexportedFlags = android.FirstUniqueStrings(depPaths.ReexportedFlags)
 	depPaths.ReexportedDeps = android.FirstUniquePaths(depPaths.ReexportedDeps)
 	depPaths.ReexportedGeneratedHeaders = android.FirstUniquePaths(depPaths.ReexportedGeneratedHeaders)
+	depPaths.ReexportedRustRlibDeps = android.FirstUniqueFunc(depPaths.ReexportedRustRlibDeps, EqRustRlibDeps)
 
 	if c.sabi != nil {
 		c.sabi.Properties.ReexportedIncludes = android.FirstUniqueStrings(c.sabi.Properties.ReexportedIncludes)
@@ -3582,12 +3631,7 @@
 		}
 	}
 
-	if ctx.DeviceConfig().VndkUseCoreVariant() && ccDep.IsVndk() && !ccDep.MustUseVendorVariant() &&
-		!c.InRamdisk() && !c.InVendorRamdisk() && !c.InRecovery() {
-		// The vendor module is a no-vendor-variant VNDK library.  Depend on the
-		// core module instead.
-		return libName
-	} else if ccDep.InVendorOrProduct() && nonSystemVariantsExist {
+	if ccDep.InVendorOrProduct() && nonSystemVariantsExist {
 		// The vendor and product modules in Make will have been renamed to not conflict with the
 		// core module, so update the dependency name here accordingly.
 		return libName + ccDep.SubName()
@@ -3800,8 +3844,6 @@
 		// TODO(b/114741097): use the correct ndk stl once build errors have been fixed
 		//family, link := getNdkStlFamilyAndLinkType(c)
 		//return fmt.Sprintf("native:ndk:%s:%s", family, link)
-	} else if actx.DeviceConfig().VndkUseCoreVariant() && !c.MustUseVendorVariant() {
-		return "native:platform_vndk"
 	} else {
 		return "native:platform"
 	}
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 3d75bf5..026d291 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -3218,3 +3218,32 @@
 	testSdkVersionFlag("libfoo", "30")
 	testSdkVersionFlag("libbar", "29")
 }
+
+func TestClangVerify(t *testing.T) {
+	t.Parallel()
+
+	ctx := testCc(t, `
+		cc_library {
+			name: "lib_no_clang_verify",
+			srcs: ["libnocv.cc"],
+		}
+
+		cc_library {
+			name: "lib_clang_verify",
+			srcs: ["libcv.cc"],
+			clang_verify: true,
+		}
+	`)
+
+	module := ctx.ModuleForTests("lib_no_clang_verify", "android_arm64_armv8-a_shared")
+
+	cFlags_no_cv := module.Rule("cc").Args["cFlags"]
+	if strings.Contains(cFlags_no_cv, "-Xclang") || strings.Contains(cFlags_no_cv, "-verify") {
+		t.Errorf("expected %q not in cflags, got %q", "-Xclang -verify", cFlags_no_cv)
+	}
+
+	cFlags_cv := ctx.ModuleForTests("lib_clang_verify", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
+	if strings.Contains(cFlags_cv, "-Xclang") && strings.Contains(cFlags_cv, "-verify") {
+		t.Errorf("expected %q in cflags, got %q", "-Xclang -verify", cFlags_cv)
+	}
+}
diff --git a/cc/compiler.go b/cc/compiler.go
index 9187b9b..34d98c0 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -120,6 +120,10 @@
 	// ban targeting bpf in cc rules instead use bpf_rules. (b/323415017)
 	Bpf_target *bool
 
+	// Add "-Xclang -verify" to the cflags and appends "touch $out" to
+	// the clang command line.
+	Clang_verify bool
+
 	Yacc *YaccProperties
 	Lex  *LexProperties
 
@@ -390,6 +394,11 @@
 	flags.Yacc = compiler.Properties.Yacc
 	flags.Lex = compiler.Properties.Lex
 
+	flags.ClangVerify = compiler.Properties.Clang_verify
+	if compiler.Properties.Clang_verify {
+		flags.Local.CFlags = append(flags.Local.CFlags, "-Xclang", "-verify")
+	}
+
 	// Include dir cflags
 	localIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Local_include_dirs)
 	if len(localIncludeDirs) > 0 {
@@ -792,6 +801,9 @@
 	// be added to the include path using -I
 	Local_include_dirs []string `android:"arch_variant,variant_prepend"`
 
+	// list of Rust static libraries.
+	Static_rlibs []string `android:"arch_variant,variant_prepend"`
+
 	// list of static libraries that provide headers for this binding.
 	Static_libs []string `android:"arch_variant,variant_prepend"`
 
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
index fdc94ad..289409f 100644
--- a/cc/config/Android.bp
+++ b/cc/config/Android.bp
@@ -15,7 +15,6 @@
         "global.go",
         "tidy.go",
         "toolchain.go",
-        "vndk.go",
 
         "bionic.go",
 
diff --git a/cc/config/global.go b/cc/config/global.go
index ffd6d7e..290a27d 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -136,6 +136,11 @@
 		// displaying logs in web browsers.
 		"-fmessage-length=0",
 
+		// Disable C++17 "relaxed template template argument matching" as a workaround for
+		// our out-dated libcxx.
+		// http://b/341084395
+		"-fno-relaxed-template-template-args",
+
 		// Using simple template names reduces the size of debug builds.
 		"-gsimple-template-names",
 
@@ -295,9 +300,6 @@
 		// New warnings to be fixed after clang-r475365
 		"-Wno-error=single-bit-bitfield-constant-conversion", // http://b/243965903
 		"-Wno-error=enum-constexpr-conversion",               // http://b/243964282
-		// New warnings to be fixed after clang-r522817
-		"-Wno-error=invalid-offsetof",
-		"-Wno-error=thread-safety-reference-return",
 
 		// Irrelevant on Android because _we_ don't use exceptions, but causes
 		// lots of build noise because libcxx/libcxxabi do. This can probably
@@ -305,9 +307,6 @@
 		// until then because it causes warnings in the _callers_, not the
 		// project itself.
 		"-Wno-deprecated-dynamic-exception-spec",
-
-		// Allow using VLA CXX extension.
-		"-Wno-vla-cxx-extension",
 	}
 
 	noOverride64GlobalCflags = []string{}
@@ -392,7 +391,7 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r522817"
+	ClangDefaultVersion      = "clang-r510928"
 	ClangDefaultShortVersion = "18"
 
 	// Directories with warnings from Android.bp files.
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index 7dc990b..5d8c351 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -209,58 +209,58 @@
 	return list
 }
 
-func LibclangRuntimeLibrary(t Toolchain, library string) string {
+func LibclangRuntimeLibrary(library string) string {
 	return "libclang_rt." + library
 }
 
-func BuiltinsRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "builtins")
+func BuiltinsRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("builtins")
 }
 
-func AddressSanitizerRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "asan")
+func AddressSanitizerRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("asan")
 }
 
-func AddressSanitizerStaticRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "asan.static")
+func AddressSanitizerStaticRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("asan.static")
 }
 
-func AddressSanitizerCXXStaticRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "asan_cxx.static")
+func AddressSanitizerCXXStaticRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("asan_cxx.static")
 }
 
-func HWAddressSanitizerRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "hwasan")
+func HWAddressSanitizerRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("hwasan")
 }
 
-func HWAddressSanitizerStaticLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "hwasan_static")
+func HWAddressSanitizerStaticLibrary() string {
+	return LibclangRuntimeLibrary("hwasan_static")
 }
 
-func UndefinedBehaviorSanitizerRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "ubsan_standalone")
+func UndefinedBehaviorSanitizerRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("ubsan_standalone")
 }
 
-func UndefinedBehaviorSanitizerMinimalRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "ubsan_minimal")
+func UndefinedBehaviorSanitizerMinimalRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("ubsan_minimal")
 }
 
-func ThreadSanitizerRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "tsan")
+func ThreadSanitizerRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("tsan")
 }
 
-func ScudoRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "scudo")
+func ScudoRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("scudo")
 }
 
-func ScudoMinimalRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "scudo_minimal")
+func ScudoMinimalRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("scudo_minimal")
 }
 
-func LibFuzzerRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "fuzzer")
+func LibFuzzerRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("fuzzer")
 }
 
-func LibFuzzerRuntimeInterceptors(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "fuzzer_interceptors")
+func LibFuzzerRuntimeInterceptors() string {
+	return LibclangRuntimeLibrary("fuzzer_interceptors")
 }
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
deleted file mode 100644
index dd612ce..0000000
--- a/cc/config/vndk.go
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2019 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package config
-
-// List of VNDK libraries that have different core variant and vendor variant.
-// For these libraries, the vendor variants must be installed even if the device
-// has VndkUseCoreVariant set.
-// Note that AIDL-generated modules must use vendor variants by default.
-var VndkMustUseVendorVariantList = []string{
-	"android.hardware.nfc@1.2",
-	"libbinder",
-	"libcrypto",
-	"libexpat",
-	"libgatekeeper",
-	"libgui",
-	"libhidlcache",
-	"libkeymaster_messages",
-	"libkeymaster_portable",
-	"libmedia_omx",
-	"libpuresoftkeymasterdevice",
-	"libselinux",
-	"libsoftkeymasterdevice",
-	"libsqlite",
-	"libssl",
-	"libstagefright_bufferpool@2.0",
-	"libstagefright_bufferqueue_helper",
-	"libstagefright_foundation",
-	"libstagefright_omx",
-	"libstagefright_omx_utils",
-	"libstagefright_xmlparser",
-	"libui",
-	"libxml2",
-}
diff --git a/cc/fuzz.go b/cc/fuzz.go
index b3e6639..92f2c5e 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -128,13 +128,13 @@
 	if ctx.Config().Getenv("FUZZ_FRAMEWORK") == "AFL" {
 		deps.HeaderLibs = append(deps.HeaderLibs, "libafl_headers")
 	} else {
-		deps.StaticLibs = append(deps.StaticLibs, config.LibFuzzerRuntimeLibrary(ctx.toolchain()))
+		deps.StaticLibs = append(deps.StaticLibs, config.LibFuzzerRuntimeLibrary())
 		// Fuzzers built with HWASAN should use the interceptors for better
 		// mutation based on signals in strcmp, memcpy, etc. This is only needed for
 		// fuzz targets, not generic HWASAN-ified binaries or libraries.
 		if module, ok := ctx.Module().(*Module); ok {
 			if module.IsSanitizerEnabled(Hwasan) {
-				deps.StaticLibs = append(deps.StaticLibs, config.LibFuzzerRuntimeInterceptors(ctx.toolchain()))
+				deps.StaticLibs = append(deps.StaticLibs, config.LibFuzzerRuntimeInterceptors())
 			}
 		}
 	}
@@ -597,7 +597,7 @@
 	ctx.WalkDeps(func(child, parent android.Module) bool {
 
 		// If this is a Rust module which is not rust_ffi_shared, we still want to bundle any transitive
-		// shared dependencies (even for rust_ffi_static)
+		// shared dependencies (even for rust_ffi_rlib or rust_ffi_static)
 		if rustmod, ok := child.(LinkableInterface); ok && rustmod.RustLibraryInterface() && !rustmod.Shared() {
 			if recursed[ctx.OtherModuleName(child)] {
 				return false
diff --git a/cc/library.go b/cc/library.go
index 895b199..b9c1466 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -274,11 +274,12 @@
 type flagExporter struct {
 	Properties FlagExporterProperties
 
-	dirs       android.Paths // Include directories to be included with -I
-	systemDirs android.Paths // System include directories to be included with -isystem
-	flags      []string      // Exported raw flags.
-	deps       android.Paths
-	headers    android.Paths
+	dirs         android.Paths // Include directories to be included with -I
+	systemDirs   android.Paths // System include directories to be included with -isystem
+	flags        []string      // Exported raw flags.
+	deps         android.Paths
+	headers      android.Paths
+	rustRlibDeps []RustRlibDep
 }
 
 // exportedIncludes returns the effective include paths for this module and
@@ -339,6 +340,10 @@
 	f.deps = append(f.deps, deps...)
 }
 
+func (f *flagExporter) reexportRustStaticDeps(deps ...RustRlibDep) {
+	f.rustRlibDeps = append(f.rustRlibDeps, deps...)
+}
+
 // addExportedGeneratedHeaders does nothing but collects generated header files.
 // This can be differ to exportedDeps which may contain phony files to minimize ninja.
 func (f *flagExporter) addExportedGeneratedHeaders(headers ...android.Path) {
@@ -356,6 +361,8 @@
 		// Used sparingly, for extra files that need to be explicitly exported to dependers,
 		// or for phony files to minimize ninja.
 		Deps: f.deps,
+		// Used for exporting rlib deps of static libraries to dependents.
+		RustRlibDeps: f.rustRlibDeps,
 		// For exported generated headers, such as exported aidl headers, proto headers, or
 		// sysprop headers.
 		GeneratedHeaders: f.headers,
@@ -412,11 +419,6 @@
 
 	postInstallCmds []string
 
-	// If useCoreVariant is true, the vendor variant of a VNDK library is
-	// not installed.
-	useCoreVariant       bool
-	checkSameCoreVariant bool
-
 	skipAPIDefine bool
 
 	// Decorated interfaces
@@ -1132,9 +1134,14 @@
 	linkerDeps = append(linkerDeps, deps.EarlySharedLibsDeps...)
 	linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
 	linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
+
+	if generatedLib := generateRustStaticlib(ctx, deps.RustRlibDeps); generatedLib != nil {
+		deps.StaticLibs = append(deps.StaticLibs, generatedLib)
+	}
+
 	transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
-		deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
-		linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs, objs.tidyDepFiles)
+		deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin,
+		deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs, objs.tidyDepFiles)
 
 	objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
 	objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
@@ -1350,20 +1357,25 @@
 
 func (library *libraryDecorator) crossVersionAbiDiff(ctx android.ModuleContext,
 	sourceDump, referenceDump android.Path,
-	baseName string, isLlndk bool, sourceVersion, prevVersion string) {
+	baseName, nameExt string, isLlndk bool, sourceVersion, prevDumpDir string) {
 
-	errorMessage := "error: Please follow https://android.googlesource.com/platform/development/+/main/vndk/tools/header-checker/README.md#configure-cross_version-abi-check to resolve the ABI difference between your source code and version " + prevVersion + "."
+	errorMessage := "error: Please follow https://android.googlesource.com/platform/development/+/main/vndk/tools/header-checker/README.md#configure-cross_version-abi-check to resolve the difference between your source code and the ABI dumps in " + prevDumpDir
 
-	library.sourceAbiDiff(ctx, sourceDump, referenceDump, baseName, prevVersion,
+	library.sourceAbiDiff(ctx, sourceDump, referenceDump, baseName, nameExt,
 		isLlndk, true /* allowExtensions */, sourceVersion, errorMessage)
 }
 
 func (library *libraryDecorator) sameVersionAbiDiff(ctx android.ModuleContext,
 	sourceDump, referenceDump android.Path,
-	baseName, nameExt string, isLlndk bool) {
+	baseName, nameExt string, isLlndk bool, lsdumpTagName string) {
 
 	libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
-	errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l " + libName
+	errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py --lib " + libName + " --lib-variant " + lsdumpTagName
+
+	targetRelease := ctx.Config().Getenv("TARGET_RELEASE")
+	if targetRelease != "" {
+		errorMessage += " --release " + targetRelease
+	}
 
 	library.sourceAbiDiff(ctx, sourceDump, referenceDump, baseName, nameExt,
 		isLlndk, false /* allowExtensions */, "current", errorMessage)
@@ -1371,13 +1383,19 @@
 
 func (library *libraryDecorator) optInAbiDiff(ctx android.ModuleContext,
 	sourceDump, referenceDump android.Path,
-	baseName, nameExt string, refDumpDir string) {
+	baseName, nameExt string, refDumpDir string, lsdumpTagName string) {
 
 	libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
-	errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l " + libName + " -ref-dump-dir $$ANDROID_BUILD_TOP/" + refDumpDir
+	errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py --lib " + libName + " --lib-variant " + lsdumpTagName + " --ref-dump-dir $$ANDROID_BUILD_TOP/" + refDumpDir
+
+	targetRelease := ctx.Config().Getenv("TARGET_RELEASE")
+	if targetRelease != "" {
+		errorMessage += " --release " + targetRelease
+	}
+
 	// Most opt-in libraries do not have dumps for all default architectures.
 	if ctx.Config().HasDeviceProduct() {
-		errorMessage += " -products " + ctx.Config().DeviceProduct()
+		errorMessage += " --product " + ctx.Config().DeviceProduct()
 	}
 
 	library.sourceAbiDiff(ctx, sourceDump, referenceDump, baseName, nameExt,
@@ -1402,6 +1420,7 @@
 
 		var llndkDump, apexVariantDump android.Path
 		tags := classifySourceAbiDump(ctx)
+		optInTags := []lsdumpTag{}
 		for _, tag := range tags {
 			if tag == llndkLsdumpTag && currVendorVersion != "" {
 				if llndkDump == nil {
@@ -1423,6 +1442,9 @@
 				}
 				addLsdumpPath(string(tag) + ":" + apexVariantDump.String())
 			} else {
+				if tag.dirName() == "" {
+					optInTags = append(optInTags, tag)
+				}
 				addLsdumpPath(string(tag) + ":" + implDump.String())
 			}
 		}
@@ -1467,7 +1489,7 @@
 			prevDumpFile := getRefAbiDumpFile(ctx, prevDumpDir, fileName)
 			if prevDumpFile.Valid() {
 				library.crossVersionAbiDiff(ctx, sourceDump, prevDumpFile.Path(),
-					fileName, isLlndk, currVersion, nameExt+prevVersion)
+					fileName, nameExt+prevVersion, isLlndk, currVersion, prevDumpDir)
 			}
 			// Check against the current version.
 			sourceDump = implDump
@@ -1487,9 +1509,15 @@
 			currDumpFile := getRefAbiDumpFile(ctx, currDumpDir, fileName)
 			if currDumpFile.Valid() {
 				library.sameVersionAbiDiff(ctx, sourceDump, currDumpFile.Path(),
-					fileName, nameExt, isLlndk)
+					fileName, nameExt, isLlndk, string(tag))
 			}
 		}
+
+		// Assert that a module is tagged with at most one of platformLsdumpTag, productLsdumpTag, or vendorLsdumpTag.
+		if len(headerAbiChecker.Ref_dump_dirs) > 0 && len(optInTags) != 1 {
+			ctx.ModuleErrorf("Expect exactly one opt-in lsdump tag when ref_dump_dirs are specified: %s", optInTags)
+			return
+		}
 		// Ensure that a module tagged with only platformLsdumpTag has ref_dump_dirs.
 		// Android.bp in vendor projects should be cleaned up before this is enforced for vendorLsdumpTag and productLsdumpTag.
 		if len(headerAbiChecker.Ref_dump_dirs) == 0 && len(tags) == 1 && tags[0] == platformLsdumpTag {
@@ -1506,7 +1534,7 @@
 			}
 			library.optInAbiDiff(ctx,
 				implDump, optInDumpFile.Path(),
-				fileName, "opt"+strconv.Itoa(i), optInDumpDirPath.String())
+				fileName, "opt"+strconv.Itoa(i), optInDumpDirPath.String(), string(optInTags[0]))
 		}
 	}
 }
@@ -1598,6 +1626,10 @@
 	library.reexportDeps(deps.ReexportedDeps...)
 	library.addExportedGeneratedHeaders(deps.ReexportedGeneratedHeaders...)
 
+	if library.static() && len(deps.ReexportedRustRlibDeps) > 0 {
+		library.reexportRustStaticDeps(deps.ReexportedRustRlibDeps...)
+	}
+
 	// Optionally export aidl headers.
 	if Bool(library.Properties.Aidl.Export_aidl_headers) {
 		if library.baseCompiler.hasAidl(deps) {
@@ -1730,27 +1762,6 @@
 				}
 			}
 
-			// In some cases we want to use core variant for VNDK-Core libs.
-			// Skip product variant since VNDKs use only the vendor variant.
-			if ctx.isVndk() && !ctx.isVndkSp() && !ctx.IsVndkExt() && !ctx.inProduct() {
-				mayUseCoreVariant := true
-
-				if ctx.mustUseVendorVariant() {
-					mayUseCoreVariant = false
-				}
-
-				if ctx.Config().CFIEnabledForPath(ctx.ModuleDir()) {
-					mayUseCoreVariant = false
-				}
-
-				if mayUseCoreVariant {
-					library.checkSameCoreVariant = true
-					if ctx.DeviceConfig().VndkUseCoreVariant() {
-						library.useCoreVariant = true
-					}
-				}
-			}
-
 			// do not install vndk libs
 			// vndk libs are packaged into VNDK APEX
 			if ctx.isVndk() && !ctx.IsVndkExt() && !ctx.Config().IsVndkDeprecated() && !ctx.inProduct() {
@@ -2125,14 +2136,12 @@
 			// Header only
 		}
 
-	} else if library, ok := mctx.Module().(LinkableInterface); ok && library.CcLibraryInterface() {
-
+	} else if library, ok := mctx.Module().(LinkableInterface); ok && (library.CcLibraryInterface() || library.RustLibraryInterface()) {
 		// Non-cc.Modules may need an empty variant for their mutators.
 		variations := []string{}
 		if library.NonCcVariants() {
 			variations = append(variations, "")
 		}
-
 		isLLNDK := false
 		if m, ok := mctx.Module().(*Module); ok {
 			isLLNDK = m.IsLlndk()
diff --git a/cc/linkable.go b/cc/linkable.go
index 10cc38f..fecc6a2 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -73,6 +73,12 @@
 	// RustLibraryInterface returns true if this is a Rust library module
 	RustLibraryInterface() bool
 
+	// CrateName returns the crateName for a Rust library, panics if not a Rust library.
+	CrateName() string
+
+	// DepFlags returns a slice of Rustc string flags, panics if not a Rust library
+	ExportedCrateLinkDirs() []string
+
 	// BaseModuleName returns the android.ModuleBase.BaseModuleName() value for this module.
 	BaseModuleName() string
 
@@ -159,7 +165,6 @@
 	// IsVndkSp returns true if this is a VNDK-SP module.
 	IsVndkSp() bool
 
-	MustUseVendorVariant() bool
 	IsVndk() bool
 	IsVndkExt() bool
 	IsVndkPrivate() bool
@@ -380,6 +385,7 @@
 	SystemIncludeDirs android.Paths // System include directories to be included with -isystem
 	Flags             []string      // Exported raw flags.
 	Deps              android.Paths
+	RustRlibDeps      []RustRlibDep
 	GeneratedHeaders  android.Paths
 }
 
diff --git a/cc/linker.go b/cc/linker.go
index 1d0f205..1b5a33a 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -39,6 +39,9 @@
 	// the dependency's .a file will be linked into this module using -Wl,--whole-archive.
 	Whole_static_libs []string `android:"arch_variant,variant_prepend"`
 
+	// list of Rust libs that should be statically linked into this module.
+	Static_rlibs []string `android:"arch_variant"`
+
 	// list of modules that should be statically linked into this module.
 	Static_libs []string `android:"arch_variant,variant_prepend"`
 
@@ -116,10 +119,14 @@
 			// product variant of the C/C++ module.
 			Static_libs []string
 
-			// list of ehader libs that only should be used to build vendor or product
+			// list of header libs that only should be used to build vendor or product
 			// variant of the C/C++ module.
 			Header_libs []string
 
+			// list of Rust libs that should be statically linked to build vendor or product
+			// variant.
+			Static_rlibs []string
+
 			// list of shared libs that should not be used to build vendor or
 			// product variant of the C/C++ module.
 			Exclude_shared_libs []string
@@ -148,6 +155,10 @@
 			// variant of the C/C++ module.
 			Static_libs []string
 
+			// list of Rust libs that should be statically linked to build the recovery
+			// variant.
+			Static_rlibs []string
+
 			// list of shared libs that should not be used to build
 			// the recovery variant of the C/C++ module.
 			Exclude_shared_libs []string
@@ -165,10 +176,14 @@
 			Exclude_runtime_libs []string
 		}
 		Ramdisk struct {
-			// list of static libs that only should be used to build the recovery
+			// list of static libs that only should be used to build the ramdisk
 			// variant of the C/C++ module.
 			Static_libs []string
 
+			// list of Rust libs that should be statically linked to build the ramdisk
+			// variant.
+			Static_rlibs []string
+
 			// list of shared libs that should not be used to build
 			// the ramdisk variant of the C/C++ module.
 			Exclude_shared_libs []string
@@ -183,9 +198,13 @@
 		}
 		Vendor_ramdisk struct {
 			// list of shared libs that should not be used to build
-			// the recovery variant of the C/C++ module.
+			// the vendor ramdisk variant of the C/C++ module.
 			Exclude_shared_libs []string
 
+			// list of Rust libs that should be statically linked to build the vendor ramdisk
+			// variant.
+			Static_rlibs []string
+
 			// list of static libs that should not be used to build
 			// the vendor ramdisk variant of the C/C++ module.
 			Exclude_static_libs []string
@@ -201,6 +220,10 @@
 			// variants.
 			Shared_libs []string
 
+			// list of Rust libs that should be statically linked to build the vendor ramdisk
+			// variant.
+			Static_rlibs []string
+
 			// list of ehader libs that only should be used to build platform variant of
 			// the C/C++ module.
 			Header_libs []string
@@ -295,6 +318,7 @@
 	deps.WholeStaticLibs = append(deps.WholeStaticLibs, linker.Properties.Whole_static_libs...)
 	deps.HeaderLibs = append(deps.HeaderLibs, linker.Properties.Header_libs...)
 	deps.StaticLibs = append(deps.StaticLibs, linker.Properties.Static_libs...)
+	deps.Rlibs = append(deps.Rlibs, linker.Properties.Static_rlibs...)
 	deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Shared_libs...)
 	deps.RuntimeLibs = append(deps.RuntimeLibs, linker.Properties.Runtime_libs...)
 
@@ -338,6 +362,7 @@
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Vendor.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Vendor.Exclude_static_libs)
 		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Vendor.Exclude_runtime_libs)
+		deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Vendor.Static_rlibs...)
 	}
 
 	if ctx.inProduct() {
@@ -351,6 +376,7 @@
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Product.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Product.Exclude_static_libs)
 		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Product.Exclude_runtime_libs)
+		deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Product.Static_rlibs...)
 	}
 
 	if ctx.inRecovery() {
@@ -364,6 +390,7 @@
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Recovery.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
 		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Recovery.Exclude_runtime_libs)
+		deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Recovery.Static_rlibs...)
 	}
 
 	if ctx.inRamdisk() {
@@ -374,6 +401,7 @@
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Ramdisk.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Ramdisk.Exclude_static_libs)
 		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Ramdisk.Exclude_runtime_libs)
+		deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Ramdisk.Static_rlibs...)
 	}
 
 	if ctx.inVendorRamdisk() {
@@ -383,6 +411,7 @@
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Vendor_ramdisk.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Vendor_ramdisk.Exclude_static_libs)
 		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Vendor_ramdisk.Exclude_runtime_libs)
+		deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Vendor_ramdisk.Static_rlibs...)
 	}
 
 	if !ctx.useSdk() {
@@ -402,7 +431,7 @@
 	if ctx.toolchain().Bionic() {
 		// libclang_rt.builtins has to be last on the command line
 		if linker.Properties.libCrt() && !ctx.header() {
-			deps.UnexportedStaticLibs = append(deps.UnexportedStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
+			deps.UnexportedStaticLibs = append(deps.UnexportedStaticLibs, config.BuiltinsRuntimeLibrary())
 		}
 
 		if inList("libdl", deps.SharedLibs) {
@@ -425,7 +454,7 @@
 		}
 	} else if ctx.toolchain().Musl() {
 		if linker.Properties.libCrt() && !ctx.header() {
-			deps.UnexportedStaticLibs = append(deps.UnexportedStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
+			deps.UnexportedStaticLibs = append(deps.UnexportedStaticLibs, config.BuiltinsRuntimeLibrary())
 		}
 	}
 
diff --git a/cc/makevars.go b/cc/makevars.go
index 51bcbf0..9d29aff 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -28,6 +28,16 @@
 	modulesWarningsAllowedKey    = android.NewOnceKey("ModulesWarningsAllowed")
 	modulesUsingWnoErrorKey      = android.NewOnceKey("ModulesUsingWnoError")
 	modulesMissingProfileFileKey = android.NewOnceKey("ModulesMissingProfileFile")
+	sanitizerVariables           = map[string]string{
+		"ADDRESS_SANITIZER_RUNTIME_LIBRARY":   config.AddressSanitizerRuntimeLibrary(),
+		"HWADDRESS_SANITIZER_RUNTIME_LIBRARY": config.HWAddressSanitizerRuntimeLibrary(),
+		"HWADDRESS_SANITIZER_STATIC_LIBRARY":  config.HWAddressSanitizerStaticLibrary(),
+		"UBSAN_RUNTIME_LIBRARY":               config.UndefinedBehaviorSanitizerRuntimeLibrary(),
+		"UBSAN_MINIMAL_RUNTIME_LIBRARY":       config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(),
+		"TSAN_RUNTIME_LIBRARY":                config.ThreadSanitizerRuntimeLibrary(),
+		"SCUDO_RUNTIME_LIBRARY":               config.ScudoRuntimeLibrary(),
+		"SCUDO_MINIMAL_RUNTIME_LIBRARY":       config.ScudoMinimalRuntimeLibrary(),
+	}
 )
 
 func init() {
@@ -261,43 +271,9 @@
 	}, " "))
 
 	if target.Os.Class == android.Device {
-		sanitizerVariables := map[string]string{
-			"ADDRESS_SANITIZER_RUNTIME_LIBRARY":   config.AddressSanitizerRuntimeLibrary(toolchain),
-			"HWADDRESS_SANITIZER_RUNTIME_LIBRARY": config.HWAddressSanitizerRuntimeLibrary(toolchain),
-			"HWADDRESS_SANITIZER_STATIC_LIBRARY":  config.HWAddressSanitizerStaticLibrary(toolchain),
-			"UBSAN_RUNTIME_LIBRARY":               config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain),
-			"UBSAN_MINIMAL_RUNTIME_LIBRARY":       config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain),
-			"TSAN_RUNTIME_LIBRARY":                config.ThreadSanitizerRuntimeLibrary(toolchain),
-			"SCUDO_RUNTIME_LIBRARY":               config.ScudoRuntimeLibrary(toolchain),
-			"SCUDO_MINIMAL_RUNTIME_LIBRARY":       config.ScudoMinimalRuntimeLibrary(toolchain),
-		}
-
 		for variable, value := range sanitizerVariables {
 			ctx.Strict(secondPrefix+variable, value)
 		}
-
-		sanitizerLibs := android.SortedStringValues(sanitizerVariables)
-		var sanitizerLibStems []string
-		ctx.VisitAllModules(func(m android.Module) {
-			if !m.Enabled(ctx) {
-				return
-			}
-
-			ccModule, _ := m.(*Module)
-			if ccModule == nil || ccModule.library == nil || !ccModule.library.shared() {
-				return
-			}
-
-			if android.InList(strings.TrimPrefix(ctx.ModuleName(m), "prebuilt_"), sanitizerLibs) &&
-				m.Target().Os == target.Os && m.Target().Arch.ArchType == target.Arch.ArchType {
-				outputFile := ccModule.outputFile
-				if outputFile.Valid() {
-					sanitizerLibStems = append(sanitizerLibStems, outputFile.Path().Base())
-				}
-			}
-		})
-		sanitizerLibStems = android.SortedUniqueStrings(sanitizerLibStems)
-		ctx.Strict(secondPrefix+"SANITIZER_STEMS", strings.Join(sanitizerLibStems, " "))
 	}
 
 	// This is used by external/gentoo/...
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 1a94729..e6075ad 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -25,6 +25,7 @@
 
 	"android/soong/android"
 	"android/soong/cc/config"
+	"android/soong/etc"
 )
 
 var (
@@ -408,6 +409,8 @@
 	android.RegisterMakeVarsProvider(pctx, cfiMakeVarsProvider)
 	android.RegisterMakeVarsProvider(pctx, hwasanMakeVarsProvider)
 	android.RegisterMakeVarsProvider(pctx, memtagStackMakeVarsProvider)
+
+	RegisterSanitizerLibrariesTxtType(android.InitRegistrationContext)
 }
 
 func (sanitize *sanitize) props() []interface{} {
@@ -1316,7 +1319,7 @@
 				} else if s.sanitizer == cfi {
 					cfiStaticLibs(mctx.Config()).add(c, c.Module().Name())
 				} else if s.sanitizer == Memtag_stack {
-					memtagStackStaticLibs(mctx.Config()).add(c, c.Module().Name());
+					memtagStackStaticLibs(mctx.Config()).add(c, c.Module().Name())
 				}
 			}
 		} else if c.IsSanitizerEnabled(s.sanitizer) {
@@ -1522,25 +1525,25 @@
 		if Bool(sanProps.Address) {
 			if toolchain.Musl() || (c.staticBinary() && toolchain.Bionic()) {
 				// Use a static runtime for musl to match what clang does for glibc.
-				addStaticDeps(config.AddressSanitizerStaticRuntimeLibrary(toolchain), false)
-				addStaticDeps(config.AddressSanitizerCXXStaticRuntimeLibrary(toolchain), false)
+				addStaticDeps(config.AddressSanitizerStaticRuntimeLibrary(), false)
+				addStaticDeps(config.AddressSanitizerCXXStaticRuntimeLibrary(), false)
 			} else {
-				runtimeSharedLibrary = config.AddressSanitizerRuntimeLibrary(toolchain)
+				runtimeSharedLibrary = config.AddressSanitizerRuntimeLibrary()
 			}
 		} else if Bool(sanProps.Hwaddress) {
 			if c.staticBinary() {
-				addStaticDeps(config.HWAddressSanitizerStaticLibrary(toolchain), true)
+				addStaticDeps(config.HWAddressSanitizerStaticLibrary(), true)
 				addStaticDeps("libdl", false)
 			} else {
-				runtimeSharedLibrary = config.HWAddressSanitizerRuntimeLibrary(toolchain)
+				runtimeSharedLibrary = config.HWAddressSanitizerRuntimeLibrary()
 			}
 		} else if Bool(sanProps.Thread) {
-			runtimeSharedLibrary = config.ThreadSanitizerRuntimeLibrary(toolchain)
+			runtimeSharedLibrary = config.ThreadSanitizerRuntimeLibrary()
 		} else if Bool(sanProps.Scudo) {
 			if len(diagSanitizers) == 0 && !c.sanitize.Properties.UbsanRuntimeDep {
-				runtimeSharedLibrary = config.ScudoMinimalRuntimeLibrary(toolchain)
+				runtimeSharedLibrary = config.ScudoMinimalRuntimeLibrary()
 			} else {
-				runtimeSharedLibrary = config.ScudoRuntimeLibrary(toolchain)
+				runtimeSharedLibrary = config.ScudoRuntimeLibrary()
 			}
 		} else if len(diagSanitizers) > 0 || c.sanitize.Properties.UbsanRuntimeDep ||
 			Bool(sanProps.Fuzzer) ||
@@ -1553,17 +1556,17 @@
 				// Also manually add a static runtime for musl to match what clang does for glibc.
 				// Otherwise dlopening libraries that depend on libclang_rt.ubsan_standalone.so fails with:
 				// Error relocating ...: initial-exec TLS resolves to dynamic definition
-				addStaticDeps(config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain)+".static", true)
+				addStaticDeps(config.UndefinedBehaviorSanitizerRuntimeLibrary()+".static", true)
 			} else {
-				runtimeSharedLibrary = config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain)
+				runtimeSharedLibrary = config.UndefinedBehaviorSanitizerRuntimeLibrary()
 			}
 		}
 
 		if enableMinimalRuntime(c.sanitize) || c.sanitize.Properties.MinimalRuntimeDep {
-			addStaticDeps(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain), true)
+			addStaticDeps(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(), true)
 		}
 		if c.sanitize.Properties.BuiltinsDep {
-			addStaticDeps(config.BuiltinsRuntimeLibrary(toolchain), true)
+			addStaticDeps(config.BuiltinsRuntimeLibrary(), true)
 		}
 
 		if runtimeSharedLibrary != "" && (toolchain.Bionic() || toolchain.Musl()) {
@@ -1787,3 +1790,126 @@
 func memtagStackMakeVarsProvider(ctx android.MakeVarsContext) {
 	memtagStackStaticLibs(ctx.Config()).exportToMake(ctx)
 }
+
+type sanitizerLibrariesTxtModule struct {
+	android.ModuleBase
+
+	outputFile android.OutputPath
+}
+
+var _ etc.PrebuiltEtcModule = (*sanitizerLibrariesTxtModule)(nil)
+var _ android.OutputFileProducer = (*sanitizerLibrariesTxtModule)(nil)
+
+func RegisterSanitizerLibrariesTxtType(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("sanitizer_libraries_txt", sanitizerLibrariesTxtFactory)
+}
+
+func sanitizerLibrariesTxtFactory() android.Module {
+	m := &sanitizerLibrariesTxtModule{}
+	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
+	return m
+}
+
+type sanitizerLibraryDependencyTag struct {
+	blueprint.BaseDependencyTag
+}
+
+func (t sanitizerLibraryDependencyTag) AllowDisabledModuleDependency(target android.Module) bool {
+	return true
+}
+
+var _ android.AllowDisabledModuleDependency = (*sanitizerLibraryDependencyTag)(nil)
+
+func (txt *sanitizerLibrariesTxtModule) DepsMutator(actx android.BottomUpMutatorContext) {
+	targets := actx.Config().Targets[android.Android]
+	depTag := sanitizerLibraryDependencyTag{}
+
+	for _, target := range targets {
+		variation := append(target.Variations(),
+			blueprint.Variation{Mutator: "image", Variation: ""},
+			blueprint.Variation{Mutator: "sdk", Variation: ""},
+			blueprint.Variation{Mutator: "link", Variation: "shared"},
+		)
+		for _, lib := range android.SortedStringValues(sanitizerVariables) {
+			if actx.OtherModuleFarDependencyVariantExists(variation, lib) {
+				actx.AddFarVariationDependencies(variation, depTag, lib)
+			}
+
+			prebuiltLibName := "prebuilt_" + lib
+			if actx.OtherModuleFarDependencyVariantExists(variation, prebuiltLibName) {
+				actx.AddFarVariationDependencies(variation, depTag, prebuiltLibName)
+			}
+		}
+	}
+
+}
+
+func (txt *sanitizerLibrariesTxtModule) getSanitizerLibs(ctx android.ModuleContext) string {
+	var sanitizerLibStems []string
+
+	ctx.VisitDirectDepsIf(func(m android.Module) bool {
+		if !m.Enabled(ctx) {
+			return false
+		}
+
+		ccModule, _ := m.(*Module)
+		if ccModule == nil || ccModule.library == nil || !ccModule.library.shared() {
+			return false
+		}
+
+		targets := ctx.Config().Targets[android.Android]
+
+		for _, target := range targets {
+			if m.Target().Os == target.Os && m.Target().Arch.ArchType == target.Arch.ArchType {
+				return true
+			}
+		}
+
+		return false
+	}, func(m android.Module) {
+		ccModule, _ := m.(*Module)
+		outputFile := ccModule.outputFile
+		if outputFile.Valid() {
+			sanitizerLibStems = append(sanitizerLibStems, outputFile.Path().Base())
+		}
+	})
+
+	sanitizerLibStems = android.SortedUniqueStrings(sanitizerLibStems)
+	return strings.Join(sanitizerLibStems, "\n")
+}
+
+func (txt *sanitizerLibrariesTxtModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	filename := txt.Name()
+
+	txt.outputFile = android.PathForModuleOut(ctx, filename).OutputPath
+	android.WriteFileRule(ctx, txt.outputFile, txt.getSanitizerLibs(ctx))
+
+	installPath := android.PathForModuleInstall(ctx, "etc")
+	ctx.InstallFile(installPath, filename, txt.outputFile)
+}
+
+func (txt *sanitizerLibrariesTxtModule) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(txt.outputFile),
+	}}
+}
+
+// PrebuiltEtcModule interface
+func (txt *sanitizerLibrariesTxtModule) OutputFile() android.OutputPath {
+	return txt.outputFile
+}
+
+// PrebuiltEtcModule interface
+func (txt *sanitizerLibrariesTxtModule) BaseDir() string {
+	return "etc"
+}
+
+// PrebuiltEtcModule interface
+func (txt *sanitizerLibrariesTxtModule) SubDir() string {
+	return ""
+}
+
+func (txt *sanitizerLibrariesTxtModule) OutputFiles(tag string) (android.Paths, error) {
+	return android.Paths{txt.outputFile}, nil
+}
diff --git a/cc/testing.go b/cc/testing.go
index c3a33cb..989be02 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -300,6 +300,7 @@
 			system_shared_libs: [],
 			stl: "none",
 			vendor_available: true,
+			vendor_ramdisk_available: true,
 			product_available: true,
 			recovery_available: true,
 			host_supported: true,
diff --git a/cc/util.go b/cc/util.go
index 3ede8ff..8ffacae 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -68,6 +68,7 @@
 		needTidyFiles:   in.NeedTidyFiles,
 		sAbiDump:        in.SAbiDump,
 		emitXrefs:       in.EmitXrefs,
+		clangVerify:     in.ClangVerify,
 
 		systemIncludeFlags: strings.Join(in.SystemIncludeFlags, " "),
 
diff --git a/cc/vndk.go b/cc/vndk.go
index 548992d..ea55835 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -20,7 +20,6 @@
 	"strings"
 
 	"android/soong/android"
-	"android/soong/cc/config"
 	"android/soong/etc"
 
 	"github.com/google/blueprint"
@@ -219,11 +218,10 @@
 type moduleListerFunc func(ctx android.SingletonContext) (moduleNames, fileNames []string)
 
 var (
-	vndkSPLibraries               = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKSP })
-	vndkCoreLibraries             = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKCore })
-	vndkPrivateLibraries          = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKPrivate })
-	vndkProductLibraries          = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKProduct })
-	vndkUsingCoreVariantLibraries = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKUsingCoreVariant })
+	vndkSPLibraries      = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKSP })
+	vndkCoreLibraries    = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKCore })
+	vndkPrivateLibraries = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKPrivate })
+	vndkProductLibraries = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKProduct })
 )
 
 // vndkModuleLister takes a predicate that operates on a Module and returns a moduleListerFunc
@@ -266,22 +264,6 @@
 	}
 }
 
-var vndkMustUseVendorVariantListKey = android.NewOnceKey("vndkMustUseVendorVariantListKey")
-
-func vndkMustUseVendorVariantList(cfg android.Config) []string {
-	return cfg.Once(vndkMustUseVendorVariantListKey, func() interface{} {
-		return config.VndkMustUseVendorVariantList
-	}).([]string)
-}
-
-// test may call this to override global configuration(config.VndkMustUseVendorVariantList)
-// when it is called, it must be before the first call to vndkMustUseVendorVariantList()
-func setVndkMustUseVendorVariantListForTest(config android.Config, mustUseVendorVariantList []string) {
-	config.Once(vndkMustUseVendorVariantListKey, func() interface{} {
-		return mustUseVendorVariantList
-	})
-}
-
 func processVndkLibrary(mctx android.BottomUpMutatorContext, m *Module) {
 	if m.InProduct() {
 		// We may skip the steps for the product variants because they
@@ -301,13 +283,6 @@
 		mctx.PropertyErrorf("vndk.enabled", "This library provides stubs. Shouldn't be VNDK. Consider making it as LLNDK")
 	}
 
-	if inList(name, vndkMustUseVendorVariantList(mctx.Config())) {
-		m.Properties.MustUseVendorVariant = true
-	}
-	if mctx.DeviceConfig().VndkUseCoreVariant() && !m.Properties.MustUseVendorVariant {
-		m.VendorProperties.IsVNDKUsingCoreVariant = true
-	}
-
 	if m.vndkdep.isVndkSp() {
 		m.VendorProperties.IsVNDKSP = true
 	} else {
@@ -356,8 +331,7 @@
 		if lib.buildStubs() {
 			return false
 		}
-		useCoreVariant := mctx.DeviceConfig().VndkUseCoreVariant() && !m.MustUseVendorVariant()
-		return lib.shared() && m.InVendor() && m.IsVndk() && !m.IsVndkExt() && !useCoreVariant
+		return lib.shared() && m.InVendor() && m.IsVndk() && !m.IsVndkExt()
 	}
 	return false
 }
@@ -400,7 +374,6 @@
 	ctx.RegisterParallelSingletonModuleType("vndkcore_libraries_txt", vndkCoreLibrariesTxtFactory)
 	ctx.RegisterParallelSingletonModuleType("vndkprivate_libraries_txt", vndkPrivateLibrariesTxtFactory)
 	ctx.RegisterParallelSingletonModuleType("vndkproduct_libraries_txt", vndkProductLibrariesTxtFactory)
-	ctx.RegisterParallelSingletonModuleType("vndkcorevariant_libraries_txt", vndkUsingCoreVariantLibrariesTxtFactory)
 }
 
 type vndkLibrariesTxt struct {
@@ -453,13 +426,6 @@
 	return newVndkLibrariesTxt(vndkProductLibraries, "VNDK_PRODUCT_LIBRARIES")
 }
 
-// vndkcorevariant_libraries_txt is a singleton module whose content is a list of VNDK libraries
-// that are using the core variant, generated by Soong but can be referenced by other modules.
-// For example, apex_vndk can depend on these files as prebuilt.
-func vndkUsingCoreVariantLibrariesTxtFactory() android.SingletonModule {
-	return newVndkLibrariesTxt(vndkUsingCoreVariantLibraries, "VNDK_USING_CORE_VARIANT_LIBRARIES")
-}
-
 func newVndkLibrariesWithMakeVarFilter(lister moduleListerFunc, makeVarName string, filter string) android.SingletonModule {
 	m := &vndkLibrariesTxt{
 		lister:               lister,
diff --git a/cmd/release_config/build_flag_declarations/Android.bp b/cmd/release_config/build_flag_declarations/Android.bp
new file mode 100644
index 0000000..e4f999f
--- /dev/null
+++ b/cmd/release_config/build_flag_declarations/Android.bp
@@ -0,0 +1,32 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+blueprint_go_binary {
+    name: "build-flag-declarations",
+    deps: [
+        "golang-protobuf-encoding-prototext",
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+        "soong-cmd-release_config-proto",
+        "soong-cmd-release_config-lib",
+    ],
+    srcs: [
+        "main.go",
+    ],
+}
+
+bootstrap_go_package {
+    name: "soong-cmd-release_config-build_flag_declarations",
+    pkgPath: "android/soong/cmd/release_config/build_flag_declarations",
+    deps: [
+        "golang-protobuf-encoding-prototext",
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+        "soong-cmd-release_config-proto",
+        "soong-cmd-release_config-lib",
+    ],
+    srcs: [
+        "main.go",
+    ],
+}
diff --git a/cmd/release_config/build_flag_declarations/main.go b/cmd/release_config/build_flag_declarations/main.go
new file mode 100644
index 0000000..cc286b6
--- /dev/null
+++ b/cmd/release_config/build_flag_declarations/main.go
@@ -0,0 +1,81 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"os"
+
+	rc_lib "android/soong/cmd/release_config/release_config_lib"
+	rc_proto "android/soong/cmd/release_config/release_config_proto"
+)
+
+type Flags struct {
+	// The path to the top of the workspace.  Default: ".".
+	top string
+
+	// Output file.
+	output string
+
+	// Format for output file
+	format string
+
+	// List of flag_declaration files to add.
+	decls rc_lib.StringList
+
+	// List of flag_artifacts files to merge.
+	intermediates rc_lib.StringList
+
+	// Disable warning messages
+	quiet bool
+
+	// Panic on errors.
+	debug bool
+}
+
+func main() {
+	var flags Flags
+	topDir, err := rc_lib.GetTopDir()
+
+	// Handle the common arguments
+	flag.StringVar(&flags.top, "top", topDir, "path to top of workspace")
+	flag.Var(&flags.decls, "decl", "path to a flag_declaration file. May be repeated")
+	flag.Var(&flags.intermediates, "intermediate", "path to a flag_artifacts file (output from a prior run). May be repeated")
+	flag.StringVar(&flags.format, "format", "pb", "output file format")
+	flag.StringVar(&flags.output, "output", "build_flags.pb", "output file")
+	flag.BoolVar(&flags.debug, "debug", false, "turn on debugging output for errors")
+	flag.BoolVar(&flags.quiet, "quiet", false, "disable warning messages")
+	flag.Parse()
+
+	errorExit := func(err error) {
+		if flags.debug {
+			panic(err)
+		}
+		fmt.Fprintf(os.Stderr, "%s\n", err)
+		os.Exit(1)
+	}
+
+	if flags.quiet {
+		rc_lib.DisableWarnings()
+	}
+
+	if err = os.Chdir(flags.top); err != nil {
+		errorExit(err)
+	}
+
+	flagArtifacts := rc_lib.FlagArtifactsFactory("")
+	intermediates := []*rc_proto.FlagDeclarationArtifacts{}
+	for _, intermediate := range flags.intermediates {
+		fda := rc_lib.FlagDeclarationArtifactsFactory(intermediate)
+		intermediates = append(intermediates, fda)
+	}
+	for _, decl := range flags.decls {
+		fa := rc_lib.FlagArtifactFactory(decl)
+		(*flagArtifacts)[*fa.FlagDeclaration.Name] = fa
+	}
+
+	message := flagArtifacts.GenerateFlagDeclarationArtifacts(intermediates)
+	err = rc_lib.WriteFormattedMessage(flags.output, flags.format, message)
+	if err != nil {
+		errorExit(err)
+	}
+}
diff --git a/cmd/release_config/release_config/main.go b/cmd/release_config/release_config/main.go
index 67b57cc..24deacc 100644
--- a/cmd/release_config/release_config/main.go
+++ b/cmd/release_config/release_config/main.go
@@ -31,7 +31,7 @@
 	var outputDir string
 	var err error
 	var configs *rc_lib.ReleaseConfigs
-	var json, pb, textproto bool
+	var json, pb, textproto, inheritance bool
 	var product string
 	var allMake bool
 	var useBuildVar bool
@@ -52,6 +52,7 @@
 	flag.BoolVar(&json, "json", true, "write artifacts as json")
 	flag.BoolVar(&pb, "pb", true, "write artifacts as binary protobuf")
 	flag.BoolVar(&allMake, "all_make", false, "write makefiles for all release configs")
+	flag.BoolVar(&inheritance, "inheritance", true, "write inheritance graph")
 	flag.BoolVar(&useBuildVar, "use_get_build_var", false, "use get_build_var PRODUCT_RELEASE_CONFIG_MAPS")
 	flag.BoolVar(&guard, "guard", true, "whether to guard with RELEASE_BUILD_FLAGS_IN_PROTOBUF")
 
@@ -77,7 +78,7 @@
 		panic(err)
 	}
 
-	makefilePath := filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.mk", product, targetRelease))
+	makefilePath := filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.varmk", product, targetRelease))
 	useProto, ok := config.FlagArtifacts["RELEASE_BUILD_FLAGS_IN_PROTOBUF"]
 	if guard && (!ok || rc_lib.MarshalValue(useProto.Value) == "") {
 		// We were told to guard operation and either we have no build flag, or it is False.
@@ -94,7 +95,7 @@
 		// Write one makefile per release config, using the canonical release name.
 		for _, c := range configs.GetSortedReleaseConfigs() {
 			if c.Name != targetRelease {
-				makefilePath = filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.mk", product, c.Name))
+				makefilePath = filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.varmk", product, c.Name))
 				err = configs.WriteMakefile(makefilePath, c.Name)
 				if err != nil {
 					panic(err)
@@ -102,6 +103,13 @@
 			}
 		}
 	}
+	if inheritance {
+		inheritPath := filepath.Join(outputDir, fmt.Sprintf("inheritance_graph-%s.dot", product))
+		err = configs.WriteInheritanceGraph(inheritPath)
+		if err != nil {
+			panic(err)
+		}
+	}
 	if json {
 		err = configs.WriteArtifact(outputDir, product, "json")
 		if err != nil {
@@ -120,7 +128,7 @@
 			panic(err)
 		}
 	}
-	if err = config.WritePartitionBuildFlags(outputDir, product, targetRelease); err != nil {
+	if err = config.WritePartitionBuildFlags(outputDir); err != nil {
 		panic(err)
 	}
 
diff --git a/cmd/release_config/release_config_lib/flag_artifact.go b/cmd/release_config/release_config_lib/flag_artifact.go
index cba1b5c..6d36595 100644
--- a/cmd/release_config/release_config_lib/flag_artifact.go
+++ b/cmd/release_config/release_config_lib/flag_artifact.go
@@ -15,7 +15,9 @@
 package release_config_lib
 
 import (
+	"cmp"
 	"fmt"
+	"slices"
 
 	rc_proto "android/soong/cmd/release_config/release_config_proto"
 
@@ -45,6 +47,85 @@
 // Key is flag name.
 type FlagArtifacts map[string]*FlagArtifact
 
+func FlagArtifactFactory(declPath string) *FlagArtifact {
+	fd := &rc_proto.FlagDeclaration{}
+	fa := &FlagArtifact{
+		FlagDeclaration:  fd,
+		DeclarationIndex: -1,
+		Traces:           []*rc_proto.Tracepoint{},
+	}
+	if declPath != "" {
+		LoadMessage(declPath, fd)
+		fa.Value = fd.GetValue()
+		fa.Traces = append(fa.Traces, &rc_proto.Tracepoint{Source: proto.String(declPath), Value: fa.Value})
+	}
+	return fa
+}
+
+func FlagArtifactsFactory(artifactsPath string) *FlagArtifacts {
+	ret := make(FlagArtifacts)
+	if artifactsPath != "" {
+		fas := &rc_proto.FlagArtifacts{}
+		LoadMessage(artifactsPath, fas)
+		for _, fa_pb := range fas.FlagArtifacts {
+			fa := &FlagArtifact{}
+			fa.FlagDeclaration = fa_pb.GetFlagDeclaration()
+			if val := fa_pb.GetValue(); val != nil {
+				fa.Value = val
+			}
+			if traces := fa_pb.GetTraces(); traces != nil {
+				fa.Traces = traces
+			}
+			ret[*fa.FlagDeclaration.Name] = fa
+		}
+	}
+	return &ret
+}
+
+func (fa *FlagArtifact) GenerateFlagDeclarationArtifact() *rc_proto.FlagDeclarationArtifact {
+	ret := &rc_proto.FlagDeclarationArtifact{
+		Name:            fa.FlagDeclaration.Name,
+		DeclarationPath: fa.Traces[0].Source,
+	}
+	if namespace := fa.FlagDeclaration.GetNamespace(); namespace != "" {
+		ret.Namespace = proto.String(namespace)
+	}
+	if description := fa.FlagDeclaration.GetDescription(); description != "" {
+		ret.Description = proto.String(description)
+	}
+	if workflow := fa.FlagDeclaration.GetWorkflow(); workflow != rc_proto.Workflow_Workflow_Unspecified {
+		ret.Workflow = &workflow
+	}
+	if containers := fa.FlagDeclaration.GetContainers(); containers != nil {
+		ret.Containers = containers
+	}
+	return ret
+}
+
+func FlagDeclarationArtifactsFactory(path string) *rc_proto.FlagDeclarationArtifacts {
+	ret := &rc_proto.FlagDeclarationArtifacts{}
+	if path != "" {
+		LoadMessage(path, ret)
+	} else {
+		ret.FlagDeclarationArtifacts = []*rc_proto.FlagDeclarationArtifact{}
+	}
+	return ret
+}
+
+func (fas *FlagArtifacts) GenerateFlagDeclarationArtifacts(intermediates []*rc_proto.FlagDeclarationArtifacts) *rc_proto.FlagDeclarationArtifacts {
+	ret := &rc_proto.FlagDeclarationArtifacts{FlagDeclarationArtifacts: []*rc_proto.FlagDeclarationArtifact{}}
+	for _, fa := range *fas {
+		ret.FlagDeclarationArtifacts = append(ret.FlagDeclarationArtifacts, fa.GenerateFlagDeclarationArtifact())
+	}
+	for _, fda := range intermediates {
+		ret.FlagDeclarationArtifacts = append(ret.FlagDeclarationArtifacts, fda.FlagDeclarationArtifacts...)
+	}
+	slices.SortFunc(ret.FlagDeclarationArtifacts, func(a, b *rc_proto.FlagDeclarationArtifact) int {
+		return cmp.Compare(*a.Name, *b.Name)
+	})
+	return ret
+}
+
 // Create a clone of the flag artifact.
 //
 // Returns:
diff --git a/cmd/release_config/release_config_lib/flag_value.go b/cmd/release_config/release_config_lib/flag_value.go
index 59021e2..76363ce 100644
--- a/cmd/release_config/release_config_lib/flag_value.go
+++ b/cmd/release_config/release_config_lib/flag_value.go
@@ -74,3 +74,22 @@
 		return ""
 	}
 }
+
+// Returns a string representation of the type of the value for make
+func ValueType(value *rc_proto.Value) string {
+	if value == nil || value.Val == nil {
+		return "unspecified"
+	}
+	switch value.Val.(type) {
+	case *rc_proto.Value_UnspecifiedValue:
+		return "unspecified"
+	case *rc_proto.Value_StringValue:
+		return "string"
+	case *rc_proto.Value_BoolValue:
+		return "bool"
+	case *rc_proto.Value_Obsolete:
+		return "obsolete"
+	default:
+		panic("Unhandled type")
+	}
+}
diff --git a/cmd/release_config/release_config_lib/release_config.go b/cmd/release_config/release_config_lib/release_config.go
index 93410a6..02b693c 100644
--- a/cmd/release_config/release_config_lib/release_config.go
+++ b/cmd/release_config/release_config_lib/release_config.go
@@ -18,6 +18,7 @@
 	"cmp"
 	"fmt"
 	"path/filepath"
+	"regexp"
 	"slices"
 	"sort"
 	"strings"
@@ -69,6 +70,9 @@
 	// Unmarshalled flag artifacts
 	FlagArtifacts FlagArtifacts
 
+	// The files used by this release config
+	FilesUsedMap map[string]bool
+
 	// Generated release config
 	ReleaseConfigArtifact *rc_proto.ReleaseConfigArtifact
 
@@ -77,13 +81,25 @@
 
 	// Partitioned artifacts for {partition}/etc/build_flags.json
 	PartitionBuildFlags map[string]*rc_proto.FlagArtifacts
+
+	// Prior stage(s) for flag advancement (during development).
+	// Once a flag has met criteria in a prior stage, it can advance to this one.
+	PriorStagesMap map[string]bool
 }
 
 func ReleaseConfigFactory(name string, index int) (c *ReleaseConfig) {
-	return &ReleaseConfig{Name: name, DeclarationIndex: index}
+	return &ReleaseConfig{
+		Name:             name,
+		DeclarationIndex: index,
+		FilesUsedMap:     make(map[string]bool),
+		PriorStagesMap:   make(map[string]bool),
+	}
 }
 
 func (config *ReleaseConfig) InheritConfig(iConfig *ReleaseConfig) error {
+	for f := range iConfig.FilesUsedMap {
+		config.FilesUsedMap[f] = true
+	}
 	for _, fa := range iConfig.FlagArtifacts {
 		name := *fa.FlagDeclaration.Name
 		myFa, ok := config.FlagArtifacts[name]
@@ -106,6 +122,10 @@
 	return nil
 }
 
+func (config *ReleaseConfig) GetSortedFileList() []string {
+	return SortedMapKeys(config.FilesUsedMap)
+}
+
 func (config *ReleaseConfig) GenerateReleaseConfig(configs *ReleaseConfigs) error {
 	if config.ReleaseConfigArtifact != nil {
 		return nil
@@ -116,9 +136,15 @@
 	config.compileInProgress = true
 	isRoot := config.Name == "root"
 
+	// Is this a build-prefix release config, such as 'ap3a'?
+	isBuildPrefix, err := regexp.MatchString("^[a-z][a-z][0-9][0-9a-z]$", config.Name)
+	if err != nil {
+		return err
+	}
 	// Start with only the flag declarations.
 	config.FlagArtifacts = configs.FlagArtifacts.Clone()
 	releaseAconfigValueSets := config.FlagArtifacts["RELEASE_ACONFIG_VALUE_SETS"]
+	releasePlatformVersion := config.FlagArtifacts["RELEASE_PLATFORM_VERSION"]
 
 	// Generate any configs we need to inherit.  This will detect loops in
 	// the config.
@@ -126,7 +152,7 @@
 	myInherits := []string{}
 	myInheritsSet := make(map[string]bool)
 	// If there is a "root" release config, it is the start of every inheritance chain.
-	_, err := configs.GetReleaseConfig("root")
+	_, err = configs.GetReleaseConfig("root")
 	if err == nil && !isRoot {
 		config.InheritNames = append([]string{"root"}, config.InheritNames...)
 	}
@@ -134,6 +160,9 @@
 		if _, ok := myInheritsSet[inherit]; ok {
 			continue
 		}
+		if isBuildPrefix && configs.Aliases[inherit] != nil {
+			return fmt.Errorf("%s cannot inherit from alias %s", config.Name, inherit)
+		}
 		myInherits = append(myInherits, inherit)
 		myInheritsSet[inherit] = true
 		iConfig, err := configs.GetReleaseConfig(inherit)
@@ -145,10 +174,33 @@
 			return err
 		}
 	}
+
+	// If we inherited nothing, then we need to mark the global files as used for this
+	// config.  If we inherited, then we already marked them as part of inheritance.
+	if len(config.InheritNames) == 0 {
+		for f := range configs.FilesUsedMap {
+			config.FilesUsedMap[f] = true
+		}
+	}
+
 	contributionsToApply = append(contributionsToApply, config.Contributions...)
 
 	workflowManual := rc_proto.Workflow(rc_proto.Workflow_MANUAL)
 	myDirsMap := make(map[int]bool)
+	if isBuildPrefix && releasePlatformVersion != nil {
+		if MarshalValue(releasePlatformVersion.Value) != strings.ToUpper(config.Name) {
+			value := FlagValue{
+				path: config.Contributions[0].path,
+				proto: rc_proto.FlagValue{
+					Name:  releasePlatformVersion.FlagDeclaration.Name,
+					Value: UnmarshalValue(strings.ToUpper(config.Name)),
+				},
+			}
+			if err := releasePlatformVersion.UpdateValue(value); err != nil {
+				return err
+			}
+		}
+	}
 	for _, contrib := range contributionsToApply {
 		contribAconfigValueSets := []string{}
 		// Gather the aconfig_value_sets from this contribution, allowing duplicates for simplicity.
@@ -165,6 +217,9 @@
 				Value:  &rc_proto.Value{Val: &rc_proto.Value_StringValue{contribAconfigValueSetsString}},
 			})
 
+		for _, priorStage := range contrib.proto.PriorStages {
+			config.PriorStagesMap[priorStage] = true
+		}
 		myDirsMap[contrib.DeclarationIndex] = true
 		if config.AconfigFlagsOnly && len(contrib.FlagValues) > 0 {
 			return fmt.Errorf("%s does not allow build flag overrides", config.Name)
@@ -248,19 +303,22 @@
 		AconfigValueSets: myAconfigValueSets,
 		Inherits:         myInherits,
 		Directories:      directories,
+		PriorStages:      SortedMapKeys(config.PriorStagesMap),
 	}
 
 	config.compileInProgress = false
 	return nil
 }
 
-func (config *ReleaseConfig) WritePartitionBuildFlags(outDir, product, targetRelease string) error {
+func (config *ReleaseConfig) WritePartitionBuildFlags(outDir string) error {
 	var err error
 	for partition, flags := range config.PartitionBuildFlags {
 		slices.SortFunc(flags.FlagArtifacts, func(a, b *rc_proto.FlagArtifact) int {
 			return cmp.Compare(*a.FlagDeclaration.Name, *b.FlagDeclaration.Name)
 		})
-		if err = WriteMessage(filepath.Join(outDir, fmt.Sprintf("build_flags_%s-%s-%s.json", partition, config.Name, product)), flags); err != nil {
+		// The json file name must not be modified as this is read from
+		// build_flags_json module
+		if err = WriteMessage(filepath.Join(outDir, fmt.Sprintf("build_flags_%s.json", partition)), flags); err != nil {
 			return err
 		}
 	}
diff --git a/cmd/release_config/release_config_lib/release_configs.go b/cmd/release_config/release_config_lib/release_configs.go
index 461ee10..4b4e355 100644
--- a/cmd/release_config/release_config_lib/release_configs.go
+++ b/cmd/release_config/release_config_lib/release_configs.go
@@ -67,6 +67,9 @@
 	// Map of directory to *ReleaseConfigMap
 	releaseConfigMapsMap map[string]*ReleaseConfigMap
 
+	// The files used by all release configs
+	FilesUsedMap map[string]bool
+
 	// The list of config directories used.
 	configDirs []string
 
@@ -75,6 +78,72 @@
 	configDirIndexes ReleaseConfigDirMap
 }
 
+func (configs *ReleaseConfigs) WriteInheritanceGraph(outFile string) error {
+	data := []string{}
+	usedAliases := make(map[string]bool)
+	priorStages := make(map[string][]string)
+	rankedStageNames := make(map[string]bool)
+	for _, config := range configs.ReleaseConfigs {
+		var fillColor string
+		inherits := []string{}
+		for _, inherit := range config.InheritNames {
+			if inherit == "root" {
+				// Only show "root" if we have no other inheritance.
+				if len(config.InheritNames) > 1 {
+					continue
+				}
+			}
+			data = append(data, fmt.Sprintf(`"%s" -> "%s"`, config.Name, inherit))
+			inherits = append(inherits, inherit)
+			// If inheriting an alias, add a link from the alias to that release config.
+			if name, found := configs.Aliases[inherit]; found {
+				if !usedAliases[inherit] {
+					usedAliases[inherit] = true
+					data = append(data, fmt.Sprintf(`"%s" -> "%s"`, inherit, *name))
+					data = append(data,
+						fmt.Sprintf(`"%s" [ label="%s\ncurrently: %s" shape=oval ]`,
+							inherit, inherit, *name))
+				}
+			}
+		}
+		// Add links for all of the advancement progressions.
+		for priorStage := range config.PriorStagesMap {
+			stageName := config.Name
+			if len(config.OtherNames) > 0 {
+				stageName = config.OtherNames[0]
+			}
+			data = append(data, fmt.Sprintf(`"%s" -> "%s" [ style=dashed color="#81c995" ]`,
+				priorStage, stageName))
+			priorStages[stageName] = append(priorStages[stageName], priorStage)
+			rankedStageNames[stageName] = true
+		}
+		label := config.Name
+		if len(inherits) > 0 {
+			label += "\\ninherits: " + strings.Join(inherits, " ")
+		}
+		if len(config.OtherNames) > 0 {
+			label += "\\nother names: " + strings.Join(config.OtherNames, " ")
+		}
+		// The active release config has a light blue fill.
+		if config.Name == *configs.Artifact.ReleaseConfig.Name {
+			fillColor = `fillcolor="#d2e3fc" `
+		}
+		data = append(data,
+			fmt.Sprintf(`"%s" [ label="%s" %s]`, config.Name, label, fillColor))
+	}
+	if len(rankedStageNames) > 0 {
+		data = append(data, fmt.Sprintf("subgraph {rank=same %s}", strings.Join(SortedMapKeys(rankedStageNames), " ")))
+	}
+	slices.Sort(data)
+	data = append([]string{
+		"digraph {",
+		"graph [ ratio=.5 ]",
+		"node [ shape=box style=filled fillcolor=white colorscheme=svg fontcolor=black ]",
+	}, data...)
+	data = append(data, "}")
+	return os.WriteFile(outFile, []byte(strings.Join(data, "\n")), 0644)
+}
+
 // Write the "all_release_configs" artifact.
 //
 // The file will be in "{outDir}/all_release_configs-{product}.{format}"
@@ -102,6 +171,7 @@
 		releaseConfigMapsMap: make(map[string]*ReleaseConfigMap),
 		configDirs:           []string{},
 		configDirIndexes:     make(ReleaseConfigDirMap),
+		FilesUsedMap:         make(map[string]bool),
 	}
 	workflowManual := rc_proto.Workflow(rc_proto.Workflow_MANUAL)
 	releaseAconfigValueSets := FlagArtifact{
@@ -180,6 +250,7 @@
 			return fmt.Errorf("Release config map %s has invalid container %s", path, container)
 		}
 	}
+	configs.FilesUsedMap[path] = true
 	dir := filepath.Dir(path)
 	// Record any aliases, checking for duplicates.
 	for _, alias := range m.proto.Aliases {
@@ -226,6 +297,7 @@
 			return fmt.Errorf("Duplicate definition of %s", *flagDeclaration.Name)
 		}
 		// Set the initial value in the flag artifact.
+		configs.FilesUsedMap[path] = true
 		configs.FlagArtifacts[name].UpdateValue(
 			FlagValue{path: path, proto: rc_proto.FlagValue{
 				Name: proto.String(name), Value: flagDeclaration.Value}})
@@ -249,7 +321,18 @@
 			configs.ReleaseConfigs[name] = ReleaseConfigFactory(name, ConfigDirIndex)
 		}
 		config := configs.ReleaseConfigs[name]
-		config.InheritNames = append(config.InheritNames, releaseConfigContribution.proto.Inherits...)
+		config.FilesUsedMap[path] = true
+		inheritNames := make(map[string]bool)
+		for _, inh := range config.InheritNames {
+			inheritNames[inh] = true
+		}
+		// If this contribution says to inherit something we already inherited, we do not want the duplicate.
+		for _, cInh := range releaseConfigContribution.proto.Inherits {
+			if !inheritNames[cInh] {
+				config.InheritNames = append(config.InheritNames, cInh)
+				inheritNames[cInh] = true
+			}
+		}
 
 		// Only walk flag_values/{RELEASE} for defined releases.
 		err2 := WalkTextprotoFiles(dir, filepath.Join("flag_values", name), func(path string, d fs.DirEntry, err error) error {
@@ -260,6 +343,7 @@
 			if *flagValue.proto.Name == "RELEASE_ACONFIG_VALUE_SETS" {
 				return fmt.Errorf("%s: %s is a reserved build flag", path, *flagValue.proto.Name)
 			}
+			config.FilesUsedMap[path] = true
 			releaseConfigContribution.FlagValues = append(releaseConfigContribution.FlagValues, flagValue)
 			return nil
 		})
@@ -340,6 +424,7 @@
 		}
 		value := MarshalValue(flag.Value)
 		makeVars[name] = value
+		addVar(name, "TYPE", ValueType(flag.Value))
 		addVar(name, "PARTITIONS", strings.Join(decl.Containers, " "))
 		addVar(name, "DEFAULT", MarshalValue(decl.Value))
 		addVar(name, "VALUE", value)
@@ -348,7 +433,7 @@
 		addVar(name, "NAMESPACE", *decl.Namespace)
 	}
 	pNames := []string{}
-	for k, _ := range partitions {
+	for k := range partitions {
 		pNames = append(pNames, k)
 	}
 	slices.SortFunc(pNames, func(a, b string) int {
@@ -369,8 +454,9 @@
 	if targetRelease != config.Name {
 		data += fmt.Sprintf("# User specified TARGET_RELEASE=%s\n", targetRelease)
 	}
-	// The variable _all_release_configs will get deleted during processing, so do not mark it read-only.
-	data += fmt.Sprintf("_all_release_configs := %s\n", strings.Join(configs.GetAllReleaseNames(), " "))
+	// As it stands this list is not per-product, but conceptually it is, and will be.
+	data += fmt.Sprintf("ALL_RELEASE_CONFIGS_FOR_PRODUCT :=$= %s\n", strings.Join(configs.GetAllReleaseNames(), " "))
+	data += fmt.Sprintf("_used_files := %s\n", strings.Join(config.GetSortedFileList(), " "))
 	data += fmt.Sprintf("_ALL_RELEASE_FLAGS :=$= %s\n", strings.Join(names, " "))
 	for _, pName := range pNames {
 		data += fmt.Sprintf("_ALL_RELEASE_FLAGS.PARTITIONS.%s :=$= %s\n", pName, strings.Join(partitions[pName], " "))
diff --git a/cmd/release_config/release_config_lib/util.go b/cmd/release_config/release_config_lib/util.go
index c0ea789..b8824d1 100644
--- a/cmd/release_config/release_config_lib/util.go
+++ b/cmd/release_config/release_config_lib/util.go
@@ -22,6 +22,7 @@
 	"os/exec"
 	"path/filepath"
 	"regexp"
+	"slices"
 	"strings"
 
 	"google.golang.org/protobuf/encoding/prototext"
@@ -58,13 +59,36 @@
 //
 //	error: any error encountered.
 func WriteMessage(path string, message proto.Message) (err error) {
+	format := filepath.Ext(path)
+	if len(format) > 1 {
+		// Strip any leading dot.
+		format = format[1:]
+	}
+	return WriteFormattedMessage(path, format, message)
+}
+
+// Write a marshalled message to a file.
+//
+// Marshal the message using the given format.
+//
+// Args:
+//
+//	path string: the path of the file to write to.  Directories are not created.
+//	  Supported extensions are: ".json", ".pb", and ".textproto".
+//	format string: one of "json", "pb", or "textproto".
+//	message proto.Message: the message to write.
+//
+// Returns:
+//
+//	error: any error encountered.
+func WriteFormattedMessage(path, format string, message proto.Message) (err error) {
 	var data []byte
-	switch filepath.Ext(path) {
-	case ".json":
+	switch format {
+	case "json":
 		data, err = json.MarshalIndent(message, "", "  ")
-	case ".pb":
+	case "pb", "binaryproto", "protobuf":
 		data, err = proto.Marshal(message)
-	case ".textproto":
+	case "textproto":
 		data, err = prototext.MarshalOptions{Multiline: true}.Marshal(message)
 	default:
 		return fmt.Errorf("Unknown message format for %s", path)
@@ -95,7 +119,7 @@
 	switch filepath.Ext(path) {
 	case ".json":
 		return json.Unmarshal(data, message)
-	case ".pb":
+	case ".pb", ".protobuf", ".binaryproto":
 		return proto.Unmarshal(data, message)
 	case ".textproto":
 		return prototext.Unmarshal(data, message)
@@ -126,13 +150,25 @@
 	disableWarnings = true
 }
 
+// warnf will log to stdout if warnings are enabled. In make code,
+// stdout is redirected to a file, so the warnings will not be shown
+// in the terminal.
 func warnf(format string, args ...any) (n int, err error) {
 	if !disableWarnings {
-		return fmt.Fprintf(os.Stderr, format, args...)
+		return fmt.Printf(format, args...)
 	}
 	return 0, nil
 }
 
+func SortedMapKeys(inputMap map[string]bool) []string {
+	ret := []string{}
+	for k := range inputMap {
+		ret = append(ret, k)
+	}
+	slices.Sort(ret)
+	return ret
+}
+
 func validContainer(container string) bool {
 	return containerRegexp.MatchString(container)
 }
@@ -193,6 +229,7 @@
 		var stdout strings.Builder
 		getBuildVar.Stdin = strings.NewReader("")
 		getBuildVar.Stdout = &stdout
+		getBuildVar.Stderr = os.Stderr
 		err = getBuildVar.Run()
 		if err != nil {
 			return
diff --git a/cmd/release_config/release_config_proto/Android.bp b/cmd/release_config/release_config_proto/Android.bp
index 8c47f2a..c34d203 100644
--- a/cmd/release_config/release_config_proto/Android.bp
+++ b/cmd/release_config/release_config_proto/Android.bp
@@ -24,6 +24,8 @@
         "golang-protobuf-runtime-protoimpl",
     ],
     srcs: [
+        "build_flags_common.pb.go",
+        "build_flags_declarations.pb.go",
         "build_flags_src.pb.go",
         "build_flags_out.pb.go",
     ],
diff --git a/cmd/release_config/release_config_proto/build_flags_common.pb.go b/cmd/release_config/release_config_proto/build_flags_common.pb.go
new file mode 100644
index 0000000..1e927db
--- /dev/null
+++ b/cmd/release_config/release_config_proto/build_flags_common.pb.go
@@ -0,0 +1,169 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.30.0
+// 	protoc        v3.21.12
+// source: build_flags_common.proto
+
+package release_config_proto
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type Workflow int32
+
+const (
+	Workflow_Workflow_Unspecified Workflow = 0
+	// Boolean value flags that progress from false to true.
+	Workflow_LAUNCH Workflow = 1
+	// String value flags that get updated with new version strings to control
+	// prebuilt inclusion.
+	Workflow_PREBUILT Workflow = 2
+	// Manually managed outside flags.  These are likely to be found in a
+	// different directory than flags with other workflows.
+	Workflow_MANUAL Workflow = 3
+)
+
+// Enum value maps for Workflow.
+var (
+	Workflow_name = map[int32]string{
+		0: "Workflow_Unspecified",
+		1: "LAUNCH",
+		2: "PREBUILT",
+		3: "MANUAL",
+	}
+	Workflow_value = map[string]int32{
+		"Workflow_Unspecified": 0,
+		"LAUNCH":               1,
+		"PREBUILT":             2,
+		"MANUAL":               3,
+	}
+)
+
+func (x Workflow) Enum() *Workflow {
+	p := new(Workflow)
+	*p = x
+	return p
+}
+
+func (x Workflow) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (Workflow) Descriptor() protoreflect.EnumDescriptor {
+	return file_build_flags_common_proto_enumTypes[0].Descriptor()
+}
+
+func (Workflow) Type() protoreflect.EnumType {
+	return &file_build_flags_common_proto_enumTypes[0]
+}
+
+func (x Workflow) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *Workflow) UnmarshalJSON(b []byte) error {
+	num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
+	if err != nil {
+		return err
+	}
+	*x = Workflow(num)
+	return nil
+}
+
+// Deprecated: Use Workflow.Descriptor instead.
+func (Workflow) EnumDescriptor() ([]byte, []int) {
+	return file_build_flags_common_proto_rawDescGZIP(), []int{0}
+}
+
+var File_build_flags_common_proto protoreflect.FileDescriptor
+
+var file_build_flags_common_proto_rawDesc = []byte{
+	0x0a, 0x18, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x5f, 0x63, 0x6f,
+	0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x61, 0x6e, 0x64, 0x72,
+	0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66,
+	0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2a, 0x4a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b,
+	0x66, 0x6c, 0x6f, 0x77, 0x12, 0x18, 0x0a, 0x14, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
+	0x5f, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x10, 0x00, 0x12, 0x0a,
+	0x0a, 0x06, 0x4c, 0x41, 0x55, 0x4e, 0x43, 0x48, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x52,
+	0x45, 0x42, 0x55, 0x49, 0x4c, 0x54, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x41, 0x4e, 0x55,
+	0x41, 0x4c, 0x10, 0x03, 0x42, 0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f,
+	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+}
+
+var (
+	file_build_flags_common_proto_rawDescOnce sync.Once
+	file_build_flags_common_proto_rawDescData = file_build_flags_common_proto_rawDesc
+)
+
+func file_build_flags_common_proto_rawDescGZIP() []byte {
+	file_build_flags_common_proto_rawDescOnce.Do(func() {
+		file_build_flags_common_proto_rawDescData = protoimpl.X.CompressGZIP(file_build_flags_common_proto_rawDescData)
+	})
+	return file_build_flags_common_proto_rawDescData
+}
+
+var file_build_flags_common_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_build_flags_common_proto_goTypes = []interface{}{
+	(Workflow)(0), // 0: android.release_config_proto.workflow
+}
+var file_build_flags_common_proto_depIdxs = []int32{
+	0, // [0:0] is the sub-list for method output_type
+	0, // [0:0] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_build_flags_common_proto_init() }
+func file_build_flags_common_proto_init() {
+	if File_build_flags_common_proto != nil {
+		return
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_build_flags_common_proto_rawDesc,
+			NumEnums:      1,
+			NumMessages:   0,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_build_flags_common_proto_goTypes,
+		DependencyIndexes: file_build_flags_common_proto_depIdxs,
+		EnumInfos:         file_build_flags_common_proto_enumTypes,
+	}.Build()
+	File_build_flags_common_proto = out.File
+	file_build_flags_common_proto_rawDesc = nil
+	file_build_flags_common_proto_goTypes = nil
+	file_build_flags_common_proto_depIdxs = nil
+}
diff --git a/cmd/release_config/release_config_proto/build_flags_common.proto b/cmd/release_config/release_config_proto/build_flags_common.proto
new file mode 100644
index 0000000..d5d6101
--- /dev/null
+++ b/cmd/release_config/release_config_proto/build_flags_common.proto
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto2";
+package android.release_config_proto;
+option go_package = "android/soong/release_config/release_config_proto";
+
+// This protobuf file defines common messages used in the rest of the build flag
+// protos.
+
+enum workflow {
+  Workflow_Unspecified = 0;
+
+  // Boolean value flags that progress from false to true.
+  LAUNCH = 1;
+
+  // String value flags that get updated with new version strings to control
+  // prebuilt inclusion.
+  PREBUILT = 2;
+
+  // Manually managed outside flags.  These are likely to be found in a
+  // different directory than flags with other workflows.
+  MANUAL = 3;
+}
diff --git a/cmd/release_config/release_config_proto/build_flags_declarations.pb.go b/cmd/release_config/release_config_proto/build_flags_declarations.pb.go
new file mode 100644
index 0000000..c0573ed
--- /dev/null
+++ b/cmd/release_config/release_config_proto/build_flags_declarations.pb.go
@@ -0,0 +1,301 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.30.0
+// 	protoc        v3.21.12
+// source: build_flags_declarations.proto
+
+package release_config_proto
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type FlagDeclarationArtifact struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The name of the flag.
+	// See # name for format detail
+	Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+	// Namespace the flag belongs to (required)
+	// See # namespace for format detail
+	Namespace *string `protobuf:"bytes,2,opt,name=namespace" json:"namespace,omitempty"`
+	// Text description of the flag's purpose.
+	Description *string `protobuf:"bytes,3,opt,name=description" json:"description,omitempty"`
+	// Where the flag was declared.
+	DeclarationPath *string `protobuf:"bytes,5,opt,name=declaration_path,json=declarationPath" json:"declaration_path,omitempty"`
+	// Workflow for this flag.
+	Workflow *Workflow `protobuf:"varint,205,opt,name=workflow,enum=android.release_config_proto.Workflow" json:"workflow,omitempty"`
+	// The container for this flag.  This overrides any default container given
+	// in the release_config_map message.
+	Containers []string `protobuf:"bytes,206,rep,name=containers" json:"containers,omitempty"`
+}
+
+func (x *FlagDeclarationArtifact) Reset() {
+	*x = FlagDeclarationArtifact{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_build_flags_declarations_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *FlagDeclarationArtifact) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FlagDeclarationArtifact) ProtoMessage() {}
+
+func (x *FlagDeclarationArtifact) ProtoReflect() protoreflect.Message {
+	mi := &file_build_flags_declarations_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use FlagDeclarationArtifact.ProtoReflect.Descriptor instead.
+func (*FlagDeclarationArtifact) Descriptor() ([]byte, []int) {
+	return file_build_flags_declarations_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *FlagDeclarationArtifact) GetName() string {
+	if x != nil && x.Name != nil {
+		return *x.Name
+	}
+	return ""
+}
+
+func (x *FlagDeclarationArtifact) GetNamespace() string {
+	if x != nil && x.Namespace != nil {
+		return *x.Namespace
+	}
+	return ""
+}
+
+func (x *FlagDeclarationArtifact) GetDescription() string {
+	if x != nil && x.Description != nil {
+		return *x.Description
+	}
+	return ""
+}
+
+func (x *FlagDeclarationArtifact) GetDeclarationPath() string {
+	if x != nil && x.DeclarationPath != nil {
+		return *x.DeclarationPath
+	}
+	return ""
+}
+
+func (x *FlagDeclarationArtifact) GetWorkflow() Workflow {
+	if x != nil && x.Workflow != nil {
+		return *x.Workflow
+	}
+	return Workflow_Workflow_Unspecified
+}
+
+func (x *FlagDeclarationArtifact) GetContainers() []string {
+	if x != nil {
+		return x.Containers
+	}
+	return nil
+}
+
+type FlagDeclarationArtifacts struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The artifacts
+	FlagDeclarationArtifacts []*FlagDeclarationArtifact `protobuf:"bytes,1,rep,name=flag_declaration_artifacts,json=flagDeclarationArtifacts" json:"flag_declaration_artifacts,omitempty"`
+}
+
+func (x *FlagDeclarationArtifacts) Reset() {
+	*x = FlagDeclarationArtifacts{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_build_flags_declarations_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *FlagDeclarationArtifacts) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FlagDeclarationArtifacts) ProtoMessage() {}
+
+func (x *FlagDeclarationArtifacts) ProtoReflect() protoreflect.Message {
+	mi := &file_build_flags_declarations_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use FlagDeclarationArtifacts.ProtoReflect.Descriptor instead.
+func (*FlagDeclarationArtifacts) Descriptor() ([]byte, []int) {
+	return file_build_flags_declarations_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *FlagDeclarationArtifacts) GetFlagDeclarationArtifacts() []*FlagDeclarationArtifact {
+	if x != nil {
+		return x.FlagDeclarationArtifacts
+	}
+	return nil
+}
+
+var File_build_flags_declarations_proto protoreflect.FileDescriptor
+
+var file_build_flags_declarations_proto_rawDesc = []byte{
+	0x0a, 0x1e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x5f, 0x64, 0x65,
+	0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x12, 0x1c, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
+	0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18,
+	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x5f, 0x63, 0x6f, 0x6d, 0x6d,
+	0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8e, 0x02, 0x0a, 0x19, 0x66, 0x6c, 0x61,
+	0x67, 0x5f, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x72,
+	0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61,
+	0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e,
+	0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63,
+	0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64,
+	0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65,
+	0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f,
+	0x6e, 0x50, 0x61, 0x74, 0x68, 0x12, 0x43, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f,
+	0x77, 0x18, 0xcd, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+	0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
+	0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
+	0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1f, 0x0a, 0x0a, 0x63, 0x6f,
+	0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0xce, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52,
+	0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x4a, 0x04, 0x08, 0x04, 0x10,
+	0x05, 0x4a, 0x06, 0x08, 0xcf, 0x01, 0x10, 0xd0, 0x01, 0x22, 0x93, 0x01, 0x0a, 0x1a, 0x66, 0x6c,
+	0x61, 0x67, 0x5f, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61,
+	0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x12, 0x75, 0x0a, 0x1a, 0x66, 0x6c, 0x61, 0x67,
+	0x5f, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x72, 0x74,
+	0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x61,
+	0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63,
+	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x66, 0x6c, 0x61, 0x67,
+	0x5f, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x72, 0x74,
+	0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x18, 0x66, 0x6c, 0x61, 0x67, 0x44, 0x65, 0x63, 0x6c, 0x61,
+	0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x42,
+	0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
+	0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f,
+	0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70,
+	0x72, 0x6f, 0x74, 0x6f,
+}
+
+var (
+	file_build_flags_declarations_proto_rawDescOnce sync.Once
+	file_build_flags_declarations_proto_rawDescData = file_build_flags_declarations_proto_rawDesc
+)
+
+func file_build_flags_declarations_proto_rawDescGZIP() []byte {
+	file_build_flags_declarations_proto_rawDescOnce.Do(func() {
+		file_build_flags_declarations_proto_rawDescData = protoimpl.X.CompressGZIP(file_build_flags_declarations_proto_rawDescData)
+	})
+	return file_build_flags_declarations_proto_rawDescData
+}
+
+var file_build_flags_declarations_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_build_flags_declarations_proto_goTypes = []interface{}{
+	(*FlagDeclarationArtifact)(nil),  // 0: android.release_config_proto.flag_declaration_artifact
+	(*FlagDeclarationArtifacts)(nil), // 1: android.release_config_proto.flag_declaration_artifacts
+	(Workflow)(0),                    // 2: android.release_config_proto.workflow
+}
+var file_build_flags_declarations_proto_depIdxs = []int32{
+	2, // 0: android.release_config_proto.flag_declaration_artifact.workflow:type_name -> android.release_config_proto.workflow
+	0, // 1: android.release_config_proto.flag_declaration_artifacts.flag_declaration_artifacts:type_name -> android.release_config_proto.flag_declaration_artifact
+	2, // [2:2] is the sub-list for method output_type
+	2, // [2:2] is the sub-list for method input_type
+	2, // [2:2] is the sub-list for extension type_name
+	2, // [2:2] is the sub-list for extension extendee
+	0, // [0:2] is the sub-list for field type_name
+}
+
+func init() { file_build_flags_declarations_proto_init() }
+func file_build_flags_declarations_proto_init() {
+	if File_build_flags_declarations_proto != nil {
+		return
+	}
+	file_build_flags_common_proto_init()
+	if !protoimpl.UnsafeEnabled {
+		file_build_flags_declarations_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*FlagDeclarationArtifact); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_build_flags_declarations_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*FlagDeclarationArtifacts); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_build_flags_declarations_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_build_flags_declarations_proto_goTypes,
+		DependencyIndexes: file_build_flags_declarations_proto_depIdxs,
+		MessageInfos:      file_build_flags_declarations_proto_msgTypes,
+	}.Build()
+	File_build_flags_declarations_proto = out.File
+	file_build_flags_declarations_proto_rawDesc = nil
+	file_build_flags_declarations_proto_goTypes = nil
+	file_build_flags_declarations_proto_depIdxs = nil
+}
diff --git a/cmd/release_config/release_config_proto/build_flags_declarations.proto b/cmd/release_config/release_config_proto/build_flags_declarations.proto
new file mode 100644
index 0000000..e0cf099
--- /dev/null
+++ b/cmd/release_config/release_config_proto/build_flags_declarations.proto
@@ -0,0 +1,75 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto2";
+package android.release_config_proto;
+option go_package = "android/soong/release_config/release_config_proto";
+
+import "build_flags_common.proto";
+
+// This protobuf file defines messages used to represent the
+// all_build_flag_declarations artifact for use in automated systems, such as
+// Gantry.
+//
+// The following format requirements apply across various message fields:
+//
+// # name: name of the flag
+//
+//    format: an uppercase string in SNAKE_CASE format starting with RELEASE_,
+//      no consecutive underscores, and no leading digit. For example
+//      RELEASE_MY_PACKAGE_FLAG is a valid name, while MY_PACKAGE_FLAG, and
+//      RELEASE_MY_PACKAGE__FLAG are invalid.
+//
+// # package: package to which the flag belongs
+//
+//    format: lowercase strings in snake_case format, delimited by dots, no
+//      consecutive underscores and no leading digit in each string. For example
+//      com.android.mypackage is a valid name while com.android.myPackage,
+//      com.android.1mypackage are invalid
+
+message flag_declaration_artifact {
+  // The name of the flag.
+  // See # name for format detail
+  optional string name = 1;
+
+  // Namespace the flag belongs to (required)
+  // See # namespace for format detail
+  optional string namespace = 2;
+
+  // Text description of the flag's purpose.
+  optional string description = 3;
+
+  // reserve this for bug, if needed.
+  reserved 4;
+
+  // Where the flag was declared.
+  optional string declaration_path = 5;
+
+  // Workflow for this flag.
+  optional workflow workflow = 205;
+
+  // The container for this flag.  This overrides any default container given
+  // in the release_config_map message.
+  repeated string containers = 206;
+
+  // The package associated with this flag.
+  // (when Gantry is ready for it) optional string package = 207;
+  reserved 207;
+}
+
+message flag_declaration_artifacts {
+  // The artifacts
+  repeated flag_declaration_artifact flag_declaration_artifacts = 1;
+}
diff --git a/cmd/release_config/release_config_proto/build_flags_out.pb.go b/cmd/release_config/release_config_proto/build_flags_out.pb.go
index 483cffa..309ec34 100644
--- a/cmd/release_config/release_config_proto/build_flags_out.pb.go
+++ b/cmd/release_config/release_config_proto/build_flags_out.pb.go
@@ -1,3 +1,7 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
 //
@@ -222,6 +226,9 @@
 	// The release config directories used for this config.
 	// For example, "build/release".
 	Directories []string `protobuf:"bytes,6,rep,name=directories" json:"directories,omitempty"`
+	// Prior stage(s) for flag advancement (during development).
+	// Once a flag has met criteria in a prior stage, it can advance to this one.
+	PriorStages []string `protobuf:"bytes,7,rep,name=prior_stages,json=priorStages" json:"prior_stages,omitempty"`
 }
 
 func (x *ReleaseConfigArtifact) Reset() {
@@ -298,6 +305,13 @@
 	return nil
 }
 
+func (x *ReleaseConfigArtifact) GetPriorStages() []string {
+	if x != nil {
+		return x.PriorStages
+	}
+	return nil
+}
+
 type ReleaseConfigsArtifact struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -399,7 +413,7 @@
 	0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f,
 	0x74, 0x6f, 0x2e, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74,
 	0x52, 0x0d, 0x66, 0x6c, 0x61, 0x67, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x22,
-	0x8e, 0x02, 0x0a, 0x17, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66,
+	0xb1, 0x02, 0x0a, 0x17, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66,
 	0x69, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e,
 	0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
 	0x1f, 0x0a, 0x0b, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02,
@@ -416,40 +430,43 @@
 	0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x73, 0x12, 0x20,
 	0x0a, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20,
 	0x03, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73,
-	0x22, 0xe8, 0x03, 0x0a, 0x18, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e,
-	0x66, 0x69, 0x67, 0x73, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x12, 0x5c, 0x0a,
-	0x0e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
-	0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e,
-	0x66, 0x69, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x0d, 0x72, 0x65,
-	0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x69, 0x0a, 0x15, 0x6f,
-	0x74, 0x68, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e,
-	0x66, 0x69, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x61, 0x6e, 0x64,
-	0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e,
-	0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
-	0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63,
-	0x74, 0x52, 0x13, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43,
-	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x17, 0x72, 0x65, 0x6c, 0x65, 0x61,
-	0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x5f, 0x6d,
-	0x61, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x50, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+	0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x67, 0x65, 0x73,
+	0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x53, 0x74, 0x61,
+	0x67, 0x65, 0x73, 0x22, 0xe8, 0x03, 0x0a, 0x18, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f,
+	0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74,
+	0x12, 0x5c, 0x0a, 0x0e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66,
+	0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
 	0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
 	0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f,
-	0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74,
-	0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61,
-	0x70, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x72, 0x65, 0x6c, 0x65,
-	0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, 0x73, 0x4d, 0x61, 0x70,
-	0x1a, 0x79, 0x0a, 0x19, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69,
-	0x67, 0x4d, 0x61, 0x70, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
-	0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
-	0x46, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30,
-	0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65,
-	0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65,
+	0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52,
+	0x0d, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x69,
+	0x0a, 0x15, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f,
+	0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e,
+	0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f,
+	0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c,
+	0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69,
+	0x66, 0x61, 0x63, 0x74, 0x52, 0x13, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x6c, 0x65, 0x61,
+	0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x17, 0x72, 0x65,
 	0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6d, 0x61, 0x70,
-	0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x33, 0x5a, 0x31, 0x61,
-	0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x72, 0x65, 0x6c,
-	0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65,
+	0x73, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x50, 0x2e, 0x61, 0x6e,
+	0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61,
+	0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66,
+	0x61, 0x63, 0x74, 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+	0x67, 0x4d, 0x61, 0x70, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x72,
+	0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, 0x73,
+	0x4d, 0x61, 0x70, 0x1a, 0x79, 0x0a, 0x19, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79,
+	0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
+	0x65, 0x79, 0x12, 0x46, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x0b, 0x32, 0x30, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65,
 	0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f,
+	0x6d, 0x61, 0x70, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x33,
+	0x5a, 0x31, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f,
+	0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72,
+	0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72,
+	0x6f, 0x74, 0x6f,
 }
 
 var (
diff --git a/cmd/release_config/release_config_proto/build_flags_out.proto b/cmd/release_config/release_config_proto/build_flags_out.proto
index 6f34d6f..0cbc157 100644
--- a/cmd/release_config/release_config_proto/build_flags_out.proto
+++ b/cmd/release_config/release_config_proto/build_flags_out.proto
@@ -1,3 +1,7 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
 //
@@ -79,6 +83,10 @@
   // The release config directories used for this config.
   // For example, "build/release".
   repeated string directories = 6;
+
+  // Prior stage(s) for flag advancement (during development).
+  // Once a flag has met criteria in a prior stage, it can advance to this one.
+  repeated string prior_stages = 7;
 }
 
 message release_configs_artifact {
diff --git a/cmd/release_config/release_config_proto/build_flags_src.pb.go b/cmd/release_config/release_config_proto/build_flags_src.pb.go
index dded975..8de340e 100644
--- a/cmd/release_config/release_config_proto/build_flags_src.pb.go
+++ b/cmd/release_config/release_config_proto/build_flags_src.pb.go
@@ -1,3 +1,7 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
 //
@@ -31,73 +35,6 @@
 	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
 )
 
-type Workflow int32
-
-const (
-	Workflow_UNSPECIFIED_workflow Workflow = 0
-	// Boolean value flags that progress from false to true.
-	Workflow_LAUNCH Workflow = 1
-	// String value flags that get updated with new version strings to control
-	// prebuilt inclusion.
-	Workflow_PREBUILT Workflow = 2
-	// Manually managed outside flags.  These are likely to be found in a
-	// different directory than flags with other workflows.
-	Workflow_MANUAL Workflow = 3
-)
-
-// Enum value maps for Workflow.
-var (
-	Workflow_name = map[int32]string{
-		0: "UNSPECIFIED_workflow",
-		1: "LAUNCH",
-		2: "PREBUILT",
-		3: "MANUAL",
-	}
-	Workflow_value = map[string]int32{
-		"UNSPECIFIED_workflow": 0,
-		"LAUNCH":               1,
-		"PREBUILT":             2,
-		"MANUAL":               3,
-	}
-)
-
-func (x Workflow) Enum() *Workflow {
-	p := new(Workflow)
-	*p = x
-	return p
-}
-
-func (x Workflow) String() string {
-	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
-}
-
-func (Workflow) Descriptor() protoreflect.EnumDescriptor {
-	return file_build_flags_src_proto_enumTypes[0].Descriptor()
-}
-
-func (Workflow) Type() protoreflect.EnumType {
-	return &file_build_flags_src_proto_enumTypes[0]
-}
-
-func (x Workflow) Number() protoreflect.EnumNumber {
-	return protoreflect.EnumNumber(x)
-}
-
-// Deprecated: Do not use.
-func (x *Workflow) UnmarshalJSON(b []byte) error {
-	num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
-	if err != nil {
-		return err
-	}
-	*x = Workflow(num)
-	return nil
-}
-
-// Deprecated: Use Workflow.Descriptor instead.
-func (Workflow) EnumDescriptor() ([]byte, []int) {
-	return file_build_flags_src_proto_rawDescGZIP(), []int{0}
-}
-
 type Value struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -295,7 +232,7 @@
 	if x != nil && x.Workflow != nil {
 		return *x.Workflow
 	}
-	return Workflow_UNSPECIFIED_workflow
+	return Workflow_Workflow_Unspecified
 }
 
 func (x *FlagDeclaration) GetContainers() []string {
@@ -389,6 +326,9 @@
 	AconfigValueSets []string `protobuf:"bytes,3,rep,name=aconfig_value_sets,json=aconfigValueSets" json:"aconfig_value_sets,omitempty"`
 	// Only aconfig flags are allowed in this release config.
 	AconfigFlagsOnly *bool `protobuf:"varint,4,opt,name=aconfig_flags_only,json=aconfigFlagsOnly" json:"aconfig_flags_only,omitempty"`
+	// Prior stage(s) for flag advancement (during development).
+	// Once a flag has met criteria in a prior stage, it can advance to this one.
+	PriorStages []string `protobuf:"bytes,5,rep,name=prior_stages,json=priorStages" json:"prior_stages,omitempty"`
 }
 
 func (x *ReleaseConfig) Reset() {
@@ -451,6 +391,13 @@
 	return false
 }
 
+func (x *ReleaseConfig) GetPriorStages() []string {
+	if x != nil {
+		return x.PriorStages
+	}
+	return nil
+}
+
 // Any aliases.  These are used for continuous integration builder config.
 type ReleaseAlias struct {
 	state         protoimpl.MessageState
@@ -582,75 +529,74 @@
 	0x0a, 0x15, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x5f, 0x73, 0x72,
 	0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
 	0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa5, 0x01, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12,
-	0x2e, 0x0a, 0x11, 0x75, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x76,
-	0x61, 0x6c, 0x75, 0x65, 0x18, 0xc8, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x10, 0x75,
-	0x6e, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12,
-	0x24, 0x0a, 0x0c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
-	0xc9, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
-	0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x20, 0x0a, 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x76, 0x61,
-	0x6c, 0x75, 0x65, 0x18, 0xca, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x09, 0x62, 0x6f,
-	0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1d, 0x0a, 0x08, 0x6f, 0x62, 0x73, 0x6f, 0x6c,
-	0x65, 0x74, 0x65, 0x18, 0xcb, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x08, 0x6f, 0x62,
-	0x73, 0x6f, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x05, 0x0a, 0x03, 0x76, 0x61, 0x6c, 0x22, 0x96, 0x02,
-	0x0a, 0x10, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69,
-	0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70,
-	0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73,
-	0x70, 0x61, 0x63, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
-	0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72,
-	0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
-	0xc9, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
-	0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c,
-	0x75, 0x65, 0x12, 0x43, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0xcd,
-	0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
-	0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x08, 0x77,
-	0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1f, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61,
-	0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0xce, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f,
-	0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x06,
-	0x08, 0xcf, 0x01, 0x10, 0xd0, 0x01, 0x22, 0x79, 0x0a, 0x0a, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x76,
-	0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
-	0x65, 0x18, 0xc9, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
-	0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
-	0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76,
-	0x61, 0x6c, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x08, 0x72, 0x65, 0x64, 0x61, 0x63, 0x74, 0x65, 0x64,
-	0x18, 0xca, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x64, 0x61, 0x63, 0x74, 0x65,
-	0x64, 0x22, 0x9c, 0x01, 0x0a, 0x0e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f,
-	0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x68, 0x65,
-	0x72, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x68, 0x65,
-	0x72, 0x69, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f,
-	0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09,
-	0x52, 0x10, 0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x53, 0x65,
-	0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x6c,
-	0x61, 0x67, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10,
-	0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x4f, 0x6e, 0x6c, 0x79,
-	0x22, 0x3b, 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61,
-	0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18,
-	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0xac, 0x01,
-	0x0a, 0x12, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
-	0x5f, 0x6d, 0x61, 0x70, 0x12, 0x45, 0x0a, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18,
-	0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
-	0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x61, 0x6c, 0x69,
-	0x61, 0x73, 0x52, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64,
-	0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a,
-	0x12, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e,
-	0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x64, 0x65, 0x66, 0x61, 0x75,
-	0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x2a, 0x4a, 0x0a, 0x08,
-	0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x18, 0x0a, 0x14, 0x55, 0x4e, 0x53, 0x50,
-	0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
-	0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x41, 0x55, 0x4e, 0x43, 0x48, 0x10, 0x01, 0x12, 0x0c,
-	0x0a, 0x08, 0x50, 0x52, 0x45, 0x42, 0x55, 0x49, 0x4c, 0x54, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06,
-	0x4d, 0x41, 0x4e, 0x55, 0x41, 0x4c, 0x10, 0x03, 0x42, 0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64, 0x72,
-	0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
-	0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65,
-	0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66, 0x6c, 0x61,
+	0x67, 0x73, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
+	0xa5, 0x01, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2e, 0x0a, 0x11, 0x75, 0x6e, 0x73,
+	0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0xc8,
+	0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x10, 0x75, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x69,
+	0x66, 0x69, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x24, 0x0a, 0x0c, 0x73, 0x74, 0x72,
+	0x69, 0x6e, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0xc9, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x48, 0x00, 0x52, 0x0b, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12,
+	0x20, 0x0a, 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0xca, 0x01,
+	0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x09, 0x62, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75,
+	0x65, 0x12, 0x1d, 0x0a, 0x08, 0x6f, 0x62, 0x73, 0x6f, 0x6c, 0x65, 0x74, 0x65, 0x18, 0xcb, 0x01,
+	0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x08, 0x6f, 0x62, 0x73, 0x6f, 0x6c, 0x65, 0x74, 0x65,
+	0x42, 0x05, 0x0a, 0x03, 0x76, 0x61, 0x6c, 0x22, 0x96, 0x02, 0x0a, 0x10, 0x66, 0x6c, 0x61, 0x67,
+	0x5f, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04,
+	0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+	0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x20,
+	0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+	0x12, 0x3a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0xc9, 0x01, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x23, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61,
+	0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
+	0x76, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x43, 0x0a, 0x08,
+	0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0xcd, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
+	0x26, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
+	0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x77,
+	0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f,
+	0x77, 0x12, 0x1f, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18,
+	0xce, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65,
+	0x72, 0x73, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x06, 0x08, 0xcf, 0x01, 0x10, 0xd0, 0x01,
+	0x22, 0x79, 0x0a, 0x0a, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12,
+	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+	0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0xc9, 0x01, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c,
+	0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1b,
+	0x0a, 0x08, 0x72, 0x65, 0x64, 0x61, 0x63, 0x74, 0x65, 0x64, 0x18, 0xca, 0x01, 0x20, 0x01, 0x28,
+	0x08, 0x52, 0x08, 0x72, 0x65, 0x64, 0x61, 0x63, 0x74, 0x65, 0x64, 0x22, 0xbf, 0x01, 0x0a, 0x0e,
+	0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12,
+	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+	0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x73, 0x18, 0x02,
+	0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x73, 0x12, 0x2c,
+	0x0a, 0x12, 0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f,
+	0x73, 0x65, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x61, 0x63, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x53, 0x65, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12,
+	0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x5f, 0x6f, 0x6e,
+	0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69,
+	0x67, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72,
+	0x69, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x67, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09,
+	0x52, 0x0b, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x67, 0x65, 0x73, 0x22, 0x3b, 0x0a,
+	0x0d, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x12,
+	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+	0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0xac, 0x01, 0x0a, 0x12, 0x72,
+	0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6d, 0x61,
+	0x70, 0x12, 0x45, 0x0a, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03,
+	0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c,
+	0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x52,
+	0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63,
+	0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64,
+	0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x64, 0x65,
+	0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73,
+	0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43,
+	0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x42, 0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64,
+	0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61,
+	0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
+	0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 }
 
 var (
@@ -665,22 +611,21 @@
 	return file_build_flags_src_proto_rawDescData
 }
 
-var file_build_flags_src_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
 var file_build_flags_src_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
 var file_build_flags_src_proto_goTypes = []interface{}{
-	(Workflow)(0),            // 0: android.release_config_proto.workflow
-	(*Value)(nil),            // 1: android.release_config_proto.value
-	(*FlagDeclaration)(nil),  // 2: android.release_config_proto.flag_declaration
-	(*FlagValue)(nil),        // 3: android.release_config_proto.flag_value
-	(*ReleaseConfig)(nil),    // 4: android.release_config_proto.release_config
-	(*ReleaseAlias)(nil),     // 5: android.release_config_proto.release_alias
-	(*ReleaseConfigMap)(nil), // 6: android.release_config_proto.release_config_map
+	(*Value)(nil),            // 0: android.release_config_proto.value
+	(*FlagDeclaration)(nil),  // 1: android.release_config_proto.flag_declaration
+	(*FlagValue)(nil),        // 2: android.release_config_proto.flag_value
+	(*ReleaseConfig)(nil),    // 3: android.release_config_proto.release_config
+	(*ReleaseAlias)(nil),     // 4: android.release_config_proto.release_alias
+	(*ReleaseConfigMap)(nil), // 5: android.release_config_proto.release_config_map
+	(Workflow)(0),            // 6: android.release_config_proto.workflow
 }
 var file_build_flags_src_proto_depIdxs = []int32{
-	1, // 0: android.release_config_proto.flag_declaration.value:type_name -> android.release_config_proto.value
-	0, // 1: android.release_config_proto.flag_declaration.workflow:type_name -> android.release_config_proto.workflow
-	1, // 2: android.release_config_proto.flag_value.value:type_name -> android.release_config_proto.value
-	5, // 3: android.release_config_proto.release_config_map.aliases:type_name -> android.release_config_proto.release_alias
+	0, // 0: android.release_config_proto.flag_declaration.value:type_name -> android.release_config_proto.value
+	6, // 1: android.release_config_proto.flag_declaration.workflow:type_name -> android.release_config_proto.workflow
+	0, // 2: android.release_config_proto.flag_value.value:type_name -> android.release_config_proto.value
+	4, // 3: android.release_config_proto.release_config_map.aliases:type_name -> android.release_config_proto.release_alias
 	4, // [4:4] is the sub-list for method output_type
 	4, // [4:4] is the sub-list for method input_type
 	4, // [4:4] is the sub-list for extension type_name
@@ -693,6 +638,7 @@
 	if File_build_flags_src_proto != nil {
 		return
 	}
+	file_build_flags_common_proto_init()
 	if !protoimpl.UnsafeEnabled {
 		file_build_flags_src_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*Value); i {
@@ -778,14 +724,13 @@
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_build_flags_src_proto_rawDesc,
-			NumEnums:      1,
+			NumEnums:      0,
 			NumMessages:   6,
 			NumExtensions: 0,
 			NumServices:   0,
 		},
 		GoTypes:           file_build_flags_src_proto_goTypes,
 		DependencyIndexes: file_build_flags_src_proto_depIdxs,
-		EnumInfos:         file_build_flags_src_proto_enumTypes,
 		MessageInfos:      file_build_flags_src_proto_msgTypes,
 	}.Build()
 	File_build_flags_src_proto = out.File
diff --git a/cmd/release_config/release_config_proto/build_flags_src.proto b/cmd/release_config/release_config_proto/build_flags_src.proto
index 0ef1a5f..4fad478 100644
--- a/cmd/release_config/release_config_proto/build_flags_src.proto
+++ b/cmd/release_config/release_config_proto/build_flags_src.proto
@@ -1,3 +1,7 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
 //
@@ -13,6 +17,8 @@
 package android.release_config_proto;
 option go_package = "android/soong/release_config/release_config_proto";
 
+import "build_flags_common.proto";
+
 // This protobuf file defines messages used to represent the build flags used by
 // a release in a more human-editable form.  It is used for on-disk files in the
 // source tree.
@@ -38,21 +44,6 @@
 //      com.android.mypackage is a valid name while com.android.myPackage,
 //      com.android.1mypackage are invalid
 
-enum workflow {
-  UNSPECIFIED_workflow = 0;
-
-  // Boolean value flags that progress from false to true.
-  LAUNCH = 1;
-
-  // String value flags that get updated with new version strings to control
-  // prebuilt inclusion.
-  PREBUILT = 2;
-
-  // Manually managed outside flags.  These are likely to be found in a
-  // different directory than flags with other workflows.
-  MANUAL = 3;
-}
-
 message value {
   oneof val {
     bool unspecified_value = 200;
@@ -122,6 +113,10 @@
 
   // Only aconfig flags are allowed in this release config.
   optional bool aconfig_flags_only = 4;
+
+  // Prior stage(s) for flag advancement (during development).
+  // Once a flag has met criteria in a prior stage, it can advance to this one.
+  repeated string prior_stages = 5;
 }
 
 // Any aliases.  These are used for continuous integration builder config.
diff --git a/cmd/release_config/release_config_proto/regen.sh b/cmd/release_config/release_config_proto/regen.sh
index 1846c4d..23e3115 100644
--- a/cmd/release_config/release_config_proto/regen.sh
+++ b/cmd/release_config/release_config_proto/regen.sh
@@ -1,3 +1,3 @@
 #!/bin/bash
 
-aprotoc --go_out=paths=source_relative:. build_flags_src.proto build_flags_out.proto
+aprotoc --go_out=paths=source_relative:. build_flags_src.proto build_flags_out.proto build_flags_common.proto build_flags_declarations.proto
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 6ab3b88..fd3b27f 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -59,6 +59,7 @@
 	ctx.RegisterModuleType("prebuilt_usr_keychars", PrebuiltUserKeyCharsFactory)
 	ctx.RegisterModuleType("prebuilt_usr_idc", PrebuiltUserIdcFactory)
 	ctx.RegisterModuleType("prebuilt_font", PrebuiltFontFactory)
+	ctx.RegisterModuleType("prebuilt_overlay", PrebuiltOverlayFactory)
 	ctx.RegisterModuleType("prebuilt_firmware", PrebuiltFirmwareFactory)
 	ctx.RegisterModuleType("prebuilt_dsp", PrebuiltDSPFactory)
 	ctx.RegisterModuleType("prebuilt_rfsa", PrebuiltRFSAFactory)
@@ -73,11 +74,11 @@
 type prebuiltEtcProperties struct {
 	// Source file of this prebuilt. Can reference a genrule type module with the ":module" syntax.
 	// Mutually exclusive with srcs.
-	Src *string `android:"path,arch_variant"`
+	Src proptools.Configurable[string] `android:"path,arch_variant,replace_instead_of_append"`
 
 	// Source files of this prebuilt. Can reference a genrule type module with the ":module" syntax.
 	// Mutually exclusive with src. When used, filename_from_src is set to true.
-	Srcs []string `android:"path,arch_variant"`
+	Srcs proptools.Configurable[[]string] `android:"path,arch_variant"`
 
 	// Optional name for the installed file. If unspecified, name of the module is used as the file
 	// name. Only available when using a single source (src).
@@ -157,6 +158,8 @@
 	installDirPath         android.InstallPath
 	additionalDependencies *android.Paths
 
+	usedSrcsProperty bool
+
 	makeClass string
 }
 
@@ -246,10 +249,10 @@
 }
 
 func (p *PrebuiltEtc) SourceFilePath(ctx android.ModuleContext) android.Path {
-	if len(p.properties.Srcs) > 0 {
+	if len(p.properties.Srcs.GetOrDefault(ctx, nil)) > 0 {
 		panic(fmt.Errorf("SourceFilePath not available on multi-source prebuilt %q", p.Name()))
 	}
-	return android.PathForModuleSrc(ctx, proptools.String(p.properties.Src))
+	return android.PathForModuleSrc(ctx, p.properties.Src.GetOrDefault(ctx, ""))
 }
 
 func (p *PrebuiltEtc) InstallDirPath() android.InstallPath {
@@ -263,7 +266,7 @@
 }
 
 func (p *PrebuiltEtc) OutputFile() android.OutputPath {
-	if len(p.properties.Srcs) > 0 {
+	if p.usedSrcsProperty {
 		panic(fmt.Errorf("OutputFile not available on multi-source prebuilt %q", p.Name()))
 	}
 	return p.outputFilePaths[0]
@@ -318,7 +321,9 @@
 func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	var installs []installProperties
 
-	if p.properties.Src != nil && len(p.properties.Srcs) > 0 {
+	srcProperty := p.properties.Src.Get(ctx)
+	srcsProperty := p.properties.Srcs.GetOrDefault(ctx, nil)
+	if srcProperty.IsPresent() && len(srcsProperty) > 0 {
 		ctx.PropertyErrorf("src", "src is set. Cannot set srcs")
 	}
 
@@ -330,8 +335,8 @@
 
 	filename := proptools.String(p.properties.Filename)
 	filenameFromSrc := proptools.Bool(p.properties.Filename_from_src)
-	if p.properties.Src != nil {
-		p.sourceFilePaths = android.PathsForModuleSrc(ctx, []string{proptools.String(p.properties.Src)})
+	if srcProperty.IsPresent() {
+		p.sourceFilePaths = android.PathsForModuleSrc(ctx, []string{srcProperty.Get()})
 		// If the source was not found, set a fake source path to
 		// support AllowMissingDependencies executions.
 		if len(p.sourceFilePaths) == 0 {
@@ -366,7 +371,8 @@
 			symlinks:       p.properties.Symlinks,
 		}
 		installs = append(installs, ip)
-	} else if len(p.properties.Srcs) > 0 {
+	} else if len(srcsProperty) > 0 {
+		p.usedSrcsProperty = true
 		if filename != "" {
 			ctx.PropertyErrorf("filename", "filename cannot be set when using srcs")
 		}
@@ -376,7 +382,7 @@
 		if p.properties.Filename_from_src != nil {
 			ctx.PropertyErrorf("filename_from_src", "filename_from_src is implicitly set to true when using srcs")
 		}
-		p.sourceFilePaths = android.PathsForModuleSrc(ctx, p.properties.Srcs)
+		p.sourceFilePaths = android.PathsForModuleSrc(ctx, srcsProperty)
 		for _, src := range p.sourceFilePaths {
 			filename := src.Base()
 			output := android.PathForModuleOut(ctx, filename).OutputPath
@@ -650,6 +656,15 @@
 	return module
 }
 
+// prebuilt_overlay is for a prebuilt artifact in <partition>/overlay directory.
+func PrebuiltOverlayFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "overlay")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	return module
+}
+
 // prebuilt_firmware installs a firmware file to <partition>/etc/firmware directory for system
 // image.
 // If soc_specific property is set to true, the firmware file is installed to the
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index 3ee2340..c44574a 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -342,6 +342,19 @@
 	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
 }
 
+func TestPrebuiltOverlayInstallDirPath(t *testing.T) {
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
+		prebuilt_overlay {
+			name: "foo.conf",
+			src: "foo.conf",
+		}
+	`)
+
+	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
+	expected := "out/soong/target/product/test_device/system/overlay"
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+}
+
 func TestPrebuiltFirmwareDirPath(t *testing.T) {
 	targetPath := "out/soong/target/product/test_device"
 	tests := []struct {
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index d8a00e2..d2572c2 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -106,7 +106,7 @@
 	Base_dir *string
 
 	// Directories to be created under root. e.g. /dev, /proc, etc.
-	Dirs []string
+	Dirs proptools.Configurable[[]string]
 
 	// Symbolic links to be created under root with "ln -sf <target> <name>".
 	Symlinks []symlinkDefinition
@@ -152,6 +152,7 @@
 func initFilesystemModule(module *filesystem) {
 	module.AddProperties(&module.properties)
 	android.InitPackageModule(module)
+	module.PackagingBase.DepsCollectFirstTargetOnly = true
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
 }
@@ -242,7 +243,7 @@
 // already in `rootDir`.
 func (f *filesystem) buildNonDepsFiles(ctx android.ModuleContext, builder *android.RuleBuilder, rootDir android.OutputPath) {
 	// create dirs and symlinks
-	for _, dir := range f.properties.Dirs {
+	for _, dir := range f.properties.Dirs.GetOrDefault(ctx, nil) {
 		// OutputPath.Join verifies dir
 		builder.Command().Text("mkdir -p").Text(rootDir.Join(ctx, dir).String())
 	}
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index 015d39a..2dc8c21 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -508,11 +508,7 @@
 		android_filesystem {
 			name: "fs_64_32",
 			compile_multilib: "both",
-			multilib: {
-				first: {
-					deps: ["foo"],
-				},
-			},
+			deps: ["foo"],
 		}
 
 		cc_binary {
diff --git a/go.mod b/go.mod
index 1174958..13834fc 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module android/soong
 
-go 1.21
+go 1.22
 
 require (
 	github.com/google/blueprint v0.0.0
diff --git a/go.work b/go.work
index 7c6022b..9a7e6db 100644
--- a/go.work
+++ b/go.work
@@ -1,4 +1,4 @@
-go 1.21
+go 1.22
 
 use (
 	.
diff --git a/java/androidmk.go b/java/androidmk.go
index 4f740b2..a1bc904 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -17,7 +17,6 @@
 import (
 	"fmt"
 	"io"
-	"strings"
 
 	"android/soong/android"
 
@@ -414,22 +413,11 @@
 					jniSymbols := app.JNISymbolsInstalls(app.installPathForJNISymbols.String())
 					entries.SetString("LOCAL_SOONG_JNI_LIBS_SYMBOLS", jniSymbols.String())
 				} else {
+					var names []string
 					for _, jniLib := range app.jniLibs {
-						entries.AddStrings("LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), jniLib.name)
-						var partitionTag string
-
-						// Mimic the creation of partition_tag in build/make,
-						// which defaults to an empty string when the partition is system.
-						// Otherwise, capitalize with a leading _
-						if jniLib.partition == "system" {
-							partitionTag = ""
-						} else {
-							split := strings.Split(jniLib.partition, "/")
-							partitionTag = "_" + strings.ToUpper(split[len(split)-1])
-						}
-						entries.AddStrings("LOCAL_SOONG_JNI_LIBS_PARTITION_"+jniLib.target.Arch.ArchType.String(),
-							jniLib.name+":"+partitionTag)
+						names = append(names, jniLib.name)
 					}
+					entries.AddStrings("LOCAL_REQUIRED_MODULES", names...)
 				}
 
 				if len(app.jniCoverageOutputs) > 0 {
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index 2978a40..243a279 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -20,8 +20,6 @@
 
 	"android/soong/android"
 	"android/soong/cc"
-
-	"github.com/google/blueprint/proptools"
 )
 
 func TestRequired(t *testing.T) {
@@ -256,148 +254,50 @@
 	}
 }
 
-func TestJniPartition(t *testing.T) {
-	bp := `
-		cc_library {
-			name: "libjni_system",
-			system_shared_libs: [],
-			sdk_version: "current",
-			stl: "none",
-		}
-
-		cc_library {
-			name: "libjni_system_ext",
-			system_shared_libs: [],
-			sdk_version: "current",
-			stl: "none",
-			system_ext_specific: true,
-		}
-
-		cc_library {
-			name: "libjni_odm",
-			system_shared_libs: [],
-			sdk_version: "current",
-			stl: "none",
-			device_specific: true,
-		}
-
-		cc_library {
-			name: "libjni_product",
-			system_shared_libs: [],
-			sdk_version: "current",
-			stl: "none",
-			product_specific: true,
-		}
-
-		cc_library {
-			name: "libjni_vendor",
-			system_shared_libs: [],
-			sdk_version: "current",
-			stl: "none",
-			soc_specific: true,
-		}
-
-		android_app {
-			name: "test_app_system_jni_system",
-			privileged: true,
-			platform_apis: true,
-			certificate: "platform",
-			jni_libs: ["libjni_system"],
-		}
-
-		android_app {
-			name: "test_app_system_jni_system_ext",
-			privileged: true,
-			platform_apis: true,
-			certificate: "platform",
-			jni_libs: ["libjni_system_ext"],
-		}
-
-		android_app {
-			name: "test_app_system_ext_jni_system",
-			privileged: true,
-			platform_apis: true,
-			certificate: "platform",
-			jni_libs: ["libjni_system"],
-			system_ext_specific: true
-		}
-
-		android_app {
-			name: "test_app_system_ext_jni_system_ext",
-			sdk_version: "core_platform",
-			jni_libs: ["libjni_system_ext"],
-			system_ext_specific: true
-		}
-
-		android_app {
-			name: "test_app_product_jni_product",
-			sdk_version: "core_platform",
-			jni_libs: ["libjni_product"],
-			product_specific: true
-		}
-
-		android_app {
-			name: "test_app_vendor_jni_odm",
-			sdk_version: "core_platform",
-			jni_libs: ["libjni_odm"],
-			soc_specific: true
-		}
-
-		android_app {
-			name: "test_app_odm_jni_vendor",
-			sdk_version: "core_platform",
-			jni_libs: ["libjni_vendor"],
-			device_specific: true
-		}
-		android_app {
-			name: "test_app_system_jni_multiple",
-			privileged: true,
-			platform_apis: true,
-			certificate: "platform",
-			jni_libs: ["libjni_system", "libjni_system_ext"],
-		}
-		android_app {
-			name: "test_app_vendor_jni_multiple",
-			sdk_version: "core_platform",
-			jni_libs: ["libjni_odm", "libjni_vendor"],
-			soc_specific: true
-		}
-		`
-	arch := "arm64"
+func TestJniAsRequiredDeps(t *testing.T) {
 	ctx := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		cc.PrepareForTestWithCcDefaultModules,
 		android.PrepareForTestWithAndroidMk,
-		android.FixtureModifyConfig(func(config android.Config) {
-			config.TestProductVariables.DeviceArch = proptools.StringPtr(arch)
-		}),
-	).
-		RunTestWithBp(t, bp)
-	testCases := []struct {
-		name           string
-		partitionNames []string
-		partitionTags  []string
+	).RunTestWithBp(t, `
+		android_app {
+			name: "app",
+			jni_libs: ["libjni"],
+			platform_apis: true,
+		}
+
+		android_app {
+			name: "app_embedded",
+			jni_libs: ["libjni"],
+			platform_apis: true,
+			use_embedded_native_libs: true,
+		}
+
+		cc_library {
+			name: "libjni",
+			system_shared_libs: [],
+			stl: "none",
+		}
+		`)
+
+	testcases := []struct {
+		name     string
+		expected []string
 	}{
-		{"test_app_system_jni_system", []string{"libjni_system"}, []string{""}},
-		{"test_app_system_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}},
-		{"test_app_system_ext_jni_system", []string{"libjni_system"}, []string{""}},
-		{"test_app_system_ext_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}},
-		{"test_app_product_jni_product", []string{"libjni_product"}, []string{"_PRODUCT"}},
-		{"test_app_vendor_jni_odm", []string{"libjni_odm"}, []string{"_ODM"}},
-		{"test_app_odm_jni_vendor", []string{"libjni_vendor"}, []string{"_VENDOR"}},
-		{"test_app_system_jni_multiple", []string{"libjni_system", "libjni_system_ext"}, []string{"", "_SYSTEM_EXT"}},
-		{"test_app_vendor_jni_multiple", []string{"libjni_odm", "libjni_vendor"}, []string{"_ODM", "_VENDOR"}},
+		{
+			name:     "app",
+			expected: []string{"libjni"},
+		},
+		{
+			name:     "app_embedded",
+			expected: nil,
+		},
 	}
 
-	for _, test := range testCases {
-		t.Run(test.name, func(t *testing.T) {
-			mod := ctx.ModuleForTests(test.name, "android_common").Module()
-			entry := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)[0]
-			for i := range test.partitionNames {
-				actual := entry.EntryMap["LOCAL_SOONG_JNI_LIBS_PARTITION_"+arch][i]
-				expected := test.partitionNames[i] + ":" + test.partitionTags[i]
-				android.AssertStringEquals(t, "Expected and actual differ", expected, actual)
-			}
-		})
+	for _, tc := range testcases {
+		mod := ctx.ModuleForTests(tc.name, "android_common").Module()
+		entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)[0]
+		required := entries.EntryMap["LOCAL_REQUIRED_MODULES"]
+		android.AssertDeepEquals(t, "unexpected required deps", tc.expected, required)
 	}
 }
diff --git a/java/app.go b/java/app.go
index f05b8a7..a24099c 100644
--- a/java/app.go
+++ b/java/app.go
@@ -274,16 +274,37 @@
 		variation := append(jniTarget.Variations(),
 			blueprint.Variation{Mutator: "link", Variation: "shared"})
 
-		// If the app builds against an Android SDK use the SDK variant of JNI dependencies
-		// unless jni_uses_platform_apis is set.
-		// Don't require the SDK variant for apps that are shipped on vendor, etc., as they already
-		// have stable APIs through the VNDK.
-		if (usesSDK && !a.RequiresStableAPIs(ctx) &&
-			!Bool(a.appProperties.Jni_uses_platform_apis)) ||
-			Bool(a.appProperties.Jni_uses_sdk_apis) {
+		// Test whether to use the SDK variant or the non-SDK variant of JNI dependencies.
+		// Many factors are considered here.
+		// 1. Basically, the selection follows whether the app has sdk_version set or not.
+		jniUsesSdkVariant := usesSDK
+		// 2. However, jni_uses_platform_apis and jni_uses_sdk_apis can override it
+		if Bool(a.appProperties.Jni_uses_sdk_apis) {
+			jniUsesSdkVariant = true
+		}
+		if Bool(a.appProperties.Jni_uses_platform_apis) {
+			jniUsesSdkVariant = false
+		}
+		// 3. Then the use of SDK variant is again prohibited for the following cases:
+		// 3.1. the app is shipped on unbundled partitions like vendor. Since the entire
+		// partition (not only the app) is considered unbudled, there's no need to use the
+		// SDK variant.
+		// 3.2. the app doesn't support embedding the JNI libs
+		if a.RequiresStableAPIs(ctx) || !a.shouldEmbedJnis(ctx) {
+			jniUsesSdkVariant = false
+		}
+		if jniUsesSdkVariant {
 			variation = append(variation, blueprint.Variation{Mutator: "sdk", Variation: "sdk"})
 		}
-		ctx.AddFarVariationDependencies(variation, jniLibTag, a.appProperties.Jni_libs...)
+
+		// Use the installable dep tag when the JNIs are not embedded
+		var tag dependencyTag
+		if a.shouldEmbedJnis(ctx) {
+			tag = jniLibTag
+		} else {
+			tag = jniInstallTag
+		}
+		ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs...)
 	}
 	for _, aconfig_declaration := range a.aaptProperties.Flags_packages {
 		ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfig_declaration)
@@ -334,27 +355,17 @@
 
 func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.checkAppSdkVersions(ctx)
+	a.checkEmbedJnis(ctx)
 	a.generateAndroidBuildActions(ctx)
 	a.generateJavaUsedByApex(ctx)
 }
 
-func (a *AndroidApp) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
-	defaultMinSdkVersion := a.Module.MinSdkVersion(ctx)
-	if proptools.Bool(a.appProperties.Updatable) {
-		overrideApiLevel := android.MinSdkVersionFromValue(ctx, ctx.DeviceConfig().ApexGlobalMinSdkVersionOverride())
-		if !overrideApiLevel.IsNone() && overrideApiLevel.CompareTo(defaultMinSdkVersion) > 0 {
-			return overrideApiLevel
-		}
-	}
-	return defaultMinSdkVersion
-}
-
 func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) {
 	if a.Updatable() {
 		if !a.SdkVersion(ctx).Stable() {
 			ctx.PropertyErrorf("sdk_version", "Updatable apps must use stable SDKs, found %v", a.SdkVersion(ctx))
 		}
-		if String(a.deviceProperties.Min_sdk_version) == "" {
+		if String(a.overridableProperties.Min_sdk_version) == "" {
 			ctx.PropertyErrorf("updatable", "updatable apps must set min_sdk_version.")
 		}
 
@@ -378,6 +389,17 @@
 	a.checkSdkVersions(ctx)
 }
 
+// Ensures that use_embedded_native_libs are set for apk-in-apex
+func (a *AndroidApp) checkEmbedJnis(ctx android.BaseModuleContext) {
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+	apkInApex := !apexInfo.IsForPlatform()
+	hasJnis := len(a.appProperties.Jni_libs) > 0
+
+	if apkInApex && hasJnis && !Bool(a.appProperties.Use_embedded_native_libs) {
+		ctx.ModuleErrorf("APK in APEX should have use_embedded_native_libs: true")
+	}
+}
+
 // If an updatable APK sets min_sdk_version, min_sdk_vesion of JNI libs should match with it.
 // This check is enforced for "updatable" APKs (including APK-in-APEX).
 func (a *AndroidApp) checkJniLibsSdkVersion(ctx android.ModuleContext, minSdkVersion android.ApiLevel) {
@@ -433,9 +455,9 @@
 }
 
 func (a *AndroidApp) shouldEmbedJnis(ctx android.BaseModuleContext) bool {
-	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	return ctx.Config().UnbundledBuild() || Bool(a.appProperties.Use_embedded_native_libs) ||
-		!apexInfo.IsForPlatform() || a.appProperties.AlwaysPackageNativeLibs
+		Bool(a.appProperties.Updatable) ||
+		a.appProperties.AlwaysPackageNativeLibs
 }
 
 func generateAaptRenamePackageFlags(packageName string, renameResourcesPackage bool) []string {
@@ -829,7 +851,9 @@
 
 	dexJarFile, packageResources := a.dexBuildActions(ctx)
 
-	jniLibs, prebuiltJniPackages, certificates := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), !Bool(a.appProperties.Jni_uses_platform_apis))
+	// No need to check the SDK version of the JNI deps unless we embed them
+	checkNativeSdkVersion := a.shouldEmbedJnis(ctx) && !Bool(a.appProperties.Jni_uses_platform_apis)
+	jniLibs, prebuiltJniPackages, certificates := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), checkNativeSdkVersion)
 	jniJarFile := a.jniBuildActions(jniLibs, prebuiltJniPackages, ctx)
 
 	if ctx.Failed() {
@@ -911,6 +935,22 @@
 			installed := ctx.InstallFile(a.installDir, extra.Base(), extra)
 			extraInstalledPaths = append(extraInstalledPaths, installed)
 		}
+		// If we don't embed jni libs, make sure that those are installed along with the
+		// app, and also place symlinks to the installed paths under the lib/<arch>
+		// directory of the app installation directory. ex:
+		// /system/app/MyApp/lib/arm64/libfoo.so -> /system/lib64/libfoo.so
+		if !a.embeddedJniLibs {
+			for _, jniLib := range jniLibs {
+				archStr := jniLib.target.Arch.ArchType.String()
+				symlinkDir := a.installDir.Join(ctx, "lib", archStr)
+				for _, installedLib := range jniLib.installPaths {
+					// install the symlink itself
+					symlinkName := installedLib.Base()
+					symlinkTarget := android.InstallPathToOnDevicePath(ctx, installedLib)
+					ctx.InstallAbsoluteSymlink(symlinkDir, symlinkName, symlinkTarget)
+				}
+			}
+		}
 		ctx.InstallFile(a.installDir, a.outputFile.Base(), a.outputFile, extraInstalledPaths...)
 	}
 
@@ -998,6 +1038,7 @@
 						coverageFile:   dep.CoverageOutputFile(),
 						unstrippedFile: dep.UnstrippedOutputFile(),
 						partition:      dep.Partition(),
+						installPaths:   dep.FilesToInstall(),
 					})
 				} else if ctx.Config().AllowMissingDependencies() {
 					ctx.AddMissingDependencies([]string{otherName})
@@ -1243,6 +1284,11 @@
 			Manifest:       proptools.StringPtr(":" + rroManifestName),
 			Resource_dirs:  a.aaptProperties.Resource_dirs,
 		}
+		if !Bool(a.aaptProperties.Aapt_include_all_resources) {
+			for _, aaptConfig := range ctx.Config().ProductAAPTConfig() {
+				rroProperties.Aaptflags = append(rroProperties.Aaptflags, "-c", aaptConfig)
+			}
+		}
 		ctx.CreateModule(RuntimeResourceOverlayFactory, &rroProperties)
 	})
 
@@ -1334,6 +1380,8 @@
 		HostRequiredModuleNames: a.HostRequiredModuleNames(),
 		TestSuites:              a.testProperties.Test_suites,
 		IsHost:                  false,
+		LocalCertificate:        a.certificate.AndroidMkString(),
+		IsUnitTest:              Bool(a.testProperties.Test_options.Unit_test),
 	})
 	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
 		TestOnly:       true,
diff --git a/java/app_test.go b/java/app_test.go
index a7c48a1..8049494 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -4322,52 +4322,6 @@
 	)
 }
 
-func TestApexGlobalMinSdkVersionOverride(t *testing.T) {
-	result := android.GroupFixturePreparers(
-		PrepareForTestWithJavaDefaultModules,
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.ApexGlobalMinSdkVersionOverride = proptools.StringPtr("Tiramisu")
-		}),
-	).RunTestWithBp(t, `
-		android_app {
-			name: "com.android.bar",
-			srcs: ["a.java"],
-			sdk_version: "current",
-		}
-		android_app {
-			name: "com.android.foo",
-			srcs: ["a.java"],
-			sdk_version: "current",
-			min_sdk_version: "S",
-			updatable: true,
-		}
-		override_android_app {
-			name: "com.android.go.foo",
-			base: "com.android.foo",
-		}
-	`)
-	foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer")
-	fooOverride := result.ModuleForTests("com.android.foo", "android_common_com.android.go.foo").Rule("manifestFixer")
-	bar := result.ModuleForTests("com.android.bar", "android_common").Rule("manifestFixer")
-
-	android.AssertStringDoesContain(t,
-		"expected manifest fixer to set com.android.bar minSdkVersion to S",
-		bar.BuildParams.Args["args"],
-		"--minSdkVersion  S",
-	)
-	android.AssertStringDoesContain(t,
-		"com.android.foo: expected manifest fixer to set minSdkVersion to T",
-		foo.BuildParams.Args["args"],
-		"--minSdkVersion  T",
-	)
-	android.AssertStringDoesContain(t,
-		"com.android.go.foo: expected manifest fixer to set minSdkVersion to T",
-		fooOverride.BuildParams.Args["args"],
-		"--minSdkVersion  T",
-	)
-
-}
-
 func TestAppFlagsPackages(t *testing.T) {
 	ctx := testApp(t, `
 		android_app {
@@ -4492,3 +4446,36 @@
 		t.Errorf("Module output does not contain expected apk %s", "foo-new.apk")
 	}
 }
+
+func TestAppMinSdkVersionOverride(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+	).RunTestWithBp(t, `
+		android_app {
+			name: "com.android.foo",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			min_sdk_version: "31",
+			updatable: true,
+		}
+		override_android_app {
+			name: "com.android.go.foo",
+			base: "com.android.foo",
+			min_sdk_version: "33",
+		}
+	`)
+	foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer")
+	fooOverride := result.ModuleForTests("com.android.foo", "android_common_com.android.go.foo").Rule("manifestFixer")
+
+	android.AssertStringDoesContain(t,
+		"com.android.foo: expected manifest fixer to set minSdkVersion to T",
+		foo.BuildParams.Args["args"],
+		"--minSdkVersion  31",
+	)
+	android.AssertStringDoesContain(t,
+		"com.android.go.foo: expected manifest fixer to set minSdkVersion to T",
+		fooOverride.BuildParams.Args["args"],
+		"--minSdkVersion  33",
+	)
+
+}
diff --git a/java/base.go b/java/base.go
index 0c28671..b4f800b 100644
--- a/java/base.go
+++ b/java/base.go
@@ -229,10 +229,6 @@
 	// If the SDK kind is empty, it will be set to public.
 	Sdk_version *string
 
-	// if not blank, set the minimum version of the sdk that the compiled artifacts will run against.
-	// Defaults to sdk_version if not set. See sdk_version for possible values.
-	Min_sdk_version *string
-
 	// if not blank, set the maximum version of the sdk that the compiled artifacts will run against.
 	// Defaults to empty string "". See sdk_version for possible values.
 	Max_sdk_version *string
@@ -312,6 +308,10 @@
 	// Otherwise, both the overridden and the overriding modules will have the same output name, which
 	// can cause the duplicate output error.
 	Stem *string
+
+	// if not blank, set the minimum version of the sdk that the compiled artifacts will run against.
+	// Defaults to sdk_version if not set. See sdk_version for possible values.
+	Min_sdk_version *string
 }
 
 // Functionality common to Module and Import
@@ -715,6 +715,7 @@
 	// doesn't make sense) or framework libraries (e.g. libraries found in the InstrumentFrameworkModules list) unless EMMA_INSTRUMENT_FRAMEWORK is true.
 	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	isJacocoAgent := ctx.ModuleName() == "jacocoagent"
+
 	if j.DirectlyInAnyApex() && !isJacocoAgent && !apexInfo.IsForPlatform() {
 		if !inList(ctx.ModuleName(), config.InstrumentFrameworkModules) {
 			return true
@@ -738,8 +739,8 @@
 }
 
 func (j *Module) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
-	if j.deviceProperties.Min_sdk_version != nil {
-		return android.ApiLevelFrom(ctx, *j.deviceProperties.Min_sdk_version)
+	if j.overridableProperties.Min_sdk_version != nil {
+		return android.ApiLevelFrom(ctx, *j.overridableProperties.Min_sdk_version)
 	}
 	return j.SdkVersion(ctx).ApiLevel
 }
@@ -1649,11 +1650,28 @@
 				classesJar:    implementationAndResourcesJar,
 				jarName:       jarName,
 			}
-			dexOutputFile = j.dexer.compileDex(ctx, params)
+			if j.GetProfileGuided() && j.optimizeOrObfuscateEnabled() && !j.EnableProfileRewriting() {
+				ctx.PropertyErrorf("enable_profile_rewriting",
+					"Enable_profile_rewriting must be true when profile_guided dexpreopt and R8 optimization/obfuscation is turned on. The attached profile should be sourced from an unoptimized/unobfuscated APK.",
+				)
+			}
+			if j.EnableProfileRewriting() {
+				profile := j.GetProfile()
+				if profile == "" || !j.GetProfileGuided() {
+					ctx.PropertyErrorf("enable_profile_rewriting", "Profile and Profile_guided must be set when enable_profile_rewriting is true")
+				}
+				params.artProfileInput = &profile
+			}
+			dexOutputFile, dexArtProfileOutput := j.dexer.compileDex(ctx, params)
 			if ctx.Failed() {
 				return
 			}
 
+			// If r8/d8 provides a profile that matches the optimized dex, use that for dexpreopt.
+			if dexArtProfileOutput != nil {
+				j.dexpreopter.SetRewrittenProfile(*dexArtProfileOutput)
+			}
+
 			// merge dex jar with resources if necessary
 			if j.resourceJar != nil {
 				jars := android.Paths{dexOutputFile, j.resourceJar}
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 82a34ca..4d3d794 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -590,13 +590,36 @@
 		// So ignore it even if it is not in PRODUCT_APEX_BOOT_JARS.
 		// TODO(b/202896428): Add better way to handle this.
 		_, unknown = android.RemoveFromList("android.car-module", unknown)
-		if isActiveModule(ctx, ctx.Module()) && len(unknown) > 0 {
-			ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_BOOT_JARS", unknown)
+		if isApexVariant(ctx) && len(unknown) > 0 {
+			if android.IsModulePrebuilt(ctx.Module()) {
+				// prebuilt bcpf. the validation of this will be done at the top-level apex
+				providerClasspathFragmentValidationInfoProvider(ctx, unknown)
+			} else if !disableSourceApexVariant(ctx) {
+				// source bcpf, and prebuilt apex are not selected.
+				ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_BOOT_JARS", unknown)
+			}
 		}
 	}
 	return jars
 }
 
+var ClasspathFragmentValidationInfoProvider = blueprint.NewProvider[ClasspathFragmentValidationInfo]()
+
+type ClasspathFragmentValidationInfo struct {
+	ClasspathFragmentModuleName string
+	UnknownJars                 []string
+}
+
+// Set a provider with the list of jars that have not been added to PRODUCT_APEX_BOOT_JARS
+// The validation will be done in the ctx of the top-level _selected_ apex
+func providerClasspathFragmentValidationInfoProvider(ctx android.ModuleContext, unknown []string) {
+	info := ClasspathFragmentValidationInfo{
+		ClasspathFragmentModuleName: ctx.ModuleName(),
+		UnknownJars:                 unknown,
+	}
+	android.SetProvider(ctx, ClasspathFragmentValidationInfoProvider, info)
+}
+
 // generateHiddenAPIBuildActions generates all the hidden API related build rules.
 func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) *HiddenAPIOutput {
 
diff --git a/java/dex.go b/java/dex.go
index 8cfffaf..32546d9 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -119,6 +119,10 @@
 	return d.resourceShrinkingEnabled(ctx) && Bool(d.Optimize.Optimized_shrink_resources)
 }
 
+func (d *dexer) optimizeOrObfuscateEnabled() bool {
+	return d.effectiveOptimizeEnabled() && (proptools.Bool(d.dexProperties.Optimize.Optimize) || proptools.Bool(d.dexProperties.Optimize.Obfuscate))
+}
+
 var d8, d8RE = pctx.MultiCommandRemoteStaticRules("d8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
@@ -253,17 +257,25 @@
 	return flags, deps
 }
 
-func d8Flags(flags javaBuilderFlags) (d8Flags []string, d8Deps android.Paths) {
+func (d *dexer) d8Flags(ctx android.ModuleContext, dexParams *compileDexParams) (d8Flags []string, d8Deps android.Paths, artProfileOutput *android.OutputPath) {
+	flags := dexParams.flags
 	d8Flags = append(d8Flags, flags.bootClasspath.FormRepeatedClassPath("--lib ")...)
 	d8Flags = append(d8Flags, flags.dexClasspath.FormRepeatedClassPath("--lib ")...)
 
 	d8Deps = append(d8Deps, flags.bootClasspath...)
 	d8Deps = append(d8Deps, flags.dexClasspath...)
 
-	return d8Flags, d8Deps
+	if flags, deps, profileOutput := d.addArtProfile(ctx, dexParams); profileOutput != nil {
+		d8Flags = append(d8Flags, flags...)
+		d8Deps = append(d8Deps, deps...)
+		artProfileOutput = profileOutput
+	}
+
+	return d8Flags, d8Deps, artProfileOutput
 }
 
-func (d *dexer) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Flags []string, r8Deps android.Paths) {
+func (d *dexer) r8Flags(ctx android.ModuleContext, dexParams *compileDexParams) (r8Flags []string, r8Deps android.Paths, artProfileOutput *android.OutputPath) {
+	flags := dexParams.flags
 	opt := d.dexProperties.Optimize
 
 	// When an app contains references to APIs that are not in the SDK specified by
@@ -375,18 +387,44 @@
 		}
 	}
 
-	return r8Flags, r8Deps
+	if flags, deps, profileOutput := d.addArtProfile(ctx, dexParams); profileOutput != nil {
+		r8Flags = append(r8Flags, flags...)
+		r8Deps = append(r8Deps, deps...)
+		artProfileOutput = profileOutput
+	}
+
+	return r8Flags, r8Deps, artProfileOutput
 }
 
 type compileDexParams struct {
-	flags         javaBuilderFlags
-	sdkVersion    android.SdkSpec
-	minSdkVersion android.ApiLevel
-	classesJar    android.Path
-	jarName       string
+	flags           javaBuilderFlags
+	sdkVersion      android.SdkSpec
+	minSdkVersion   android.ApiLevel
+	classesJar      android.Path
+	jarName         string
+	artProfileInput *string
 }
 
-func (d *dexer) compileDex(ctx android.ModuleContext, dexParams *compileDexParams) android.OutputPath {
+// Adds --art-profile to r8/d8 command.
+// r8/d8 will output a generated profile file to match the optimized dex code.
+func (d *dexer) addArtProfile(ctx android.ModuleContext, dexParams *compileDexParams) (flags []string, deps android.Paths, artProfileOutputPath *android.OutputPath) {
+	if dexParams.artProfileInput != nil {
+		artProfileInputPath := android.PathForModuleSrc(ctx, *dexParams.artProfileInput)
+		artProfileOutputPathValue := android.PathForModuleOut(ctx, "profile.prof.txt").OutputPath
+		artProfileOutputPath = &artProfileOutputPathValue
+		flags = []string{
+			"--art-profile",
+			artProfileInputPath.String(),
+			artProfileOutputPath.String(),
+		}
+		deps = append(deps, artProfileInputPath)
+	}
+	return flags, deps, artProfileOutputPath
+
+}
+
+// Return the compiled dex jar and (optional) profile _after_ r8 optimization
+func (d *dexer) compileDex(ctx android.ModuleContext, dexParams *compileDexParams) (android.OutputPath, *android.OutputPath) {
 
 	// Compile classes.jar into classes.dex and then javalib.jar
 	javalibJar := android.PathForModuleOut(ctx, "dex", dexParams.jarName).OutputPath
@@ -406,6 +444,7 @@
 	}
 
 	useR8 := d.effectiveOptimizeEnabled()
+	var artProfileOutputPath *android.OutputPath
 	if useR8 {
 		proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
 		d.proguardDictionary = android.OptionalPathForPath(proguardDictionary)
@@ -418,8 +457,19 @@
 		d.proguardUsageZip = android.OptionalPathForPath(proguardUsageZip)
 		resourcesOutput := android.PathForModuleOut(ctx, "package-res-shrunken.apk")
 		d.resourcesOutput = android.OptionalPathForPath(resourcesOutput)
-		r8Flags, r8Deps := d.r8Flags(ctx, dexParams.flags)
-		r8Deps = append(r8Deps, commonDeps...)
+		implicitOutputs := android.WritablePaths{
+			proguardDictionary,
+			proguardUsageZip,
+			proguardConfiguration,
+		}
+		r8Flags, r8Deps, r8ArtProfileOutputPath := d.r8Flags(ctx, dexParams)
+		if r8ArtProfileOutputPath != nil {
+			artProfileOutputPath = r8ArtProfileOutputPath
+			implicitOutputs = append(
+				implicitOutputs,
+				artProfileOutputPath,
+			)
+		}
 		rule := r8
 		args := map[string]string{
 			"r8Flags":        strings.Join(append(commonFlags, r8Flags...), " "),
@@ -436,10 +486,6 @@
 			rule = r8RE
 			args["implicits"] = strings.Join(r8Deps.Strings(), ",")
 		}
-		implicitOutputs := android.WritablePaths{
-			proguardDictionary,
-			proguardUsageZip,
-			proguardConfiguration}
 		if d.resourcesInput.Valid() {
 			implicitOutputs = append(implicitOutputs, resourcesOutput)
 			args["resourcesOutput"] = resourcesOutput.String()
@@ -454,18 +500,27 @@
 			Args:            args,
 		})
 	} else {
-		d8Flags, d8Deps := d8Flags(dexParams.flags)
+		implicitOutputs := android.WritablePaths{}
+		d8Flags, d8Deps, d8ArtProfileOutputPath := d.d8Flags(ctx, dexParams)
+		if d8ArtProfileOutputPath != nil {
+			artProfileOutputPath = d8ArtProfileOutputPath
+			implicitOutputs = append(
+				implicitOutputs,
+				artProfileOutputPath,
+			)
+		}
 		d8Deps = append(d8Deps, commonDeps...)
 		rule := d8
 		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_D8") {
 			rule = d8RE
 		}
 		ctx.Build(pctx, android.BuildParams{
-			Rule:        rule,
-			Description: "d8",
-			Output:      javalibJar,
-			Input:       dexParams.classesJar,
-			Implicits:   d8Deps,
+			Rule:            rule,
+			Description:     "d8",
+			Output:          javalibJar,
+			Input:           dexParams.classesJar,
+			ImplicitOutputs: implicitOutputs,
+			Implicits:       d8Deps,
 			Args: map[string]string{
 				"d8Flags":        strings.Join(append(commonFlags, d8Flags...), " "),
 				"zipFlags":       zipFlags,
@@ -480,5 +535,5 @@
 		javalibJar = alignedJavalibJar
 	}
 
-	return javalibJar
+	return javalibJar, artProfileOutputPath
 }
diff --git a/java/dex_test.go b/java/dex_test.go
index 1ecdae0..4862d06 100644
--- a/java/dex_test.go
+++ b/java/dex_test.go
@@ -662,3 +662,54 @@
 	android.AssertStringDoesContain(t, "expected aarimports's proguard flags",
 		appR8.Args["r8Flags"], "proguard.txt")
 }
+
+func TestR8FlagsArtProfile(t *testing.T) {
+	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
+		android_app {
+			name: "app",
+			srcs: ["foo.java"],
+			platform_apis: true,
+			dex_preopt: {
+				profile_guided: true,
+				profile: "profile.txt.prof",
+				enable_profile_rewriting: true,
+			},
+		}
+	`)
+
+	app := result.ModuleForTests("app", "android_common")
+	appR8 := app.Rule("r8")
+	android.AssertStringDoesContain(t, "expected --art-profile in app r8 flags",
+		appR8.Args["r8Flags"], "--art-profile")
+
+	appDexpreopt := app.Rule("dexpreopt")
+	android.AssertStringDoesContain(t,
+		"expected --art-profile output to be used to create .prof binary",
+		appDexpreopt.RuleParams.Command,
+		"--create-profile-from=out/soong/.intermediates/app/android_common/profile.prof.txt --output-profile-type=app",
+	)
+}
+
+// This test checks that users explicitly set `enable_profile_rewriting` to true when the following are true
+// 1. optimize or obfuscate is enabled AND
+// 2. dex_preopt.profile_guided is enabled
+//
+// The rewritten profile should be used since the dex signatures in the checked-in profile will not match the optimized binary.
+func TestEnableProfileRewritingIsRequiredForOptimizedApps(t *testing.T) {
+	testJavaError(t,
+		"Enable_profile_rewriting must be true when profile_guided dexpreopt and R8 optimization/obfuscation is turned on",
+		`
+android_app {
+	name: "app",
+	srcs: ["foo.java"],
+	platform_apis: true,
+	dex_preopt: {
+		profile_guided: true,
+		profile: "profile.txt.prof",
+		// enable_profile_rewriting is not set, this is an error
+	},
+	optimize: {
+		optimize: true,
+	}
+}`)
+}
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 1acac1b..832b850 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -19,6 +19,8 @@
 	"sort"
 	"strings"
 
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 	"android/soong/dexpreopt"
 )
@@ -139,6 +141,10 @@
 	// The path to the profile that dexpreopter accepts. It must be in the binary format. If this is
 	// set, it overrides the profile settings in `dexpreoptProperties`.
 	inputProfilePathOnHost android.Path
+
+	// The path to the profile that matches the dex optimized by r8/d8. It is in text format. If this is
+	// set, it will be converted to a binary profile which will be subsequently used for dexpreopt.
+	rewrittenProfile android.Path
 }
 
 type DexpreoptProperties struct {
@@ -158,6 +164,11 @@
 		// defaults to searching for a file that matches the name of this module in the default
 		// profile location set by PRODUCT_DEX_PREOPT_PROFILE_DIR, or empty if not found.
 		Profile *string `android:"path"`
+
+		// If set to true, r8/d8 will use `profile` as input to generate a new profile that matches
+		// the optimized dex.
+		// The new profile will be subsequently used as the profile to dexpreopt the dex file.
+		Enable_profile_rewriting *bool
 	}
 
 	Dex_preopt_result struct {
@@ -262,6 +273,20 @@
 		if !isApexSystemServerJar {
 			return true
 		}
+		ai, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+		allApexInfos := []android.ApexInfo{}
+		if allApexInfosProvider, ok := android.ModuleProvider(ctx, android.AllApexInfoProvider); ok {
+			allApexInfos = allApexInfosProvider.ApexInfos
+		}
+		if len(allApexInfos) > 0 && !ai.MinSdkVersion.EqualTo(allApexInfos[0].MinSdkVersion) {
+			// Apex system server jars are dexpreopted and installed on to the system image.
+			// Since we can have BigAndroid and Go variants of system server jar providing apexes,
+			// and these two variants can have different min_sdk_versions, hide one of the apex variants
+			// from make to prevent collisions.
+			//
+			// Unlike cc, min_sdk_version does not have an effect on the build actions of java libraries.
+			ctx.Module().MakeUninstallable()
+		}
 	} else {
 		// Don't preopt the platform variant of an APEX system server jar to avoid conflicts.
 		if isApexSystemServerJar {
@@ -407,13 +432,17 @@
 	if d.inputProfilePathOnHost != nil {
 		profileClassListing = android.OptionalPathForPath(d.inputProfilePathOnHost)
 	} else if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) && !forPrebuiltApex(ctx) {
-		// If dex_preopt.profile_guided is not set, default it based on the existence of the
-		// dexprepot.profile option or the profile class listing.
-		if String(d.dexpreoptProperties.Dex_preopt.Profile) != "" {
+		// If enable_profile_rewriting is set, use the rewritten profile instead of the checked-in profile
+		if d.EnableProfileRewriting() {
+			profileClassListing = android.OptionalPathForPath(d.GetRewrittenProfile())
+			profileIsTextListing = true
+		} else if profile := d.GetProfile(); profile != "" {
+			// If dex_preopt.profile_guided is not set, default it based on the existence of the
+			// dexprepot.profile option or the profile class listing.
 			profileClassListing = android.OptionalPathForPath(
-				android.PathForModuleSrc(ctx, String(d.dexpreoptProperties.Dex_preopt.Profile)))
+				android.PathForModuleSrc(ctx, profile))
 			profileBootListing = android.ExistentPathForSource(ctx,
-				ctx.ModuleDir(), String(d.dexpreoptProperties.Dex_preopt.Profile)+"-boot")
+				ctx.ModuleDir(), profile+"-boot")
 			profileIsTextListing = true
 		} else if global.ProfileDir != "" {
 			profileClassListing = android.ExistentPathForSource(ctx,
@@ -502,7 +531,7 @@
 	// Prebuilts are active, do not copy the dexpreopt'd source javalib to out/soong/system_server_dexjars
 	// The javalib from the deapexed prebuilt will be copied to this location.
 	// TODO (b/331665856): Implement a principled solution for this.
-	copyApexSystemServerJarDex := !disableSourceApexVariant(ctx)
+	copyApexSystemServerJarDex := !disableSourceApexVariant(ctx) && !ctx.Module().IsHideFromMake()
 	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(
 		ctx, globalSoong, global, dexpreoptConfig, appProductPackages, copyApexSystemServerJarDex)
 	if err != nil {
@@ -574,3 +603,23 @@
 func (d *dexpreopter) disableDexpreopt() {
 	d.shouldDisableDexpreopt = true
 }
+
+func (d *dexpreopter) EnableProfileRewriting() bool {
+	return proptools.Bool(d.dexpreoptProperties.Dex_preopt.Enable_profile_rewriting)
+}
+
+func (d *dexpreopter) GetProfile() string {
+	return proptools.String(d.dexpreoptProperties.Dex_preopt.Profile)
+}
+
+func (d *dexpreopter) GetProfileGuided() bool {
+	return proptools.Bool(d.dexpreoptProperties.Dex_preopt.Profile_guided)
+}
+
+func (d *dexpreopter) GetRewrittenProfile() android.Path {
+	return d.rewrittenProfile
+}
+
+func (d *dexpreopter) SetRewrittenProfile(p android.Path) {
+	d.rewrittenProfile = p
+}
diff --git a/java/droidstubs.go b/java/droidstubs.go
index ca81343..5ca6c25 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -993,6 +993,7 @@
 func (d *Droidstubs) everythingOptionalCmd(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, doApiLint bool, doCheckReleased bool) {
 
 	// Add API lint options.
+	treatDocumentationIssuesAsErrors := false
 	if doApiLint {
 		var newSince android.Paths
 		if d.properties.Check_api.Api_lint.New_since != nil {
@@ -1006,7 +1007,7 @@
 		// TODO(b/154317059): Clean up this allowlist by baselining and/or checking in last-released.
 		if d.Name() != "android.car-system-stubs-docs" &&
 			d.Name() != "android.car-stubs-docs" {
-			cmd.Flag("--lints-as-errors")
+			treatDocumentationIssuesAsErrors = true
 			cmd.Flag("--warnings-as-errors") // Most lints are actually warnings.
 		}
 
@@ -1052,6 +1053,11 @@
 		cmd.FlagWithArg("--error-message:api-lint ", msg)
 	}
 
+	if !treatDocumentationIssuesAsErrors {
+		// Treat documentation issues as warnings, but error when new.
+		cmd.Flag("--error-when-new-category").Flag("Documentation")
+	}
+
 	// Add "check released" options. (Detect incompatible API changes from the last public release)
 	if doCheckReleased {
 		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file)
@@ -1147,6 +1153,9 @@
 		}
 	}
 
+	// Treat documentation issues as warnings, but error when new.
+	cmd.Flag("--error-when-new-category").Flag("Documentation")
+
 	if params.stubConfig.generateStubs {
 		rule.Command().
 			BuiltTool("soong_zip").
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index 330013e..6229797 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -198,13 +198,22 @@
 				hiddenApiFixtureFactory,
 				tc.preparer,
 				prepareForTestWithDefaultPlatformBootclasspath,
+				// Make sure that we have atleast one platform library so that we can check the monolithic hiddenapi
+				// file creation.
+				FixtureConfigureBootJars("platform:foo"),
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 					variables.Always_use_prebuilt_sdks = proptools.BoolPtr(tc.unbundledBuild)
 					variables.BuildFlags = map[string]string{
 						"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
 					}
 				}),
-			).RunTest(t)
+			).RunTestWithBp(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			compile_dex: true,
+		}
+		`)
 
 			hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
 			hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
diff --git a/java/java.go b/java/java.go
index 0df96a3..ccccbac 100644
--- a/java/java.go
+++ b/java/java.go
@@ -366,14 +366,14 @@
 	toolchain bool
 
 	static bool
+
+	installable bool
 }
 
-// installDependencyTag is a dependency tag that is annotated to cause the installed files of the
-// dependency to be installed when the parent module is installed.
-type installDependencyTag struct {
-	blueprint.BaseDependencyTag
-	android.InstallAlwaysNeededDependencyTag
-	name string
+var _ android.InstallNeededDependencyTag = (*dependencyTag)(nil)
+
+func (d dependencyTag) InstallDepNeeded() bool {
+	return d.installable
 }
 
 func (d dependencyTag) LicenseAnnotations() []android.LicenseAnnotation {
@@ -405,7 +405,7 @@
 }
 
 func IsJniDepTag(depTag blueprint.DependencyTag) bool {
-	return depTag == jniLibTag
+	return depTag == jniLibTag || depTag == jniInstallTag
 }
 
 var (
@@ -434,8 +434,8 @@
 	javaApiContributionTag  = dependencyTag{name: "java-api-contribution"}
 	depApiSrcsTag           = dependencyTag{name: "dep-api-srcs"}
 	aconfigDeclarationTag   = dependencyTag{name: "aconfig-declaration"}
-	jniInstallTag           = installDependencyTag{name: "jni install"}
-	binaryInstallTag        = installDependencyTag{name: "binary install"}
+	jniInstallTag           = dependencyTag{name: "jni install", runtimeLinked: true, installable: true}
+	binaryInstallTag        = dependencyTag{name: "binary install", runtimeLinked: true, installable: true}
 	usesLibReqTag           = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, false)
 	usesLibOptTag           = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, true)
 	usesLibCompat28OptTag   = makeUsesLibraryDependencyTag(28, true)
@@ -491,6 +491,7 @@
 	coverageFile   android.OptionalPath
 	unstrippedFile android.Path
 	partition      string
+	installPaths   android.InstallPaths
 }
 
 func sdkDeps(ctx android.BottomUpMutatorContext, sdkContext android.SdkContext, d dexer) {
@@ -566,6 +567,12 @@
 		return normalizeJavaVersion(ctx, javaVersion)
 	} else if ctx.Device() {
 		return defaultJavaLanguageVersion(ctx, sdkContext.SdkVersion(ctx))
+	} else if ctx.Config().TargetsJava21() {
+		// Temporary experimental flag to be able to try and build with
+		// java version 21 options.  The flag, if used, just sets Java
+		// 21 as the default version, leaving any components that
+		// target an older version intact.
+		return JAVA_VERSION_21
 	} else {
 		return JAVA_VERSION_17
 	}
@@ -908,7 +915,7 @@
 
 	// Check min_sdk_version of the transitive dependencies if this module is created from
 	// java_sdk_library.
-	if j.deviceProperties.Min_sdk_version != nil && j.SdkLibraryName() != nil {
+	if j.overridableProperties.Min_sdk_version != nil && j.SdkLibraryName() != nil {
 		j.CheckDepsMinSdkVersion(ctx)
 	}
 
@@ -1096,7 +1103,7 @@
 
 	// If the min_sdk_version was set then add the canonical representation of the API level to the
 	// snapshot.
-	if j.deviceProperties.Min_sdk_version != nil {
+	if j.overridableProperties.Min_sdk_version != nil {
 		canonical, err := android.ReplaceFinalizedCodenames(ctx.SdkModuleContext().Config(), j.minSdkVersion.String())
 		if err != nil {
 			ctx.ModuleErrorf("%s", err)
@@ -1497,6 +1504,8 @@
 		RequiredModuleNames: j.RequiredModuleNames(),
 		TestSuites:          j.testProperties.Test_suites,
 		IsHost:              true,
+		LocalSdkVersion:     j.sdkVersion.String(),
+		IsUnitTest:          Bool(j.testProperties.Test_options.Unit_test),
 	})
 }
 
@@ -2332,7 +2341,7 @@
 		classesJar:    al.stubsJar,
 		jarName:       ctx.ModuleName() + ".jar",
 	}
-	dexOutputFile := al.dexer.compileDex(ctx, dexParams)
+	dexOutputFile, _ := al.dexer.compileDex(ctx, dexParams)
 	uncompressed := true
 	al.initHiddenAPI(ctx, makeDexJarPathFromPath(dexOutputFile), al.stubsJar, &uncompressed)
 	dexOutputFile = al.hiddenAPIEncodeDex(ctx, dexOutputFile)
@@ -2716,7 +2725,7 @@
 				jarName:       jarName,
 			}
 
-			dexOutputFile = j.dexer.compileDex(ctx, dexParams)
+			dexOutputFile, _ = j.dexer.compileDex(ctx, dexParams)
 			if ctx.Failed() {
 				return
 			}
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index b3c9ce5..8d4cf68 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -294,6 +294,15 @@
 
 // generateHiddenAPIBuildActions generates all the hidden API related build rules.
 func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, fragments []android.Module) bootDexJarByModule {
+	createEmptyHiddenApiFiles := func() {
+		paths := android.OutputPaths{b.hiddenAPIFlagsCSV, b.hiddenAPIIndexCSV, b.hiddenAPIMetadataCSV}
+		for _, path := range paths {
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   android.Touch,
+				Output: path,
+			})
+		}
+	}
 
 	// Save the paths to the monolithic files for retrieval via OutputFiles().
 	b.hiddenAPIFlagsCSV = hiddenAPISingletonPaths(ctx).flags
@@ -306,13 +315,7 @@
 	// optimization that can be used to reduce the incremental build time but as its name suggests it
 	// can be unsafe to use, e.g. when the changes affect anything that goes on the bootclasspath.
 	if ctx.Config().DisableHiddenApiChecks() {
-		paths := android.OutputPaths{b.hiddenAPIFlagsCSV, b.hiddenAPIIndexCSV, b.hiddenAPIMetadataCSV}
-		for _, path := range paths {
-			ctx.Build(pctx, android.BuildParams{
-				Rule:   android.Touch,
-				Output: path,
-			})
-		}
+		createEmptyHiddenApiFiles()
 		return bootDexJarByModule
 	}
 
@@ -325,6 +328,13 @@
 	// the fragments will have already provided the flags that are needed.
 	classesJars := monolithicInfo.ClassesJars
 
+	if len(classesJars) == 0 {
+		// This product does not include any monolithic jars. Monolithic hiddenapi flag generation is not required.
+		// However, generate an empty file so that the dist tags in f/b/boot/Android.bp can be resolved, and `m dist` works.
+		createEmptyHiddenApiFiles()
+		return bootDexJarByModule
+	}
+
 	// Create the input to pass to buildRuleToGenerateHiddenAPIStubFlagsFile
 	input := newHiddenAPIFlagInput()
 
diff --git a/java/sdk.go b/java/sdk.go
index d972c19..4ef4ee2 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -65,6 +65,12 @@
 		return JAVA_VERSION_9
 	} else if sdk.FinalOrFutureInt() <= 33 {
 		return JAVA_VERSION_11
+	} else if ctx.Config().TargetsJava21() {
+		// Temporary experimental flag to be able to try and build with
+		// java version 21 options.  The flag, if used, just sets Java
+		// 21 as the default version, leaving any components that
+		// target an older version intact.
+		return JAVA_VERSION_21
 	} else {
 		return JAVA_VERSION_17
 	}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 677b32a..72eb6e3 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1504,6 +1504,18 @@
 
 var implLibraryTag = sdkLibraryComponentTag{name: "impl-library"}
 
+var _ android.InstallNeededDependencyTag = sdkLibraryComponentTag{}
+
+// To satisfy the CopyDirectlyInAnyApexTag interface. Implementation library of the sdk library
+// in an apex is considered to be directly in the apex, as if it was listed in java_libs.
+func (t sdkLibraryComponentTag) CopyDirectlyInAnyApex() {}
+
+var _ android.CopyDirectlyInAnyApexTag = implLibraryTag
+
+func (t sdkLibraryComponentTag) InstallDepNeeded() bool {
+	return t.name == "xml-permissions-file" || t.name == "impl-library"
+}
+
 // Add the dependencies on the child modules in the component deps mutator.
 func (module *SdkLibrary) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
 	for _, apiScope := range module.getGeneratedApiScopes(ctx) {
@@ -1650,6 +1662,14 @@
 		module.dexpreopter.configPath = module.implLibraryModule.dexpreopter.configPath
 		module.dexpreopter.outputProfilePathOnHost = module.implLibraryModule.dexpreopter.outputProfilePathOnHost
 
+		// Properties required for Library.AndroidMkEntries
+		module.logtagsSrcs = module.implLibraryModule.logtagsSrcs
+		module.dexpreopter.builtInstalled = module.implLibraryModule.dexpreopter.builtInstalled
+		module.jacocoReportClassesFile = module.implLibraryModule.jacocoReportClassesFile
+		module.dexer.proguardDictionary = module.implLibraryModule.dexer.proguardDictionary
+		module.dexer.proguardUsageZip = module.implLibraryModule.dexer.proguardUsageZip
+		module.linter.reports = module.implLibraryModule.linter.reports
+
 		if !module.Host() {
 			module.hostdexInstallFile = module.implLibraryModule.hostdexInstallFile
 		}
@@ -1814,7 +1834,6 @@
 	props := struct {
 		Name           *string
 		Visibility     []string
-		Instrument     bool
 		Libs           []string
 		Static_libs    []string
 		Apex_available []string
@@ -1822,8 +1841,6 @@
 	}{
 		Name:       proptools.StringPtr(module.implLibraryModuleName()),
 		Visibility: visibility,
-		// Set the instrument property to ensure it is instrumented when instrumentation is required.
-		Instrument: true,
 
 		Libs: append(module.properties.Libs, module.sdkLibraryProperties.Impl_only_libs...),
 
@@ -1842,6 +1859,7 @@
 		&module.dexProperties,
 		&module.dexpreoptProperties,
 		&module.linter.properties,
+		&module.overridableProperties,
 		&props,
 		module.sdkComponentPropertiesForChildLibrary(),
 	}
diff --git a/rust/builder.go b/rust/builder.go
index 4f45e33..1ce92f4 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -21,6 +21,7 @@
 	"github.com/google/blueprint"
 
 	"android/soong/android"
+	"android/soong/cc"
 	"android/soong/rust/config"
 )
 
@@ -118,42 +119,129 @@
 
 func init() {
 	pctx.HostBinToolVariable("SoongZipCmd", "soong_zip")
+	cc.TransformRlibstoStaticlib = TransformRlibstoStaticlib
+}
+
+type transformProperties struct {
+	crateName       string
+	targetTriple    string
+	is64Bit         bool
+	bootstrap       bool
+	inRecovery      bool
+	inRamdisk       bool
+	inVendorRamdisk bool
+	cargoOutDir     android.OptionalPath
+	synthetic       bool
+	crateType       string
+}
+
+// Populates a standard transformProperties struct for Rust modules
+func getTransformProperties(ctx ModuleContext, crateType string) transformProperties {
+	module := ctx.RustModule()
+	return transformProperties{
+		crateName:       module.CrateName(),
+		is64Bit:         ctx.toolchain().Is64Bit(),
+		targetTriple:    ctx.toolchain().RustTriple(),
+		bootstrap:       module.Bootstrap(),
+		inRecovery:      module.InRecovery(),
+		inRamdisk:       module.InRamdisk(),
+		inVendorRamdisk: module.InVendorRamdisk(),
+		cargoOutDir:     module.compiler.cargoOutDir(),
+
+		// crateType indicates what type of crate to build
+		crateType: crateType,
+
+		// synthetic indicates whether this is an actual Rust module or not
+		synthetic: false,
+	}
 }
 
 func TransformSrcToBinary(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath) buildOutput {
-	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
+	if ctx.RustModule().compiler.Thinlto() {
+		flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
+	}
 
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin")
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "bin"))
 }
 
 func TransformSrctoRlib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath) buildOutput {
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "rlib")
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "rlib"))
+}
+
+func TransformRlibstoStaticlib(ctx android.ModuleContext, mainSrc android.Path, deps []cc.RustRlibDep,
+	outputFile android.WritablePath) android.Path {
+
+	var rustPathDeps PathDeps
+	var rustFlags Flags
+
+	for _, rlibDep := range deps {
+		rustPathDeps.RLibs = append(rustPathDeps.RLibs, RustLibrary{Path: rlibDep.LibPath, CrateName: rlibDep.CrateName})
+		rustPathDeps.linkDirs = append(rustPathDeps.linkDirs, rlibDep.LinkDirs...)
+	}
+
+	ccModule := ctx.(cc.ModuleContext).Module().(*cc.Module)
+	toolchain := config.FindToolchain(ctx.Os(), ctx.Arch())
+	t := transformProperties{
+		// Crate name can be a predefined value as this is a staticlib and
+		// it does not need to be unique. The crate name is used for name
+		// mangling, but it is mixed with the metadata for that purpose, which we
+		// already set to the module name.
+		crateName:       "generated_rust_staticlib",
+		is64Bit:         toolchain.Is64Bit(),
+		targetTriple:    toolchain.RustTriple(),
+		bootstrap:       ccModule.Bootstrap(),
+		inRecovery:      ccModule.InRecovery(),
+		inRamdisk:       ccModule.InRamdisk(),
+		inVendorRamdisk: ccModule.InVendorRamdisk(),
+
+		// crateType indicates what type of crate to build
+		crateType: "staticlib",
+
+		// synthetic indicates whether this is an actual Rust module or not
+		synthetic: true,
+	}
+
+	rustFlags = CommonDefaultFlags(ctx, toolchain, rustFlags)
+	rustFlags = CommonLibraryCompilerFlags(ctx, rustFlags)
+	rustFlags.GlobalRustFlags = append(rustFlags.GlobalRustFlags, "-C lto=thin")
+
+	rustFlags.EmitXrefs = false
+
+	return transformSrctoCrate(ctx, mainSrc, rustPathDeps, rustFlags, outputFile, t).outputFile
 }
 
 func TransformSrctoDylib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath) buildOutput {
-	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
+	if ctx.RustModule().compiler.Thinlto() {
+		flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
+	}
 
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib")
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "dylib"))
 }
 
 func TransformSrctoStatic(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath) buildOutput {
-	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib")
+	if ctx.RustModule().compiler.Thinlto() {
+		flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
+	}
+
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "staticlib"))
 }
 
 func TransformSrctoShared(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath) buildOutput {
-	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib")
+	if ctx.RustModule().compiler.Thinlto() {
+		flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
+	}
+
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "cdylib"))
 }
 
 func TransformSrctoProcMacro(ctx ModuleContext, mainSrc android.Path, deps PathDeps,
 	flags Flags, outputFile android.WritablePath) buildOutput {
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "proc-macro")
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "proc-macro"))
 }
 
 func rustLibsToPaths(libs RustLibraries) android.Paths {
@@ -185,18 +273,18 @@
 	return libFlags
 }
 
-func rustEnvVars(ctx ModuleContext, deps PathDeps) []string {
+func rustEnvVars(ctx android.ModuleContext, deps PathDeps, crateName string, cargoOutDir android.OptionalPath) []string {
 	var envVars []string
 
 	// libstd requires a specific environment variable to be set. This is
 	// not officially documented and may be removed in the future. See
 	// https://github.com/rust-lang/rust/blob/master/library/std/src/env.rs#L866.
-	if ctx.RustModule().CrateName() == "std" {
-		envVars = append(envVars, "STD_ENV_ARCH="+config.StdEnvArch[ctx.RustModule().Arch().ArchType])
+	if crateName == "std" {
+		envVars = append(envVars, "STD_ENV_ARCH="+config.StdEnvArch[ctx.Arch().ArchType])
 	}
 
-	if len(deps.SrcDeps) > 0 {
-		moduleGenDir := ctx.RustModule().compiler.cargoOutDir()
+	if len(deps.SrcDeps) > 0 && cargoOutDir.Valid() {
+		moduleGenDir := cargoOutDir
 		// We must calculate an absolute path for OUT_DIR since Rust's include! macro (which normally consumes this)
 		// assumes that paths are relative to the source file.
 		var outDirPrefix string
@@ -215,13 +303,15 @@
 
 	envVars = append(envVars, "ANDROID_RUST_VERSION="+config.GetRustVersion(ctx))
 
-	if ctx.RustModule().compiler.cargoEnvCompat() {
-		if bin, ok := ctx.RustModule().compiler.(*binaryDecorator); ok {
+	if rustMod, ok := ctx.Module().(*Module); ok && rustMod.compiler.cargoEnvCompat() {
+		// We only emulate cargo environment variables for 3p code, which is only ever built
+		// by defining a Rust module, so we only need to set these for true Rust modules.
+		if bin, ok := rustMod.compiler.(*binaryDecorator); ok {
 			envVars = append(envVars, "CARGO_BIN_NAME="+bin.getStem(ctx))
 		}
-		envVars = append(envVars, "CARGO_CRATE_NAME="+ctx.RustModule().CrateName())
-		envVars = append(envVars, "CARGO_PKG_NAME="+ctx.RustModule().CrateName())
-		pkgVersion := ctx.RustModule().compiler.cargoPkgVersion()
+		envVars = append(envVars, "CARGO_CRATE_NAME="+crateName)
+		envVars = append(envVars, "CARGO_PKG_NAME="+crateName)
+		pkgVersion := rustMod.compiler.cargoPkgVersion()
 		if pkgVersion != "" {
 			envVars = append(envVars, "CARGO_PKG_VERSION="+pkgVersion)
 
@@ -245,8 +335,8 @@
 	return envVars
 }
 
-func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, flags Flags,
-	outputFile android.WritablePath, crateType string) buildOutput {
+func transformSrctoCrate(ctx android.ModuleContext, main android.Path, deps PathDeps, flags Flags,
+	outputFile android.WritablePath, t transformProperties) buildOutput {
 
 	var inputs android.Paths
 	var implicits android.Paths
@@ -256,23 +346,21 @@
 	var earlyLinkFlags string
 
 	output.outputFile = outputFile
-	crateName := ctx.RustModule().CrateName()
-	targetTriple := ctx.toolchain().RustTriple()
 
-	envVars := rustEnvVars(ctx, deps)
+	envVars := rustEnvVars(ctx, deps, t.crateName, t.cargoOutDir)
 
 	inputs = append(inputs, main)
 
 	// Collect rustc flags
 	rustcFlags = append(rustcFlags, flags.GlobalRustFlags...)
 	rustcFlags = append(rustcFlags, flags.RustFlags...)
-	rustcFlags = append(rustcFlags, "--crate-type="+crateType)
-	if crateName != "" {
-		rustcFlags = append(rustcFlags, "--crate-name="+crateName)
+	rustcFlags = append(rustcFlags, "--crate-type="+t.crateType)
+	if t.crateName != "" {
+		rustcFlags = append(rustcFlags, "--crate-name="+t.crateName)
 	}
-	if targetTriple != "" {
-		rustcFlags = append(rustcFlags, "--target="+targetTriple)
-		linkFlags = append(linkFlags, "-target "+targetTriple)
+	if t.targetTriple != "" {
+		rustcFlags = append(rustcFlags, "--target="+t.targetTriple)
+		linkFlags = append(linkFlags, "-target "+t.targetTriple)
 	}
 
 	// Suppress an implicit sysroot
@@ -302,9 +390,9 @@
 	linkFlags = append(linkFlags, flags.LinkFlags...)
 
 	// Check if this module needs to use the bootstrap linker
-	if ctx.RustModule().Bootstrap() && !ctx.RustModule().InRecovery() && !ctx.RustModule().InRamdisk() && !ctx.RustModule().InVendorRamdisk() {
+	if t.bootstrap && !t.inRecovery && !t.inRamdisk && !t.inVendorRamdisk {
 		dynamicLinker := "-Wl,-dynamic-linker,/system/bin/bootstrap/linker"
-		if ctx.toolchain().Is64Bit() {
+		if t.is64Bit {
 			dynamicLinker += "64"
 		}
 		linkFlags = append(linkFlags, dynamicLinker)
@@ -326,49 +414,56 @@
 
 	orderOnly = append(orderOnly, deps.SharedLibs...)
 
-	if len(deps.SrcDeps) > 0 {
-		moduleGenDir := ctx.RustModule().compiler.cargoOutDir()
-		var outputs android.WritablePaths
+	if !t.synthetic {
+		// Only worry about OUT_DIR for actual Rust modules.
+		// Libraries built from cc use generated source, and do not utilize OUT_DIR.
+		if len(deps.SrcDeps) > 0 {
+			var outputs android.WritablePaths
 
-		for _, genSrc := range deps.SrcDeps {
-			if android.SuffixInList(outputs.Strings(), genSubDir+genSrc.Base()) {
-				ctx.PropertyErrorf("srcs",
-					"multiple source providers generate the same filename output: "+genSrc.Base())
+			for _, genSrc := range deps.SrcDeps {
+				if android.SuffixInList(outputs.Strings(), genSubDir+genSrc.Base()) {
+					ctx.PropertyErrorf("srcs",
+						"multiple source providers generate the same filename output: "+genSrc.Base())
+				}
+				outputs = append(outputs, android.PathForModuleOut(ctx, genSubDir+genSrc.Base()))
 			}
-			outputs = append(outputs, android.PathForModuleOut(ctx, genSubDir+genSrc.Base()))
-		}
 
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        cp,
-			Description: "cp " + moduleGenDir.Path().Rel(),
-			Outputs:     outputs,
-			Inputs:      deps.SrcDeps,
-			Args: map[string]string{
-				"outDir": moduleGenDir.String(),
-			},
-		})
-		implicits = append(implicits, outputs.Paths()...)
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        cp,
+				Description: "cp " + t.cargoOutDir.Path().Rel(),
+				Outputs:     outputs,
+				Inputs:      deps.SrcDeps,
+				Args: map[string]string{
+					"outDir": t.cargoOutDir.String(),
+				},
+			})
+			implicits = append(implicits, outputs.Paths()...)
+		}
 	}
 
-	if flags.Clippy {
-		clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy")
-		ctx.Build(pctx, android.BuildParams{
-			Rule:            clippyDriver,
-			Description:     "clippy " + main.Rel(),
-			Output:          clippyFile,
-			ImplicitOutputs: nil,
-			Inputs:          inputs,
-			Implicits:       implicits,
-			OrderOnly:       orderOnly,
-			Args: map[string]string{
-				"rustcFlags":  strings.Join(rustcFlags, " "),
-				"libFlags":    strings.Join(libFlags, " "),
-				"clippyFlags": strings.Join(flags.ClippyFlags, " "),
-				"envVars":     strings.Join(envVars, " "),
-			},
-		})
-		// Declare the clippy build as an implicit dependency of the original crate.
-		implicits = append(implicits, clippyFile)
+	if !t.synthetic {
+		// Only worry about clippy for actual Rust modules.
+		// Libraries built from cc use generated source, and don't need to run clippy.
+		if flags.Clippy {
+			clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy")
+			ctx.Build(pctx, android.BuildParams{
+				Rule:            clippyDriver,
+				Description:     "clippy " + main.Rel(),
+				Output:          clippyFile,
+				ImplicitOutputs: nil,
+				Inputs:          inputs,
+				Implicits:       implicits,
+				OrderOnly:       orderOnly,
+				Args: map[string]string{
+					"rustcFlags":  strings.Join(rustcFlags, " "),
+					"libFlags":    strings.Join(libFlags, " "),
+					"clippyFlags": strings.Join(flags.ClippyFlags, " "),
+					"envVars":     strings.Join(envVars, " "),
+				},
+			})
+			// Declare the clippy build as an implicit dependency of the original crate.
+			implicits = append(implicits, clippyFile)
+		}
 	}
 
 	ctx.Build(pctx, android.BuildParams{
@@ -389,25 +484,28 @@
 		},
 	})
 
-	if flags.EmitXrefs {
-		kytheFile := android.PathForModuleOut(ctx, outputFile.Base()+".kzip")
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        kytheExtract,
-			Description: "Xref Rust extractor " + main.Rel(),
-			Output:      kytheFile,
-			Inputs:      inputs,
-			Implicits:   implicits,
-			OrderOnly:   orderOnly,
-			Args: map[string]string{
-				"rustcFlags": strings.Join(rustcFlags, " "),
-				"linkFlags":  strings.Join(linkFlags, " "),
-				"libFlags":   strings.Join(libFlags, " "),
-				"crtBegin":   strings.Join(deps.CrtBegin.Strings(), " "),
-				"crtEnd":     strings.Join(deps.CrtEnd.Strings(), " "),
-				"envVars":    strings.Join(envVars, " "),
-			},
-		})
-		output.kytheFile = kytheFile
+	if !t.synthetic {
+		// Only emit xrefs for true Rust modules.
+		if flags.EmitXrefs {
+			kytheFile := android.PathForModuleOut(ctx, outputFile.Base()+".kzip")
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        kytheExtract,
+				Description: "Xref Rust extractor " + main.Rel(),
+				Output:      kytheFile,
+				Inputs:      inputs,
+				Implicits:   implicits,
+				OrderOnly:   orderOnly,
+				Args: map[string]string{
+					"rustcFlags": strings.Join(rustcFlags, " "),
+					"linkFlags":  strings.Join(linkFlags, " "),
+					"libFlags":   strings.Join(libFlags, " "),
+					"crtBegin":   strings.Join(deps.CrtBegin.Strings(), " "),
+					"crtEnd":     strings.Join(deps.CrtEnd.Strings(), " "),
+					"envVars":    strings.Join(envVars, " "),
+				},
+			})
+			output.kytheFile = kytheFile
+		}
 	}
 	return output
 }
@@ -457,7 +555,7 @@
 		Args: map[string]string{
 			"rustdocFlags": strings.Join(rustdocFlags, " "),
 			"outDir":       docDir.String(),
-			"envVars":      strings.Join(rustEnvVars(ctx, deps), " "),
+			"envVars":      strings.Join(rustEnvVars(ctx, deps, crateName, ctx.RustModule().compiler.cargoOutDir()), " "),
 		},
 	})
 
diff --git a/rust/builder_test.go b/rust/builder_test.go
index 639f6d4..c093ac4 100644
--- a/rust/builder_test.go
+++ b/rust/builder_test.go
@@ -46,6 +46,9 @@
 }
 
 func TestCompilationOutputFiles(t *testing.T) {
+
+	// Note: Rustdoc output is produced for the PrimaryModule, so if the variant
+	// order changes, then it may be produced for a different variant.
 	ctx := testRust(t, `
 		rust_library {
 			name: "libfizz_buzz",
@@ -126,6 +129,16 @@
 			},
 		},
 		{
+			testName:   "rust_ffi rlib",
+			moduleName: "librust_ffi",
+			variant:    "android_arm64_armv8-a_rlib_rlib-std",
+			expectedFiles: []string{
+				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_rlib_rlib-std/librust_ffi.rlib",
+				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_rlib_rlib-std/librust_ffi.rlib.clippy",
+				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_rlib_rlib-std/meta_lic",
+			},
+		},
+		{
 			testName:   "rust_ffi shared",
 			moduleName: "librust_ffi",
 			variant:    "android_arm64_armv8-a_shared",
diff --git a/rust/compiler.go b/rust/compiler.go
index 03fdf2b..a2546a1 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -47,6 +47,7 @@
 	edition() string
 	features() []string
 	rustdoc(ctx ModuleContext, flags Flags, deps PathDeps) android.OptionalPath
+	Thinlto() bool
 
 	// Output directory in which source-generated code from dependencies is
 	// copied. This is equivalent to Cargo's OUT_DIR variable.
@@ -196,7 +197,7 @@
 	Features []string `android:"arch_variant"`
 
 	// list of configuration options to enable for this crate. To enable features, use the "features" property.
-	Cfgs []string `android:"arch_variant"`
+	Cfgs proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// specific rust edition that should be used if the default version is not desired
 	Edition *string `android:"arch_variant"`
@@ -231,6 +232,15 @@
 
 	// If cargo_env_compat is true, sets the CARGO_PKG_VERSION env var to this value.
 	Cargo_pkg_version *string
+
+	// Control whether LTO is used for the final (Rust) linkage. This does not impact
+	// cross-language LTO.
+	Lto struct {
+		// Whether thin LTO should be enabled. By default this is true.
+		// LTO provides such a large code size benefit for Rust, this should always
+		// be enabled for production builds unless there's a clear need to disable it.
+		Thin *bool `android:"arch_variant"`
+	} `android:"arch_variant"`
 }
 
 type baseCompiler struct {
@@ -273,6 +283,11 @@
 	return false
 }
 
+// Thin LTO is enabled by default.
+func (compiler *baseCompiler) Thinlto() bool {
+	return BoolDefault(compiler.Properties.Lto.Thin, true)
+}
+
 func (compiler *baseCompiler) SetDisabled() {
 	panic("baseCompiler does not implement SetDisabled()")
 }
@@ -322,9 +337,9 @@
 	return []interface{}{&compiler.Properties}
 }
 
-func (compiler *baseCompiler) cfgsToFlags() []string {
-	flags := []string{}
-	for _, cfg := range compiler.Properties.Cfgs {
+func cfgsToFlags(cfgs []string) []string {
+	flags := make([]string, 0, len(cfgs))
+	for _, cfg := range cfgs {
 		flags = append(flags, "--cfg '"+cfg+"'")
 	}
 
@@ -351,23 +366,62 @@
 	return flags
 }
 
-func (compiler *baseCompiler) cfgFlags(ctx ModuleContext, flags Flags) Flags {
-	if ctx.RustModule().InVendorOrProduct() {
-		compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_vndk")
-		if ctx.RustModule().InVendor() {
-			compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_vendor")
-		} else if ctx.RustModule().InProduct() {
-			compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_product")
+func CommonDefaultCfgFlags(flags Flags, vendor bool, product bool) Flags {
+	var cfgs []string
+	if vendor || product {
+		cfgs = append(cfgs, "android_vndk")
+		if vendor {
+			cfgs = append(cfgs, "android_vendor")
+		} else if product {
+			cfgs = append(cfgs, "android_product")
 		}
 	}
 
-	flags.RustFlags = append(flags.RustFlags, compiler.cfgsToFlags()...)
-	flags.RustdocFlags = append(flags.RustdocFlags, compiler.cfgsToFlags()...)
+	flags.RustFlags = append(flags.RustFlags, cfgsToFlags(cfgs)...)
+	flags.RustdocFlags = append(flags.RustdocFlags, cfgsToFlags(cfgs)...)
+	return flags
+}
+
+func (compiler *baseCompiler) cfgFlags(ctx ModuleContext, flags Flags) Flags {
+	flags = CommonDefaultCfgFlags(flags, ctx.RustModule().InVendor(), ctx.RustModule().InProduct())
+
+	cfgFlags := cfgsToFlags(compiler.Properties.Cfgs.GetOrDefault(ctx, nil))
+	flags.RustFlags = append(flags.RustFlags, cfgFlags...)
+	flags.RustdocFlags = append(flags.RustdocFlags, cfgFlags...)
+
+	return flags
+}
+
+func CommonDefaultFlags(ctx android.ModuleContext, toolchain config.Toolchain, flags Flags) Flags {
+	flags.GlobalRustFlags = append(flags.GlobalRustFlags, config.GlobalRustFlags...)
+	flags.GlobalRustFlags = append(flags.GlobalRustFlags, toolchain.ToolchainRustFlags())
+	flags.GlobalLinkFlags = append(flags.GlobalLinkFlags, toolchain.ToolchainLinkFlags())
+	flags.EmitXrefs = ctx.Config().EmitXrefRules()
+
+	if ctx.Host() && !ctx.Windows() {
+		flags.LinkFlags = append(flags.LinkFlags, cc.RpathFlags(ctx)...)
+	}
+
+	if ctx.Os() == android.Linux {
+		// Add -lc, -lrt, -ldl, -lpthread, -lm and -lgcc_s to glibc builds to match
+		// the default behavior of device builds.
+		flags.LinkFlags = append(flags.LinkFlags, config.LinuxHostGlobalLinkFlags...)
+	} else if ctx.Os() == android.Darwin {
+		// Add -lc, -ldl, -lpthread and -lm to glibc darwin builds to match the default
+		// behavior of device builds.
+		flags.LinkFlags = append(flags.LinkFlags,
+			"-lc",
+			"-ldl",
+			"-lpthread",
+			"-lm",
+		)
+	}
 	return flags
 }
 
 func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags) Flags {
 
+	flags = CommonDefaultFlags(ctx, ctx.toolchain(), flags)
 	lintFlags, err := config.RustcLintsForDir(ctx.ModuleDir(), compiler.Properties.Lints)
 	if err != nil {
 		ctx.PropertyErrorf("lints", err.Error())
@@ -396,29 +450,7 @@
 	flags.RustFlags = append(flags.RustFlags, "--edition="+compiler.edition())
 	flags.RustdocFlags = append(flags.RustdocFlags, "--edition="+compiler.edition())
 	flags.LinkFlags = append(flags.LinkFlags, compiler.Properties.Ld_flags...)
-	flags.GlobalRustFlags = append(flags.GlobalRustFlags, config.GlobalRustFlags...)
-	flags.GlobalRustFlags = append(flags.GlobalRustFlags, ctx.toolchain().ToolchainRustFlags())
-	flags.GlobalLinkFlags = append(flags.GlobalLinkFlags, ctx.toolchain().ToolchainLinkFlags())
-	flags.EmitXrefs = ctx.Config().EmitXrefRules()
 
-	if ctx.Host() && !ctx.Windows() {
-		flags.LinkFlags = append(flags.LinkFlags, cc.RpathFlags(ctx)...)
-	}
-
-	if ctx.Os() == android.Linux {
-		// Add -lc, -lrt, -ldl, -lpthread, -lm and -lgcc_s to glibc builds to match
-		// the default behavior of device builds.
-		flags.LinkFlags = append(flags.LinkFlags, config.LinuxHostGlobalLinkFlags...)
-	} else if ctx.Os() == android.Darwin {
-		// Add -lc, -ldl, -lpthread and -lm to glibc darwin builds to match the default
-		// behavior of device builds.
-		flags.LinkFlags = append(flags.LinkFlags,
-			"-lc",
-			"-ldl",
-			"-lpthread",
-			"-lm",
-		)
-	}
 	return flags
 }
 
@@ -568,11 +600,11 @@
 	compiler.installDeps = append(compiler.installDeps, installedData...)
 }
 
-func (compiler *baseCompiler) getStem(ctx ModuleContext) string {
+func (compiler *baseCompiler) getStem(ctx android.ModuleContext) string {
 	return compiler.getStemWithoutSuffix(ctx) + String(compiler.Properties.Suffix)
 }
 
-func (compiler *baseCompiler) getStemWithoutSuffix(ctx BaseModuleContext) string {
+func (compiler *baseCompiler) getStemWithoutSuffix(ctx android.BaseModuleContext) string {
 	stem := ctx.ModuleName()
 	if String(compiler.Properties.Stem) != "" {
 		stem = String(compiler.Properties.Stem)
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index 89f4d1a..4caa12b 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -63,6 +63,35 @@
 	}
 }
 
+func TestLtoFlag(t *testing.T) {
+	ctx := testRust(t, `
+		rust_library_host {
+			name: "libfoo",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+			lto: {
+				thin: false,
+			}
+		}
+
+		rust_library_host {
+			name: "libfoo_lto",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+		}
+		`)
+
+	libfoo := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
+	libfooLto := ctx.ModuleForTests("libfoo_lto", "linux_glibc_x86_64_dylib").Rule("rustc")
+
+	if strings.Contains(libfoo.Args["rustcFlags"], "-C lto=thin") {
+		t.Fatalf("libfoo expected to disable lto -- rustcFlags: %#v", libfoo.Args["rustcFlags"])
+	}
+	if !strings.Contains(libfooLto.Args["rustcFlags"], "-C lto=thin") {
+		t.Fatalf("libfoo expected to enable lto by default -- rustcFlags: %#v", libfooLto.Args["rustcFlags"])
+	}
+}
+
 // Test that we reject multiple source files.
 func TestEnforceSingleSourceFile(t *testing.T) {
 
diff --git a/rust/coverage.go b/rust/coverage.go
index 91a7806..e0e919c 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -47,7 +47,7 @@
 
 		// no_std modules are missing libprofiler_builtins which provides coverage, so we need to add it as a dependency.
 		if rustModule, ok := ctx.Module().(*Module); ok && rustModule.compiler.noStdlibs() {
-			ctx.AddVariationDependencies([]blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}, rlibDepTag, ProfilerBuiltins)
+			ctx.AddVariationDependencies([]blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}, {Mutator: "link", Variation: ""}}, rlibDepTag, ProfilerBuiltins)
 		}
 	}
 
diff --git a/rust/fuzz_test.go b/rust/fuzz_test.go
index ee28c6d..0d4622a 100644
--- a/rust/fuzz_test.go
+++ b/rust/fuzz_test.go
@@ -120,13 +120,17 @@
 			}
 			cc_fuzz {
 				name: "fuzz_static_libtest",
+				static_rlibs: ["libtest_fuzzing"],
+			}
+			cc_fuzz {
+				name: "fuzz_staticffi_libtest",
 				static_libs: ["libtest_fuzzing"],
 			}
-
 	`)
 
 	fuzz_shared_libtest := ctx.ModuleForTests("fuzz_shared_libtest", "android_arm64_armv8-a_fuzzer").Module().(cc.LinkableInterface)
 	fuzz_static_libtest := ctx.ModuleForTests("fuzz_static_libtest", "android_arm64_armv8-a_fuzzer").Module().(cc.LinkableInterface)
+	fuzz_staticffi_libtest := ctx.ModuleForTests("fuzz_staticffi_libtest", "android_arm64_armv8-a_fuzzer").Module().(cc.LinkableInterface)
 
 	if !strings.Contains(fuzz_shared_libtest.FuzzSharedLibraries().String(), ":libcc_transitive_dep.so") {
 		t.Errorf("cc_fuzz does not contain the expected bundled transitive shared libs from rust_ffi_shared ('libcc_transitive_dep'): %#v", fuzz_shared_libtest.FuzzSharedLibraries().String())
@@ -134,4 +138,7 @@
 	if !strings.Contains(fuzz_static_libtest.FuzzSharedLibraries().String(), ":libcc_transitive_dep.so") {
 		t.Errorf("cc_fuzz does not contain the expected bundled transitive shared libs from rust_ffi_static ('libcc_transitive_dep'): %#v", fuzz_static_libtest.FuzzSharedLibraries().String())
 	}
+	if !strings.Contains(fuzz_staticffi_libtest.FuzzSharedLibraries().String(), ":libcc_transitive_dep.so") {
+		t.Errorf("cc_fuzz does not contain the expected bundled transitive shared libs from rust_ffi_rlib ('libcc_transitive_dep'): %#v", fuzz_staticffi_libtest.FuzzSharedLibraries().String())
+	}
 }
diff --git a/rust/image_test.go b/rust/image_test.go
index ba94906..71e271c 100644
--- a/rust/image_test.go
+++ b/rust/image_test.go
@@ -22,33 +22,45 @@
 	"android/soong/cc"
 )
 
-// Test that cc modules can link against vendor_available rust_ffi_static libraries.
+// Test that cc modules can link against vendor_available rust_ffi_rlib/rust_ffi_static libraries.
 func TestVendorLinkage(t *testing.T) {
 	ctx := testRust(t, `
 			cc_binary {
-				name: "fizz_vendor",
-				static_libs: ["libfoo_vendor"],
+				name: "fizz_vendor_available",
+				static_libs: ["libfoo_vendor_static"],
+				static_rlibs: ["libfoo_vendor"],
+				vendor_available: true,
+			}
+			cc_binary {
+				name: "fizz_soc_specific",
+				static_rlibs: ["libfoo_vendor"],
 				soc_specific: true,
 			}
-			rust_ffi_static {
+			rust_ffi_rlib {
 				name: "libfoo_vendor",
 				crate_name: "foo",
 				srcs: ["foo.rs"],
 				vendor_available: true,
 			}
+			rust_ffi_static {
+				name: "libfoo_vendor_static",
+				crate_name: "foo",
+				srcs: ["foo.rs"],
+				vendor_available: true,
+			}
 		`)
 
-	vendorBinary := ctx.ModuleForTests("fizz_vendor", "android_vendor_arm64_armv8-a").Module().(*cc.Module)
+	vendorBinary := ctx.ModuleForTests("fizz_vendor_available", "android_vendor_arm64_armv8-a").Module().(*cc.Module)
 
-	if !android.InList("libfoo_vendor.vendor", vendorBinary.Properties.AndroidMkStaticLibs) {
-		t.Errorf("vendorBinary should have a dependency on libfoo_vendor: %#v", vendorBinary.Properties.AndroidMkStaticLibs)
+	if !android.InList("libfoo_vendor_static.vendor", vendorBinary.Properties.AndroidMkStaticLibs) {
+		t.Errorf("vendorBinary should have a dependency on libfoo_vendor_static.vendor: %#v", vendorBinary.Properties.AndroidMkStaticLibs)
 	}
 }
 
 // Test that variants which use the vndk emit the appropriate cfg flag.
 func TestImageCfgFlag(t *testing.T) {
 	ctx := testRust(t, `
-			rust_ffi_static {
+			rust_ffi_shared {
 				name: "libfoo",
 				crate_name: "foo",
 				srcs: ["foo.rs"],
@@ -57,7 +69,7 @@
 			}
 		`)
 
-	vendor := ctx.ModuleForTests("libfoo", "android_vendor_arm64_armv8-a_static").Rule("rustc")
+	vendor := ctx.ModuleForTests("libfoo", "android_vendor_arm64_armv8-a_shared").Rule("rustc")
 
 	if !strings.Contains(vendor.Args["rustcFlags"], "--cfg 'android_vndk'") {
 		t.Errorf("missing \"--cfg 'android_vndk'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"])
@@ -69,7 +81,7 @@
 		t.Errorf("unexpected \"--cfg 'android_product'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"])
 	}
 
-	product := ctx.ModuleForTests("libfoo", "android_product_arm64_armv8-a_static").Rule("rustc")
+	product := ctx.ModuleForTests("libfoo", "android_product_arm64_armv8-a_shared").Rule("rustc")
 	if !strings.Contains(product.Args["rustcFlags"], "--cfg 'android_vndk'") {
 		t.Errorf("missing \"--cfg 'android_vndk'\" for libfoo product variant, rustcFlags: %#v", product.Args["rustcFlags"])
 	}
@@ -80,7 +92,7 @@
 		t.Errorf("missing \"--cfg 'android_product'\" for libfoo product variant, rustcFlags: %#v", product.Args["rustcFlags"])
 	}
 
-	system := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Rule("rustc")
+	system := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("rustc")
 	if strings.Contains(system.Args["rustcFlags"], "--cfg 'android_vndk'") {
 		t.Errorf("unexpected \"--cfg 'android_vndk'\" for libfoo system variant, rustcFlags: %#v", system.Args["rustcFlags"])
 	}
@@ -93,27 +105,34 @@
 
 }
 
-// Test that cc modules can link against vendor_ramdisk_available rust_ffi_static libraries.
+// Test that cc modules can link against vendor_ramdisk_available rust_ffi_rlib and rust_ffi_static libraries.
 func TestVendorRamdiskLinkage(t *testing.T) {
 	ctx := testRust(t, `
-			cc_library_static {
+			cc_library_shared {
 				name: "libcc_vendor_ramdisk",
-				static_libs: ["libfoo_vendor_ramdisk"],
+				static_rlibs: ["libfoo_vendor_ramdisk"],
+				static_libs: ["libfoo_static_vendor_ramdisk"],
 				system_shared_libs: [],
 				vendor_ramdisk_available: true,
 			}
-			rust_ffi_static {
+			rust_ffi_rlib {
 				name: "libfoo_vendor_ramdisk",
 				crate_name: "foo",
 				srcs: ["foo.rs"],
 				vendor_ramdisk_available: true,
 			}
+			rust_ffi_static {
+				name: "libfoo_static_vendor_ramdisk",
+				crate_name: "foo",
+				srcs: ["foo.rs"],
+				vendor_ramdisk_available: true,
+			}
 		`)
 
-	vendorRamdiskLibrary := ctx.ModuleForTests("libcc_vendor_ramdisk", "android_vendor_ramdisk_arm64_armv8-a_static").Module().(*cc.Module)
+	vendorRamdiskLibrary := ctx.ModuleForTests("libcc_vendor_ramdisk", "android_vendor_ramdisk_arm64_armv8-a_shared").Module().(*cc.Module)
 
-	if !android.InList("libfoo_vendor_ramdisk.vendor_ramdisk", vendorRamdiskLibrary.Properties.AndroidMkStaticLibs) {
-		t.Errorf("libcc_vendor_ramdisk should have a dependency on libfoo_vendor_ramdisk")
+	if !android.InList("libfoo_static_vendor_ramdisk.vendor_ramdisk", vendorRamdiskLibrary.Properties.AndroidMkStaticLibs) {
+		t.Errorf("libcc_vendor_ramdisk should have a dependency on libfoo_static_vendor_ramdisk")
 	}
 }
 
diff --git a/rust/library.go b/rust/library.go
index f58a54f..2a21263 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -37,10 +37,15 @@
 	android.RegisterModuleType("rust_library_host_rlib", RustLibraryRlibHostFactory)
 	android.RegisterModuleType("rust_ffi", RustFFIFactory)
 	android.RegisterModuleType("rust_ffi_shared", RustFFISharedFactory)
-	android.RegisterModuleType("rust_ffi_static", RustFFIStaticFactory)
+	android.RegisterModuleType("rust_ffi_rlib", RustFFIRlibFactory)
 	android.RegisterModuleType("rust_ffi_host", RustFFIHostFactory)
 	android.RegisterModuleType("rust_ffi_host_shared", RustFFISharedHostFactory)
-	android.RegisterModuleType("rust_ffi_host_static", RustFFIStaticHostFactory)
+	android.RegisterModuleType("rust_ffi_host_rlib", RustFFIRlibHostFactory)
+
+	// TODO: Remove when all instances of rust_ffi_static have been switched to rust_ffi_rlib
+	// Alias rust_ffi_static to the combined rust_ffi_rlib factory
+	android.RegisterModuleType("rust_ffi_static", RustFFIStaticRlibFactory)
+	android.RegisterModuleType("rust_ffi_host_static", RustFFIStaticRlibHostFactory)
 }
 
 type VariantLibraryProperties struct {
@@ -104,6 +109,8 @@
 	includeDirs       android.Paths
 	sourceProvider    SourceProvider
 
+	isFFI bool
+
 	// table-of-contents file for cdylib crates to optimize out relinking when possible
 	tocFile android.OptionalPath
 }
@@ -143,6 +150,8 @@
 	BuildOnlyShared()
 
 	toc() android.OptionalPath
+
+	isFFILibrary() bool
 }
 
 func (library *libraryDecorator) nativeCoverage() bool {
@@ -250,7 +259,7 @@
 }
 
 func (library *libraryDecorator) stdLinkage(ctx *depsContext) RustLinkage {
-	if library.static() || library.MutatedProperties.VariantIsStaticStd {
+	if library.static() || library.MutatedProperties.VariantIsStaticStd || (library.rlib() && library.isFFILibrary()) {
 		return RlibLinkage
 	} else if library.baseCompiler.preferRlib() {
 		return RlibLinkage
@@ -270,8 +279,8 @@
 	return module.Init()
 }
 
-// rust_ffi produces all FFI variants (rust_ffi_shared and
-// rust_ffi_static).
+// rust_ffi produces all FFI variants (rust_ffi_shared, rust_ffi_static, and
+// rust_ffi_rlib).
 func RustFFIFactory() android.Module {
 	module, library := NewRustLibrary(android.HostAndDeviceSupported)
 	library.BuildOnlyFFI()
@@ -300,14 +309,6 @@
 	return module.Init()
 }
 
-// rust_ffi_static produces a static library (Rust crate type
-// "staticlib").
-func RustFFIStaticFactory() android.Module {
-	module, library := NewRustLibrary(android.HostAndDeviceSupported)
-	library.BuildOnlyStatic()
-	return module.Init()
-}
-
 // rust_library_host produces all Rust variants for the host
 // (rust_library_dylib_host and rust_library_rlib_host).
 func RustLibraryHostFactory() android.Module {
@@ -317,7 +318,7 @@
 }
 
 // rust_ffi_host produces all FFI variants for the host
-// (rust_ffi_static_host and rust_ffi_shared_host).
+// (rust_ffi_rlib_host, rust_ffi_static_host, and rust_ffi_shared_host).
 func RustFFIHostFactory() android.Module {
 	module, library := NewRustLibrary(android.HostSupported)
 	library.BuildOnlyFFI()
@@ -340,14 +341,6 @@
 	return module.Init()
 }
 
-// rust_ffi_static_host produces a static library for the host (Rust
-// crate type "staticlib").
-func RustFFIStaticHostFactory() android.Module {
-	module, library := NewRustLibrary(android.HostSupported)
-	library.BuildOnlyStatic()
-	return module.Init()
-}
-
 // rust_ffi_shared_host produces an shared library for the host (Rust
 // crate type "cdylib").
 func RustFFISharedHostFactory() android.Module {
@@ -356,11 +349,51 @@
 	return module.Init()
 }
 
+// rust_ffi_rlib_host produces an rlib for the host (Rust crate
+// type "rlib").
+func RustFFIRlibHostFactory() android.Module {
+	module, library := NewRustLibrary(android.HostSupported)
+	library.BuildOnlyRlibStatic()
+
+	library.isFFI = true
+	return module.Init()
+}
+
+// rust_ffi_rlib produces an rlib (Rust crate type "rlib").
+func RustFFIRlibFactory() android.Module {
+	module, library := NewRustLibrary(android.HostAndDeviceSupported)
+	library.BuildOnlyRlib()
+
+	library.isFFI = true
+	return module.Init()
+}
+
+// rust_ffi_static produces a staticlib and an rlib variant
+func RustFFIStaticRlibFactory() android.Module {
+	module, library := NewRustLibrary(android.HostAndDeviceSupported)
+	library.BuildOnlyRlibStatic()
+
+	library.isFFI = true
+	return module.Init()
+}
+
+// rust_ffi_static_host produces a staticlib and an rlib variant for the host
+func RustFFIStaticRlibHostFactory() android.Module {
+	module, library := NewRustLibrary(android.HostSupported)
+	library.BuildOnlyRlibStatic()
+
+	library.isFFI = true
+	return module.Init()
+}
+
 func (library *libraryDecorator) BuildOnlyFFI() {
 	library.MutatedProperties.BuildDylib = false
-	library.MutatedProperties.BuildRlib = false
+	// we build rlibs for later static ffi linkage.
+	library.MutatedProperties.BuildRlib = true
 	library.MutatedProperties.BuildShared = true
 	library.MutatedProperties.BuildStatic = true
+
+	library.isFFI = true
 }
 
 func (library *libraryDecorator) BuildOnlyRust() {
@@ -384,11 +417,21 @@
 	library.MutatedProperties.BuildStatic = false
 }
 
+func (library *libraryDecorator) BuildOnlyRlibStatic() {
+	library.MutatedProperties.BuildDylib = false
+	library.MutatedProperties.BuildRlib = true
+	library.MutatedProperties.BuildShared = false
+	library.MutatedProperties.BuildStatic = true
+	library.isFFI = true
+}
+
 func (library *libraryDecorator) BuildOnlyStatic() {
 	library.MutatedProperties.BuildRlib = false
 	library.MutatedProperties.BuildDylib = false
 	library.MutatedProperties.BuildShared = false
 	library.MutatedProperties.BuildStatic = true
+
+	library.isFFI = true
 }
 
 func (library *libraryDecorator) BuildOnlyShared() {
@@ -396,6 +439,12 @@
 	library.MutatedProperties.BuildDylib = false
 	library.MutatedProperties.BuildStatic = false
 	library.MutatedProperties.BuildShared = true
+
+	library.isFFI = true
+}
+
+func (library *libraryDecorator) isFFILibrary() bool {
+	return library.isFFI
 }
 
 func NewRustLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
@@ -446,17 +495,35 @@
 	return library.getStem(ctx) + ctx.toolchain().SharedLibSuffix()
 }
 
+// Library cfg flags common to all variants
+func CommonLibraryCfgFlags(ctx android.ModuleContext, flags Flags) Flags {
+	return flags
+}
+
 func (library *libraryDecorator) cfgFlags(ctx ModuleContext, flags Flags) Flags {
 	flags = library.baseCompiler.cfgFlags(ctx, flags)
+	flags = CommonLibraryCfgFlags(ctx, flags)
+
+	cfgs := library.baseCompiler.Properties.Cfgs.GetOrDefault(ctx, nil)
+
 	if library.dylib() {
 		// We need to add a dependency on std in order to link crates as dylibs.
 		// The hack to add this dependency is guarded by the following cfg so
 		// that we don't force a dependency when it isn't needed.
-		library.baseCompiler.Properties.Cfgs = append(library.baseCompiler.Properties.Cfgs, "android_dylib")
+		cfgs = append(cfgs, "android_dylib")
 	}
 
-	flags.RustFlags = append(flags.RustFlags, library.baseCompiler.cfgsToFlags()...)
-	flags.RustdocFlags = append(flags.RustdocFlags, library.baseCompiler.cfgsToFlags()...)
+	cfgFlags := cfgsToFlags(cfgs)
+
+	flags.RustFlags = append(flags.RustFlags, cfgFlags...)
+	flags.RustdocFlags = append(flags.RustdocFlags, cfgFlags...)
+
+	return flags
+}
+
+// Common flags applied to all libraries irrespective of properties or variant should be included here
+func CommonLibraryCompilerFlags(ctx android.ModuleContext, flags Flags) Flags {
+	flags.RustFlags = append(flags.RustFlags, "-C metadata="+ctx.ModuleName())
 
 	return flags
 }
@@ -464,11 +531,13 @@
 func (library *libraryDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
 	flags = library.baseCompiler.compilerFlags(ctx, flags)
 
-	flags.RustFlags = append(flags.RustFlags, "-C metadata="+ctx.ModuleName())
-	if library.shared() || library.static() {
+	flags = CommonLibraryCompilerFlags(ctx, flags)
+
+	if library.isFFI {
 		library.includeDirs = append(library.includeDirs, android.PathsForModuleSrc(ctx, library.Properties.Include_dirs)...)
 		library.includeDirs = append(library.includeDirs, android.PathsForModuleSrc(ctx, library.Properties.Export_include_dirs)...)
 	}
+
 	if library.shared() {
 		if ctx.Darwin() {
 			flags.LinkFlags = append(
@@ -494,6 +563,9 @@
 		deps.srcProviderFiles = append(deps.srcProviderFiles, library.sourceProvider.Srcs()...)
 	}
 
+	// Ensure link dirs are not duplicated
+	deps.linkDirs = android.FirstUniqueStrings(deps.linkDirs)
+
 	// Calculate output filename
 	if library.rlib() {
 		fileName = library.getStem(ctx) + ctx.toolchain().RlibSuffix()
@@ -549,9 +621,10 @@
 		library.flagExporter.exportLinkObjects(deps.linkObjects...)
 	}
 
-	if library.static() || library.shared() {
+	// Since we have FFI rlibs, we need to collect their includes as well
+	if library.static() || library.shared() || library.rlib() {
 		android.SetProvider(ctx, cc.FlagExporterInfoProvider, cc.FlagExporterInfo{
-			IncludeDirs: library.includeDirs,
+			IncludeDirs: android.FirstUniquePaths(library.includeDirs),
 		})
 	}
 
@@ -666,6 +739,11 @@
 		return
 	}
 
+	// Don't produce rlib/dylib/source variants for shared or static variants
+	if library.shared() || library.static() {
+		return
+	}
+
 	var variants []string
 	// The source variant is used for SourceProvider modules. The other variants (i.e. rlib and dylib)
 	// depend on this variant. It must be the first variant to be declared.
@@ -705,6 +783,9 @@
 			// The source variant does not produce any library.
 			// Disable the compilation steps.
 			v.(*Module).compiler.SetDisabled()
+		case "":
+			// if there's an empty variant, alias it so it is the default variant
+			mctx.AliasVariation("")
 		}
 	}
 
@@ -729,20 +810,29 @@
 		case libraryInterface:
 			// Only create a variant if a library is actually being built.
 			if library.rlib() && !library.sysroot() {
-				variants := []string{"rlib-std", "dylib-std"}
-				modules := mctx.CreateLocalVariations(variants...)
+				// If this is a rust_ffi variant it only needs rlib-std
+				if library.isFFILibrary() {
+					variants := []string{"rlib-std"}
+					modules := mctx.CreateLocalVariations(variants...)
+					rlib := modules[0].(*Module)
+					rlib.compiler.(libraryInterface).setRlibStd()
+					rlib.Properties.RustSubName += RlibStdlibSuffix
+				} else {
+					variants := []string{"rlib-std", "dylib-std"}
+					modules := mctx.CreateLocalVariations(variants...)
 
-				rlib := modules[0].(*Module)
-				dylib := modules[1].(*Module)
-				rlib.compiler.(libraryInterface).setRlibStd()
-				dylib.compiler.(libraryInterface).setDylibStd()
-				if dylib.ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation {
-					// TODO(b/165791368)
-					// Disable rlibs that link against dylib-std on vendor ramdisk variations until those dylib
-					// variants are properly supported.
-					dylib.Disable()
+					rlib := modules[0].(*Module)
+					dylib := modules[1].(*Module)
+					rlib.compiler.(libraryInterface).setRlibStd()
+					dylib.compiler.(libraryInterface).setDylibStd()
+					if dylib.ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation {
+						// TODO(b/165791368)
+						// Disable rlibs that link against dylib-std on vendor ramdisk variations until those dylib
+						// variants are properly supported.
+						dylib.Disable()
+					}
+					rlib.Properties.RustSubName += RlibStdlibSuffix
 				}
-				rlib.Properties.RustSubName += RlibStdlibSuffix
 			}
 		}
 	}
diff --git a/rust/library_test.go b/rust/library_test.go
index 7275b66..1133c28 100644
--- a/rust/library_test.go
+++ b/rust/library_test.go
@@ -37,9 +37,10 @@
 		}`)
 
 	// Test all variants are being built.
+	libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static").Rule("rustc")
 	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")
 	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
-	libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static").Rule("rustc")
+	libfooFFIRlib := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")
 	libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared").Rule("rustc")
 
 	rlibCrateType := "rlib"
@@ -62,6 +63,11 @@
 		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", staticCrateType, libfooStatic.Args["rustcFlags"])
 	}
 
+	// Test crate type for FFI rlibs is correct
+	if !strings.Contains(libfooFFIRlib.Args["rustcFlags"], "crate-type="+rlibCrateType) {
+		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", rlibCrateType, libfooFFIRlib.Args["rustcFlags"])
+	}
+
 	// Test crate type for C shared libraries is correct.
 	if !strings.Contains(libfooShared.Args["rustcFlags"], "crate-type="+sharedCrateType) {
 		t.Errorf("missing crate-type for shared variant, expecting %#v, got rustcFlags: %#v", sharedCrateType, libfooShared.Args["rustcFlags"])
@@ -182,15 +188,20 @@
 
 func TestStaticLibraryLinkage(t *testing.T) {
 	ctx := testRust(t, `
-		rust_ffi_static {
+		rust_ffi {
 			name: "libfoo",
 			srcs: ["foo.rs"],
 			crate_name: "foo",
 		}`)
 
-	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static")
+	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_rlib-std")
+	libfooStatic := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static")
 
 	if !android.InList("libstd", libfoo.Module().(*Module).Properties.AndroidMkRlibs) {
+		t.Errorf("Static libstd rlib expected to be a dependency of Rust rlib libraries. Rlib deps are: %#v",
+			libfoo.Module().(*Module).Properties.AndroidMkDylibs)
+	}
+	if !android.InList("libstd", libfooStatic.Module().(*Module).Properties.AndroidMkRlibs) {
 		t.Errorf("Static libstd rlib expected to be a dependency of Rust static libraries. Rlib deps are: %#v",
 			libfoo.Module().(*Module).Properties.AndroidMkDylibs)
 	}
@@ -198,6 +209,12 @@
 
 func TestNativeDependencyOfRlib(t *testing.T) {
 	ctx := testRust(t, `
+		rust_ffi_rlib {
+			name: "libffi_rlib",
+			crate_name: "ffi_rlib",
+			rlibs: ["librust_rlib"],
+			srcs: ["foo.rs"],
+		}
 		rust_ffi_static {
 			name: "libffi_static",
 			crate_name: "ffi_static",
@@ -224,10 +241,12 @@
 	rustRlibRlibStd := ctx.ModuleForTests("librust_rlib", "android_arm64_armv8-a_rlib_rlib-std")
 	rustRlibDylibStd := ctx.ModuleForTests("librust_rlib", "android_arm64_armv8-a_rlib_dylib-std")
 	ffiStatic := ctx.ModuleForTests("libffi_static", "android_arm64_armv8-a_static")
+	ffiRlib := ctx.ModuleForTests("libffi_rlib", "android_arm64_armv8-a_rlib_rlib-std")
 
 	modules := []android.TestingModule{
 		rustRlibRlibStd,
 		rustRlibDylibStd,
+		ffiRlib,
 		ffiStatic,
 	}
 
@@ -290,27 +309,28 @@
 
 	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std")
 	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib")
+	libfooFFIRlib := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_rlib_rlib-std")
 	libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static")
 	libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared")
 
-	for _, static := range []android.TestingModule{libfooRlib, libfooStatic} {
+	for _, static := range []android.TestingModule{libfooRlib, libfooStatic, libfooFFIRlib} {
 		if !android.InList("libbar.rlib-std", static.Module().(*Module).Properties.AndroidMkRlibs) {
-			t.Errorf("libbar not present as rlib dependency in static lib")
+			t.Errorf("libbar not present as rlib dependency in static lib: %s", static.Module().Name())
 		}
 		if android.InList("libbar", static.Module().(*Module).Properties.AndroidMkDylibs) {
-			t.Errorf("libbar present as dynamic dependency in static lib")
+			t.Errorf("libbar present as dynamic dependency in static lib: %s", static.Module().Name())
 		}
 	}
 
 	for _, dyn := range []android.TestingModule{libfooDylib, libfooShared} {
 		if !android.InList("libbar", dyn.Module().(*Module).Properties.AndroidMkDylibs) {
-			t.Errorf("libbar not present as dynamic dependency in dynamic lib")
+			t.Errorf("libbar not present as dynamic dependency in dynamic lib: %s", dyn.Module().Name())
 		}
 		if android.InList("libbar", dyn.Module().(*Module).Properties.AndroidMkRlibs) {
-			t.Errorf("libbar present as rlib dependency in dynamic lib")
+			t.Errorf("libbar present as rlib dependency in dynamic lib: %s", dyn.Module().Name())
 		}
 		if !android.InList("librlib_only", dyn.Module().(*Module).Properties.AndroidMkRlibs) {
-			t.Errorf("librlib_only should be selected by rustlibs as an rlib.")
+			t.Errorf("librlib_only should be selected by rustlibs as an rlib: %s.", dyn.Module().Name())
 		}
 	}
 }
@@ -375,6 +395,7 @@
 
 	libbarShared := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module().(*Module)
 	libbarStatic := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_static").Module().(*Module)
+	libbarFFIRlib := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_rlib_rlib-std").Module().(*Module)
 
 	// prefer_rlib works the same for both rust_library and rust_ffi, so a single check is sufficient here.
 	libbarRlibStd := ctx.ModuleForTests("libbar.prefer_rlib", "android_arm64_armv8-a_shared").Module().(*Module)
@@ -398,6 +419,12 @@
 	if !android.InList("libfoo.rlib-std", libbarStatic.Properties.AndroidMkRlibs) {
 		t.Errorf("Device rust_ffi_static does not link dependent rustlib rlib-std variant")
 	}
+	if !android.InList("libstd", libbarFFIRlib.Properties.AndroidMkRlibs) {
+		t.Errorf("Device rust_ffi_rlib does not link libstd as an rlib")
+	}
+	if !android.InList("libfoo.rlib-std", libbarFFIRlib.Properties.AndroidMkRlibs) {
+		t.Errorf("Device rust_ffi_rlib does not link dependent rustlib rlib-std variant")
+	}
 	if !android.InList("libstd", libbarRlibStd.Properties.AndroidMkRlibs) {
 		t.Errorf("rust_ffi with prefer_rlib does not link libstd as an rlib")
 	}
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index b491449..1ff6637 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -76,6 +76,7 @@
 	srcPath := crateRootPath(ctx, procMacro)
 	ret := TransformSrctoProcMacro(ctx, srcPath, deps, flags, outputFile)
 	procMacro.baseCompiler.unstrippedOutputFile = outputFile
+
 	return ret
 }
 
diff --git a/rust/rust.go b/rust/rust.go
index e4bb99c..5790dd6 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -158,6 +158,8 @@
 	sourceProvider   SourceProvider
 	subAndroidMkOnce map[SubAndroidMkProvider]bool
 
+	exportedLinkDirs []string
+
 	// Output file to be installed, may be stripped or unstripped.
 	outputFile android.OptionalPath
 
@@ -231,8 +233,8 @@
 
 func (mod *Module) NonCcVariants() bool {
 	if mod.compiler != nil {
-		if _, ok := mod.compiler.(libraryInterface); ok {
-			return false
+		if library, ok := mod.compiler.(libraryInterface); ok {
+			return library.buildRlib() || library.buildDylib()
 		}
 	}
 	panic(fmt.Errorf("NonCcVariants called on non-library module: %q", mod.BaseModuleName()))
@@ -339,10 +341,6 @@
 	return Bool(mod.Properties.Bootstrap)
 }
 
-func (mod *Module) MustUseVendorVariant() bool {
-	return true
-}
-
 func (mod *Module) SubName() string {
 	return mod.Properties.SubName
 }
@@ -462,6 +460,11 @@
 	linkDirs    []string
 	linkObjects []string
 
+	// exportedLinkDirs are exported linkDirs for direct rlib dependencies to
+	// cc_library_static dependants of rlibs.
+	// Track them separately from linkDirs so superfluous -L flags don't get emitted.
+	exportedLinkDirs []string
+
 	// Used by bindgen modules which call clang
 	depClangFlags         []string
 	depIncludePaths       android.Paths
@@ -474,6 +477,9 @@
 	// Paths to generated source files
 	SrcDeps          android.Paths
 	srcProviderFiles android.Paths
+
+	// Used by Generated Libraries
+	depExportedRlibs []cc.RustRlibDep
 }
 
 type RustLibraries []RustLibrary
@@ -540,6 +546,10 @@
 	return mod.Properties.VndkVersion
 }
 
+func (mod *Module) ExportedCrateLinkDirs() []string {
+	return mod.exportedLinkDirs
+}
+
 func (mod *Module) PreventInstall() bool {
 	return mod.Properties.PreventInstall
 }
@@ -654,15 +664,6 @@
 	return nil
 }
 
-func (mod *Module) IncludeDirs() android.Paths {
-	if mod.compiler != nil {
-		if library, ok := mod.compiler.(*libraryDecorator); ok {
-			return library.includeDirs
-		}
-	}
-	panic(fmt.Errorf("IncludeDirs called on non-library module: %q", mod.BaseModuleName()))
-}
-
 func (mod *Module) SetStatic() {
 	if mod.compiler != nil {
 		if library, ok := mod.compiler.(libraryInterface); ok {
@@ -911,6 +912,10 @@
 	}
 
 	deps := mod.depsToPaths(ctx)
+	// Export linkDirs for CC rust generatedlibs
+	mod.exportedLinkDirs = append(mod.exportedLinkDirs, deps.exportedLinkDirs...)
+	mod.exportedLinkDirs = append(mod.exportedLinkDirs, deps.linkDirs...)
+
 	flags := Flags{
 		Toolchain: toolchain,
 	}
@@ -988,6 +993,9 @@
 			if ctx.Failed() {
 				return
 			}
+			// Export your own directory as a linkDir
+			mod.exportedLinkDirs = append(mod.exportedLinkDirs, linkPathFromFilePath(mod.OutputFile().Path()))
+
 		}
 
 		ctx.Phony("rust", ctx.RustModule().OutputFile().Path())
@@ -1218,7 +1226,7 @@
 			return
 		}
 
-		if rustDep, ok := dep.(*Module); ok && !rustDep.CcLibraryInterface() {
+		if rustDep, ok := dep.(*Module); ok && !rustDep.Static() && !rustDep.Shared() {
 			//Handle Rust Modules
 			makeLibName := rustMakeLibName(ctx, mod, rustDep, depName+rustDep.Properties.RustSubName)
 
@@ -1244,9 +1252,16 @@
 				mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, makeLibName)
 				mod.Properties.SnapshotRlibs = append(mod.Properties.SnapshotRlibs, cc.BaseLibName(depName))
 
+				// rust_ffi rlibs may export include dirs, so collect those here.
+				exportedInfo, _ := android.OtherModuleProvider(ctx, dep, cc.FlagExporterInfoProvider)
+				depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
+				depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(rustDep.OutputFile().Path()))
+
 			case procMacroDepTag:
 				directProcMacroDeps = append(directProcMacroDeps, rustDep)
 				mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, makeLibName)
+				// proc_macro link dirs need to be exported, so collect those here.
+				depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(rustDep.OutputFile().Path()))
 
 			case sourceDepTag:
 				if _, ok := mod.sourceProvider.(*protobufDecorator); ok {
@@ -1276,12 +1291,12 @@
 				directSrcProvidersDeps = append(directSrcProvidersDeps, rustDep)
 			}
 
+			exportedInfo, _ := android.OtherModuleProvider(ctx, dep, FlagExporterInfoProvider)
 			//Append the dependencies exportedDirs, except for proc-macros which target a different arch/OS
 			if depTag != procMacroDepTag {
-				exportedInfo, _ := android.OtherModuleProvider(ctx, dep, FlagExporterInfoProvider)
-				depPaths.linkDirs = append(depPaths.linkDirs, exportedInfo.LinkDirs...)
 				depPaths.depFlags = append(depPaths.depFlags, exportedInfo.Flags...)
 				depPaths.linkObjects = append(depPaths.linkObjects, exportedInfo.LinkObjects...)
+				depPaths.linkDirs = append(depPaths.linkDirs, exportedInfo.LinkDirs...)
 			}
 
 			if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag {
@@ -1291,6 +1306,7 @@
 					lib.exportLinkDirs(linkDir)
 				}
 			}
+
 			if depTag == sourceDepTag {
 				if _, ok := mod.sourceProvider.(*protobufDecorator); ok && mod.Source() {
 					if _, ok := rustDep.sourceProvider.(*protobufDecorator); ok {
@@ -1555,6 +1571,7 @@
 	}
 
 	rlibDepVariations := commonDepVariations
+	rlibDepVariations = append(rlibDepVariations, blueprint.Variation{Mutator: "link", Variation: ""})
 
 	if lib, ok := mod.compiler.(libraryInterface); !ok || !lib.sysroot() {
 		rlibDepVariations = append(rlibDepVariations,
@@ -1570,6 +1587,8 @@
 
 	// dylibs
 	dylibDepVariations := append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: dylibVariation})
+	dylibDepVariations = append(dylibDepVariations, blueprint.Variation{Mutator: "link", Variation: ""})
+
 	for _, lib := range deps.Dylibs {
 		actx.AddVariationDependencies(dylibDepVariations, dylibDepTag, lib)
 	}
@@ -1589,7 +1608,7 @@
 					// otherwise select the rlib variant.
 					autoDepVariations := append(commonDepVariations,
 						blueprint.Variation{Mutator: "rust_libraries", Variation: autoDep.variation})
-
+					autoDepVariations = append(autoDepVariations, blueprint.Variation{Mutator: "link", Variation: ""})
 					if actx.OtherModuleDependencyVariantExists(autoDepVariations, lib) {
 						actx.AddVariationDependencies(autoDepVariations, autoDep.depTag, lib)
 
@@ -1604,7 +1623,11 @@
 			for _, lib := range deps.Rustlibs {
 				srcProviderVariations := append(commonDepVariations,
 					blueprint.Variation{Mutator: "rust_libraries", Variation: "source"})
+				srcProviderVariations = append(srcProviderVariations, blueprint.Variation{Mutator: "link", Variation: ""})
 
+				// Only add rustlib dependencies if they're source providers themselves.
+				// This is used to track which crate names need to be added to the source generated
+				// in the rust_protobuf mod.rs.
 				if actx.OtherModuleDependencyVariantExists(srcProviderVariations, lib) {
 					actx.AddVariationDependencies(srcProviderVariations, sourceDepTag, lib)
 				}
@@ -1616,7 +1639,7 @@
 	if deps.Stdlibs != nil {
 		if mod.compiler.stdLinkage(ctx) == RlibLinkage {
 			for _, lib := range deps.Stdlibs {
-				actx.AddVariationDependencies(append(commonDepVariations, []blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}...),
+				actx.AddVariationDependencies(append(commonDepVariations, []blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}, {Mutator: "link", Variation: ""}}...),
 					rlibDepTag, lib)
 			}
 		} else {
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 6d083f6..8b96df8 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -150,15 +150,11 @@
 			host_supported: true,
 			name: "cc_stubs_dep",
 		}
-		rust_ffi_host_static {
+		cc_library_host_static {
 			name: "libstatic",
-			srcs: ["foo.rs"],
-			crate_name: "static",
 		}
-		rust_ffi_host_static {
+		cc_library_host_static {
 			name: "libwholestatic",
-			srcs: ["foo.rs"],
-			crate_name: "wholestatic",
 		}
 		rust_ffi_host_shared {
 			name: "libshared",
@@ -435,6 +431,105 @@
 	}
 }
 
+func TestRustRlibs(t *testing.T) {
+	ctx := testRust(t, `
+		rust_ffi_rlib {
+			name: "libbar",
+			crate_name: "bar",
+			srcs: ["src/lib.rs"],
+			export_include_dirs: ["bar_includes"]
+		}
+
+		rust_ffi_rlib {
+			name: "libfoo",
+			crate_name: "foo",
+			srcs: ["src/lib.rs"],
+			export_include_dirs: ["foo_includes"]
+		}
+
+		cc_library_shared {
+			name: "libcc_shared",
+			srcs:["foo.c"],
+			static_rlibs: ["libbar"],
+		}
+
+		cc_library_static {
+			name: "libcc_static",
+			srcs:["foo.c"],
+			static_rlibs: ["libfoo"],
+		}
+
+		cc_binary {
+			name: "ccBin",
+			srcs:["foo.c"],
+			static_rlibs: ["libbar"],
+			static_libs: ["libcc_static"],
+		}
+		`)
+
+	libbar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_rlib_rlib-std").Rule("rustc")
+	libcc_shared_rustc := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("rustc")
+	libcc_shared_ld := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("ld")
+	libcc_shared_cc := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("cc")
+	ccbin_rustc := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("rustc")
+	ccbin_ld := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("ld")
+	ccbin_cc := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("cc")
+
+	if !strings.Contains(libbar.Args["rustcFlags"], "crate-type=rlib") {
+		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", "rlib", libbar.Args["rustcFlags"])
+	}
+
+	// Make sure there's a rustc command, and it's producing a staticlib
+	if !strings.Contains(libcc_shared_rustc.Args["rustcFlags"], "crate-type=staticlib") {
+		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v",
+			"staticlib", libcc_shared_rustc.Args["rustcFlags"])
+	}
+
+	// Make sure the static lib is included in the ld command
+	if !strings.Contains(libcc_shared_ld.Args["libFlags"], "generated_rust_staticlib/liblibcc_shared_rust_staticlib.a") {
+		t.Errorf("missing generated static library in linker step libFlags %#v, libFlags: %#v",
+			"libcc_shared.generated_rust_staticlib.a", libcc_shared_ld.Args["libFlags"])
+	}
+
+	// Make sure the static lib includes are in the cc command
+	if !strings.Contains(libcc_shared_cc.Args["cFlags"], "-Ibar_includes") {
+		t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v",
+			"-Ibar_includes", libcc_shared_cc.Args["cFlags"])
+	}
+
+	// Make sure there's a rustc command, and it's producing a staticlib
+	if !strings.Contains(ccbin_rustc.Args["rustcFlags"], "crate-type=staticlib") {
+		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", "staticlib", ccbin_rustc.Args["rustcFlags"])
+	}
+
+	// Make sure the static lib is included in the cc command
+	if !strings.Contains(ccbin_ld.Args["libFlags"], "generated_rust_staticlib/libccBin_rust_staticlib.a") {
+		t.Errorf("missing generated static library in linker step libFlags, expecting %#v, libFlags: %#v",
+			"ccBin.generated_rust_staticlib.a", ccbin_ld.Args["libFlags"])
+	}
+
+	// Make sure the static lib includes are in the ld command
+	if !strings.Contains(ccbin_cc.Args["cFlags"], "-Ibar_includes") {
+		t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v",
+			"-Ibar_includes", ccbin_cc.Args)
+	}
+
+	// Make sure that direct dependencies and indirect dependencies are
+	// propagating correctly to the generated rlib.
+	if !strings.Contains(ccbin_rustc.Args["libFlags"], "--extern foo=") {
+		t.Errorf("Missing indirect dependency libfoo when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"])
+	}
+	if !strings.Contains(ccbin_rustc.Args["libFlags"], "--extern bar=") {
+		t.Errorf("Missing direct dependency libbar when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"])
+	}
+
+	// Test indirect includes propagation
+	if !strings.Contains(ccbin_cc.Args["cFlags"], "-Ifoo_includes") {
+		t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v",
+			"-Ifoo_includes", ccbin_cc.Args)
+	}
+}
+
 func assertString(t *testing.T, got, expected string) {
 	t.Helper()
 	if got != expected {
diff --git a/rust/testing.go b/rust/testing.go
index 5837dcc..f31c591 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -49,16 +49,28 @@
 func GatherRequiredDepsForTest() string {
 	bp := `
 		rust_prebuilt_library {
-				name: "libstd",
-				crate_name: "std",
-				rlib: {
-					srcs: ["libstd.rlib"],
-				},
-				dylib: {
-					srcs: ["libstd.so"],
-				},
-				host_supported: true,
-				sysroot: true,
+			name: "libstd",
+			crate_name: "std",
+			rlib: {
+				srcs: ["libstd/libstd.rlib"],
+			},
+			dylib: {
+				srcs: ["libstd/libstd.so"],
+			},
+			host_supported: true,
+			sysroot: true,
+		}
+		rust_prebuilt_library {
+			name: "libcore.sysroot",
+			crate_name: "core",
+			rlib: {
+				srcs: ["libcore/libcore.rlib"],
+			},
+			dylib: {
+				srcs: ["libcore/libcore.so"],
+			},
+			host_supported: true,
+			sysroot: true,
 		}
 		//////////////////////////////
 		// Device module requirements
@@ -176,10 +188,12 @@
 	ctx.RegisterModuleType("rust_fuzz_host", RustFuzzHostFactory)
 	ctx.RegisterModuleType("rust_ffi", RustFFIFactory)
 	ctx.RegisterModuleType("rust_ffi_shared", RustFFISharedFactory)
-	ctx.RegisterModuleType("rust_ffi_static", RustFFIStaticFactory)
+	ctx.RegisterModuleType("rust_ffi_rlib", RustFFIRlibFactory)
+	ctx.RegisterModuleType("rust_ffi_static", RustFFIStaticRlibFactory)
 	ctx.RegisterModuleType("rust_ffi_host", RustFFIHostFactory)
 	ctx.RegisterModuleType("rust_ffi_host_shared", RustFFISharedHostFactory)
-	ctx.RegisterModuleType("rust_ffi_host_static", RustFFIStaticHostFactory)
+	ctx.RegisterModuleType("rust_ffi_host_rlib", RustFFIRlibHostFactory)
+	ctx.RegisterModuleType("rust_ffi_host_static", RustFFIStaticRlibHostFactory)
 	ctx.RegisterModuleType("rust_proc_macro", ProcMacroFactory)
 	ctx.RegisterModuleType("rust_protobuf", RustProtobufFactory)
 	ctx.RegisterModuleType("rust_protobuf_host", RustProtobufHostFactory)
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 6e25122..7048a15 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -281,6 +281,19 @@
 				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
 			}
 		}),
+		// Make sure that we have atleast one platform library so that we can check the monolithic hiddenapi
+		// file creation.
+		java.FixtureConfigureBootJars("platform:foo"),
+		android.FixtureModifyMockFS(func(fs android.MockFS) {
+			fs["platform/Android.bp"] = []byte(`
+		java_library {
+			name: "foo",
+			srcs: ["Test.java"],
+			compile_dex: true,
+		}
+		`)
+			fs["platform/Test.java"] = nil
+		}),
 
 		android.FixtureWithRootAndroidBp(sdk+`
 			apex {
diff --git a/tradefed/providers.go b/tradefed/providers.go
index 66cb625..0abac12 100644
--- a/tradefed/providers.go
+++ b/tradefed/providers.go
@@ -6,7 +6,8 @@
 	"github.com/google/blueprint"
 )
 
-// Output files we need from a base test that we derive from.
+// Data that test_module_config[_host] modules types will need from
+// their dependencies to write out build rules and AndroidMkEntries.
 type BaseTestProviderData struct {
 	// data files and apps for android_test
 	InstalledFiles android.Paths
@@ -19,8 +20,14 @@
 	RequiredModuleNames     []string
 	// List of test suites base uses.
 	TestSuites []string
-	// Used for bases that are Host
+	// True indicates the base modules is built for Host.
 	IsHost bool
+	// Base's sdk version for AndroidMkEntries, generally only used for Host modules.
+	LocalSdkVersion string
+	// Base's certificate for AndroidMkEntries, generally only used for device modules.
+	LocalCertificate string
+	// Indicates if the base module was a unit test.
+	IsUnitTest bool
 }
 
 var BaseTestProviderKey = blueprint.NewProvider[BaseTestProviderData]()
diff --git a/tradefed_modules/test_module_config.go b/tradefed_modules/test_module_config.go
index b2d5631..f9622d3 100644
--- a/tradefed_modules/test_module_config.go
+++ b/tradefed_modules/test_module_config.go
@@ -5,6 +5,8 @@
 	"android/soong/tradefed"
 	"encoding/json"
 	"fmt"
+	"io"
+	"strings"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -23,14 +25,17 @@
 type testModuleConfigModule struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
-	base android.Module
 
 	tradefedProperties
 
 	// Our updated testConfig.
 	testConfig android.OutputPath
-	manifest   android.InstallPath
+	manifest   android.OutputPath
 	provider   tradefed.BaseTestProviderData
+
+	supportFiles android.InstallPaths
+
+	isHost bool
 }
 
 // Host is mostly the same as non-host, just some diffs for AddDependency and
@@ -94,7 +99,6 @@
 
 // Takes base's Tradefed Config xml file and generates a new one with the test properties
 // appeneded from this module.
-// Rewrite the name of the apk in "test-file-name" to be our module's name, rather than the original one.
 func (m *testModuleConfigModule) fixTestConfig(ctx android.ModuleContext, baseTestConfig android.Path) android.OutputPath {
 	// Test safe to do when no test_runner_options, but check for that earlier?
 	fixedConfig := android.PathForModuleOut(ctx, "test_config_fixer", ctx.ModuleName()+".config")
@@ -106,9 +110,8 @@
 	}
 	xmlTestModuleConfigSnippet, _ := json.Marshal(options)
 	escaped := proptools.NinjaAndShellEscape(string(xmlTestModuleConfigSnippet))
-	command.FlagWithArg("--test-file-name=", ctx.ModuleName()+".apk").
-		FlagWithArg("--orig-test-file-name=", *m.tradefedProperties.Base+".apk").
-		FlagWithArg("--test-runner-options=", escaped)
+	command.FlagWithArg("--test-runner-options=", escaped)
+
 	rule.Build("fix_test_config", "fix test config")
 	return fixedConfig.OutputPath
 }
@@ -190,6 +193,7 @@
 	module.AddProperties(&module.tradefedProperties)
 	android.InitAndroidMultiTargetsArchModule(module, android.HostSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
+	module.isHost = true
 
 	return module
 }
@@ -198,39 +202,66 @@
 var _ android.AndroidMkEntriesProvider = (*testModuleConfigModule)(nil)
 
 func (m *testModuleConfigModule) AndroidMkEntries() []android.AndroidMkEntries {
-	// We rely on base writing LOCAL_COMPATIBILITY_SUPPORT_FILES for its data files
-	entriesList := m.base.(android.AndroidMkEntriesProvider).AndroidMkEntries()
-	entries := &entriesList[0]
-	entries.OutputFile = android.OptionalPathForPath(m.provider.OutputFile)
-	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-		entries.SetString("LOCAL_MODULE", m.Name()) //  out module name, not base's
+	appClass := "APPS"
+	include := "$(BUILD_SYSTEM)/soong_app_prebuilt.mk"
+	if m.isHost {
+		appClass = "JAVA_LIBRARIES"
+		include = "$(BUILD_SYSTEM)/soong_java_prebuilt.mk"
+	}
+	return []android.AndroidMkEntries{{
+		Class:      appClass,
+		OutputFile: android.OptionalPathForPath(m.manifest),
+		Include:    include,
+		Required:   []string{*m.Base},
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetPath("LOCAL_FULL_TEST_CONFIG", m.testConfig)
+				entries.SetString("LOCAL_MODULE_TAGS", "tests")
+				entries.SetString("LOCAL_TEST_MODULE_CONFIG_BASE", *m.Base)
+				if m.provider.LocalSdkVersion != "" {
+					entries.SetString("LOCAL_SDK_VERSION", m.provider.LocalSdkVersion)
+				}
+				if m.provider.LocalCertificate != "" {
+					entries.SetString("LOCAL_CERTIFICATE", m.provider.LocalCertificate)
+				}
 
-		// Out update config file with extra options.
-		entries.SetPath("LOCAL_FULL_TEST_CONFIG", m.testConfig)
-		entries.SetString("LOCAL_MODULE_TAGS", "tests")
+				entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", m.provider.IsUnitTest)
+				entries.AddCompatibilityTestSuites(m.tradefedProperties.Test_suites...)
 
-		// Don't append to base's test-suites, only use the ones we define, so clear it before
-		// appending to it.
-		entries.SetString("LOCAL_COMPATIBILITY_SUITE", "")
-		entries.AddCompatibilityTestSuites(m.tradefedProperties.Test_suites...)
+				// The app_prebuilt_internal.mk files try create a copy of the OutputFile as an .apk.
+				// Normally, this copies the "package.apk" from the intermediate directory here.
+				// To prevent the copy of the large apk and to prevent confusion with the real .apk we
+				// link to, we set the STEM here to a bogus name and we set OutputFile to a small file (our manifest).
+				// We do this so we don't have to add more conditionals to base_rules.mk
+				// soong_java_prebult has the same issue for .jars so use this in both module types.
+				entries.SetString("LOCAL_MODULE_STEM", fmt.Sprintf("UNUSED-%s", *m.Base))
 
-		if len(m.provider.HostRequiredModuleNames) > 0 {
-			entries.AddStrings("LOCAL_HOST_REQUIRED_MODULES", m.provider.HostRequiredModuleNames...)
-		}
-		if len(m.provider.RequiredModuleNames) > 0 {
-			entries.AddStrings("LOCAL_REQUIRED_MODULES", m.provider.RequiredModuleNames...)
-		}
-
-		if m.provider.IsHost == false {
-			// Not needed for jar_host_test
-			//
-			// Clear the JNI symbols because they belong to base not us. Either transform the names in the string
-			// or clear the variable because we don't need it, we are copying bases libraries not generating
-			// new ones.
-			entries.SetString("LOCAL_SOONG_JNI_LIBS_SYMBOLS", "")
-		}
-	})
-	return entriesList
+				// In normal java/app modules, the module writes LOCAL_COMPATIBILITY_SUPPORT_FILES
+				// and then base_rules.mk ends up copying each of those dependencies from .intermediates to the install directory.
+				// tasks/general-tests.mk, tasks/devices-tests.mk also use these to figure out
+				// which testcase files to put in a zip for running tests on another machine.
+				//
+				// We need our files to end up in the zip, but we don't want \.mk files to
+				// `install` files for us.
+				// So we create a new make variable to indicate these should be in the zip
+				// but not installed.
+				entries.AddStrings("LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES", m.supportFiles.Strings()...)
+			},
+		},
+		// Ensure each of our supportFiles depends on the installed file in base so that our symlinks will always
+		// resolve.  The provider gives us the .intermediate path for the support file in base, we change it to
+		// the installed path with a string substitution.
+		ExtraFooters: []android.AndroidMkExtraFootersFunc{
+			func(w io.Writer, name, prefix, moduleDir string) {
+				for _, f := range m.supportFiles.Strings() {
+					// convert out/.../testcases/FrameworksServicesTests_contentprotection/file1.apk
+					// to      out/.../testcases/FrameworksServicesTests/file1.apk
+					basePath := strings.Replace(f, "/"+m.Name()+"/", "/"+*m.Base+"/", 1)
+					fmt.Fprintf(w, "%s: %s\n", f, basePath)
+				}
+			},
+		},
+	}}
 }
 
 func (m *testModuleConfigHostModule) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -248,7 +279,7 @@
 //   - written via soong_java_prebuilt.mk
 //
 // 4) out/host/linux-x86/testcases/derived-module/* # data dependencies from base.
-//   - written via soong_java_prebuilt.mk
+//   - written via our InstallSymlink
 func (m *testModuleConfigHostModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	m.validateBase(ctx, &testModuleConfigHostTag, "java_test_host", true)
 	m.generateManifestAndConfig(ctx)
@@ -260,7 +291,6 @@
 	ctx.VisitDirectDepsWithTag(*depTag, func(dep android.Module) {
 		if provider, ok := android.OtherModuleProvider(ctx, dep, tradefed.BaseTestProviderKey); ok {
 			if baseShouldBeHost == provider.IsHost {
-				m.base = dep
 				m.provider = provider
 			} else {
 				if baseShouldBeHost {
@@ -277,7 +307,9 @@
 
 // Actions to write:
 //  1. manifest file to testcases dir
-//  2. New Module.config / AndroidTest.xml file with our options.
+//  2. Symlink to base.apk under base's arch dir
+//  3. Symlink to all data dependencies
+//  4. New Module.config / AndroidTest.xml file with our options.
 func (m *testModuleConfigModule) generateManifestAndConfig(ctx android.ModuleContext) {
 	// Keep before early returns.
 	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
@@ -292,7 +324,6 @@
 	if m.provider.TestConfig == nil {
 		return
 	}
-
 	// 1) A manifest file listing the base, write text to a tiny file.
 	installDir := android.PathForModuleInstall(ctx, ctx.ModuleName())
 	manifest := android.PathForModuleOut(ctx, "test_module_config.manifest")
@@ -301,9 +332,53 @@
 	//    Assume the primary install file is last
 	// so we need to Install our file last.
 	ctx.InstallFile(installDir, manifest.Base(), manifest)
+	m.manifest = manifest.OutputPath
 
-	// 2) Module.config / AndroidTest.xml
+	// 2) Symlink to base.apk
+	baseApk := m.provider.OutputFile
+
+	// Typically looks like this for baseApk
+	// FrameworksServicesTests
+	// └── x86_64
+	//    └── FrameworksServicesTests.apk
+	symlinkName := fmt.Sprintf("%s/%s", ctx.DeviceConfig().DeviceArch(), baseApk.Base())
+	// Only android_test, not java_host_test puts the output in the DeviceArch dir.
+	if m.provider.IsHost || ctx.DeviceConfig().DeviceArch() == "" {
+		// testcases/CtsDevicePolicyManagerTestCases
+		// ├── CtsDevicePolicyManagerTestCases.jar
+		symlinkName = baseApk.Base()
+	}
+	target := installedBaseRelativeToHere(symlinkName, *m.tradefedProperties.Base)
+	installedApk := ctx.InstallAbsoluteSymlink(installDir, symlinkName, target)
+	m.supportFiles = append(m.supportFiles, installedApk)
+
+	// 3) Symlink for all data deps
+	// And like this for data files and required modules
+	// FrameworksServicesTests
+	// ├── data
+	// │   └── broken_shortcut.xml
+	// ├── JobTestApp.apk
+	for _, f := range m.provider.InstalledFiles {
+		symlinkName := f.Rel()
+		target := installedBaseRelativeToHere(symlinkName, *m.tradefedProperties.Base)
+		installedPath := ctx.InstallAbsoluteSymlink(installDir, symlinkName, target)
+		m.supportFiles = append(m.supportFiles, installedPath)
+	}
+
+	// 4) Module.config / AndroidTest.xml
 	m.testConfig = m.fixTestConfig(ctx, m.provider.TestConfig)
 }
 
 var _ android.AndroidMkEntriesProvider = (*testModuleConfigHostModule)(nil)
+
+// Given a relative path to a file in the current directory or a subdirectory,
+// return a relative path under our sibling directory named `base`.
+// There should be one "../" for each subdir we descend plus one to backup to "base".
+//
+//	 ThisDir/file1
+//	 ThisDir/subdir/file2
+//	would return "../base/file1" or "../../subdir/file2"
+func installedBaseRelativeToHere(targetFileName string, base string) string {
+	backup := strings.Repeat("../", strings.Count(targetFileName, "/")+1)
+	return fmt.Sprintf("%s%s/%s", backup, base, targetFileName)
+}
diff --git a/tradefed_modules/test_module_config_test.go b/tradefed_modules/test_module_config_test.go
index b2049b1..97179f5 100644
--- a/tradefed_modules/test_module_config_test.go
+++ b/tradefed_modules/test_module_config_test.go
@@ -16,6 +16,7 @@
 import (
 	"android/soong/android"
 	"android/soong/java"
+	"fmt"
 	"strconv"
 	"strings"
 	"testing"
@@ -69,15 +70,36 @@
 	entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, derived.Module())[0]
 
 	// Ensure some entries from base are there, specifically support files for data and helper apps.
-	assertEntryPairValues(t, entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{"HelperApp.apk", "data/testfile"})
+	// Do not use LOCAL_COMPATIBILITY_SUPPORT_FILES, but instead use LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES
+	android.AssertStringPathsRelativeToTopEquals(t, "support-files", ctx.Config,
+		[]string{"out/soong/target/product/test_device/testcases/derived_test/arm64/base.apk",
+			"out/soong/target/product/test_device/testcases/derived_test/HelperApp.apk",
+			"out/soong/target/product/test_device/testcases/derived_test/data/testfile"},
+		entries.EntryMap["LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES"])
+	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{})
+
+	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_REQUIRED_MODULES"], []string{"base"})
+	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_CERTIFICATE"], []string{"build/make/target/product/security/testkey.x509.pem"})
+	android.AssertStringEquals(t, "", entries.Class, "APPS")
 
 	// And some new derived entries are there.
 	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_MODULE_TAGS"], []string{"tests"})
 
-	// And ones we override
-	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_SOONG_JNI_LIBS_SYMBOLS"], []string{""})
-
 	android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0], "derived_test/android_common/test_config_fixer/derived_test.config")
+
+	// Check the footer lines.  Our support files should depend on base's support files.
+	convertedActual := make([]string, 5)
+	for i, e := range entries.FooterLinesForTests() {
+		// AssertStringPathsRelativeToTop doesn't replace both instances
+		convertedActual[i] = strings.Replace(e, ctx.Config.SoongOutDir(), "", 2)
+	}
+	android.AssertArrayString(t, fmt.Sprintf("%s", ctx.Config.SoongOutDir()), convertedActual, []string{
+		"include $(BUILD_SYSTEM)/soong_app_prebuilt.mk",
+		"/target/product/test_device/testcases/derived_test/arm64/base.apk: /target/product/test_device/testcases/base/arm64/base.apk",
+		"/target/product/test_device/testcases/derived_test/HelperApp.apk: /target/product/test_device/testcases/base/HelperApp.apk",
+		"/target/product/test_device/testcases/derived_test/data/testfile: /target/product/test_device/testcases/base/data/testfile",
+		"",
+	})
 }
 
 // Make sure we call test-config-fixer with the right args.
@@ -92,7 +114,7 @@
 	derived := ctx.ModuleForTests("derived_test", "android_common")
 	rule_cmd := derived.Rule("fix_test_config").RuleParams.Command
 	android.AssertStringDoesContain(t, "Bad FixConfig rule inputs", rule_cmd,
-		`--test-file-name=derived_test.apk --orig-test-file-name=base.apk --test-runner-options='[{"Name":"exclude-filter","Key":"","Value":"android.test.example.devcodelab.DevCodelabTest#testHelloFail"},{"Name":"include-annotation","Key":"","Value":"android.platform.test.annotations.LargeTest"}]'`)
+		`--test-runner-options='[{"Name":"exclude-filter","Key":"","Value":"android.test.example.devcodelab.DevCodelabTest#testHelloFail"},{"Name":"include-annotation","Key":"","Value":"android.platform.test.annotations.LargeTest"}]'`)
 }
 
 // Ensure we error for a base we don't support.
@@ -195,8 +217,14 @@
 			name: "base",
 			sdk_version: "current",
                         srcs: ["a.java"],
+                        data: [":HelperApp", "data/testfile"],
 		}
 
+                android_test_helper_app {
+                        name: "HelperApp",
+                        srcs: ["helper.java"],
+                }
+
                 test_module_config {
                         name: "derived_test",
                         base: "base",
@@ -220,8 +248,12 @@
 		derived := ctx.ModuleForTests("derived_test", "android_common")
 		entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, derived.Module())[0]
 		// All these should be the same in both derived tests
-		assertEntryPairValues(t, entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{"HelperApp.apk", "data/testfile"})
-		android.AssertArrayString(t, "", entries.EntryMap["LOCAL_SOONG_JNI_LIBS_SYMBOLS"], []string{""})
+		android.AssertStringPathsRelativeToTopEquals(t, "support-files", ctx.Config,
+			[]string{"out/soong/target/product/test_device/testcases/derived_test/arm64/base.apk",
+				"out/soong/target/product/test_device/testcases/derived_test/HelperApp.apk",
+				"out/soong/target/product/test_device/testcases/derived_test/data/testfile"},
+			entries.EntryMap["LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES"])
+
 		// Except this one, which points to the updated tradefed xml file.
 		android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0], "derived_test/android_common/test_config_fixer/derived_test.config")
 		// And this one, the module name.
@@ -232,8 +264,11 @@
 		derived := ctx.ModuleForTests("another_derived_test", "android_common")
 		entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, derived.Module())[0]
 		// All these should be the same in both derived tests
-		assertEntryPairValues(t, entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{"HelperApp.apk", "data/testfile"})
-		android.AssertArrayString(t, "", entries.EntryMap["LOCAL_SOONG_JNI_LIBS_SYMBOLS"], []string{""})
+		android.AssertStringPathsRelativeToTopEquals(t, "support-files", ctx.Config,
+			[]string{"out/soong/target/product/test_device/testcases/another_derived_test/arm64/base.apk",
+				"out/soong/target/product/test_device/testcases/another_derived_test/HelperApp.apk",
+				"out/soong/target/product/test_device/testcases/another_derived_test/data/testfile"},
+			entries.EntryMap["LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES"])
 		// Except this one, which points to the updated tradefed xml file.
 		android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0], "another_derived_test/android_common/test_config_fixer/another_derived_test.config")
 		// And this one, the module name.
@@ -269,6 +304,8 @@
 	allEntries := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)
 	entries := allEntries[0]
 	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_MODULE"], []string{"derived_test"})
+	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_SDK_VERSION"], []string{"private_current"})
+	android.AssertStringEquals(t, "", entries.Class, "JAVA_LIBRARIES")
 
 	if !mod.Host() {
 		t.Errorf("host bit is not set for a java_test_host module.")
@@ -385,16 +422,3 @@
 		t.Errorf("test-only: Expected but not found: %v, Found but not expected: %v", left, right)
 	}
 }
-
-// Use for situations where the entries map contains pairs:  [srcPath:installedPath1, srcPath2:installedPath2]
-// and we want to compare the RHS of the pairs, i.e. installedPath1, installedPath2
-func assertEntryPairValues(t *testing.T, actual []string, expected []string) {
-	for i, e := range actual {
-		parts := strings.Split(e, ":")
-		if len(parts) != 2 {
-			t.Errorf("Expected entry to have a value delimited by :, received: %s", e)
-			return
-		}
-		android.AssertStringEquals(t, "", parts[1], expected[i])
-	}
-}
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 79584c6..9955b1f 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -15,7 +15,6 @@
 package build
 
 import (
-	"android/soong/ui/tracer"
 	"fmt"
 	"io/fs"
 	"os"
@@ -26,6 +25,8 @@
 	"sync/atomic"
 	"time"
 
+	"android/soong/ui/tracer"
+
 	"android/soong/bazel"
 	"android/soong/ui/metrics"
 	"android/soong/ui/metrics/metrics_proto"
@@ -270,7 +271,13 @@
 	} else if !exists {
 		// The tree is out of date for the current epoch, delete files used by bootstrap
 		// and force the primary builder to rerun.
-		os.Remove(config.SoongNinjaFile())
+		soongNinjaFile := config.SoongNinjaFile()
+		os.Remove(soongNinjaFile)
+		for _, file := range blueprint.GetNinjaShardFiles(soongNinjaFile) {
+			if ok, _ := fileExists(file); ok {
+				os.Remove(file)
+			}
+		}
 		for _, globFile := range bootstrapGlobFileList(config) {
 			os.Remove(globFile)
 		}
@@ -680,7 +687,13 @@
 
 	loadSoongBuildMetrics(ctx, config, beforeSoongTimestamp)
 
-	distGzipFile(ctx, config, config.SoongNinjaFile(), "soong")
+	soongNinjaFile := config.SoongNinjaFile()
+	distGzipFile(ctx, config, soongNinjaFile, "soong")
+	for _, file := range blueprint.GetNinjaShardFiles(soongNinjaFile) {
+		if ok, _ := fileExists(file); ok {
+			distGzipFile(ctx, config, file, "soong")
+		}
+	}
 	distFile(ctx, config, config.SoongVarsFile(), "soong")
 
 	if !config.SkipKati() {